blob: d38982a0d7999f06950ee3b81cc90c6c1e29ca8a [file] [log] [blame]
Keir Mierle8ebb0732012-04-30 23:09:08 -07001// Ceres Solver - A fast non-linear least squares minimizer
Keir Mierle7492b0d2015-03-17 22:30:16 -07002// Copyright 2015 Google Inc. All rights reserved.
3// 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: keir@google.com (Keir Mierle)
30// sameeragarwal@google.com (Sameer Agarwal)
31
32#include "ceres/solver.h"
33
Sameer Agarwal1745dd62014-06-05 21:30:13 -070034#include <algorithm>
Keir Mierle7c4e8a42018-03-30 16:16:59 -070035#include <memory>
Mike Vitusf408f892018-02-22 10:28:39 -080036#include <sstream> // NOLINT
Keir Mierle8ebb0732012-04-30 23:09:08 -070037#include <vector>
Keir Mierle7c4e8a42018-03-30 16:16:59 -070038
Mike Vitusf408f892018-02-22 10:28:39 -080039#include "ceres/casts.h"
40#include "ceres/context.h"
41#include "ceres/context_impl.h"
Sameer Agarwal1cfec3c2014-08-18 15:46:02 -070042#include "ceres/detect_structure.h"
Sameer Agarwald76da162014-09-07 18:42:49 -070043#include "ceres/gradient_checking_cost_function.h"
Sameer Agarwal1745dd62014-06-05 21:30:13 -070044#include "ceres/internal/port.h"
45#include "ceres/parameter_block_ordering.h"
46#include "ceres/preprocessor.h"
Keir Mierle6196cba2012-06-18 11:03:40 -070047#include "ceres/problem.h"
48#include "ceres/problem_impl.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070049#include "ceres/program.h"
Sameer Agarwal1cfec3c2014-08-18 15:46:02 -070050#include "ceres/schur_templates.h"
Sameer Agarwald76da162014-09-07 18:42:49 -070051#include "ceres/solver_utils.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070052#include "ceres/stringprintf.h"
Sameer Agarwal70af9a52014-06-02 09:40:08 -070053#include "ceres/types.h"
Sameer Agarwal70af9a52014-06-02 09:40:08 -070054#include "ceres/wall_time.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070055
56namespace ceres {
Sameer Agarwal977be7c2013-01-26 16:01:54 -080057namespace {
58
Sameer Agarwalbcc865f2014-12-17 07:35:09 -080059using std::map;
Sameer Agarwal05a07ec2015-01-07 15:10:46 -080060using std::string;
61using std::vector;
Sameer Agarwale9397ad2018-05-08 13:20:17 -070062using internal::StringAppendF;
63using internal::StringPrintf;
Sameer Agarwalbcc865f2014-12-17 07:35:09 -080064
Sameer Agarwal70af9a52014-06-02 09:40:08 -070065#define OPTION_OP(x, y, OP) \
66 if (!(options.x OP y)) { \
67 std::stringstream ss; \
68 ss << "Invalid configuration. "; \
69 ss << string("Solver::Options::" #x " = ") << options.x << ". "; \
70 ss << "Violated constraint: "; \
71 ss << string("Solver::Options::" #x " " #OP " "#y); \
72 *error = ss.str(); \
Sameer Agarwal16936452014-05-29 15:39:01 -070073 return false; \
74 }
75
Sameer Agarwal70af9a52014-06-02 09:40:08 -070076#define OPTION_OP_OPTION(x, y, OP) \
77 if (!(options.x OP options.y)) { \
78 std::stringstream ss; \
79 ss << "Invalid configuration. "; \
80 ss << string("Solver::Options::" #x " = ") << options.x << ". "; \
81 ss << string("Solver::Options::" #y " = ") << options.y << ". "; \
82 ss << "Violated constraint: "; \
Sameer Agarwal8de27be2014-08-19 08:22:40 -070083 ss << string("Solver::Options::" #x); \
Sameer Agarwal70af9a52014-06-02 09:40:08 -070084 ss << string(#OP " Solver::Options::" #y "."); \
85 *error = ss.str(); \
Sameer Agarwal16936452014-05-29 15:39:01 -070086 return false; \
87 }
88
Sameer Agarwal70af9a52014-06-02 09:40:08 -070089#define OPTION_GE(x, y) OPTION_OP(x, y, >=);
90#define OPTION_GT(x, y) OPTION_OP(x, y, >);
91#define OPTION_LE(x, y) OPTION_OP(x, y, <=);
92#define OPTION_LT(x, y) OPTION_OP(x, y, <);
Sameer Agarwal8de27be2014-08-19 08:22:40 -070093#define OPTION_LE_OPTION(x, y) OPTION_OP_OPTION(x, y, <=)
94#define OPTION_LT_OPTION(x, y) OPTION_OP_OPTION(x, y, <)
Sameer Agarwal16936452014-05-29 15:39:01 -070095
96bool CommonOptionsAreValid(const Solver::Options& options, string* error) {
97 OPTION_GE(max_num_iterations, 0);
98 OPTION_GE(max_solver_time_in_seconds, 0.0);
99 OPTION_GE(function_tolerance, 0.0);
100 OPTION_GE(gradient_tolerance, 0.0);
101 OPTION_GE(parameter_tolerance, 0.0);
102 OPTION_GT(num_threads, 0);
Sameer Agarwal16936452014-05-29 15:39:01 -0700103 if (options.check_gradients) {
104 OPTION_GT(gradient_check_relative_precision, 0.0);
Sameer Agarwal79a28d12016-08-31 06:47:45 -0700105 OPTION_GT(gradient_check_numeric_derivative_relative_step_size, 0.0);
Sameer Agarwal16936452014-05-29 15:39:01 -0700106 }
107 return true;
108}
109
110bool TrustRegionOptionsAreValid(const Solver::Options& options, string* error) {
111 OPTION_GT(initial_trust_region_radius, 0.0);
112 OPTION_GT(min_trust_region_radius, 0.0);
113 OPTION_GT(max_trust_region_radius, 0.0);
114 OPTION_LE_OPTION(min_trust_region_radius, max_trust_region_radius);
115 OPTION_LE_OPTION(min_trust_region_radius, initial_trust_region_radius);
116 OPTION_LE_OPTION(initial_trust_region_radius, max_trust_region_radius);
Sameer Agarwal29fe0722014-06-01 00:25:34 -0700117 OPTION_GE(min_relative_decrease, 0.0);
Sameer Agarwal16936452014-05-29 15:39:01 -0700118 OPTION_GE(min_lm_diagonal, 0.0);
119 OPTION_GE(max_lm_diagonal, 0.0);
120 OPTION_LE_OPTION(min_lm_diagonal, max_lm_diagonal);
121 OPTION_GE(max_num_consecutive_invalid_steps, 0);
122 OPTION_GT(eta, 0.0);
Sameer Agarwal6f89d852014-08-27 11:51:50 -0700123 OPTION_GE(min_linear_solver_iterations, 0);
Sameer Agarwal16936452014-05-29 15:39:01 -0700124 OPTION_GE(max_linear_solver_iterations, 1);
125 OPTION_LE_OPTION(min_linear_solver_iterations, max_linear_solver_iterations);
126
127 if (options.use_inner_iterations) {
128 OPTION_GE(inner_iteration_tolerance, 0.0);
129 }
130
Keir Mierle7bdceb42018-01-10 17:14:57 -0800131 if (options.use_inner_iterations &&
132 options.evaluation_callback != NULL) {
133 *error = "Inner iterations (use_inner_iterations = true) can't be "
134 "combined with an evaluation callback "
135 "(options.evaluation_callback != NULL).";
136 return false;
137 }
138
Sameer Agarwal16936452014-05-29 15:39:01 -0700139 if (options.use_nonmonotonic_steps) {
140 OPTION_GT(max_consecutive_nonmonotonic_steps, 0);
141 }
142
Sameer Agarwalb44cfde2014-09-29 07:53:54 -0700143 if (options.linear_solver_type == ITERATIVE_SCHUR &&
144 options.use_explicit_schur_complement &&
145 options.preconditioner_type != SCHUR_JACOBI) {
Sameer Agarwal46b84612014-09-29 15:10:58 -0700146 *error = "use_explicit_schur_complement only supports "
Sameer Agarwalb44cfde2014-09-29 07:53:54 -0700147 "SCHUR_JACOBI as the preconditioner.";
148 return false;
149 }
150
Sameer Agarwale9397ad2018-05-08 13:20:17 -0700151 if (options.dense_linear_algebra_library_type == LAPACK &&
152 !IsDenseLinearAlgebraLibraryTypeAvailable(LAPACK) &&
153 (options.linear_solver_type == DENSE_NORMAL_CHOLESKY ||
154 options.linear_solver_type == DENSE_QR ||
155 options.linear_solver_type == DENSE_SCHUR)) {
156 *error = StringPrintf(
157 "Can't use %s with "
158 "Solver::Options::dense_linear_algebra_library_type = LAPACK "
159 "because LAPACK was not enabled when Ceres was built.",
160 LinearSolverTypeToString(options.linear_solver_type));
Sameer Agarwal16936452014-05-29 15:39:01 -0700161 return false;
162 }
163
Alex Stewart60cc5202014-12-29 13:05:07 +0000164 if (options.sparse_linear_algebra_library_type == NO_SPARSE) {
Sameer Agarwale9397ad2018-05-08 13:20:17 -0700165 const char* error_template =
166 "Can't use %s with "
167 "Solver::Options::sparse_linear_algebra_library_type = NO_SPARSE.";
168 const char* name = nullptr;
169
170 if (options.linear_solver_type == SPARSE_NORMAL_CHOLESKY ||
171 options.linear_solver_type == SPARSE_SCHUR) {
172 name = LinearSolverTypeToString(options.linear_solver_type);
173 } else if (options.linear_solver_type == ITERATIVE_SCHUR &&
174 (options.preconditioner_type == CLUSTER_JACOBI ||
175 options.preconditioner_type == CLUSTER_TRIDIAGONAL)) {
176 name = PreconditionerTypeToString(options.preconditioner_type);
177 }
178
179 if (name != nullptr) {
180 *error = StringPrintf(error_template, name);
Alex Stewart60cc5202014-12-29 13:05:07 +0000181 return false;
Sameer Agarwale9397ad2018-05-08 13:20:17 -0700182 }
183 } else if (!IsSparseLinearAlgebraLibraryTypeAvailable(
184 options.sparse_linear_algebra_library_type)) {
185 const char* error_template =
186 "Can't use %s with "
187 "Solver::Options::sparse_linear_algebra_library_type = %s, "
188 "because support was not enabled when Ceres Solver was built.";
189 const char* name = nullptr;
190 if (options.linear_solver_type == SPARSE_NORMAL_CHOLESKY ||
191 options.linear_solver_type == SPARSE_SCHUR) {
192 name = LinearSolverTypeToString(options.linear_solver_type);
193 } else if (options.linear_solver_type == ITERATIVE_SCHUR &&
194 (options.preconditioner_type == CLUSTER_JACOBI ||
195 options.preconditioner_type == CLUSTER_TRIDIAGONAL)) {
196 name = PreconditionerTypeToString(options.preconditioner_type);
197 }
198
199 if (name != nullptr) {
200 *error = StringPrintf(error_template,
201 name,
202 SparseLinearAlgebraLibraryTypeToString(
203 options.sparse_linear_algebra_library_type));
Alex Stewart60cc5202014-12-29 13:05:07 +0000204 return false;
205 }
206 }
207
Sameer Agarwal16936452014-05-29 15:39:01 -0700208 if (options.trust_region_strategy_type == DOGLEG) {
209 if (options.linear_solver_type == ITERATIVE_SCHUR ||
210 options.linear_solver_type == CGNR) {
211 *error = "DOGLEG only supports exact factorization based linear "
212 "solvers. If you want to use an iterative solver please "
213 "use LEVENBERG_MARQUARDT as the trust_region_strategy_type";
214 return false;
215 }
216 }
217
218 if (options.trust_region_minimizer_iterations_to_dump.size() > 0 &&
219 options.trust_region_problem_dump_format_type != CONSOLE &&
220 options.trust_region_problem_dump_directory.empty()) {
221 *error = "Solver::Options::trust_region_problem_dump_directory is empty.";
222 return false;
223 }
224
Sameer Agarwal603277d2014-06-10 10:00:45 -0700225 if (options.dynamic_sparsity &&
226 options.linear_solver_type != SPARSE_NORMAL_CHOLESKY) {
227 *error = "Dynamic sparsity is only supported with SPARSE_NORMAL_CHOLESKY.";
228 return false;
229 }
230
Sameer Agarwal16936452014-05-29 15:39:01 -0700231 return true;
232}
233
234bool LineSearchOptionsAreValid(const Solver::Options& options, string* error) {
235 OPTION_GT(max_lbfgs_rank, 0);
236 OPTION_GT(min_line_search_step_size, 0.0);
237 OPTION_GT(max_line_search_step_contraction, 0.0);
238 OPTION_LT(max_line_search_step_contraction, 1.0);
239 OPTION_LT_OPTION(max_line_search_step_contraction,
240 min_line_search_step_contraction);
241 OPTION_LE(min_line_search_step_contraction, 1.0);
242 OPTION_GT(max_num_line_search_step_size_iterations, 0);
243 OPTION_GT(line_search_sufficient_function_decrease, 0.0);
244 OPTION_LT_OPTION(line_search_sufficient_function_decrease,
245 line_search_sufficient_curvature_decrease);
246 OPTION_LT(line_search_sufficient_curvature_decrease, 1.0);
247 OPTION_GT(max_line_search_step_expansion, 1.0);
248
249 if ((options.line_search_direction_type == ceres::BFGS ||
250 options.line_search_direction_type == ceres::LBFGS) &&
251 options.line_search_type != ceres::WOLFE) {
252 *error =
Sameer Agarwal70af9a52014-06-02 09:40:08 -0700253 string("Invalid configuration: Solver::Options::line_search_type = ")
254 + string(LineSearchTypeToString(options.line_search_type))
255 + string(". When using (L)BFGS, "
256 "Solver::Options::line_search_type must be set to WOLFE.");
Sameer Agarwal16936452014-05-29 15:39:01 -0700257 return false;
258 }
259
260 // Warn user if they have requested BISECTION interpolation, but constraints
261 // on max/min step size change during line search prevent bisection scaling
262 // from occurring. Warn only, as this is likely a user mistake, but one which
263 // does not prevent us from continuing.
264 LOG_IF(WARNING,
265 (options.line_search_interpolation_type == ceres::BISECTION &&
266 (options.max_line_search_step_contraction > 0.5 ||
267 options.min_line_search_step_contraction < 0.5)))
268 << "Line search interpolation type is BISECTION, but specified "
269 << "max_line_search_step_contraction: "
270 << options.max_line_search_step_contraction << ", and "
271 << "min_line_search_step_contraction: "
272 << options.min_line_search_step_contraction
273 << ", prevent bisection (0.5) scaling, continuing with solve regardless.";
274
275 return true;
276}
277
Sameer Agarwal70af9a52014-06-02 09:40:08 -0700278#undef OPTION_OP
279#undef OPTION_OP_OPTION
Sameer Agarwal16936452014-05-29 15:39:01 -0700280#undef OPTION_GT
281#undef OPTION_GE
282#undef OPTION_LE
283#undef OPTION_LT
284#undef OPTION_LE_OPTION
285#undef OPTION_LT_OPTION
286
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800287void StringifyOrdering(const vector<int>& ordering, string* report) {
288 if (ordering.size() == 0) {
289 internal::StringAppendF(report, "AUTOMATIC");
290 return;
291 }
292
293 for (int i = 0; i < ordering.size() - 1; ++i) {
Sameer Agarwal1cfec3c2014-08-18 15:46:02 -0700294 internal::StringAppendF(report, "%d,", ordering[i]);
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800295 }
296 internal::StringAppendF(report, "%d", ordering.back());
297}
298
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700299void SummarizeGivenProgram(const internal::Program& program,
300 Solver::Summary* summary) {
301 summary->num_parameter_blocks = program.NumParameterBlocks();
302 summary->num_parameters = program.NumParameters();
303 summary->num_effective_parameters = program.NumEffectiveParameters();
304 summary->num_residual_blocks = program.NumResidualBlocks();
305 summary->num_residuals = program.NumResiduals();
306}
307
308void SummarizeReducedProgram(const internal::Program& program,
309 Solver::Summary* summary) {
310 summary->num_parameter_blocks_reduced = program.NumParameterBlocks();
311 summary->num_parameters_reduced = program.NumParameters();
312 summary->num_effective_parameters_reduced = program.NumEffectiveParameters();
313 summary->num_residual_blocks_reduced = program.NumResidualBlocks();
314 summary->num_residuals_reduced = program.NumResiduals();
315}
316
317void PreSolveSummarize(const Solver::Options& options,
318 const internal::ProblemImpl* problem,
319 Solver::Summary* summary) {
320 SummarizeGivenProgram(problem->program(), summary);
321 internal::OrderingToGroupSizes(options.linear_solver_ordering.get(),
322 &(summary->linear_solver_ordering_given));
323 internal::OrderingToGroupSizes(options.inner_iteration_ordering.get(),
324 &(summary->inner_iteration_ordering_given));
325
Sameer Agarwal8de27be2014-08-19 08:22:40 -0700326 summary->dense_linear_algebra_library_type = options.dense_linear_algebra_library_type; // NOLINT
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700327 summary->dogleg_type = options.dogleg_type;
328 summary->inner_iteration_time_in_seconds = 0.0;
Sameer Agarwal992ae552015-12-01 07:25:06 -0800329 summary->num_line_search_steps = 0;
Alex Stewart9ad59a72014-11-17 22:20:46 +0000330 summary->line_search_cost_evaluation_time_in_seconds = 0.0;
331 summary->line_search_gradient_evaluation_time_in_seconds = 0.0;
332 summary->line_search_polynomial_minimization_time_in_seconds = 0.0;
333 summary->line_search_total_time_in_seconds = 0.0;
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700334 summary->inner_iterations_given = options.use_inner_iterations;
Sameer Agarwal8de27be2014-08-19 08:22:40 -0700335 summary->line_search_direction_type = options.line_search_direction_type; // NOLINT
336 summary->line_search_interpolation_type = options.line_search_interpolation_type; // NOLINT
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700337 summary->line_search_type = options.line_search_type;
338 summary->linear_solver_type_given = options.linear_solver_type;
339 summary->max_lbfgs_rank = options.max_lbfgs_rank;
340 summary->minimizer_type = options.minimizer_type;
Sameer Agarwal8de27be2014-08-19 08:22:40 -0700341 summary->nonlinear_conjugate_gradient_type = options.nonlinear_conjugate_gradient_type; // NOLINT
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700342 summary->num_threads_given = options.num_threads;
343 summary->preconditioner_type_given = options.preconditioner_type;
Sameer Agarwal8de27be2014-08-19 08:22:40 -0700344 summary->sparse_linear_algebra_library_type = options.sparse_linear_algebra_library_type; // NOLINT
345 summary->trust_region_strategy_type = options.trust_region_strategy_type; // NOLINT
346 summary->visibility_clustering_type = options.visibility_clustering_type; // NOLINT
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700347}
348
349void PostSolveSummarize(const internal::PreprocessedProblem& pp,
350 Solver::Summary* summary) {
351 internal::OrderingToGroupSizes(pp.options.linear_solver_ordering.get(),
352 &(summary->linear_solver_ordering_used));
353 internal::OrderingToGroupSizes(pp.options.inner_iteration_ordering.get(),
354 &(summary->inner_iteration_ordering_used));
355
Sameer Agarwal8de27be2014-08-19 08:22:40 -0700356 summary->inner_iterations_used = pp.inner_iteration_minimizer.get() != NULL; // NOLINT
Sameer Agarwal1ebaff82017-02-27 14:58:47 -0800357 summary->linear_solver_type_used = pp.linear_solver_options.type;
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700358 summary->num_threads_used = pp.options.num_threads;
Sameer Agarwal3d933752018-02-27 17:02:04 -0800359 summary->preconditioner_type_used = pp.options.preconditioner_type;
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700360
Sameer Agarwald76da162014-09-07 18:42:49 -0700361 internal::SetSummaryFinalCost(summary);
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700362
363 if (pp.reduced_program.get() != NULL) {
364 SummarizeReducedProgram(*pp.reduced_program, summary);
365 }
366
Sameer Agarwal2145c102018-02-05 16:35:06 -0800367 using internal::CallStatistics;
368
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700369 // It is possible that no evaluator was created. This would be the
370 // case if the preprocessor failed, or if the reduced problem did
371 // not contain any parameter blocks. Thus, only extract the
372 // evaluator statistics if one exists.
373 if (pp.evaluator.get() != NULL) {
Sameer Agarwal2145c102018-02-05 16:35:06 -0800374 const map<string, CallStatistics>& evaluator_statistics =
375 pp.evaluator->Statistics();
376 {
377 const CallStatistics& call_stats = FindWithDefault(
378 evaluator_statistics, "Evaluator::Residual", CallStatistics());
Sameer Agarwal02513592018-02-05 15:15:40 -0800379
Sameer Agarwal2145c102018-02-05 16:35:06 -0800380 summary->residual_evaluation_time_in_seconds = call_stats.time;
381 summary->num_residual_evaluations = call_stats.calls;
382 }
383 {
384 const CallStatistics& call_stats = FindWithDefault(
385 evaluator_statistics, "Evaluator::Jacobian", CallStatistics());
386
387 summary->jacobian_evaluation_time_in_seconds = call_stats.time;
388 summary->num_jacobian_evaluations = call_stats.calls;
389 }
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700390 }
391
392 // Again, like the evaluator, there may or may not be a linear
393 // solver from which we can extract run time statistics. In
394 // particular the line search solver does not use a linear solver.
395 if (pp.linear_solver.get() != NULL) {
Sameer Agarwal2145c102018-02-05 16:35:06 -0800396 const map<string, CallStatistics>& linear_solver_statistics =
397 pp.linear_solver->Statistics();
398 const CallStatistics& call_stats = FindWithDefault(
399 linear_solver_statistics, "LinearSolver::Solve", CallStatistics());
400 summary->num_linear_solves = call_stats.calls;
401 summary->linear_solver_time_in_seconds = call_stats.time;
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700402 }
403}
404
405void Minimize(internal::PreprocessedProblem* pp,
406 Solver::Summary* summary) {
407 using internal::Program;
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700408 using internal::Minimizer;
409
410 Program* program = pp->reduced_program.get();
411 if (pp->reduced_program->NumParameterBlocks() == 0) {
412 summary->message = "Function tolerance reached. "
413 "No non-constant parameter blocks found.";
414 summary->termination_type = CONVERGENCE;
415 VLOG_IF(1, pp->options.logging_type != SILENT) << summary->message;
416 summary->initial_cost = summary->fixed_cost;
417 summary->final_cost = summary->fixed_cost;
418 return;
419 }
420
Sameer Agarwal83098e12018-03-02 17:19:42 -0800421 const Vector original_reduced_parameters = pp->reduced_parameters;
Keir Mierle7c4e8a42018-03-30 16:16:59 -0700422 std::unique_ptr<Minimizer> minimizer(
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700423 Minimizer::Create(pp->options.minimizer_type));
424 minimizer->Minimize(pp->minimizer_options,
425 pp->reduced_parameters.data(),
426 summary);
427
Sameer Agarwal83098e12018-03-02 17:19:42 -0800428 program->StateVectorToParameterBlocks(
429 summary->IsSolutionUsable()
430 ? pp->reduced_parameters.data()
431 : original_reduced_parameters.data());
432 program->CopyParameterBlockStateToUserState();
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700433}
434
Sameer Agarwal1cfec3c2014-08-18 15:46:02 -0700435std::string SchurStructureToString(const int row_block_size,
436 const int e_block_size,
437 const int f_block_size) {
438 const std::string row =
439 (row_block_size == Eigen::Dynamic)
440 ? "d" : internal::StringPrintf("%d", row_block_size);
441
442 const std::string e =
443 (e_block_size == Eigen::Dynamic)
444 ? "d" : internal::StringPrintf("%d", e_block_size);
445
446 const std::string f =
447 (f_block_size == Eigen::Dynamic)
448 ? "d" : internal::StringPrintf("%d", f_block_size);
449
Sameer Agarwal5f87f352017-02-27 11:00:49 -0800450 return internal::StringPrintf("%s,%s,%s", row.c_str(), e.c_str(), f.c_str());
Sameer Agarwal1cfec3c2014-08-18 15:46:02 -0700451}
452
Sameer Agarwal8de27be2014-08-19 08:22:40 -0700453} // namespace
Sameer Agarwal16936452014-05-29 15:39:01 -0700454
455bool Solver::Options::IsValid(string* error) const {
456 if (!CommonOptionsAreValid(*this, error)) {
457 return false;
458 }
459
Sameer Agarwalb58a8772014-11-14 10:56:19 -0800460 if (minimizer_type == TRUST_REGION &&
461 !TrustRegionOptionsAreValid(*this, error)) {
462 return false;
Sameer Agarwal16936452014-05-29 15:39:01 -0700463 }
464
Sameer Agarwalb58a8772014-11-14 10:56:19 -0800465 // We do not know if the problem is bounds constrained or not, if it
466 // is then the trust region solver will also use the line search
467 // solver to do a projection onto the box constraints, so make sure
468 // that the line search options are checked independent of what
469 // minimizer algorithm is being used.
Sameer Agarwal16936452014-05-29 15:39:01 -0700470 return LineSearchOptionsAreValid(*this, error);
471}
Keir Mierle8ebb0732012-04-30 23:09:08 -0700472
473Solver::~Solver() {}
474
Keir Mierle8ebb0732012-04-30 23:09:08 -0700475void Solver::Solve(const Solver::Options& options,
476 Problem* problem,
477 Solver::Summary* summary) {
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700478 using internal::PreprocessedProblem;
479 using internal::Preprocessor;
480 using internal::ProblemImpl;
481 using internal::Program;
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700482 using internal::WallTimeInSeconds;
483
Sameer Agarwal16936452014-05-29 15:39:01 -0700484 CHECK_NOTNULL(problem);
485 CHECK_NOTNULL(summary);
486
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700487 double start_time = WallTimeInSeconds();
Sameer Agarwal16936452014-05-29 15:39:01 -0700488 *summary = Summary();
489 if (!options.IsValid(&summary->message)) {
490 LOG(ERROR) << "Terminating: " << summary->message;
491 return;
492 }
493
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700494 ProblemImpl* problem_impl = problem->problem_impl_.get();
495 Program* program = problem_impl->mutable_program();
496 PreSolveSummarize(options, problem_impl, summary);
497
Mike Vitusf408f892018-02-22 10:28:39 -0800498 // The main thread also does work so we only need to launch num_threads - 1.
Sameer Agarwal3d933752018-02-27 17:02:04 -0800499 problem_impl->context()->EnsureMinimumThreads(options.num_threads - 1);
Mike Vitusf408f892018-02-22 10:28:39 -0800500
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700501 // Make sure that all the parameter blocks states are set to the
502 // values provided by the user.
503 program->SetParameterBlockStatePtrsToUserStatePtrs();
504
David Gossowac3b8e82016-04-29 16:07:11 +0200505 // If gradient_checking is enabled, wrap all cost functions in a
506 // gradient checker and install a callback that terminates if any gradient
507 // error is detected.
Keir Mierle7c4e8a42018-03-30 16:16:59 -0700508 std::unique_ptr<internal::ProblemImpl> gradient_checking_problem;
David Gossowac3b8e82016-04-29 16:07:11 +0200509 internal::GradientCheckingIterationCallback gradient_checking_callback;
510 Solver::Options modified_options = options;
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700511 if (options.check_gradients) {
David Gossowac3b8e82016-04-29 16:07:11 +0200512 modified_options.callbacks.push_back(&gradient_checking_callback);
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700513 gradient_checking_problem.reset(
514 CreateGradientCheckingProblemImpl(
515 problem_impl,
Sameer Agarwal79a28d12016-08-31 06:47:45 -0700516 options.gradient_check_numeric_derivative_relative_step_size,
David Gossowac3b8e82016-04-29 16:07:11 +0200517 options.gradient_check_relative_precision,
518 &gradient_checking_callback));
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700519 problem_impl = gradient_checking_problem.get();
520 program = problem_impl->mutable_program();
521 }
522
Keir Mierle7c4e8a42018-03-30 16:16:59 -0700523 std::unique_ptr<Preprocessor> preprocessor(
David Gossowac3b8e82016-04-29 16:07:11 +0200524 Preprocessor::Create(modified_options.minimizer_type));
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700525 PreprocessedProblem pp;
Sameer Agarwal1cfec3c2014-08-18 15:46:02 -0700526
David Gossowac3b8e82016-04-29 16:07:11 +0200527 const bool status = preprocessor->Preprocess(modified_options, problem_impl, &pp);
Sameer Agarwal1cfec3c2014-08-18 15:46:02 -0700528
Sameer Agarwal1ebaff82017-02-27 14:58:47 -0800529 // We check the linear_solver_options.type rather than
530 // modified_options.linear_solver_type because, depending on the
531 // lack of a Schur structure, the preprocessor may change the linear
532 // solver type.
533 if (IsSchurType(pp.linear_solver_options.type)) {
Sameer Agarwal1cfec3c2014-08-18 15:46:02 -0700534 // TODO(sameeragarwal): We can likely eliminate the duplicate call
535 // to DetectStructure here and inside the linear solver, by
536 // calling this in the preprocessor.
537 int row_block_size;
538 int e_block_size;
539 int f_block_size;
540 DetectStructure(*static_cast<internal::BlockSparseMatrix*>(
541 pp.minimizer_options.jacobian.get())
542 ->block_structure(),
543 pp.linear_solver_options.elimination_groups[0],
544 &row_block_size,
545 &e_block_size,
546 &f_block_size);
547 summary->schur_structure_given =
548 SchurStructureToString(row_block_size, e_block_size, f_block_size);
549 internal::GetBestSchurTemplateSpecialization(&row_block_size,
550 &e_block_size,
551 &f_block_size);
552 summary->schur_structure_used =
553 SchurStructureToString(row_block_size, e_block_size, f_block_size);
554 }
555
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700556 summary->fixed_cost = pp.fixed_cost;
557 summary->preprocessor_time_in_seconds = WallTimeInSeconds() - start_time;
558
559 if (status) {
560 const double minimizer_start_time = WallTimeInSeconds();
561 Minimize(&pp, summary);
562 summary->minimizer_time_in_seconds =
563 WallTimeInSeconds() - minimizer_start_time;
564 } else {
565 summary->message = pp.error;
566 }
567
568 const double postprocessor_start_time = WallTimeInSeconds();
569 problem_impl = problem->problem_impl_.get();
570 program = problem_impl->mutable_program();
571 // On exit, ensure that the parameter blocks again point at the user
572 // provided values and the parameter blocks are numbered according
573 // to their position in the original user provided program.
574 program->SetParameterBlockStatePtrsToUserStatePtrs();
575 program->SetParameterOffsetsAndIndex();
576 PostSolveSummarize(pp, summary);
577 summary->postprocessor_time_in_seconds =
578 WallTimeInSeconds() - postprocessor_start_time;
579
David Gossowac3b8e82016-04-29 16:07:11 +0200580 // If the gradient checker reported an error, we want to report FAILURE
581 // instead of USER_FAILURE and provide the error log.
582 if (gradient_checking_callback.gradient_error_detected()) {
583 summary->termination_type = FAILURE;
584 summary->message = gradient_checking_callback.error_log();
585 }
586
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700587 summary->total_time_in_seconds = WallTimeInSeconds() - start_time;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700588}
589
590void Solve(const Solver::Options& options,
591 Problem* problem,
592 Solver::Summary* summary) {
Keir Mierle6196cba2012-06-18 11:03:40 -0700593 Solver solver;
594 solver.Solve(options, problem, summary);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700595}
596
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800597string Solver::Summary::BriefReport() const {
598 return StringPrintf("Ceres Solver Report: "
599 "Iterations: %d, "
600 "Initial cost: %e, "
601 "Final cost: %e, "
602 "Termination: %s",
603 num_successful_steps + num_unsuccessful_steps,
604 initial_cost,
605 final_cost,
606 TerminationTypeToString(termination_type));
Sameer Agarwalbcc865f2014-12-17 07:35:09 -0800607}
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800608
Keir Mierle8ebb0732012-04-30 23:09:08 -0700609string Solver::Summary::FullReport() const {
Sameer Agarwald76da162014-09-07 18:42:49 -0700610 using internal::VersionString;
611
Sameer Agarwal79491a32014-08-28 13:57:50 -0700612 string report = string("\nSolver Summary (v " + VersionString() + ")\n\n");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700613
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800614 StringAppendF(&report, "%45s %21s\n", "Original", "Reduced");
615 StringAppendF(&report, "Parameter blocks % 25d% 25d\n",
616 num_parameter_blocks, num_parameter_blocks_reduced);
617 StringAppendF(&report, "Parameters % 25d% 25d\n",
618 num_parameters, num_parameters_reduced);
619 if (num_effective_parameters_reduced != num_parameters_reduced) {
620 StringAppendF(&report, "Effective parameters% 25d% 25d\n",
621 num_effective_parameters, num_effective_parameters_reduced);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700622 }
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800623 StringAppendF(&report, "Residual blocks % 25d% 25d\n",
624 num_residual_blocks, num_residual_blocks_reduced);
Sameer Agarwal02513592018-02-05 15:15:40 -0800625 StringAppendF(&report, "Residuals % 25d% 25d\n",
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800626 num_residuals, num_residuals_reduced);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700627
Sameer Agarwald010de52013-02-15 14:26:56 -0800628 if (minimizer_type == TRUST_REGION) {
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700629 // TRUST_SEARCH HEADER
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800630 StringAppendF(&report, "\nMinimizer %19s\n",
631 "TRUST_REGION");
Sameer Agarwal367b65e2013-08-09 10:35:37 -0700632
633 if (linear_solver_type_used == DENSE_NORMAL_CHOLESKY ||
634 linear_solver_type_used == DENSE_SCHUR ||
635 linear_solver_type_used == DENSE_QR) {
636 StringAppendF(&report, "\nDense linear algebra library %15s\n",
637 DenseLinearAlgebraLibraryTypeToString(
638 dense_linear_algebra_library_type));
639 }
640
Sameer Agarwald010de52013-02-15 14:26:56 -0800641 if (linear_solver_type_used == SPARSE_NORMAL_CHOLESKY ||
642 linear_solver_type_used == SPARSE_SCHUR ||
643 (linear_solver_type_used == ITERATIVE_SCHUR &&
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700644 (preconditioner_type_used == CLUSTER_JACOBI ||
645 preconditioner_type_used == CLUSTER_TRIDIAGONAL))) {
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700646 StringAppendF(&report, "\nSparse linear algebra library %15s\n",
Sameer Agarwald010de52013-02-15 14:26:56 -0800647 SparseLinearAlgebraLibraryTypeToString(
Sameer Agarwal367b65e2013-08-09 10:35:37 -0700648 sparse_linear_algebra_library_type));
Sameer Agarwal1e289202012-08-21 18:00:54 -0700649 }
Sameer Agarwald010de52013-02-15 14:26:56 -0800650
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700651 StringAppendF(&report, "Trust region strategy %19s",
Sameer Agarwald010de52013-02-15 14:26:56 -0800652 TrustRegionStrategyTypeToString(
653 trust_region_strategy_type));
654 if (trust_region_strategy_type == DOGLEG) {
655 if (dogleg_type == TRADITIONAL_DOGLEG) {
656 StringAppendF(&report, " (TRADITIONAL)");
657 } else {
658 StringAppendF(&report, " (SUBSPACE)");
659 }
660 }
661 StringAppendF(&report, "\n");
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800662 StringAppendF(&report, "\n");
Sameer Agarwal1e289202012-08-21 18:00:54 -0700663
Sameer Agarwal931c3092013-02-25 09:46:21 -0800664 StringAppendF(&report, "%45s %21s\n", "Given", "Used");
Sameer Agarwald010de52013-02-15 14:26:56 -0800665 StringAppendF(&report, "Linear solver %25s%25s\n",
666 LinearSolverTypeToString(linear_solver_type_given),
667 LinearSolverTypeToString(linear_solver_type_used));
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800668
Sameer Agarwald010de52013-02-15 14:26:56 -0800669 if (linear_solver_type_given == CGNR ||
670 linear_solver_type_given == ITERATIVE_SCHUR) {
671 StringAppendF(&report, "Preconditioner %25s%25s\n",
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700672 PreconditionerTypeToString(preconditioner_type_given),
673 PreconditionerTypeToString(preconditioner_type_used));
Sameer Agarwald010de52013-02-15 14:26:56 -0800674 }
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800675
Sameer Agarwal1745dd62014-06-05 21:30:13 -0700676 if (preconditioner_type_used == CLUSTER_JACOBI ||
677 preconditioner_type_used == CLUSTER_TRIDIAGONAL) {
Sameer Agarwalf06b9fa2013-10-27 21:38:13 -0700678 StringAppendF(&report, "Visibility clustering%24s%25s\n",
Sameer Agarwal9ba0b352013-11-05 13:04:56 -0800679 VisibilityClusteringTypeToString(
680 visibility_clustering_type),
681 VisibilityClusteringTypeToString(
682 visibility_clustering_type));
Sameer Agarwalf06b9fa2013-10-27 21:38:13 -0700683 }
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700684 StringAppendF(&report, "Threads % 25d% 25d\n",
Sameer Agarwald010de52013-02-15 14:26:56 -0800685 num_threads_given, num_threads_used);
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800686
Sameer Agarwale1bcc6e2016-05-18 07:52:48 -0700687 string given;
688 StringifyOrdering(linear_solver_ordering_given, &given);
689 string used;
690 StringifyOrdering(linear_solver_ordering_used, &used);
691 StringAppendF(&report,
692 "Linear solver ordering %22s %24s\n",
693 given.c_str(),
694 used.c_str());
Sameer Agarwal1cfec3c2014-08-18 15:46:02 -0700695 if (IsSchurType(linear_solver_type_used)) {
696 StringAppendF(&report,
697 "Schur structure %22s %24s\n",
698 schur_structure_given.c_str(),
699 schur_structure_used.c_str());
700 }
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800701
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700702 if (inner_iterations_given) {
703 StringAppendF(&report,
704 "Use inner iterations %20s %20s\n",
705 inner_iterations_given ? "True" : "False",
706 inner_iterations_used ? "True" : "False");
707 }
708
709 if (inner_iterations_used) {
Sameer Agarwald010de52013-02-15 14:26:56 -0800710 string given;
711 StringifyOrdering(inner_iteration_ordering_given, &given);
712 string used;
713 StringifyOrdering(inner_iteration_ordering_used, &used);
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800714 StringAppendF(&report,
715 "Inner iteration ordering %20s %24s\n",
716 given.c_str(),
717 used.c_str());
Sameer Agarwald010de52013-02-15 14:26:56 -0800718 }
Sameer Agarwald010de52013-02-15 14:26:56 -0800719 } else {
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700720 // LINE_SEARCH HEADER
Sameer Agarwald010de52013-02-15 14:26:56 -0800721 StringAppendF(&report, "\nMinimizer %19s\n", "LINE_SEARCH");
Sameer Agarwald010de52013-02-15 14:26:56 -0800722
Sameer Agarwal4f010b22013-07-01 08:01:01 -0700723
724 string line_search_direction_string;
725 if (line_search_direction_type == LBFGS) {
726 line_search_direction_string = StringPrintf("LBFGS (%d)", max_lbfgs_rank);
727 } else if (line_search_direction_type == NONLINEAR_CONJUGATE_GRADIENT) {
728 line_search_direction_string =
729 NonlinearConjugateGradientTypeToString(
730 nonlinear_conjugate_gradient_type);
731 } else {
732 line_search_direction_string =
733 LineSearchDirectionTypeToString(line_search_direction_type);
734 }
735
736 StringAppendF(&report, "Line search direction %19s\n",
737 line_search_direction_string.c_str());
738
739 const string line_search_type_string =
740 StringPrintf("%s %s",
Sameer Agarwal7a8f7972013-07-03 09:03:55 -0700741 LineSearchInterpolationTypeToString(
742 line_search_interpolation_type),
Sameer Agarwal4f010b22013-07-01 08:01:01 -0700743 LineSearchTypeToString(line_search_type));
744 StringAppendF(&report, "Line search type %19s\n",
745 line_search_type_string.c_str());
Sameer Agarwald010de52013-02-15 14:26:56 -0800746 StringAppendF(&report, "\n");
747
Sameer Agarwalbeb45052013-02-22 13:37:01 -0800748 StringAppendF(&report, "%45s %21s\n", "Given", "Used");
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700749 StringAppendF(&report, "Threads % 25d% 25d\n",
Sameer Agarwald010de52013-02-15 14:26:56 -0800750 num_threads_given, num_threads_used);
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800751 }
Keir Mierle8ebb0732012-04-30 23:09:08 -0700752
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700753 StringAppendF(&report, "\nCost:\n");
754 StringAppendF(&report, "Initial % 30e\n", initial_cost);
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800755 if (termination_type != FAILURE &&
756 termination_type != USER_FAILURE) {
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700757 StringAppendF(&report, "Final % 30e\n", final_cost);
758 StringAppendF(&report, "Change % 30e\n",
759 initial_cost - final_cost);
760 }
761
762 StringAppendF(&report, "\nMinimizer iterations % 16d\n",
763 num_successful_steps + num_unsuccessful_steps);
Sameer Agarwal4f010b22013-07-01 08:01:01 -0700764
765 // Successful/Unsuccessful steps only matter in the case of the
766 // trust region solver. Line search terminates when it encounters
767 // the first unsuccessful step.
768 if (minimizer_type == TRUST_REGION) {
769 StringAppendF(&report, "Successful steps % 14d\n",
770 num_successful_steps);
771 StringAppendF(&report, "Unsuccessful steps % 14d\n",
772 num_unsuccessful_steps);
773 }
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700774 if (inner_iterations_used) {
775 StringAppendF(&report, "Steps with inner iterations % 14d\n",
776 num_inner_iteration_steps);
777 }
778
Sameer Agarwal992ae552015-12-01 07:25:06 -0800779 const bool line_search_used =
780 (minimizer_type == LINE_SEARCH ||
781 (minimizer_type == TRUST_REGION && is_constrained));
782
783 if (line_search_used) {
784 StringAppendF(&report, "Line search steps % 14d\n",
785 num_line_search_steps);
786 }
Alex Stewart9ad59a72014-11-17 22:20:46 +0000787
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700788 StringAppendF(&report, "\nTime (in seconds):\n");
Thomas Gamperd7279742017-09-26 15:40:02 +0200789 StringAppendF(&report, "Preprocessor %25.6f\n",
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700790 preprocessor_time_in_seconds);
791
Sameer Agarwal02513592018-02-05 15:15:40 -0800792 StringAppendF(&report, "\n Residual only evaluation %18.6f (%d)\n",
793 residual_evaluation_time_in_seconds, num_residual_evaluations);
Sameer Agarwal992ae552015-12-01 07:25:06 -0800794 if (line_search_used) {
Thomas Gamperd7279742017-09-26 15:40:02 +0200795 StringAppendF(&report, " Line search cost evaluation %10.6f\n",
Alex Stewart9ad59a72014-11-17 22:20:46 +0000796 line_search_cost_evaluation_time_in_seconds);
797 }
Sameer Agarwal02513592018-02-05 15:15:40 -0800798 StringAppendF(&report, " Jacobian & residual evaluation %12.6f (%d)\n",
799 jacobian_evaluation_time_in_seconds, num_jacobian_evaluations);
Sameer Agarwal992ae552015-12-01 07:25:06 -0800800 if (line_search_used) {
Thomas Gamperd7279742017-09-26 15:40:02 +0200801 StringAppendF(&report, " Line search gradient evaluation %6.6f\n",
Alex Stewart9ad59a72014-11-17 22:20:46 +0000802 line_search_gradient_evaluation_time_in_seconds);
803 }
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700804
805 if (minimizer_type == TRUST_REGION) {
Sameer Agarwal2145c102018-02-05 16:35:06 -0800806 StringAppendF(&report, " Linear solver %23.6f (%d)\n",
807 linear_solver_time_in_seconds, num_linear_solves);
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700808 }
809
810 if (inner_iterations_used) {
Thomas Gamperd7279742017-09-26 15:40:02 +0200811 StringAppendF(&report, " Inner iterations %23.6f\n",
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700812 inner_iteration_time_in_seconds);
813 }
814
Sameer Agarwal992ae552015-12-01 07:25:06 -0800815 if (line_search_used) {
Thomas Gamperd7279742017-09-26 15:40:02 +0200816 StringAppendF(&report, " Line search polynomial minimization %.6f\n",
Alex Stewart9ad59a72014-11-17 22:20:46 +0000817 line_search_polynomial_minimization_time_in_seconds);
818 }
819
Thomas Gamperd7279742017-09-26 15:40:02 +0200820 StringAppendF(&report, "Minimizer %25.6f\n\n",
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700821 minimizer_time_in_seconds);
822
Thomas Gamperd7279742017-09-26 15:40:02 +0200823 StringAppendF(&report, "Postprocessor %24.6f\n",
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700824 postprocessor_time_in_seconds);
825
Thomas Gamperd7279742017-09-26 15:40:02 +0200826 StringAppendF(&report, "Total %25.6f\n\n",
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700827 total_time_in_seconds);
828
Sameer Agarwal1da92922014-02-23 16:10:33 -0800829 StringAppendF(&report, "Termination: %25s (%s)\n",
830 TerminationTypeToString(termination_type), message.c_str());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700831 return report;
Sameer Agarwalbcc865f2014-12-17 07:35:09 -0800832}
Keir Mierle8ebb0732012-04-30 23:09:08 -0700833
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800834bool Solver::Summary::IsSolutionUsable() const {
Sameer Agarwald76da162014-09-07 18:42:49 -0700835 return internal::IsSolutionUsable(*this);
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800836}
837
Keir Mierle8ebb0732012-04-30 23:09:08 -0700838} // namespace ceres