blob: 07537e3d1ec688dd4f51faccc7f1d2ef41cacc96 [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
Sameer Agarwal8140f0f2013-03-12 09:45:08 -070031#if !defined(CERES_NO_SUITESPARSE) || !defined(CERES_NO_CXSPARSE)
32
Keir Mierle8ebb0732012-04-30 23:09:08 -070033#include "ceres/sparse_normal_cholesky_solver.h"
34
35#include <algorithm>
36#include <cstring>
37#include <ctime>
Sameer Agarwalb0518732012-05-29 00:27:57 -070038
Keir Mierle8ebb0732012-04-30 23:09:08 -070039#include "ceres/compressed_row_sparse_matrix.h"
Sergey Sharybinf258e462013-08-15 14:50:08 +060040#include "ceres/cxsparse.h"
Sameer Agarwal42a84b82013-02-01 12:22:53 -080041#include "ceres/internal/eigen.h"
42#include "ceres/internal/scoped_ptr.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070043#include "ceres/linear_solver.h"
44#include "ceres/suitesparse.h"
45#include "ceres/triplet_sparse_matrix.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070046#include "ceres/types.h"
Sameer Agarwal42a84b82013-02-01 12:22:53 -080047#include "ceres/wall_time.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070048
49namespace ceres {
50namespace internal {
51
52SparseNormalCholeskySolver::SparseNormalCholeskySolver(
53 const LinearSolver::Options& options)
Sergey Sharybinf258e462013-08-15 14:50:08 +060054 : factor_(NULL),
55 cxsparse_factor_(NULL),
56 options_(options) {
Sameer Agarwalb0518732012-05-29 00:27:57 -070057}
Keir Mierle8ebb0732012-04-30 23:09:08 -070058
Richard Stebbing32530782014-04-26 07:42:23 +010059void SparseNormalCholeskySolver::FreeFactorization() {
Sameer Agarwalb0518732012-05-29 00:27:57 -070060#ifndef CERES_NO_SUITESPARSE
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -070061 if (factor_ != NULL) {
62 ss_.Free(factor_);
63 factor_ = NULL;
Keir Mierle8ebb0732012-04-30 23:09:08 -070064 }
Richard Stebbing32530782014-04-26 07:42:23 +010065#endif // CERES_NO_SUITESPARSE
Petter Strandmark1e3cbd92012-08-29 09:39:56 -070066
67#ifndef CERES_NO_CXSPARSE
68 if (cxsparse_factor_ != NULL) {
69 cxsparse_.Free(cxsparse_factor_);
70 cxsparse_factor_ = NULL;
71 }
72#endif // CERES_NO_CXSPARSE
Keir Mierle8ebb0732012-04-30 23:09:08 -070073}
74
Richard Stebbing32530782014-04-26 07:42:23 +010075SparseNormalCholeskySolver::~SparseNormalCholeskySolver() {
76 FreeFactorization();
77}
78
Keir Mierle8ebb0732012-04-30 23:09:08 -070079LinearSolver::Summary SparseNormalCholeskySolver::SolveImpl(
80 CompressedRowSparseMatrix* A,
81 const double* b,
82 const LinearSolver::PerSolveOptions& per_solve_options,
83 double * x) {
Sameer Agarwalb16e1182013-11-25 05:47:43 -080084
Sameer Agarwalb0518732012-05-29 00:27:57 -070085 const int num_cols = A->num_cols();
Sameer Agarwalf14f6bf2013-12-26 09:50:45 -080086 VectorRef(x, num_cols).setZero();
87 A->LeftMultiply(b, x);
Sameer Agarwalb0518732012-05-29 00:27:57 -070088
89 if (per_solve_options.D != NULL) {
90 // Temporarily append a diagonal block to the A matrix, but undo
91 // it before returning the matrix to the user.
Sameer Agarwal2b16b002013-12-20 15:22:26 -080092 scoped_ptr<CompressedRowSparseMatrix> regularizer;
93 if (A->col_blocks().size() > 0) {
94 regularizer.reset(CompressedRowSparseMatrix::CreateBlockDiagonalMatrix(
95 per_solve_options.D, A->col_blocks()));
96 } else {
97 regularizer.reset(new CompressedRowSparseMatrix(
98 per_solve_options.D, num_cols));
99 }
100 A->AppendRows(*regularizer);
Sameer Agarwalb0518732012-05-29 00:27:57 -0700101 }
102
Sameer Agarwalf14f6bf2013-12-26 09:50:45 -0800103 LinearSolver::Summary summary;
104 switch (options_.sparse_linear_algebra_library_type) {
105 case SUITE_SPARSE:
106 summary = SolveImplUsingSuiteSparse(A, per_solve_options, x);
107 break;
108 case CX_SPARSE:
109 summary = SolveImplUsingCXSparse(A, per_solve_options, x);
110 break;
111 default:
112 LOG(FATAL) << "Unknown sparse linear algebra library : "
113 << options_.sparse_linear_algebra_library_type;
114 }
Sameer Agarwalb0518732012-05-29 00:27:57 -0700115
Sameer Agarwalf14f6bf2013-12-26 09:50:45 -0800116 if (per_solve_options.D != NULL) {
117 A->DeleteRows(num_cols);
118 }
119
120 return summary;
121}
122
123#ifndef CERES_NO_CXSPARSE
124LinearSolver::Summary SparseNormalCholeskySolver::SolveImplUsingCXSparse(
125 CompressedRowSparseMatrix* A,
126 const LinearSolver::PerSolveOptions& per_solve_options,
127 double * rhs_and_solution) {
128 EventLogger event_logger("SparseNormalCholeskySolver::CXSparse::Solve");
129
130 LinearSolver::Summary summary;
131 summary.num_iterations = 1;
132 summary.termination_type = LINEAR_SOLVER_SUCCESS;
133 summary.message = "Success.";
Sameer Agarwalb0518732012-05-29 00:27:57 -0700134
135 // Compute the normal equations. J'J delta = J'f and solve them
136 // using a sparse Cholesky factorization. Notice that when compared
137 // to SuiteSparse we have to explicitly compute the transpose of Jt,
138 // and then the normal equations before they can be
139 // factorized. CHOLMOD/SuiteSparse on the other hand can just work
140 // off of Jt to compute the Cholesky factorization of the normal
141 // equations.
Sameer Agarwalf14f6bf2013-12-26 09:50:45 -0800142 if (outer_product_.get() == NULL) {
143 outer_product_.reset(
144 CompressedRowSparseMatrix::CreateOuterProductMatrixAndProgram(
145 *A, &pattern_));
Sameer Agarwalb0518732012-05-29 00:27:57 -0700146 }
Sameer Agarwalf14f6bf2013-12-26 09:50:45 -0800147
148 CompressedRowSparseMatrix::ComputeOuterProduct(
149 *A, pattern_, outer_product_.get());
150 cs_di AtA_view =
151 cxsparse_.CreateSparseMatrixTransposeView(outer_product_.get());
152 cs_di* AtA = &AtA_view;
153
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800154 event_logger.AddEvent("Setup");
155
Petter Strandmark1e3cbd92012-08-29 09:39:56 -0700156 // Compute symbolic factorization if not available.
Richard Stebbing32530782014-04-26 07:42:23 +0100157 if (options_.dynamic_sparsity) {
158 FreeFactorization();
159 }
Petter Strandmark1e3cbd92012-08-29 09:39:56 -0700160 if (cxsparse_factor_ == NULL) {
Sameer Agarwald5b93bf2013-04-26 21:17:49 -0700161 if (options_.use_postordering) {
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800162 cxsparse_factor_ = cxsparse_.BlockAnalyzeCholesky(AtA,
163 A->col_blocks(),
164 A->col_blocks());
Sameer Agarwald5b93bf2013-04-26 21:17:49 -0700165 } else {
Richard Stebbing32530782014-04-26 07:42:23 +0100166 if (options_.dynamic_sparsity) {
167 cxsparse_factor_ = cxsparse_.AnalyzeCholesky(AtA);
168 } else {
169 cxsparse_factor_ = cxsparse_.AnalyzeCholeskyWithNaturalOrdering(AtA);
170 }
Sameer Agarwald5b93bf2013-04-26 21:17:49 -0700171 }
Petter Strandmark1e3cbd92012-08-29 09:39:56 -0700172 }
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800173 event_logger.AddEvent("Analysis");
174
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800175 if (cxsparse_factor_ == NULL) {
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800176 summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
Sameer Agarwal89a592f2013-11-26 11:35:49 -0800177 summary.message =
178 "CXSparse failure. Unable to find symbolic factorization.";
Sameer Agarwalf14f6bf2013-12-26 09:50:45 -0800179 } else if (!cxsparse_.SolveCholesky(AtA, cxsparse_factor_, rhs_and_solution)) {
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800180 summary.termination_type = LINEAR_SOLVER_FAILURE;
Sameer Agarwalb0518732012-05-29 00:27:57 -0700181 }
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800182 event_logger.AddEvent("Solve");
Richard Stebbing32530782014-04-26 07:42:23 +0100183
Sameer Agarwalb0518732012-05-29 00:27:57 -0700184 return summary;
185}
186#else
187LinearSolver::Summary SparseNormalCholeskySolver::SolveImplUsingCXSparse(
188 CompressedRowSparseMatrix* A,
Sameer Agarwalb0518732012-05-29 00:27:57 -0700189 const LinearSolver::PerSolveOptions& per_solve_options,
Sameer Agarwalf14f6bf2013-12-26 09:50:45 -0800190 double * rhs_and_solution) {
Sameer Agarwalb0518732012-05-29 00:27:57 -0700191 LOG(FATAL) << "No CXSparse support in Ceres.";
Keir Mierleefe7ac62012-06-24 22:25:28 -0700192
193 // Unreachable but MSVC does not know this.
194 return LinearSolver::Summary();
Sameer Agarwalb0518732012-05-29 00:27:57 -0700195}
196#endif
197
198#ifndef CERES_NO_SUITESPARSE
199LinearSolver::Summary SparseNormalCholeskySolver::SolveImplUsingSuiteSparse(
200 CompressedRowSparseMatrix* A,
Sameer Agarwalb0518732012-05-29 00:27:57 -0700201 const LinearSolver::PerSolveOptions& per_solve_options,
Sameer Agarwalf14f6bf2013-12-26 09:50:45 -0800202 double * rhs_and_solution) {
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800203 EventLogger event_logger("SparseNormalCholeskySolver::SuiteSparse::Solve");
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800204 LinearSolver::Summary summary;
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800205 summary.termination_type = LINEAR_SOLVER_SUCCESS;
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800206 summary.num_iterations = 1;
Sameer Agarwal89a592f2013-11-26 11:35:49 -0800207 summary.message = "Success.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700208
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800209 const int num_cols = A->num_cols();
Sameer Agarwal2560b172013-04-19 08:19:11 -0700210 cholmod_sparse lhs = ss_.CreateSparseMatrixTransposeView(A);
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800211 event_logger.AddEvent("Setup");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700212
Richard Stebbing32530782014-04-26 07:42:23 +0100213 if (options_.dynamic_sparsity) {
214 FreeFactorization();
215 }
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700216 if (factor_ == NULL) {
Sameer Agarwal9189f4e2013-04-19 17:09:49 -0700217 if (options_.use_postordering) {
Sameer Agarwal79bde352013-11-21 21:33:51 -0800218 factor_ = ss_.BlockAnalyzeCholesky(&lhs,
219 A->col_blocks(),
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800220 A->row_blocks(),
Sameer Agarwal89a592f2013-11-26 11:35:49 -0800221 &summary.message);
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700222 } else {
Richard Stebbing32530782014-04-26 07:42:23 +0100223 if (options_.dynamic_sparsity) {
224 factor_ = ss_.AnalyzeCholesky(&lhs, &summary.message);
225 } else {
226 factor_ = ss_.AnalyzeCholeskyWithNaturalOrdering(&lhs, &summary.message);
227 }
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700228 }
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700229 }
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800230 event_logger.AddEvent("Analysis");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700231
Sameer Agarwal79bde352013-11-21 21:33:51 -0800232 if (factor_ == NULL) {
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800233 summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
Sameer Agarwal79bde352013-11-21 21:33:51 -0800234 return summary;
235 }
236
Sameer Agarwal89a592f2013-11-26 11:35:49 -0800237 summary.termination_type = ss_.Cholesky(&lhs, factor_, &summary.message);
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800238 if (summary.termination_type != LINEAR_SOLVER_SUCCESS) {
Sameer Agarwal79bde352013-11-21 21:33:51 -0800239 return summary;
240 }
241
Sameer Agarwalf14f6bf2013-12-26 09:50:45 -0800242 cholmod_dense* rhs = ss_.CreateDenseVector(rhs_and_solution, num_cols, num_cols);
243 cholmod_dense* solution = ss_.Solve(factor_, rhs, &summary.message);
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800244 event_logger.AddEvent("Solve");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700245
246 ss_.Free(rhs);
Sameer Agarwalf14f6bf2013-12-26 09:50:45 -0800247 if (solution != NULL) {
248 memcpy(rhs_and_solution, solution->x, num_cols * sizeof(*rhs_and_solution));
249 ss_.Free(solution);
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800250 } else {
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800251 summary.termination_type = LINEAR_SOLVER_FAILURE;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700252 }
253
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800254 event_logger.AddEvent("Teardown");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700255 return summary;
256}
Sameer Agarwalb0518732012-05-29 00:27:57 -0700257#else
258LinearSolver::Summary SparseNormalCholeskySolver::SolveImplUsingSuiteSparse(
259 CompressedRowSparseMatrix* A,
Sameer Agarwalb0518732012-05-29 00:27:57 -0700260 const LinearSolver::PerSolveOptions& per_solve_options,
Sameer Agarwalf14f6bf2013-12-26 09:50:45 -0800261 double * rhs_and_solution) {
Sameer Agarwalb0518732012-05-29 00:27:57 -0700262 LOG(FATAL) << "No SuiteSparse support in Ceres.";
Keir Mierleefe7ac62012-06-24 22:25:28 -0700263
264 // Unreachable but MSVC does not know this.
265 return LinearSolver::Summary();
Sameer Agarwalb0518732012-05-29 00:27:57 -0700266}
267#endif
Keir Mierle8ebb0732012-04-30 23:09:08 -0700268
269} // namespace internal
270} // namespace ceres
Sameer Agarwal8140f0f2013-03-12 09:45:08 -0700271
272#endif // !defined(CERES_NO_SUITESPARSE) || !defined(CERES_NO_CXSPARSE)