blob: 6d38535da62a628d4f5baefec60249a378bbad40 [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: keir@google.com (Keir Mierle)
30// sameeragarwal@google.com (Sameer Agarwal)
31
32#include "ceres/solver.h"
33
34#include <vector>
Keir Mierle6196cba2012-06-18 11:03:40 -070035#include "ceres/problem.h"
36#include "ceres/problem_impl.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070037#include "ceres/program.h"
38#include "ceres/solver_impl.h"
39#include "ceres/stringprintf.h"
Petter Strandmark76533b32012-09-04 22:08:50 -070040#include "ceres/wall_time.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070041
42namespace ceres {
Sameer Agarwal977be7c2013-01-26 16:01:54 -080043namespace {
44
45void StringifyOrdering(const vector<int>& ordering, string* report) {
46 if (ordering.size() == 0) {
47 internal::StringAppendF(report, "AUTOMATIC");
48 return;
49 }
50
51 for (int i = 0; i < ordering.size() - 1; ++i) {
52 internal::StringAppendF(report, "%d, ", ordering[i]);
53 }
54 internal::StringAppendF(report, "%d", ordering.back());
55}
56
57} // namespace
Keir Mierle8ebb0732012-04-30 23:09:08 -070058
Sameer Agarwalba8d9672012-10-02 00:48:57 -070059Solver::Options::~Options() {
Sameer Agarwal68b32a92012-10-06 23:10:51 -070060 delete linear_solver_ordering;
Sameer Agarwalba8d9672012-10-02 00:48:57 -070061 delete inner_iteration_ordering;
62}
63
Keir Mierle8ebb0732012-04-30 23:09:08 -070064Solver::~Solver() {}
65
Keir Mierle8ebb0732012-04-30 23:09:08 -070066void Solver::Solve(const Solver::Options& options,
67 Problem* problem,
68 Solver::Summary* summary) {
Petter Strandmark76533b32012-09-04 22:08:50 -070069 double start_time_seconds = internal::WallTimeInSeconds();
Keir Mierle6196cba2012-06-18 11:03:40 -070070 internal::ProblemImpl* problem_impl =
71 CHECK_NOTNULL(problem)->problem_impl_.get();
72 internal::SolverImpl::Solve(options, problem_impl, summary);
Petter Strandmark76533b32012-09-04 22:08:50 -070073 summary->total_time_in_seconds =
74 internal::WallTimeInSeconds() - start_time_seconds;
Keir Mierle8ebb0732012-04-30 23:09:08 -070075}
76
77void Solve(const Solver::Options& options,
78 Problem* problem,
79 Solver::Summary* summary) {
Keir Mierle6196cba2012-06-18 11:03:40 -070080 Solver solver;
81 solver.Solve(options, problem, summary);
Keir Mierle8ebb0732012-04-30 23:09:08 -070082}
83
84Solver::Summary::Summary()
85 // Invalid values for most fields, to ensure that we are not
86 // accidentally reporting default values.
Sameer Agarwald010de52013-02-15 14:26:56 -080087 : minimizer_type(TRUST_REGION),
88 termination_type(DID_NOT_RUN),
Keir Mierle8ebb0732012-04-30 23:09:08 -070089 initial_cost(-1.0),
90 final_cost(-1.0),
91 fixed_cost(-1.0),
92 num_successful_steps(-1),
93 num_unsuccessful_steps(-1),
94 preprocessor_time_in_seconds(-1.0),
95 minimizer_time_in_seconds(-1.0),
Sameer Agarwalfa015192012-06-11 14:21:42 -070096 postprocessor_time_in_seconds(-1.0),
Keir Mierle8ebb0732012-04-30 23:09:08 -070097 total_time_in_seconds(-1.0),
Sameer Agarwal42a84b82013-02-01 12:22:53 -080098 linear_solver_time_in_seconds(-1.0),
99 residual_evaluation_time_in_seconds(-1.0),
100 jacobian_evaluation_time_in_seconds(-1.0),
Keir Mierle8ebb0732012-04-30 23:09:08 -0700101 num_parameter_blocks(-1),
102 num_parameters(-1),
103 num_residual_blocks(-1),
104 num_residuals(-1),
105 num_parameter_blocks_reduced(-1),
106 num_parameters_reduced(-1),
107 num_residual_blocks_reduced(-1),
108 num_residuals_reduced(-1),
Keir Mierle8ebb0732012-04-30 23:09:08 -0700109 num_threads_given(-1),
110 num_threads_used(-1),
111 num_linear_solver_threads_given(-1),
112 num_linear_solver_threads_used(-1),
113 linear_solver_type_given(SPARSE_NORMAL_CHOLESKY),
114 linear_solver_type_used(SPARSE_NORMAL_CHOLESKY),
115 preconditioner_type(IDENTITY),
Sameer Agarwal97fb6d92012-06-17 10:08:19 -0700116 trust_region_strategy_type(LEVENBERG_MARQUARDT),
Sameer Agarwald010de52013-02-15 14:26:56 -0800117 inner_iterations(false),
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800118 sparse_linear_algebra_library(SUITE_SPARSE),
Sameer Agarwald010de52013-02-15 14:26:56 -0800119 line_search_direction_type(LBFGS),
120 line_search_type(ARMIJO) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700121}
122
123string Solver::Summary::BriefReport() const {
124 string report = "Ceres Solver Report: ";
125 if (termination_type == DID_NOT_RUN) {
126 CHECK(!error.empty())
127 << "Solver terminated with DID_NOT_RUN but the solver did not "
128 << "return a reason. This is a Ceres error. Please report this "
129 << "to the Ceres team";
130 return report + "Termination: DID_NOT_RUN, because " + error;
131 }
132
133 internal::StringAppendF(&report, "Iterations: %d",
134 num_successful_steps + num_unsuccessful_steps);
135 internal::StringAppendF(&report, ", Initial cost: %e", initial_cost);
136
137 // If the solver failed or was aborted, then the final_cost has no
138 // meaning.
139 if (termination_type != NUMERICAL_FAILURE &&
140 termination_type != USER_ABORT) {
141 internal::StringAppendF(&report, ", Final cost: %e", final_cost);
142 }
143
144 internal::StringAppendF(&report, ", Termination: %s.",
145 SolverTerminationTypeToString(termination_type));
146 return report;
147};
148
Sameer Agarwald010de52013-02-15 14:26:56 -0800149using internal::StringAppendF;
150
Keir Mierle8ebb0732012-04-30 23:09:08 -0700151string Solver::Summary::FullReport() const {
Sameer Agarwald010de52013-02-15 14:26:56 -0800152
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800153
Keir Mierle8ebb0732012-04-30 23:09:08 -0700154 string report =
155 "\n"
156 "Ceres Solver Report\n"
157 "-------------------\n";
158
159 if (termination_type == DID_NOT_RUN) {
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800160 StringAppendF(&report, " Original\n");
161 StringAppendF(&report, "Parameter blocks % 10d\n",
162 num_parameter_blocks);
163 StringAppendF(&report, "Parameters % 10d\n",
164 num_parameters);
165 StringAppendF(&report, "Residual blocks % 10d\n",
166 num_residual_blocks);
167 StringAppendF(&report, "Residuals % 10d\n\n",
168 num_residuals);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700169 } else {
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800170 StringAppendF(&report, "%45s %21s\n", "Original", "Reduced");
171 StringAppendF(&report, "Parameter blocks % 25d% 25d\n",
172 num_parameter_blocks, num_parameter_blocks_reduced);
173 StringAppendF(&report, "Parameters % 25d% 25d\n",
174 num_parameters, num_parameters_reduced);
175 StringAppendF(&report, "Residual blocks % 25d% 25d\n",
176 num_residual_blocks, num_residual_blocks_reduced);
177 StringAppendF(&report, "Residual % 25d% 25d\n",
178 num_residuals, num_residuals_reduced);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700179 }
180
Sameer Agarwald010de52013-02-15 14:26:56 -0800181 // TODO(sameeragarwal): Refactor this into separate functions.
Sameer Agarwal97fb6d92012-06-17 10:08:19 -0700182
Sameer Agarwald010de52013-02-15 14:26:56 -0800183 if (minimizer_type == TRUST_REGION) {
184 StringAppendF(&report, "\nMinimizer %19s\n", "TRUST_REGION");
185 if (linear_solver_type_used == SPARSE_NORMAL_CHOLESKY ||
186 linear_solver_type_used == SPARSE_SCHUR ||
187 (linear_solver_type_used == ITERATIVE_SCHUR &&
Sameer Agarwal290b9752013-02-17 16:50:37 -0800188 (preconditioner_type == CLUSTER_JACOBI ||
Sameer Agarwald010de52013-02-15 14:26:56 -0800189 preconditioner_type == CLUSTER_TRIDIAGONAL))) {
190 StringAppendF(&report, "\nSparse Linear Algebra Library %15s\n",
191 SparseLinearAlgebraLibraryTypeToString(
192 sparse_linear_algebra_library));
Sameer Agarwal1e289202012-08-21 18:00:54 -0700193 }
Sameer Agarwald010de52013-02-15 14:26:56 -0800194
195 StringAppendF(&report, "Trust Region Strategy %19s",
196 TrustRegionStrategyTypeToString(
197 trust_region_strategy_type));
198 if (trust_region_strategy_type == DOGLEG) {
199 if (dogleg_type == TRADITIONAL_DOGLEG) {
200 StringAppendF(&report, " (TRADITIONAL)");
201 } else {
202 StringAppendF(&report, " (SUBSPACE)");
203 }
204 }
205 StringAppendF(&report, "\n");
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800206 StringAppendF(&report, "\n");
Sameer Agarwal1e289202012-08-21 18:00:54 -0700207
Sameer Agarwald010de52013-02-15 14:26:56 -0800208 StringAppendF(&report, "%45s %21s\n", "Given", "Used");
209 StringAppendF(&report, "Linear solver %25s%25s\n",
210 LinearSolverTypeToString(linear_solver_type_given),
211 LinearSolverTypeToString(linear_solver_type_used));
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800212
Sameer Agarwald010de52013-02-15 14:26:56 -0800213 if (linear_solver_type_given == CGNR ||
214 linear_solver_type_given == ITERATIVE_SCHUR) {
215 StringAppendF(&report, "Preconditioner %25s%25s\n",
216 PreconditionerTypeToString(preconditioner_type),
217 PreconditionerTypeToString(preconditioner_type));
218 } else {
219 StringAppendF(&report, "Preconditioner %25s%25s\n",
220 "N/A", "N/A");
221 }
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800222
Sameer Agarwald010de52013-02-15 14:26:56 -0800223 StringAppendF(&report, "Threads: % 25d% 25d\n",
224 num_threads_given, num_threads_used);
225 StringAppendF(&report, "Linear solver threads % 23d% 25d\n",
226 num_linear_solver_threads_given,
227 num_linear_solver_threads_used);
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800228
Sameer Agarwald010de52013-02-15 14:26:56 -0800229 if (IsSchurType(linear_solver_type_used)) {
230 string given;
231 StringifyOrdering(linear_solver_ordering_given, &given);
232 string used;
233 StringifyOrdering(linear_solver_ordering_used, &used);
234 StringAppendF(&report,
235 "Linear solver ordering %22s %24s\n",
236 given.c_str(),
237 used.c_str());
238 }
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800239
Sameer Agarwald010de52013-02-15 14:26:56 -0800240 if (inner_iterations) {
241 string given;
242 StringifyOrdering(inner_iteration_ordering_given, &given);
243 string used;
244 StringifyOrdering(inner_iteration_ordering_used, &used);
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800245 StringAppendF(&report,
246 "Inner iteration ordering %20s %24s\n",
247 given.c_str(),
248 used.c_str());
Sameer Agarwald010de52013-02-15 14:26:56 -0800249 }
250
251 if (termination_type == DID_NOT_RUN) {
252 CHECK(!error.empty())
253 << "Solver terminated with DID_NOT_RUN but the solver did not "
254 << "return a reason. This is a Ceres error. Please report this "
255 << "to the Ceres team";
256 StringAppendF(&report, "Termination: %20s\n",
257 "DID_NOT_RUN");
258 StringAppendF(&report, "Reason: %s\n", error.c_str());
259 return report;
260 }
261
262 StringAppendF(&report, "\nCost:\n");
263 StringAppendF(&report, "Initial % 30e\n", initial_cost);
264 if (termination_type != NUMERICAL_FAILURE && termination_type != USER_ABORT) {
265 StringAppendF(&report, "Final % 30e\n", final_cost);
266 StringAppendF(&report, "Change % 30e\n",
267 initial_cost - final_cost);
268 }
269
270 StringAppendF(&report, "\nNumber of iterations:\n");
271 StringAppendF(&report, "Successful % 20d\n",
272 num_successful_steps);
273 StringAppendF(&report, "Unsuccessful % 20d\n",
274 num_unsuccessful_steps);
275 StringAppendF(&report, "Total % 20d\n",
276 num_successful_steps + num_unsuccessful_steps);
277
278 StringAppendF(&report, "\nTime (in seconds):\n");
279 StringAppendF(&report, "Preprocessor %25.3f\n",
280 preprocessor_time_in_seconds);
281 StringAppendF(&report, "\n Residual Evaluations %22.3f\n",
282 residual_evaluation_time_in_seconds);
283 StringAppendF(&report, " Jacobian Evaluations %22.3f\n",
284 jacobian_evaluation_time_in_seconds);
285 StringAppendF(&report, " Linear Solver %23.3f\n",
286 linear_solver_time_in_seconds);
287 StringAppendF(&report, "Minimizer %25.3f\n\n",
288 minimizer_time_in_seconds);
289
290 StringAppendF(&report, "Postprocessor %24.3f\n",
291 postprocessor_time_in_seconds);
292
293 StringAppendF(&report, "Total %25.3f\n\n",
294 total_time_in_seconds);
295
296 StringAppendF(&report, "Termination: %25s\n",
297 SolverTerminationTypeToString(termination_type));
298 } else {
299 // LINE_SEARCH
300 StringAppendF(&report, "\nMinimizer %19s\n", "LINE_SEARCH");
301 if (line_search_direction_type == LBFGS) {
302 StringAppendF(&report, "Line search direction %19s(%d)\n",
303 LineSearchDirectionTypeToString(line_search_direction_type),
304 max_lbfgs_rank);
305 } else {
306 StringAppendF(&report, "Line search direction %19s\n",
307 LineSearchDirectionTypeToString(line_search_direction_type));
308 }
309 StringAppendF(&report, "Line search type %19s\n",
310 LineSearchTypeToString(line_search_type));
311
312 StringAppendF(&report, "\n");
313
314 StringAppendF(&report, "%45s %21s\n", "Given", "Used");
315 StringAppendF(&report, "Threads: % 25d% 25d\n",
316 num_threads_given, num_threads_used);
317
318 if (termination_type == DID_NOT_RUN) {
319 CHECK(!error.empty())
320 << "Solver terminated with DID_NOT_RUN but the solver did not "
321 << "return a reason. This is a Ceres error. Please report this "
322 << "to the Ceres team";
323 StringAppendF(&report, "Termination: %20s\n",
324 "DID_NOT_RUN");
325 StringAppendF(&report, "Reason: %s\n", error.c_str());
326 return report;
327 }
328
329 StringAppendF(&report, "\nCost:\n");
330 StringAppendF(&report, "Initial % 30e\n", initial_cost);
331 if (termination_type != NUMERICAL_FAILURE && termination_type != USER_ABORT) {
332 StringAppendF(&report, "Final % 30e\n", final_cost);
333 StringAppendF(&report, "Change % 30e\n",
334 initial_cost - final_cost);
335 }
336
337 StringAppendF(&report, "\nNumber of iterations: % 20ld\n", iterations.size() - 1);
338
339 StringAppendF(&report, "\nTime (in seconds):\n");
340 StringAppendF(&report, "Preprocessor %25.3f\n",
341 preprocessor_time_in_seconds);
342 StringAppendF(&report, "\n Residual Evaluations %22.3f\n",
343 residual_evaluation_time_in_seconds);
344 StringAppendF(&report, " Jacobian Evaluations %22.3f\n",
345 jacobian_evaluation_time_in_seconds);
346 StringAppendF(&report, "Minimizer %25.3f\n\n",
347 minimizer_time_in_seconds);
348
349 StringAppendF(&report, "Postprocessor %24.3f\n",
350 postprocessor_time_in_seconds);
351
352 StringAppendF(&report, "Total %25.3f\n\n",
353 total_time_in_seconds);
354
355 StringAppendF(&report, "Termination: %25s\n",
356 SolverTerminationTypeToString(termination_type));
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800357 }
Keir Mierle8ebb0732012-04-30 23:09:08 -0700358
Keir Mierle8ebb0732012-04-30 23:09:08 -0700359 return report;
360};
361
362} // namespace ceres