blob: 0167f9886488ddf7c19089a1cca5e68ae8f9560d [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2015 Google Inc. All rights reserved.
3// http://ceres-solver.org/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are met:
7//
8// * Redistributions of source code must retain the above copyright notice,
9// this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above copyright notice,
11// this list of conditions and the following disclaimer in the documentation
12// and/or other materials provided with the distribution.
13// * Neither the name of Google Inc. nor the names of its contributors may be
14// used to endorse or promote products derived from this software without
15// specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27// POSSIBILITY OF SUCH DAMAGE.
28//
29// Author: strandmark@google.com (Petter Strandmark)
30
31// This include must come before any #ifndef check on Ceres compile options.
32#include "ceres/internal/port.h"
33
34#ifndef CERES_NO_CXSPARSE
35
Austin Schuh70cc9552019-01-21 19:46:48 -080036#include <string>
37#include <vector>
38
39#include "ceres/compressed_col_sparse_matrix_utils.h"
40#include "ceres/compressed_row_sparse_matrix.h"
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080041#include "ceres/cxsparse.h"
Austin Schuh70cc9552019-01-21 19:46:48 -080042#include "ceres/triplet_sparse_matrix.h"
43#include "glog/logging.h"
44
45namespace ceres {
46namespace internal {
47
48using std::vector;
49
50CXSparse::CXSparse() : scratch_(NULL), scratch_size_(0) {}
51
52CXSparse::~CXSparse() {
53 if (scratch_size_ > 0) {
54 cs_di_free(scratch_);
55 }
56}
57
58csn* CXSparse::Cholesky(cs_di* A, cs_dis* symbolic_factor) {
59 return cs_di_chol(A, symbolic_factor);
60}
61
62void CXSparse::Solve(cs_dis* symbolic_factor, csn* numeric_factor, double* b) {
63 // Make sure we have enough scratch space available.
64 const int num_cols = numeric_factor->L->n;
65 if (scratch_size_ < num_cols) {
66 if (scratch_size_ > 0) {
67 cs_di_free(scratch_);
68 }
69 scratch_ =
70 reinterpret_cast<CS_ENTRY*>(cs_di_malloc(num_cols, sizeof(CS_ENTRY)));
71 scratch_size_ = num_cols;
72 }
73
74 // When the Cholesky factor succeeded, these methods are
75 // guaranteed to succeeded as well. In the comments below, "x"
76 // refers to the scratch space.
77 //
78 // Set x = P * b.
79 CHECK(cs_di_ipvec(symbolic_factor->pinv, b, scratch_, num_cols));
80 // Set x = L \ x.
81 CHECK(cs_di_lsolve(numeric_factor->L, scratch_));
82 // Set x = L' \ x.
83 CHECK(cs_di_ltsolve(numeric_factor->L, scratch_));
84 // Set b = P' * x.
85 CHECK(cs_di_pvec(symbolic_factor->pinv, scratch_, b, num_cols));
86}
87
88bool CXSparse::SolveCholesky(cs_di* lhs, double* rhs_and_solution) {
89 return cs_cholsol(1, lhs, rhs_and_solution);
90}
91
92cs_dis* CXSparse::AnalyzeCholesky(cs_di* A) {
93 // order = 1 for Cholesky factor.
94 return cs_schol(1, A);
95}
96
97cs_dis* CXSparse::AnalyzeCholeskyWithNaturalOrdering(cs_di* A) {
98 // order = 0 for Natural ordering.
99 return cs_schol(0, A);
100}
101
102cs_dis* CXSparse::BlockAnalyzeCholesky(cs_di* A,
103 const vector<int>& row_blocks,
104 const vector<int>& col_blocks) {
105 const int num_row_blocks = row_blocks.size();
106 const int num_col_blocks = col_blocks.size();
107
108 vector<int> block_rows;
109 vector<int> block_cols;
110 CompressedColumnScalarMatrixToBlockMatrix(
111 A->i, A->p, row_blocks, col_blocks, &block_rows, &block_cols);
112 cs_di block_matrix;
113 block_matrix.m = num_row_blocks;
114 block_matrix.n = num_col_blocks;
115 block_matrix.nz = -1;
116 block_matrix.nzmax = block_rows.size();
117 block_matrix.p = &block_cols[0];
118 block_matrix.i = &block_rows[0];
119 block_matrix.x = NULL;
120
121 int* ordering = cs_amd(1, &block_matrix);
122 vector<int> block_ordering(num_row_blocks, -1);
123 std::copy(ordering, ordering + num_row_blocks, &block_ordering[0]);
124 cs_free(ordering);
125
126 vector<int> scalar_ordering;
127 BlockOrderingToScalarOrdering(row_blocks, block_ordering, &scalar_ordering);
128
129 cs_dis* symbolic_factor =
130 reinterpret_cast<cs_dis*>(cs_calloc(1, sizeof(cs_dis)));
131 symbolic_factor->pinv = cs_pinv(&scalar_ordering[0], A->n);
132 cs* permuted_A = cs_symperm(A, symbolic_factor->pinv, 0);
133
134 symbolic_factor->parent = cs_etree(permuted_A, 0);
135 int* postordering = cs_post(symbolic_factor->parent, A->n);
136 int* column_counts =
137 cs_counts(permuted_A, symbolic_factor->parent, postordering, 0);
138 cs_free(postordering);
139 cs_spfree(permuted_A);
140
141 symbolic_factor->cp = (int*)cs_malloc(A->n + 1, sizeof(int));
142 symbolic_factor->lnz = cs_cumsum(symbolic_factor->cp, column_counts, A->n);
143 symbolic_factor->unz = symbolic_factor->lnz;
144
145 cs_free(column_counts);
146
147 if (symbolic_factor->lnz < 0) {
148 cs_sfree(symbolic_factor);
149 symbolic_factor = NULL;
150 }
151
152 return symbolic_factor;
153}
154
155cs_di CXSparse::CreateSparseMatrixTransposeView(CompressedRowSparseMatrix* A) {
156 cs_di At;
157 At.m = A->num_cols();
158 At.n = A->num_rows();
159 At.nz = -1;
160 At.nzmax = A->num_nonzeros();
161 At.p = A->mutable_rows();
162 At.i = A->mutable_cols();
163 At.x = A->mutable_values();
164 return At;
165}
166
167cs_di* CXSparse::CreateSparseMatrix(TripletSparseMatrix* tsm) {
168 cs_di_sparse tsm_wrapper;
169 tsm_wrapper.nzmax = tsm->num_nonzeros();
170 tsm_wrapper.nz = tsm->num_nonzeros();
171 tsm_wrapper.m = tsm->num_rows();
172 tsm_wrapper.n = tsm->num_cols();
173 tsm_wrapper.p = tsm->mutable_cols();
174 tsm_wrapper.i = tsm->mutable_rows();
175 tsm_wrapper.x = tsm->mutable_values();
176
177 return cs_compress(&tsm_wrapper);
178}
179
180void CXSparse::ApproximateMinimumDegreeOrdering(cs_di* A, int* ordering) {
181 int* cs_ordering = cs_amd(1, A);
182 std::copy(cs_ordering, cs_ordering + A->m, ordering);
183 cs_free(cs_ordering);
184}
185
186cs_di* CXSparse::TransposeMatrix(cs_di* A) { return cs_di_transpose(A, 1); }
187
188cs_di* CXSparse::MatrixMatrixMultiply(cs_di* A, cs_di* B) {
189 return cs_di_multiply(A, B);
190}
191
192void CXSparse::Free(cs_di* sparse_matrix) { cs_di_spfree(sparse_matrix); }
193
194void CXSparse::Free(cs_dis* symbolic_factor) { cs_di_sfree(symbolic_factor); }
195
196void CXSparse::Free(csn* numeric_factor) { cs_di_nfree(numeric_factor); }
197
198std::unique_ptr<SparseCholesky> CXSparseCholesky::Create(
199 const OrderingType ordering_type) {
200 return std::unique_ptr<SparseCholesky>(new CXSparseCholesky(ordering_type));
201}
202
203CompressedRowSparseMatrix::StorageType CXSparseCholesky::StorageType() const {
204 return CompressedRowSparseMatrix::LOWER_TRIANGULAR;
205}
206
207CXSparseCholesky::CXSparseCholesky(const OrderingType ordering_type)
208 : ordering_type_(ordering_type),
209 symbolic_factor_(NULL),
210 numeric_factor_(NULL) {}
211
212CXSparseCholesky::~CXSparseCholesky() {
213 FreeSymbolicFactorization();
214 FreeNumericFactorization();
215}
216
217LinearSolverTerminationType CXSparseCholesky::Factorize(
218 CompressedRowSparseMatrix* lhs, std::string* message) {
219 CHECK_EQ(lhs->storage_type(), StorageType());
220 if (lhs == NULL) {
221 *message = "Failure: Input lhs is NULL.";
222 return LINEAR_SOLVER_FATAL_ERROR;
223 }
224
225 cs_di cs_lhs = cs_.CreateSparseMatrixTransposeView(lhs);
226
227 if (symbolic_factor_ == NULL) {
228 if (ordering_type_ == NATURAL) {
229 symbolic_factor_ = cs_.AnalyzeCholeskyWithNaturalOrdering(&cs_lhs);
230 } else {
231 if (!lhs->col_blocks().empty() && !(lhs->row_blocks().empty())) {
232 symbolic_factor_ = cs_.BlockAnalyzeCholesky(
233 &cs_lhs, lhs->col_blocks(), lhs->row_blocks());
234 } else {
235 symbolic_factor_ = cs_.AnalyzeCholesky(&cs_lhs);
236 }
237 }
238
239 if (symbolic_factor_ == NULL) {
240 *message = "CXSparse Failure : Symbolic factorization failed.";
241 return LINEAR_SOLVER_FATAL_ERROR;
242 }
243 }
244
245 FreeNumericFactorization();
246 numeric_factor_ = cs_.Cholesky(&cs_lhs, symbolic_factor_);
247 if (numeric_factor_ == NULL) {
248 *message = "CXSparse Failure : Numeric factorization failed.";
249 return LINEAR_SOLVER_FAILURE;
250 }
251
252 return LINEAR_SOLVER_SUCCESS;
253}
254
255LinearSolverTerminationType CXSparseCholesky::Solve(const double* rhs,
256 double* solution,
257 std::string* message) {
258 CHECK(numeric_factor_ != NULL)
259 << "Solve called without a call to Factorize first.";
260 const int num_cols = numeric_factor_->L->n;
261 memcpy(solution, rhs, num_cols * sizeof(*solution));
262 cs_.Solve(symbolic_factor_, numeric_factor_, solution);
263 return LINEAR_SOLVER_SUCCESS;
264}
265
266void CXSparseCholesky::FreeSymbolicFactorization() {
267 if (symbolic_factor_ != NULL) {
268 cs_.Free(symbolic_factor_);
269 symbolic_factor_ = NULL;
270 }
271}
272
273void CXSparseCholesky::FreeNumericFactorization() {
274 if (numeric_factor_ != NULL) {
275 cs_.Free(numeric_factor_);
276 numeric_factor_ = NULL;
277 }
278}
279
280} // namespace internal
281} // namespace ceres
282
283#endif // CERES_NO_CXSPARSE