blob: 5dcc53f0167a40af828453438da57107c52d7ba6 [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2017 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: 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
36// This include must come before any #ifndef check on Ceres compile options.
37#include "ceres/internal/port.h"
38
39#ifndef CERES_NO_SUITESPARSE
40
41#include <cstring>
42#include <string>
43#include <vector>
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080044
Austin Schuh70cc9552019-01-21 19:46:48 -080045#include "SuiteSparseQR.hpp"
46#include "ceres/linear_solver.h"
47#include "ceres/sparse_cholesky.h"
48#include "cholmod.h"
49#include "glog/logging.h"
50
51// Before SuiteSparse version 4.2.0, cholmod_camd was only enabled
52// if SuiteSparse was compiled with Metis support. This makes
53// calling and linking into cholmod_camd problematic even though it
54// has nothing to do with Metis. This has been fixed reliably in
55// 4.2.0.
56//
57// The fix was actually committed in 4.1.0, but there is
58// some confusion about a silent update to the tar ball, so we are
59// being conservative and choosing the next minor version where
60// things are stable.
61#if (SUITESPARSE_VERSION < 4002)
62#define CERES_NO_CAMD
63#endif
64
65// UF_long is deprecated but SuiteSparse_long is only available in
66// newer versions of SuiteSparse. So for older versions of
67// SuiteSparse, we define SuiteSparse_long to be the same as UF_long,
68// which is what recent versions of SuiteSparse do anyways.
69#ifndef SuiteSparse_long
70#define SuiteSparse_long UF_long
71#endif
72
73namespace ceres {
74namespace internal {
75
76class CompressedRowSparseMatrix;
77class TripletSparseMatrix;
78
79// The raw CHOLMOD and SuiteSparseQR libraries have a slightly
80// cumbersome c like calling format. This object abstracts it away and
81// provides the user with a simpler interface. The methods here cannot
82// be static as a cholmod_common object serves as a global variable
83// for all cholmod function calls.
84class SuiteSparse {
85 public:
86 SuiteSparse();
87 ~SuiteSparse();
88
89 // Functions for building cholmod_sparse objects from sparse
90 // matrices stored in triplet form. The matrix A is not
91 // modifed. Called owns the result.
92 cholmod_sparse* CreateSparseMatrix(TripletSparseMatrix* A);
93
94 // This function works like CreateSparseMatrix, except that the
95 // return value corresponds to A' rather than A.
96 cholmod_sparse* CreateSparseMatrixTranspose(TripletSparseMatrix* A);
97
98 // Create a cholmod_sparse wrapper around the contents of A. This is
99 // a shallow object, which refers to the contents of A and does not
100 // use the SuiteSparse machinery to allocate memory.
101 cholmod_sparse CreateSparseMatrixTransposeView(CompressedRowSparseMatrix* A);
102
103 // Create a cholmod_dense vector around the contents of the array x.
104 // This is a shallow object, which refers to the contents of x and
105 // does not use the SuiteSparse machinery to allocate memory.
106 cholmod_dense CreateDenseVectorView(const double* x, int size);
107
108 // Given a vector x, build a cholmod_dense vector of size out_size
109 // with the first in_size entries copied from x. If x is NULL, then
110 // an all zeros vector is returned. Caller owns the result.
111 cholmod_dense* CreateDenseVector(const double* x, int in_size, int out_size);
112
113 // The matrix A is scaled using the matrix whose diagonal is the
114 // vector scale. mode describes how scaling is applied. Possible
115 // values are CHOLMOD_ROW for row scaling - diag(scale) * A,
116 // CHOLMOD_COL for column scaling - A * diag(scale) and CHOLMOD_SYM
117 // for symmetric scaling which scales both the rows and the columns
118 // - diag(scale) * A * diag(scale).
119 void Scale(cholmod_dense* scale, int mode, cholmod_sparse* A) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800120 cholmod_scale(scale, mode, A, &cc_);
Austin Schuh70cc9552019-01-21 19:46:48 -0800121 }
122
123 // Create and return a matrix m = A * A'. Caller owns the
124 // result. The matrix A is not modified.
125 cholmod_sparse* AATranspose(cholmod_sparse* A) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800126 cholmod_sparse* m = cholmod_aat(A, NULL, A->nrow, 1, &cc_);
Austin Schuh70cc9552019-01-21 19:46:48 -0800127 m->stype = 1; // Pay attention to the upper triangular part.
128 return m;
129 }
130
131 // y = alpha * A * x + beta * y. Only y is modified.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800132 void SparseDenseMultiply(cholmod_sparse* A,
133 double alpha,
134 double beta,
135 cholmod_dense* x,
136 cholmod_dense* y) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800137 double alpha_[2] = {alpha, 0};
138 double beta_[2] = {beta, 0};
139 cholmod_sdmult(A, 0, alpha_, beta_, x, y, &cc_);
140 }
141
142 // Find an ordering of A or AA' (if A is unsymmetric) that minimizes
143 // the fill-in in the Cholesky factorization of the corresponding
144 // matrix. This is done by using the AMD algorithm.
145 //
146 // Using this ordering, the symbolic Cholesky factorization of A (or
147 // AA') is computed and returned.
148 //
149 // A is not modified, only the pattern of non-zeros of A is used,
150 // the actual numerical values in A are of no consequence.
151 //
152 // message contains an explanation of the failures if any.
153 //
154 // Caller owns the result.
155 cholmod_factor* AnalyzeCholesky(cholmod_sparse* A, std::string* message);
156
157 cholmod_factor* BlockAnalyzeCholesky(cholmod_sparse* A,
158 const std::vector<int>& row_blocks,
159 const std::vector<int>& col_blocks,
160 std::string* message);
161
162 // If A is symmetric, then compute the symbolic Cholesky
163 // factorization of A(ordering, ordering). If A is unsymmetric, then
164 // compute the symbolic factorization of
165 // A(ordering,:) A(ordering,:)'.
166 //
167 // A is not modified, only the pattern of non-zeros of A is used,
168 // the actual numerical values in A are of no consequence.
169 //
170 // message contains an explanation of the failures if any.
171 //
172 // Caller owns the result.
173 cholmod_factor* AnalyzeCholeskyWithUserOrdering(
174 cholmod_sparse* A,
175 const std::vector<int>& ordering,
176 std::string* message);
177
178 // Perform a symbolic factorization of A without re-ordering A. No
179 // postordering of the elimination tree is performed. This ensures
180 // that the symbolic factor does not introduce an extra permutation
181 // on the matrix. See the documentation for CHOLMOD for more details.
182 //
183 // message contains an explanation of the failures if any.
184 cholmod_factor* AnalyzeCholeskyWithNaturalOrdering(cholmod_sparse* A,
185 std::string* message);
186
187 // Use the symbolic factorization in L, to find the numerical
188 // factorization for the matrix A or AA^T. Return true if
189 // successful, false otherwise. L contains the numeric factorization
190 // on return.
191 //
192 // message contains an explanation of the failures if any.
193 LinearSolverTerminationType Cholesky(cholmod_sparse* A,
194 cholmod_factor* L,
195 std::string* message);
196
197 // Given a Cholesky factorization of a matrix A = LL^T, solve the
198 // linear system Ax = b, and return the result. If the Solve fails
199 // NULL is returned. Caller owns the result.
200 //
201 // message contains an explanation of the failures if any.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800202 cholmod_dense* Solve(cholmod_factor* L,
203 cholmod_dense* b,
204 std::string* message);
Austin Schuh70cc9552019-01-21 19:46:48 -0800205
206 // By virtue of the modeling layer in Ceres being block oriented,
207 // all the matrices used by Ceres are also block oriented. When
208 // doing sparse direct factorization of these matrices the
209 // fill-reducing ordering algorithms (in particular AMD) can either
210 // be run on the block or the scalar form of these matrices. The two
211 // SuiteSparse::AnalyzeCholesky methods allows the client to
212 // compute the symbolic factorization of a matrix by either using
213 // AMD on the matrix or a user provided ordering of the rows.
214 //
215 // But since the underlying matrices are block oriented, it is worth
216 // running AMD on just the block structure of these matrices and then
217 // lifting these block orderings to a full scalar ordering. This
218 // preserves the block structure of the permuted matrix, and exposes
219 // more of the super-nodal structure of the matrix to the numerical
220 // factorization routines.
221 //
222 // Find the block oriented AMD ordering of a matrix A, whose row and
223 // column blocks are given by row_blocks, and col_blocks
224 // respectively. The matrix may or may not be symmetric. The entries
225 // of col_blocks do not need to sum to the number of columns in
226 // A. If this is the case, only the first sum(col_blocks) are used
227 // to compute the ordering.
228 bool BlockAMDOrdering(const cholmod_sparse* A,
229 const std::vector<int>& row_blocks,
230 const std::vector<int>& col_blocks,
231 std::vector<int>* ordering);
232
233 // Find a fill reducing approximate minimum degree
234 // ordering. ordering is expected to be large enough to hold the
235 // ordering.
236 bool ApproximateMinimumDegreeOrdering(cholmod_sparse* matrix, int* ordering);
237
Austin Schuh70cc9552019-01-21 19:46:48 -0800238 // Before SuiteSparse version 4.2.0, cholmod_camd was only enabled
239 // if SuiteSparse was compiled with Metis support. This makes
240 // calling and linking into cholmod_camd problematic even though it
241 // has nothing to do with Metis. This has been fixed reliably in
242 // 4.2.0.
243 //
244 // The fix was actually committed in 4.1.0, but there is
245 // some confusion about a silent update to the tar ball, so we are
246 // being conservative and choosing the next minor version where
247 // things are stable.
248 static bool IsConstrainedApproximateMinimumDegreeOrderingAvailable() {
249 return (SUITESPARSE_VERSION > 4001);
250 }
251
252 // Find a fill reducing approximate minimum degree
253 // ordering. constraints is an array which associates with each
254 // column of the matrix an elimination group. i.e., all columns in
255 // group 0 are eliminated first, all columns in group 1 are
256 // eliminated next etc. This function finds a fill reducing ordering
257 // that obeys these constraints.
258 //
259 // Calling ApproximateMinimumDegreeOrdering is equivalent to calling
260 // ConstrainedApproximateMinimumDegreeOrdering with a constraint
261 // array that puts all columns in the same elimination group.
262 //
263 // If CERES_NO_CAMD is defined then calling this function will
264 // result in a crash.
265 bool ConstrainedApproximateMinimumDegreeOrdering(cholmod_sparse* matrix,
266 int* constraints,
267 int* ordering);
268
269 void Free(cholmod_sparse* m) { cholmod_free_sparse(&m, &cc_); }
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800270 void Free(cholmod_dense* m) { cholmod_free_dense(&m, &cc_); }
Austin Schuh70cc9552019-01-21 19:46:48 -0800271 void Free(cholmod_factor* m) { cholmod_free_factor(&m, &cc_); }
272
273 void Print(cholmod_sparse* m, const std::string& name) {
274 cholmod_print_sparse(m, const_cast<char*>(name.c_str()), &cc_);
275 }
276
277 void Print(cholmod_dense* m, const std::string& name) {
278 cholmod_print_dense(m, const_cast<char*>(name.c_str()), &cc_);
279 }
280
281 void Print(cholmod_triplet* m, const std::string& name) {
282 cholmod_print_triplet(m, const_cast<char*>(name.c_str()), &cc_);
283 }
284
285 cholmod_common* mutable_cc() { return &cc_; }
286
287 private:
288 cholmod_common cc_;
289};
290
291class SuiteSparseCholesky : public SparseCholesky {
292 public:
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800293 static std::unique_ptr<SparseCholesky> Create(OrderingType ordering_type);
Austin Schuh70cc9552019-01-21 19:46:48 -0800294
295 // SparseCholesky interface.
296 virtual ~SuiteSparseCholesky();
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800297 CompressedRowSparseMatrix::StorageType StorageType() const final;
298 LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs,
299 std::string* message) final;
300 LinearSolverTerminationType Solve(const double* rhs,
301 double* solution,
302 std::string* message) final;
303
Austin Schuh70cc9552019-01-21 19:46:48 -0800304 private:
305 SuiteSparseCholesky(const OrderingType ordering_type);
306
307 const OrderingType ordering_type_;
308 SuiteSparse ss_;
309 cholmod_factor* factor_;
310};
311
312} // namespace internal
313} // namespace ceres
314
315#else // CERES_NO_SUITESPARSE
316
317typedef void cholmod_factor;
318
319namespace ceres {
320namespace internal {
321
322class SuiteSparse {
323 public:
324 // Defining this static function even when SuiteSparse is not
325 // available, allows client code to check for the presence of CAMD
326 // without checking for the absence of the CERES_NO_CAMD symbol.
327 //
328 // This is safer because the symbol maybe missing due to a user
329 // accidentally not including suitesparse.h in their code when
330 // checking for the symbol.
331 static bool IsConstrainedApproximateMinimumDegreeOrderingAvailable() {
332 return false;
333 }
334
335 void Free(void* arg) {}
336};
337
338} // namespace internal
339} // namespace ceres
340
341#endif // CERES_NO_SUITESPARSE
342
343#endif // CERES_INTERNAL_SUITESPARSE_H_