blob: e0c485d0ba18c942d4b9d128010c5f092f6f2237 [file] [log] [blame]
Sameer Agarwal3faac6a2013-11-28 07:13:26 -08001// Ceres Solver - A fast non-linear least squares minimizer
Sameer Agarwal29c21f52017-04-12 12:48:44 -07002// Copyright 2017 Google Inc. All rights reserved.
Keir Mierle7492b0d2015-03-17 22:30:16 -07003// http://ceres-solver.org/
Keir Mierle8ebb0732012-04-30 23:09:08 -07004//
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// A simple C++ interface to the SuiteSparse and CHOLMOD libraries.
32
33#ifndef CERES_INTERNAL_SUITESPARSE_H_
34#define CERES_INTERNAL_SUITESPARSE_H_
35
Alex Stewartea765852014-05-07 20:46:17 +010036// This include must come before any #ifndef check on Ceres compile options.
Sergiu Deitschf90833f2022-02-07 23:43:19 +010037#include "ceres/internal/config.h"
Sameer Agarwald5b93bf2013-04-26 21:17:49 -070038
Keir Mierle8ebb0732012-04-30 23:09:08 -070039#ifndef CERES_NO_SUITESPARSE
40
41#include <cstring>
Sameer Agarwal446487c2022-02-12 08:11:33 -080042#include <memory>
Keir Mierle8ebb0732012-04-30 23:09:08 -070043#include <string>
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -070044#include <vector>
Nikolaus Demmel7b8f6752020-09-20 21:45:24 +020045
Sameer Agarwal29c21f52017-04-12 12:48:44 -070046#include "SuiteSparseQR.hpp"
Sameer Agarwal79bde352013-11-21 21:33:51 -080047#include "ceres/linear_solver.h"
Sameer Agarwal29c21f52017-04-12 12:48:44 -070048#include "ceres/sparse_cholesky.h"
Sameer Agarwal509f68c2013-02-20 01:39:03 -080049#include "cholmod.h"
50#include "glog/logging.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070051
Sergiu Deitschf90833f2022-02-07 23:43:19 +010052#include "ceres/internal/disable_warnings.h"
53
Sameer Agarwalcaf614a2022-04-21 17:41:10 -070054namespace ceres::internal {
Keir Mierle8ebb0732012-04-30 23:09:08 -070055
56class CompressedRowSparseMatrix;
57class TripletSparseMatrix;
58
59// The raw CHOLMOD and SuiteSparseQR libraries have a slightly
60// cumbersome c like calling format. This object abstracts it away and
61// provides the user with a simpler interface. The methods here cannot
62// be static as a cholmod_common object serves as a global variable
63// for all cholmod function calls.
Sergiu Deitschf90833f2022-02-07 23:43:19 +010064class CERES_NO_EXPORT SuiteSparse {
Keir Mierle8ebb0732012-04-30 23:09:08 -070065 public:
Sameer Agarwal222ca202013-04-01 09:11:07 -070066 SuiteSparse();
67 ~SuiteSparse();
Keir Mierle8ebb0732012-04-30 23:09:08 -070068
69 // Functions for building cholmod_sparse objects from sparse
70 // matrices stored in triplet form. The matrix A is not
Evan Levinef1414cb2022-04-24 19:07:37 -070071 // modified. Called owns the result.
Keir Mierle8ebb0732012-04-30 23:09:08 -070072 cholmod_sparse* CreateSparseMatrix(TripletSparseMatrix* A);
73
74 // This function works like CreateSparseMatrix, except that the
75 // return value corresponds to A' rather than A.
76 cholmod_sparse* CreateSparseMatrixTranspose(TripletSparseMatrix* A);
77
78 // Create a cholmod_sparse wrapper around the contents of A. This is
79 // a shallow object, which refers to the contents of A and does not
Sameer Agarwal2755fce2017-04-10 21:13:29 -070080 // use the SuiteSparse machinery to allocate memory.
81 cholmod_sparse CreateSparseMatrixTransposeView(CompressedRowSparseMatrix* A);
Keir Mierle8ebb0732012-04-30 23:09:08 -070082
Sameer Agarwal83f70e52018-04-09 13:52:38 -070083 // Create a cholmod_dense vector around the contents of the array x.
84 // This is a shallow object, which refers to the contents of x and
85 // does not use the SuiteSparse machinery to allocate memory.
86 cholmod_dense CreateDenseVectorView(const double* x, int size);
87
Keir Mierle8ebb0732012-04-30 23:09:08 -070088 // Given a vector x, build a cholmod_dense vector of size out_size
Sameer Agarwalae652192022-02-02 13:17:29 -080089 // with the first in_size entries copied from x. If x is nullptr, then
Keir Mierle8ebb0732012-04-30 23:09:08 -070090 // an all zeros vector is returned. Caller owns the result.
91 cholmod_dense* CreateDenseVector(const double* x, int in_size, int out_size);
92
93 // The matrix A is scaled using the matrix whose diagonal is the
94 // vector scale. mode describes how scaling is applied. Possible
95 // values are CHOLMOD_ROW for row scaling - diag(scale) * A,
96 // CHOLMOD_COL for column scaling - A * diag(scale) and CHOLMOD_SYM
97 // for symmetric scaling which scales both the rows and the columns
98 // - diag(scale) * A * diag(scale).
99 void Scale(cholmod_dense* scale, int mode, cholmod_sparse* A) {
Nikolaus Demmel7b8f6752020-09-20 21:45:24 +0200100 cholmod_scale(scale, mode, A, &cc_);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700101 }
102
103 // Create and return a matrix m = A * A'. Caller owns the
104 // result. The matrix A is not modified.
105 cholmod_sparse* AATranspose(cholmod_sparse* A) {
Sameer Agarwalae652192022-02-02 13:17:29 -0800106 cholmod_sparse* m = cholmod_aat(A, nullptr, A->nrow, 1, &cc_);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700107 m->stype = 1; // Pay attention to the upper triangular part.
108 return m;
109 }
110
111 // y = alpha * A * x + beta * y. Only y is modified.
Nikolaus Demmel7b8f6752020-09-20 21:45:24 +0200112 void SparseDenseMultiply(cholmod_sparse* A,
113 double alpha,
114 double beta,
115 cholmod_dense* x,
116 cholmod_dense* y) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700117 double alpha_[2] = {alpha, 0};
118 double beta_[2] = {beta, 0};
119 cholmod_sdmult(A, 0, alpha_, beta_, x, y, &cc_);
120 }
121
Sameer Agarwal41c5fb12022-05-19 00:47:24 -0700122 // Compute a symbolic factorization for A or AA' (if A is
123 // unsymmetric). If ordering_type is NATURAL, then no fill reducing
124 // ordering is computed, otherwise depending on the value of
125 // ordering_type AMD or Nested Dissection is used to compute a fill
126 // reducing ordering before the symbolic factorization is computed.
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700127 //
128 // A is not modified, only the pattern of non-zeros of A is used,
129 // the actual numerical values in A are of no consequence.
130 //
Sameer Agarwaled923662013-11-28 06:50:43 -0800131 // message contains an explanation of the failures if any.
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800132 //
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700133 // Caller owns the result.
Sameer Agarwal41c5fb12022-05-19 00:47:24 -0700134 cholmod_factor* AnalyzeCholesky(cholmod_sparse* A,
135 OrderingType ordering_type,
136 std::string* message);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700137
Sameer Agarwal41c5fb12022-05-19 00:47:24 -0700138 // Block oriented version of AnalyzeCholesky.
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700139 cholmod_factor* BlockAnalyzeCholesky(cholmod_sparse* A,
Sameer Agarwal41c5fb12022-05-19 00:47:24 -0700140 OrderingType ordering_type,
Sameer Agarwalbcc865f2014-12-17 07:35:09 -0800141 const std::vector<int>& row_blocks,
142 const std::vector<int>& col_blocks,
Sameer Agarwal05a07ec2015-01-07 15:10:46 -0800143 std::string* message);
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700144
145 // If A is symmetric, then compute the symbolic Cholesky
146 // factorization of A(ordering, ordering). If A is unsymmetric, then
147 // compute the symbolic factorization of
148 // A(ordering,:) A(ordering,:)'.
149 //
150 // A is not modified, only the pattern of non-zeros of A is used,
151 // the actual numerical values in A are of no consequence.
152 //
Sameer Agarwaled923662013-11-28 06:50:43 -0800153 // message contains an explanation of the failures if any.
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800154 //
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700155 // Caller owns the result.
Sameer Agarwal41c5fb12022-05-19 00:47:24 -0700156 cholmod_factor* AnalyzeCholeskyWithGivenOrdering(
Sameer Agarwalbcc865f2014-12-17 07:35:09 -0800157 cholmod_sparse* A,
158 const std::vector<int>& ordering,
Sameer Agarwal05a07ec2015-01-07 15:10:46 -0800159 std::string* message);
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700160
Keir Mierle8ebb0732012-04-30 23:09:08 -0700161 // Use the symbolic factorization in L, to find the numerical
162 // factorization for the matrix A or AA^T. Return true if
163 // successful, false otherwise. L contains the numeric factorization
164 // on return.
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800165 //
Sameer Agarwaled923662013-11-28 06:50:43 -0800166 // message contains an explanation of the failures if any.
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800167 LinearSolverTerminationType Cholesky(cholmod_sparse* A,
168 cholmod_factor* L,
Sameer Agarwal05a07ec2015-01-07 15:10:46 -0800169 std::string* message);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700170
171 // Given a Cholesky factorization of a matrix A = LL^T, solve the
172 // linear system Ax = b, and return the result. If the Solve fails
Sameer Agarwalae652192022-02-02 13:17:29 -0800173 // nullptr is returned. Caller owns the result.
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800174 //
Sameer Agarwaled923662013-11-28 06:50:43 -0800175 // message contains an explanation of the failures if any.
Nikolaus Demmel7b8f6752020-09-20 21:45:24 +0200176 cholmod_dense* Solve(cholmod_factor* L,
177 cholmod_dense* b,
178 std::string* message);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700179
Sameer Agarwal41c5fb12022-05-19 00:47:24 -0700180 // Find a fill reducing ordering. ordering is expected to be large
181 // enough to hold the ordering. ordering_type must be AMD or NESDIS.
182 bool Ordering(cholmod_sparse* matrix,
183 OrderingType ordering_type,
184 int* ordering);
185
186 // Find the block oriented fill reducing ordering of a matrix A,
187 // whose row and column blocks are given by row_blocks, and
188 // col_blocks respectively. The matrix may or may not be
189 // symmetric. The entries of col_blocks do not need to sum to the
190 // number of columns in A. If this is the case, only the first
191 // sum(col_blocks) are used to compute the ordering.
192 //
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700193 // By virtue of the modeling layer in Ceres being block oriented,
194 // all the matrices used by Ceres are also block oriented. When
195 // doing sparse direct factorization of these matrices the
Sameer Agarwal41c5fb12022-05-19 00:47:24 -0700196 // fill-reducing ordering algorithms can either be run on the block
197 // or the scalar form of these matrices. But since the underlying
198 // matrices are block oriented, it is worth running the fill
199 // reducing ordering on just the block structure of these matrices
200 // and then lifting these block orderings to a full scalar
201 // ordering. This preserves the block structure of the permuted
202 // matrix, and exposes more of the super-nodal structure of the
203 // matrix to the numerical factorization routines.
204 bool BlockOrdering(const cholmod_sparse* A,
205 OrderingType ordering_type,
206 const std::vector<int>& row_blocks,
207 const std::vector<int>& col_blocks,
208 std::vector<int>* ordering);
Sameer Agarwalbb3a40c2022-05-14 15:06:53 -0700209
Sameer Agarwalbb3a40c2022-05-14 15:06:53 -0700210 // Nested dissection is only available if SuiteSparse is compiled
211 // with Metis support.
Sameer Agarwal41c5fb12022-05-19 00:47:24 -0700212 static bool IsNestedDissectionAvailable();
Sameer Agarwalbb3a40c2022-05-14 15:06:53 -0700213
Sameer Agarwald5b93bf2013-04-26 21:17:49 -0700214 // Find a fill reducing approximate minimum degree
215 // ordering. constraints is an array which associates with each
216 // column of the matrix an elimination group. i.e., all columns in
217 // group 0 are eliminated first, all columns in group 1 are
218 // eliminated next etc. This function finds a fill reducing ordering
219 // that obeys these constraints.
220 //
221 // Calling ApproximateMinimumDegreeOrdering is equivalent to calling
222 // ConstrainedApproximateMinimumDegreeOrdering with a constraint
223 // array that puts all columns in the same elimination group.
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800224 bool ConstrainedApproximateMinimumDegreeOrdering(cholmod_sparse* matrix,
Sameer Agarwald5b93bf2013-04-26 21:17:49 -0700225 int* constraints,
226 int* ordering);
227
Keir Mierle8ebb0732012-04-30 23:09:08 -0700228 void Free(cholmod_sparse* m) { cholmod_free_sparse(&m, &cc_); }
Nikolaus Demmel7b8f6752020-09-20 21:45:24 +0200229 void Free(cholmod_dense* m) { cholmod_free_dense(&m, &cc_); }
Keir Mierle8ebb0732012-04-30 23:09:08 -0700230 void Free(cholmod_factor* m) { cholmod_free_factor(&m, &cc_); }
231
Sameer Agarwal05a07ec2015-01-07 15:10:46 -0800232 void Print(cholmod_sparse* m, const std::string& name) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700233 cholmod_print_sparse(m, const_cast<char*>(name.c_str()), &cc_);
234 }
235
Sameer Agarwal05a07ec2015-01-07 15:10:46 -0800236 void Print(cholmod_dense* m, const std::string& name) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700237 cholmod_print_dense(m, const_cast<char*>(name.c_str()), &cc_);
238 }
239
Sameer Agarwal05a07ec2015-01-07 15:10:46 -0800240 void Print(cholmod_triplet* m, const std::string& name) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700241 cholmod_print_triplet(m, const_cast<char*>(name.c_str()), &cc_);
242 }
243
244 cholmod_common* mutable_cc() { return &cc_; }
245
246 private:
247 cholmod_common cc_;
248};
249
Sameer Agarwal84e16962022-02-17 17:06:49 -0800250class CERES_NO_EXPORT SuiteSparseCholesky final : public SparseCholesky {
Sameer Agarwal29c21f52017-04-12 12:48:44 -0700251 public:
Nikolaus Demmel7b8f6752020-09-20 21:45:24 +0200252 static std::unique_ptr<SparseCholesky> Create(OrderingType ordering_type);
Sameer Agarwal29c21f52017-04-12 12:48:44 -0700253
254 // SparseCholesky interface.
Sergiu Deitsch484d3412022-02-09 00:34:05 +0100255 ~SuiteSparseCholesky() override;
Sameer Agarwal2ffddac2019-07-14 00:16:13 +0200256 CompressedRowSparseMatrix::StorageType StorageType() const final;
Nikolaus Demmel7b8f6752020-09-20 21:45:24 +0200257 LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs,
258 std::string* message) final;
Sameer Agarwal2ffddac2019-07-14 00:16:13 +0200259 LinearSolverTerminationType Solve(const double* rhs,
260 double* solution,
261 std::string* message) final;
Nikolaus Demmel7b8f6752020-09-20 21:45:24 +0200262
Sameer Agarwal29c21f52017-04-12 12:48:44 -0700263 private:
Sergiu Deitschc8658c82022-02-20 02:22:17 +0100264 explicit SuiteSparseCholesky(const OrderingType ordering_type);
Sameer Agarwal29c21f52017-04-12 12:48:44 -0700265
266 const OrderingType ordering_type_;
267 SuiteSparse ss_;
268 cholmod_factor* factor_;
Sameer Agarwal29c21f52017-04-12 12:48:44 -0700269};
270
Sameer Agarwalcaf614a2022-04-21 17:41:10 -0700271} // namespace ceres::internal
Keir Mierle8ebb0732012-04-30 23:09:08 -0700272
Sergiu Deitschf90833f2022-02-07 23:43:19 +0100273#include "ceres/internal/reenable_warnings.h"
274
Sameer Agarwald61b68a2013-08-16 17:02:56 -0700275#else // CERES_NO_SUITESPARSE
Sergey Sharybinf258e462013-08-15 14:50:08 +0600276
Sergey Sharybinf258e462013-08-15 14:50:08 +0600277typedef void cholmod_factor;
278
Sergiu Deitschf90833f2022-02-07 23:43:19 +0100279#include "ceres/internal/disable_warnings.h"
280
Sameer Agarwal29c21f52017-04-12 12:48:44 -0700281namespace ceres {
282namespace internal {
283
Sergiu Deitschf90833f2022-02-07 23:43:19 +0100284class CERES_NO_EXPORT SuiteSparse {
Sameer Agarwal03159822014-07-17 14:35:18 -0700285 public:
Sameer Agarwal41c5fb12022-05-19 00:47:24 -0700286 // Nested dissection is only available if SuiteSparse is compiled
287 // with Metis support.
288 static bool IsNestedDissectionAvailable() { return false; }
Austin Schuhe0e14a52020-12-23 22:37:54 -0800289 void Free(void* /*arg*/) {}
Sameer Agarwal558ee402014-05-29 14:12:34 -0700290};
291
Sameer Agarwal29c21f52017-04-12 12:48:44 -0700292} // namespace internal
293} // namespace ceres
294
Sergiu Deitschf90833f2022-02-07 23:43:19 +0100295#include "ceres/internal/reenable_warnings.h"
296
Keir Mierle8ebb0732012-04-30 23:09:08 -0700297#endif // CERES_NO_SUITESPARSE
298
299#endif // CERES_INTERNAL_SUITESPARSE_H_