blob: 8079bc16ebbf181fabd35103212bb4afcef2c66a [file] [log] [blame]
Keir Mierle8ebb0732012-04-30 23:09:08 -07001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2010, 2011, 2012 Google Inc. All rights reserved.
3// http://code.google.com/p/ceres-solver/
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: sameeragarwal@google.com (Sameer Agarwal)
30
31#ifndef CERES_NO_SUITESPARSE
Keir Mierle8ebb0732012-04-30 23:09:08 -070032#include "ceres/suitesparse.h"
33
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -070034#include <vector>
Keir Mierle8ebb0732012-04-30 23:09:08 -070035#include "cholmod.h"
Sameer Agarwal344c09f2013-04-20 16:07:56 -070036#include "ceres/compressed_col_sparse_matrix_utils.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070037#include "ceres/compressed_row_sparse_matrix.h"
Sameer Agarwal79bde352013-11-21 21:33:51 -080038#include "ceres/linear_solver.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070039#include "ceres/triplet_sparse_matrix.h"
Sameer Agarwal222ca202013-04-01 09:11:07 -070040
Keir Mierle8ebb0732012-04-30 23:09:08 -070041namespace ceres {
42namespace internal {
Sameer Agarwal222ca202013-04-01 09:11:07 -070043
44SuiteSparse::SuiteSparse() {
45 cholmod_start(&cc_);
46}
47
48SuiteSparse::~SuiteSparse() {
49 cholmod_finish(&cc_);
50}
51
Keir Mierle8ebb0732012-04-30 23:09:08 -070052cholmod_sparse* SuiteSparse::CreateSparseMatrix(TripletSparseMatrix* A) {
53 cholmod_triplet triplet;
54
55 triplet.nrow = A->num_rows();
56 triplet.ncol = A->num_cols();
57 triplet.nzmax = A->max_num_nonzeros();
58 triplet.nnz = A->num_nonzeros();
59 triplet.i = reinterpret_cast<void*>(A->mutable_rows());
60 triplet.j = reinterpret_cast<void*>(A->mutable_cols());
61 triplet.x = reinterpret_cast<void*>(A->mutable_values());
62 triplet.stype = 0; // Matrix is not symmetric.
63 triplet.itype = CHOLMOD_INT;
64 triplet.xtype = CHOLMOD_REAL;
65 triplet.dtype = CHOLMOD_DOUBLE;
66
67 return cholmod_triplet_to_sparse(&triplet, triplet.nnz, &cc_);
68}
69
70
71cholmod_sparse* SuiteSparse::CreateSparseMatrixTranspose(
72 TripletSparseMatrix* A) {
73 cholmod_triplet triplet;
74
75 triplet.ncol = A->num_rows(); // swap row and columns
76 triplet.nrow = A->num_cols();
77 triplet.nzmax = A->max_num_nonzeros();
78 triplet.nnz = A->num_nonzeros();
79
80 // swap rows and columns
81 triplet.j = reinterpret_cast<void*>(A->mutable_rows());
82 triplet.i = reinterpret_cast<void*>(A->mutable_cols());
83 triplet.x = reinterpret_cast<void*>(A->mutable_values());
84 triplet.stype = 0; // Matrix is not symmetric.
85 triplet.itype = CHOLMOD_INT;
86 triplet.xtype = CHOLMOD_REAL;
87 triplet.dtype = CHOLMOD_DOUBLE;
88
89 return cholmod_triplet_to_sparse(&triplet, triplet.nnz, &cc_);
90}
91
Sameer Agarwal2560b172013-04-19 08:19:11 -070092cholmod_sparse SuiteSparse::CreateSparseMatrixTransposeView(
Keir Mierle8ebb0732012-04-30 23:09:08 -070093 CompressedRowSparseMatrix* A) {
Sameer Agarwal2560b172013-04-19 08:19:11 -070094 cholmod_sparse m;
95 m.nrow = A->num_cols();
96 m.ncol = A->num_rows();
97 m.nzmax = A->num_nonzeros();
98 m.nz = NULL;
99 m.p = reinterpret_cast<void*>(A->mutable_rows());
100 m.i = reinterpret_cast<void*>(A->mutable_cols());
101 m.x = reinterpret_cast<void*>(A->mutable_values());
102 m.z = NULL;
103 m.stype = 0; // Matrix is not symmetric.
104 m.itype = CHOLMOD_INT;
105 m.xtype = CHOLMOD_REAL;
106 m.dtype = CHOLMOD_DOUBLE;
107 m.sorted = 1;
108 m.packed = 1;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700109
110 return m;
111}
112
113cholmod_dense* SuiteSparse::CreateDenseVector(const double* x,
114 int in_size,
115 int out_size) {
116 CHECK_LE(in_size, out_size);
117 cholmod_dense* v = cholmod_zeros(out_size, 1, CHOLMOD_REAL, &cc_);
118 if (x != NULL) {
119 memcpy(v->x, x, in_size*sizeof(*x));
120 }
121 return v;
122}
123
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800124cholmod_factor* SuiteSparse::AnalyzeCholesky(cholmod_sparse* A,
Sameer Agarwaled923662013-11-28 06:50:43 -0800125 string* message) {
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700126 // Cholmod can try multiple re-ordering strategies to find a fill
127 // reducing ordering. Here we just tell it use AMD with automatic
128 // matrix dependence choice of supernodal versus simplicial
129 // factorization.
130 cc_.nmethods = 1;
131 cc_.method[0].ordering = CHOLMOD_AMD;
132 cc_.supernodal = CHOLMOD_AUTO;
Sameer Agarwal222ca202013-04-01 09:11:07 -0700133
Keir Mierle8ebb0732012-04-30 23:09:08 -0700134 cholmod_factor* factor = cholmod_analyze(A, &cc_);
Sameer Agarwal222ca202013-04-01 09:11:07 -0700135 if (VLOG_IS_ON(2)) {
136 cholmod_print_common(const_cast<char*>("Symbolic Analysis"), &cc_);
137 }
138
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800139 if (cc_.status != CHOLMOD_OK) {
Sameer Agarwaled923662013-11-28 06:50:43 -0800140 *message = StringPrintf("cholmod_analyze failed. error code: %d",
Sameer Agarwald73acd02013-12-02 12:02:03 -0800141 cc_.status);
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800142 return NULL;
143 }
144
145 return CHECK_NOTNULL(factor);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700146}
147
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700148cholmod_factor* SuiteSparse::BlockAnalyzeCholesky(
149 cholmod_sparse* A,
150 const vector<int>& row_blocks,
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800151 const vector<int>& col_blocks,
Sameer Agarwaled923662013-11-28 06:50:43 -0800152 string* message) {
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700153 vector<int> ordering;
154 if (!BlockAMDOrdering(A, row_blocks, col_blocks, &ordering)) {
155 return NULL;
156 }
Sameer Agarwaled923662013-11-28 06:50:43 -0800157 return AnalyzeCholeskyWithUserOrdering(A, ordering, message);
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700158}
159
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800160cholmod_factor* SuiteSparse::AnalyzeCholeskyWithUserOrdering(
161 cholmod_sparse* A,
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800162 const vector<int>& ordering,
Sameer Agarwaled923662013-11-28 06:50:43 -0800163 string* message) {
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700164 CHECK_EQ(ordering.size(), A->nrow);
Sameer Agarwal222ca202013-04-01 09:11:07 -0700165
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800166 cc_.nmethods = 1;
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700167 cc_.method[0].ordering = CHOLMOD_GIVEN;
Sameer Agarwal222ca202013-04-01 09:11:07 -0700168
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700169 cholmod_factor* factor =
170 cholmod_analyze_p(A, const_cast<int*>(&ordering[0]), NULL, 0, &cc_);
Sameer Agarwal222ca202013-04-01 09:11:07 -0700171 if (VLOG_IS_ON(2)) {
172 cholmod_print_common(const_cast<char*>("Symbolic Analysis"), &cc_);
173 }
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800174 if (cc_.status != CHOLMOD_OK) {
Sameer Agarwaled923662013-11-28 06:50:43 -0800175 *message = StringPrintf("cholmod_analyze failed. error code: %d",
Sameer Agarwald73acd02013-12-02 12:02:03 -0800176 cc_.status);
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800177 return NULL;
178 }
Sameer Agarwal222ca202013-04-01 09:11:07 -0700179
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800180 return CHECK_NOTNULL(factor);
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700181}
182
Sameer Agarwalcbdeb792013-04-22 10:18:18 -0700183cholmod_factor* SuiteSparse::AnalyzeCholeskyWithNaturalOrdering(
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800184 cholmod_sparse* A,
Sameer Agarwaled923662013-11-28 06:50:43 -0800185 string* message) {
Sameer Agarwal2560b172013-04-19 08:19:11 -0700186 cc_.nmethods = 1;
187 cc_.method[0].ordering = CHOLMOD_NATURAL;
188 cc_.postorder = 0;
189
190 cholmod_factor* factor = cholmod_analyze(A, &cc_);
Sameer Agarwal2560b172013-04-19 08:19:11 -0700191 if (VLOG_IS_ON(2)) {
192 cholmod_print_common(const_cast<char*>("Symbolic Analysis"), &cc_);
193 }
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800194 if (cc_.status != CHOLMOD_OK) {
Sameer Agarwaled923662013-11-28 06:50:43 -0800195 *message = StringPrintf("cholmod_analyze failed. error code: %d",
Sameer Agarwald73acd02013-12-02 12:02:03 -0800196 cc_.status);
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800197 return NULL;
198 }
Sameer Agarwal2560b172013-04-19 08:19:11 -0700199
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800200 return CHECK_NOTNULL(factor);
Sameer Agarwal2560b172013-04-19 08:19:11 -0700201}
202
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700203bool SuiteSparse::BlockAMDOrdering(const cholmod_sparse* A,
204 const vector<int>& row_blocks,
205 const vector<int>& col_blocks,
206 vector<int>* ordering) {
207 const int num_row_blocks = row_blocks.size();
208 const int num_col_blocks = col_blocks.size();
209
210 // Arrays storing the compressed column structure of the matrix
211 // incoding the block sparsity of A.
212 vector<int> block_cols;
213 vector<int> block_rows;
214
Sameer Agarwal344c09f2013-04-20 16:07:56 -0700215 CompressedColumnScalarMatrixToBlockMatrix(reinterpret_cast<const int*>(A->i),
216 reinterpret_cast<const int*>(A->p),
217 row_blocks,
218 col_blocks,
219 &block_rows,
220 &block_cols);
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700221
222 cholmod_sparse_struct block_matrix;
223 block_matrix.nrow = num_row_blocks;
224 block_matrix.ncol = num_col_blocks;
225 block_matrix.nzmax = block_rows.size();
226 block_matrix.p = reinterpret_cast<void*>(&block_cols[0]);
227 block_matrix.i = reinterpret_cast<void*>(&block_rows[0]);
228 block_matrix.x = NULL;
229 block_matrix.stype = A->stype;
230 block_matrix.itype = CHOLMOD_INT;
231 block_matrix.xtype = CHOLMOD_PATTERN;
232 block_matrix.dtype = CHOLMOD_DOUBLE;
233 block_matrix.sorted = 1;
234 block_matrix.packed = 1;
235
236 vector<int> block_ordering(num_row_blocks);
237 if (!cholmod_amd(&block_matrix, NULL, 0, &block_ordering[0], &cc_)) {
238 return false;
239 }
240
241 BlockOrderingToScalarOrdering(row_blocks, block_ordering, ordering);
242 return true;
243}
244
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800245LinearSolverTerminationType SuiteSparse::Cholesky(cholmod_sparse* A,
246 cholmod_factor* L,
Sameer Agarwaled923662013-11-28 06:50:43 -0800247 string* message) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700248 CHECK_NOTNULL(A);
249 CHECK_NOTNULL(L);
250
Sameer Agarwal222ca202013-04-01 09:11:07 -0700251 // Save the current print level and silence CHOLMOD, otherwise
252 // CHOLMOD is prone to dumping stuff to stderr, which can be
253 // distracting when the error (matrix is indefinite) is not a fatal
254 // failure.
255 const int old_print_level = cc_.print;
256 cc_.print = 0;
257
Keir Mierle8ebb0732012-04-30 23:09:08 -0700258 cc_.quick_return_if_not_posdef = 1;
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800259 int cholmod_status = cholmod_factorize(A, L, &cc_);
Sameer Agarwal222ca202013-04-01 09:11:07 -0700260 cc_.print = old_print_level;
261
262 // TODO(sameeragarwal): This switch statement is not consistent. It
263 // treats all kinds of CHOLMOD failures as warnings. Some of these
264 // like out of memory are definitely not warnings. The problem is
265 // that the return value Cholesky is two valued, but the state of
266 // the linear solver is really three valued. SUCCESS,
267 // NON_FATAL_FAILURE (e.g., indefinite matrix) and FATAL_FAILURE
268 // (e.g. out of memory).
Keir Mierle8ebb0732012-04-30 23:09:08 -0700269 switch (cc_.status) {
270 case CHOLMOD_NOT_INSTALLED:
Sameer Agarwaled923662013-11-28 06:50:43 -0800271 *message = "CHOLMOD failure: Method not installed.";
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800272 return LINEAR_SOLVER_FATAL_ERROR;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700273 case CHOLMOD_OUT_OF_MEMORY:
Sameer Agarwaled923662013-11-28 06:50:43 -0800274 *message = "CHOLMOD failure: Out of memory.";
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800275 return LINEAR_SOLVER_FATAL_ERROR;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700276 case CHOLMOD_TOO_LARGE:
Sameer Agarwaled923662013-11-28 06:50:43 -0800277 *message = "CHOLMOD failure: Integer overflow occured.";
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800278 return LINEAR_SOLVER_FATAL_ERROR;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700279 case CHOLMOD_INVALID:
Sameer Agarwaled923662013-11-28 06:50:43 -0800280 *message = "CHOLMOD failure: Invalid input.";
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800281 return LINEAR_SOLVER_FATAL_ERROR;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700282 case CHOLMOD_NOT_POSDEF:
Sameer Agarwaled923662013-11-28 06:50:43 -0800283 *message = "CHOLMOD warning: Matrix not positive definite.";
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800284 return LINEAR_SOLVER_FAILURE;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700285 case CHOLMOD_DSMALL:
Sameer Agarwaled923662013-11-28 06:50:43 -0800286 *message = "CHOLMOD warning: D for LDL' or diag(L) or "
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800287 "LL' has tiny absolute value.";
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800288 return LINEAR_SOLVER_FAILURE;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700289 case CHOLMOD_OK:
Sameer Agarwal3faac6a2013-11-28 07:13:26 -0800290 if (cholmod_status != 0) {
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800291 return LINEAR_SOLVER_SUCCESS;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700292 }
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800293
Sameer Agarwaled923662013-11-28 06:50:43 -0800294 *message = "CHOLMOD failure: cholmod_factorize returned false "
Sameer Agarwald73acd02013-12-02 12:02:03 -0800295 "but cholmod_common::status is CHOLMOD_OK."
296 "Please report this to ceres-solver@googlegroups.com.";
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800297 return LINEAR_SOLVER_FATAL_ERROR;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700298 default:
Sameer Agarwaled923662013-11-28 06:50:43 -0800299 *message =
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800300 StringPrintf("Unknown cholmod return code: %d. "
301 "Please report this to ceres-solver@googlegroups.com.",
302 cc_.status);
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800303 return LINEAR_SOLVER_FATAL_ERROR;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700304 }
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800305
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800306 return LINEAR_SOLVER_FATAL_ERROR;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700307}
308
309cholmod_dense* SuiteSparse::Solve(cholmod_factor* L,
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800310 cholmod_dense* b,
Sameer Agarwaled923662013-11-28 06:50:43 -0800311 string* message) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700312 if (cc_.status != CHOLMOD_OK) {
Sameer Agarwaled923662013-11-28 06:50:43 -0800313 *message = "cholmod_solve failed. CHOLMOD status is not CHOLMOD_OK";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700314 return NULL;
315 }
316
317 return cholmod_solve(CHOLMOD_A, L, b, &cc_);
318}
319
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800320bool SuiteSparse::ApproximateMinimumDegreeOrdering(cholmod_sparse* matrix,
Sameer Agarwalf7ed22e2013-04-19 14:24:48 -0700321 int* ordering) {
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800322 return cholmod_amd(matrix, NULL, 0, ordering, &cc_);
Sameer Agarwalf7ed22e2013-04-19 14:24:48 -0700323}
324
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800325bool SuiteSparse::ConstrainedApproximateMinimumDegreeOrdering(
Sameer Agarwald5b93bf2013-04-26 21:17:49 -0700326 cholmod_sparse* matrix,
327 int* constraints,
328 int* ordering) {
329#ifndef CERES_NO_CAMD
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800330 return cholmod_camd(matrix, NULL, 0, constraints, ordering, &cc_);
Sameer Agarwald5b93bf2013-04-26 21:17:49 -0700331#else
332 LOG(FATAL) << "Congratulations you have found a bug in Ceres."
333 << "Ceres Solver was compiled with SuiteSparse "
334 << "version 4.1.0 or less. Calling this function "
335 << "in that case is a bug. Please contact the"
Sameer Agarwal0e0a4542013-04-29 17:27:26 -0700336 << "the Ceres Solver developers.";
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800337 return false;
Sameer Agarwald5b93bf2013-04-26 21:17:49 -0700338#endif
339}
340
Keir Mierle8ebb0732012-04-30 23:09:08 -0700341} // namespace internal
342} // namespace ceres
343
344#endif // CERES_NO_SUITESPARSE