solver.cc 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140
  1. // Ceres Solver - A fast non-linear least squares minimizer
  2. // Copyright 2022 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: keir@google.com (Keir Mierle)
  30. // sameeragarwal@google.com (Sameer Agarwal)
  31. #include "ceres/solver.h"
  32. #include <algorithm>
  33. #include <map>
  34. #include <memory>
  35. #include <sstream> // NOLINT
  36. #include <string>
  37. #include <vector>
  38. #include "ceres/casts.h"
  39. #include "ceres/context.h"
  40. #include "ceres/context_impl.h"
  41. #include "ceres/detect_structure.h"
  42. #include "ceres/eigensparse.h"
  43. #include "ceres/gradient_checking_cost_function.h"
  44. #include "ceres/internal/export.h"
  45. #include "ceres/parameter_block_ordering.h"
  46. #include "ceres/preprocessor.h"
  47. #include "ceres/problem.h"
  48. #include "ceres/problem_impl.h"
  49. #include "ceres/program.h"
  50. #include "ceres/schur_templates.h"
  51. #include "ceres/solver_utils.h"
  52. #include "ceres/stringprintf.h"
  53. #include "ceres/suitesparse.h"
  54. #include "ceres/types.h"
  55. #include "ceres/wall_time.h"
  56. namespace ceres {
  57. namespace {
  58. using internal::StringAppendF;
  59. using internal::StringPrintf;
  60. #define OPTION_OP(x, y, OP) \
  61. if (!(options.x OP y)) { \
  62. std::stringstream ss; \
  63. ss << "Invalid configuration. "; \
  64. ss << std::string("Solver::Options::" #x " = ") << options.x << ". "; \
  65. ss << "Violated constraint: "; \
  66. ss << std::string("Solver::Options::" #x " " #OP " " #y); \
  67. *error = ss.str(); \
  68. return false; \
  69. }
  70. #define OPTION_OP_OPTION(x, y, OP) \
  71. if (!(options.x OP options.y)) { \
  72. std::stringstream ss; \
  73. ss << "Invalid configuration. "; \
  74. ss << std::string("Solver::Options::" #x " = ") << options.x << ". "; \
  75. ss << std::string("Solver::Options::" #y " = ") << options.y << ". "; \
  76. ss << "Violated constraint: "; \
  77. ss << std::string("Solver::Options::" #x); \
  78. ss << std::string(#OP " Solver::Options::" #y "."); \
  79. *error = ss.str(); \
  80. return false; \
  81. }
  82. #define OPTION_GE(x, y) OPTION_OP(x, y, >=);
  83. #define OPTION_GT(x, y) OPTION_OP(x, y, >);
  84. #define OPTION_LE(x, y) OPTION_OP(x, y, <=);
  85. #define OPTION_LT(x, y) OPTION_OP(x, y, <);
  86. #define OPTION_LE_OPTION(x, y) OPTION_OP_OPTION(x, y, <=)
  87. #define OPTION_LT_OPTION(x, y) OPTION_OP_OPTION(x, y, <)
  88. bool CommonOptionsAreValid(const Solver::Options& options, std::string* error) {
  89. OPTION_GE(max_num_iterations, 0);
  90. OPTION_GE(max_solver_time_in_seconds, 0.0);
  91. OPTION_GE(function_tolerance, 0.0);
  92. OPTION_GE(gradient_tolerance, 0.0);
  93. OPTION_GE(parameter_tolerance, 0.0);
  94. OPTION_GT(num_threads, 0);
  95. if (options.check_gradients) {
  96. OPTION_GT(gradient_check_relative_precision, 0.0);
  97. OPTION_GT(gradient_check_numeric_derivative_relative_step_size, 0.0);
  98. }
  99. return true;
  100. }
  101. bool IsNestedDissectionAvailable(SparseLinearAlgebraLibraryType type) {
  102. return (((type == SUITE_SPARSE) &&
  103. internal::SuiteSparse::IsNestedDissectionAvailable()) ||
  104. (type == ACCELERATE_SPARSE) ||
  105. ((type == EIGEN_SPARSE) &&
  106. internal::EigenSparse::IsNestedDissectionAvailable()));
  107. }
  108. bool IsIterativeSolver(LinearSolverType type) {
  109. return (type == CGNR || type == ITERATIVE_SCHUR);
  110. }
  111. bool OptionsAreValidForDenseSolver(const Solver::Options& options,
  112. std::string* error) {
  113. const char* library_name = DenseLinearAlgebraLibraryTypeToString(
  114. options.dense_linear_algebra_library_type);
  115. const char* solver_name =
  116. LinearSolverTypeToString(options.linear_solver_type);
  117. constexpr char kFormat[] =
  118. "Can't use %s with dense_linear_algebra_library_type = %s "
  119. "because support not enabled when Ceres was built.";
  120. if (!IsDenseLinearAlgebraLibraryTypeAvailable(
  121. options.dense_linear_algebra_library_type)) {
  122. *error = StringPrintf(kFormat, solver_name, library_name);
  123. return false;
  124. }
  125. return true;
  126. }
  127. bool OptionsAreValidForSparseCholeskyBasedSolver(const Solver::Options& options,
  128. std::string* error) {
  129. const char* library_name = SparseLinearAlgebraLibraryTypeToString(
  130. options.sparse_linear_algebra_library_type);
  131. // Sparse factorization based solvers and some preconditioners require a
  132. // sparse Cholesky factorization.
  133. const char* solver_name =
  134. IsIterativeSolver(options.linear_solver_type)
  135. ? PreconditionerTypeToString(options.preconditioner_type)
  136. : LinearSolverTypeToString(options.linear_solver_type);
  137. constexpr char kNoSparseFormat[] =
  138. "Can't use %s with sparse_linear_algebra_library_type = %s.";
  139. constexpr char kNoLibraryFormat[] =
  140. "Can't use %s sparse_linear_algebra_library_type = %s, because support "
  141. "was not enabled when Ceres Solver was built.";
  142. constexpr char kNoNesdisFormat[] =
  143. "NESDIS is not available with sparse_linear_algebra_library_type = %s.";
  144. constexpr char kMixedFormat[] =
  145. "use_mixed_precision_solves with %s is not supported with "
  146. "sparse_linear_algebra_library_type = %s";
  147. constexpr char kDynamicSparsityFormat[] =
  148. "dynamic sparsity is not supported with "
  149. "sparse_linear_algebra_library_type = %s";
  150. if (options.sparse_linear_algebra_library_type == NO_SPARSE) {
  151. *error = StringPrintf(kNoSparseFormat, solver_name, library_name);
  152. return false;
  153. }
  154. if (!IsSparseLinearAlgebraLibraryTypeAvailable(
  155. options.sparse_linear_algebra_library_type)) {
  156. *error = StringPrintf(kNoLibraryFormat, solver_name, library_name);
  157. return false;
  158. }
  159. if (options.linear_solver_ordering_type == ceres::NESDIS &&
  160. !IsNestedDissectionAvailable(
  161. options.sparse_linear_algebra_library_type)) {
  162. *error = StringPrintf(kNoNesdisFormat, library_name);
  163. return false;
  164. }
  165. if (options.use_mixed_precision_solves &&
  166. options.sparse_linear_algebra_library_type == SUITE_SPARSE) {
  167. *error = StringPrintf(kMixedFormat, solver_name, library_name);
  168. return false;
  169. }
  170. if (options.dynamic_sparsity &&
  171. options.sparse_linear_algebra_library_type == ACCELERATE_SPARSE) {
  172. *error = StringPrintf(kDynamicSparsityFormat, library_name);
  173. return false;
  174. }
  175. return true;
  176. }
  177. bool OptionsAreValidForDenseNormalCholesky(const Solver::Options& options,
  178. std::string* error) {
  179. CHECK_EQ(options.linear_solver_type, DENSE_NORMAL_CHOLESKY);
  180. return OptionsAreValidForDenseSolver(options, error);
  181. }
  182. bool OptionsAreValidForDenseQr(const Solver::Options& options,
  183. std::string* error) {
  184. CHECK_EQ(options.linear_solver_type, DENSE_QR);
  185. if (!OptionsAreValidForDenseSolver(options, error)) {
  186. return false;
  187. }
  188. if (options.use_mixed_precision_solves) {
  189. *error = "Can't use use_mixed_precision_solves with DENSE_QR.";
  190. return false;
  191. }
  192. return true;
  193. }
  194. bool OptionsAreValidForSparseNormalCholesky(const Solver::Options& options,
  195. std::string* error) {
  196. CHECK_EQ(options.linear_solver_type, SPARSE_NORMAL_CHOLESKY);
  197. return OptionsAreValidForSparseCholeskyBasedSolver(options, error);
  198. }
  199. bool OptionsAreValidForDenseSchur(const Solver::Options& options,
  200. std::string* error) {
  201. CHECK_EQ(options.linear_solver_type, DENSE_SCHUR);
  202. if (options.dynamic_sparsity) {
  203. *error = "dynamic sparsity is only supported with SPARSE_NORMAL_CHOLESKY";
  204. return false;
  205. }
  206. if (!OptionsAreValidForDenseSolver(options, error)) {
  207. return false;
  208. }
  209. return true;
  210. }
  211. bool OptionsAreValidForSparseSchur(const Solver::Options& options,
  212. std::string* error) {
  213. CHECK_EQ(options.linear_solver_type, SPARSE_SCHUR);
  214. if (options.dynamic_sparsity) {
  215. *error = "Dynamic sparsity is only supported with SPARSE_NORMAL_CHOLESKY.";
  216. return false;
  217. }
  218. return OptionsAreValidForSparseCholeskyBasedSolver(options, error);
  219. }
  220. bool OptionsAreValidForIterativeSchur(const Solver::Options& options,
  221. std::string* error) {
  222. CHECK_EQ(options.linear_solver_type, ITERATIVE_SCHUR);
  223. if (options.dynamic_sparsity) {
  224. *error = "Dynamic sparsity is only supported with SPARSE_NORMAL_CHOLESKY.";
  225. return false;
  226. }
  227. if (options.use_explicit_schur_complement) {
  228. if (options.preconditioner_type != SCHUR_JACOBI) {
  229. *error =
  230. "use_explicit_schur_complement only supports "
  231. "SCHUR_JACOBI as the preconditioner.";
  232. return false;
  233. }
  234. if (options.use_spse_initialization) {
  235. *error =
  236. "use_explicit_schur_complement does not support "
  237. "use_spse_initialization.";
  238. return false;
  239. }
  240. }
  241. if (options.use_spse_initialization ||
  242. options.preconditioner_type == SCHUR_POWER_SERIES_EXPANSION) {
  243. OPTION_GE(max_num_spse_iterations, 1)
  244. OPTION_GE(spse_tolerance, 0.0)
  245. }
  246. if (options.use_mixed_precision_solves) {
  247. *error = "Can't use use_mixed_precision_solves with ITERATIVE_SCHUR";
  248. return false;
  249. }
  250. if (options.dynamic_sparsity) {
  251. *error = "Dynamic sparsity is only supported with SPARSE_NORMAL_CHOLESKY.";
  252. return false;
  253. }
  254. if (options.preconditioner_type == SUBSET) {
  255. *error = "Can't use SUBSET preconditioner with ITERATIVE_SCHUR";
  256. return false;
  257. }
  258. // CLUSTER_JACOBI and CLUSTER_TRIDIAGONAL require sparse Cholesky
  259. // factorization.
  260. if (options.preconditioner_type == CLUSTER_JACOBI ||
  261. options.preconditioner_type == CLUSTER_TRIDIAGONAL) {
  262. return OptionsAreValidForSparseCholeskyBasedSolver(options, error);
  263. }
  264. return true;
  265. }
  266. bool OptionsAreValidForCgnr(const Solver::Options& options,
  267. std::string* error) {
  268. CHECK_EQ(options.linear_solver_type, CGNR);
  269. if (options.preconditioner_type != IDENTITY &&
  270. options.preconditioner_type != JACOBI &&
  271. options.preconditioner_type != SUBSET) {
  272. *error =
  273. StringPrintf("Can't use CGNR with preconditioner_type = %s.",
  274. PreconditionerTypeToString(options.preconditioner_type));
  275. return false;
  276. }
  277. if (options.use_mixed_precision_solves) {
  278. *error = "use_mixed_precision_solves cannot be used with CGNR";
  279. return false;
  280. }
  281. if (options.dynamic_sparsity) {
  282. *error = "Dynamic sparsity is only supported with SPARSE_NORMAL_CHOLESKY.";
  283. return false;
  284. }
  285. if (options.preconditioner_type == SUBSET) {
  286. if (options.sparse_linear_algebra_library_type == CUDA_SPARSE) {
  287. *error =
  288. "Can't use CGNR with preconditioner_type = SUBSET when "
  289. "sparse_linear_algebra_library_type = CUDA_SPARSE.";
  290. return false;
  291. }
  292. if (options.residual_blocks_for_subset_preconditioner.empty()) {
  293. *error =
  294. "When using SUBSET preconditioner, "
  295. "residual_blocks_for_subset_preconditioner cannot be empty";
  296. return false;
  297. }
  298. // SUBSET preconditioner requires sparse Cholesky factorization.
  299. if (!OptionsAreValidForSparseCholeskyBasedSolver(options, error)) {
  300. return false;
  301. }
  302. }
  303. // Check options for CGNR with CUDA_SPARSE.
  304. if (options.sparse_linear_algebra_library_type == CUDA_SPARSE) {
  305. if (!IsSparseLinearAlgebraLibraryTypeAvailable(CUDA_SPARSE)) {
  306. *error =
  307. "Can't use CGNR with sparse_linear_algebra_library_type = "
  308. "CUDA_SPARSE because support was not enabled when Ceres was built.";
  309. return false;
  310. }
  311. }
  312. return true;
  313. }
  314. bool OptionsAreValidForLinearSolver(const Solver::Options& options,
  315. std::string* error) {
  316. switch (options.linear_solver_type) {
  317. case DENSE_NORMAL_CHOLESKY:
  318. return OptionsAreValidForDenseNormalCholesky(options, error);
  319. case DENSE_QR:
  320. return OptionsAreValidForDenseQr(options, error);
  321. case SPARSE_NORMAL_CHOLESKY:
  322. return OptionsAreValidForSparseNormalCholesky(options, error);
  323. case DENSE_SCHUR:
  324. return OptionsAreValidForDenseSchur(options, error);
  325. case SPARSE_SCHUR:
  326. return OptionsAreValidForSparseSchur(options, error);
  327. case ITERATIVE_SCHUR:
  328. return OptionsAreValidForIterativeSchur(options, error);
  329. case CGNR:
  330. return OptionsAreValidForCgnr(options, error);
  331. default:
  332. LOG(FATAL) << "Congratulations you have found a bug. Please report "
  333. "this to the "
  334. "Ceres Solver developers. Unknown linear solver type: "
  335. << LinearSolverTypeToString(options.linear_solver_type);
  336. }
  337. return false;
  338. }
  339. bool TrustRegionOptionsAreValid(const Solver::Options& options,
  340. std::string* error) {
  341. OPTION_GT(initial_trust_region_radius, 0.0);
  342. OPTION_GT(min_trust_region_radius, 0.0);
  343. OPTION_GT(max_trust_region_radius, 0.0);
  344. OPTION_LE_OPTION(min_trust_region_radius, max_trust_region_radius);
  345. OPTION_LE_OPTION(min_trust_region_radius, initial_trust_region_radius);
  346. OPTION_LE_OPTION(initial_trust_region_radius, max_trust_region_radius);
  347. OPTION_GE(min_relative_decrease, 0.0);
  348. OPTION_GE(min_lm_diagonal, 0.0);
  349. OPTION_GE(max_lm_diagonal, 0.0);
  350. OPTION_LE_OPTION(min_lm_diagonal, max_lm_diagonal);
  351. OPTION_GE(max_num_consecutive_invalid_steps, 0);
  352. OPTION_GT(eta, 0.0);
  353. OPTION_GE(min_linear_solver_iterations, 0);
  354. OPTION_GE(max_linear_solver_iterations, 0);
  355. OPTION_LE_OPTION(min_linear_solver_iterations, max_linear_solver_iterations);
  356. if (options.use_inner_iterations) {
  357. OPTION_GE(inner_iteration_tolerance, 0.0);
  358. }
  359. if (options.use_nonmonotonic_steps) {
  360. OPTION_GT(max_consecutive_nonmonotonic_steps, 0);
  361. }
  362. if ((options.trust_region_strategy_type == DOGLEG) &&
  363. IsIterativeSolver(options.linear_solver_type)) {
  364. *error =
  365. "DOGLEG only supports exact factorization based linear "
  366. "solvers. If you want to use an iterative solver please "
  367. "use LEVENBERG_MARQUARDT as the trust_region_strategy_type";
  368. return false;
  369. }
  370. if (!OptionsAreValidForLinearSolver(options, error)) {
  371. return false;
  372. }
  373. if (!options.trust_region_minimizer_iterations_to_dump.empty() &&
  374. options.trust_region_problem_dump_format_type != CONSOLE &&
  375. options.trust_region_problem_dump_directory.empty()) {
  376. *error = "Solver::Options::trust_region_problem_dump_directory is empty.";
  377. return false;
  378. }
  379. return true;
  380. }
  381. bool LineSearchOptionsAreValid(const Solver::Options& options,
  382. std::string* error) {
  383. OPTION_GT(max_lbfgs_rank, 0);
  384. OPTION_GT(min_line_search_step_size, 0.0);
  385. OPTION_GT(max_line_search_step_contraction, 0.0);
  386. OPTION_LT(max_line_search_step_contraction, 1.0);
  387. OPTION_LT_OPTION(max_line_search_step_contraction,
  388. min_line_search_step_contraction);
  389. OPTION_LE(min_line_search_step_contraction, 1.0);
  390. OPTION_GE(max_num_line_search_step_size_iterations,
  391. (options.minimizer_type == ceres::TRUST_REGION ? 0 : 1));
  392. OPTION_GT(line_search_sufficient_function_decrease, 0.0);
  393. OPTION_LT_OPTION(line_search_sufficient_function_decrease,
  394. line_search_sufficient_curvature_decrease);
  395. OPTION_LT(line_search_sufficient_curvature_decrease, 1.0);
  396. OPTION_GT(max_line_search_step_expansion, 1.0);
  397. if ((options.line_search_direction_type == ceres::BFGS ||
  398. options.line_search_direction_type == ceres::LBFGS) &&
  399. options.line_search_type != ceres::WOLFE) {
  400. *error =
  401. std::string(
  402. "Invalid configuration: Solver::Options::line_search_type = ") +
  403. std::string(LineSearchTypeToString(options.line_search_type)) +
  404. std::string(
  405. ". When using (L)BFGS, "
  406. "Solver::Options::line_search_type must be set to WOLFE.");
  407. return false;
  408. }
  409. // Warn user if they have requested BISECTION interpolation, but constraints
  410. // on max/min step size change during line search prevent bisection scaling
  411. // from occurring. Warn only, as this is likely a user mistake, but one
  412. // which does not prevent us from continuing.
  413. if (options.line_search_interpolation_type == ceres::BISECTION &&
  414. (options.max_line_search_step_contraction > 0.5 ||
  415. options.min_line_search_step_contraction < 0.5)) {
  416. LOG(WARNING)
  417. << "Line search interpolation type is BISECTION, but specified "
  418. << "max_line_search_step_contraction: "
  419. << options.max_line_search_step_contraction << ", and "
  420. << "min_line_search_step_contraction: "
  421. << options.min_line_search_step_contraction
  422. << ", prevent bisection (0.5) scaling, continuing with solve "
  423. "regardless.";
  424. }
  425. return true;
  426. }
  427. #undef OPTION_OP
  428. #undef OPTION_OP_OPTION
  429. #undef OPTION_GT
  430. #undef OPTION_GE
  431. #undef OPTION_LE
  432. #undef OPTION_LT
  433. #undef OPTION_LE_OPTION
  434. #undef OPTION_LT_OPTION
  435. void StringifyOrdering(const std::vector<int>& ordering, std::string* report) {
  436. if (ordering.empty()) {
  437. internal::StringAppendF(report, "AUTOMATIC");
  438. return;
  439. }
  440. for (int i = 0; i < ordering.size() - 1; ++i) {
  441. internal::StringAppendF(report, "%d,", ordering[i]);
  442. }
  443. internal::StringAppendF(report, "%d", ordering.back());
  444. }
  445. void SummarizeGivenProgram(const internal::Program& program,
  446. Solver::Summary* summary) {
  447. // clang-format off
  448. summary->num_parameter_blocks = program.NumParameterBlocks();
  449. summary->num_parameters = program.NumParameters();
  450. summary->num_effective_parameters = program.NumEffectiveParameters();
  451. summary->num_residual_blocks = program.NumResidualBlocks();
  452. summary->num_residuals = program.NumResiduals();
  453. // clang-format on
  454. }
  455. void SummarizeReducedProgram(const internal::Program& program,
  456. Solver::Summary* summary) {
  457. // clang-format off
  458. summary->num_parameter_blocks_reduced = program.NumParameterBlocks();
  459. summary->num_parameters_reduced = program.NumParameters();
  460. summary->num_effective_parameters_reduced = program.NumEffectiveParameters();
  461. summary->num_residual_blocks_reduced = program.NumResidualBlocks();
  462. summary->num_residuals_reduced = program.NumResiduals();
  463. // clang-format on
  464. }
  465. void PreSolveSummarize(const Solver::Options& options,
  466. const internal::ProblemImpl* problem,
  467. Solver::Summary* summary) {
  468. SummarizeGivenProgram(problem->program(), summary);
  469. internal::OrderingToGroupSizes(options.linear_solver_ordering.get(),
  470. &(summary->linear_solver_ordering_given));
  471. internal::OrderingToGroupSizes(options.inner_iteration_ordering.get(),
  472. &(summary->inner_iteration_ordering_given));
  473. // clang-format off
  474. summary->dense_linear_algebra_library_type = options.dense_linear_algebra_library_type;
  475. summary->dogleg_type = options.dogleg_type;
  476. summary->inner_iteration_time_in_seconds = 0.0;
  477. summary->num_line_search_steps = 0;
  478. summary->line_search_cost_evaluation_time_in_seconds = 0.0;
  479. summary->line_search_gradient_evaluation_time_in_seconds = 0.0;
  480. summary->line_search_polynomial_minimization_time_in_seconds = 0.0;
  481. summary->line_search_total_time_in_seconds = 0.0;
  482. summary->inner_iterations_given = options.use_inner_iterations;
  483. summary->line_search_direction_type = options.line_search_direction_type;
  484. summary->line_search_interpolation_type = options.line_search_interpolation_type;
  485. summary->line_search_type = options.line_search_type;
  486. summary->linear_solver_type_given = options.linear_solver_type;
  487. summary->max_lbfgs_rank = options.max_lbfgs_rank;
  488. summary->minimizer_type = options.minimizer_type;
  489. summary->nonlinear_conjugate_gradient_type = options.nonlinear_conjugate_gradient_type;
  490. summary->num_threads_given = options.num_threads;
  491. summary->preconditioner_type_given = options.preconditioner_type;
  492. summary->sparse_linear_algebra_library_type = options.sparse_linear_algebra_library_type;
  493. summary->linear_solver_ordering_type = options.linear_solver_ordering_type;
  494. summary->trust_region_strategy_type = options.trust_region_strategy_type;
  495. summary->visibility_clustering_type = options.visibility_clustering_type;
  496. // clang-format on
  497. }
  498. void PostSolveSummarize(const internal::PreprocessedProblem& pp,
  499. Solver::Summary* summary) {
  500. internal::OrderingToGroupSizes(pp.options.linear_solver_ordering.get(),
  501. &(summary->linear_solver_ordering_used));
  502. // TODO(sameeragarwal): Update the preprocessor to collapse the
  503. // second and higher groups into one group when nested dissection is
  504. // used.
  505. internal::OrderingToGroupSizes(pp.options.inner_iteration_ordering.get(),
  506. &(summary->inner_iteration_ordering_used));
  507. // clang-format off
  508. summary->inner_iterations_used = pp.inner_iteration_minimizer != nullptr;
  509. summary->linear_solver_type_used = pp.linear_solver_options.type;
  510. summary->mixed_precision_solves_used = pp.options.use_mixed_precision_solves;
  511. summary->num_threads_used = pp.options.num_threads;
  512. summary->preconditioner_type_used = pp.options.preconditioner_type;
  513. // clang-format on
  514. internal::SetSummaryFinalCost(summary);
  515. if (pp.reduced_program != nullptr) {
  516. SummarizeReducedProgram(*pp.reduced_program, summary);
  517. }
  518. using internal::CallStatistics;
  519. // It is possible that no evaluator was created. This would be the
  520. // case if the preprocessor failed, or if the reduced problem did
  521. // not contain any parameter blocks. Thus, only extract the
  522. // evaluator statistics if one exists.
  523. if (pp.evaluator != nullptr) {
  524. const std::map<std::string, CallStatistics>& evaluator_statistics =
  525. pp.evaluator->Statistics();
  526. {
  527. const CallStatistics& call_stats = FindWithDefault(
  528. evaluator_statistics, "Evaluator::Residual", CallStatistics());
  529. summary->residual_evaluation_time_in_seconds = call_stats.time;
  530. summary->num_residual_evaluations = call_stats.calls;
  531. }
  532. {
  533. const CallStatistics& call_stats = FindWithDefault(
  534. evaluator_statistics, "Evaluator::Jacobian", CallStatistics());
  535. summary->jacobian_evaluation_time_in_seconds = call_stats.time;
  536. summary->num_jacobian_evaluations = call_stats.calls;
  537. }
  538. }
  539. // Again, like the evaluator, there may or may not be a linear
  540. // solver from which we can extract run time statistics. In
  541. // particular the line search solver does not use a linear solver.
  542. if (pp.linear_solver != nullptr) {
  543. const std::map<std::string, CallStatistics>& linear_solver_statistics =
  544. pp.linear_solver->Statistics();
  545. const CallStatistics& call_stats = FindWithDefault(
  546. linear_solver_statistics, "LinearSolver::Solve", CallStatistics());
  547. summary->num_linear_solves = call_stats.calls;
  548. summary->linear_solver_time_in_seconds = call_stats.time;
  549. }
  550. }
  551. void Minimize(internal::PreprocessedProblem* pp, Solver::Summary* summary) {
  552. using internal::Minimizer;
  553. using internal::Program;
  554. Program* program = pp->reduced_program.get();
  555. if (pp->reduced_program->NumParameterBlocks() == 0) {
  556. summary->message =
  557. "Function tolerance reached. "
  558. "No non-constant parameter blocks found.";
  559. summary->termination_type = CONVERGENCE;
  560. if (pp->options.logging_type != SILENT) {
  561. VLOG(1) << summary->message;
  562. }
  563. summary->initial_cost = summary->fixed_cost;
  564. summary->final_cost = summary->fixed_cost;
  565. return;
  566. }
  567. const Vector original_reduced_parameters = pp->reduced_parameters;
  568. auto minimizer = Minimizer::Create(pp->options.minimizer_type);
  569. minimizer->Minimize(
  570. pp->minimizer_options, pp->reduced_parameters.data(), summary);
  571. program->StateVectorToParameterBlocks(
  572. summary->IsSolutionUsable() ? pp->reduced_parameters.data()
  573. : original_reduced_parameters.data());
  574. program->CopyParameterBlockStateToUserState();
  575. }
  576. std::string SchurStructureToString(const int row_block_size,
  577. const int e_block_size,
  578. const int f_block_size) {
  579. const std::string row = (row_block_size == Eigen::Dynamic)
  580. ? "d"
  581. : internal::StringPrintf("%d", row_block_size);
  582. const std::string e = (e_block_size == Eigen::Dynamic)
  583. ? "d"
  584. : internal::StringPrintf("%d", e_block_size);
  585. const std::string f = (f_block_size == Eigen::Dynamic)
  586. ? "d"
  587. : internal::StringPrintf("%d", f_block_size);
  588. return internal::StringPrintf("%s,%s,%s", row.c_str(), e.c_str(), f.c_str());
  589. }
  590. #ifndef CERES_NO_CUDA
  591. bool IsCudaRequired(const Solver::Options& options) {
  592. if (options.linear_solver_type == DENSE_NORMAL_CHOLESKY ||
  593. options.linear_solver_type == DENSE_SCHUR ||
  594. options.linear_solver_type == DENSE_QR) {
  595. return (options.dense_linear_algebra_library_type == CUDA);
  596. }
  597. if (options.linear_solver_type == CGNR) {
  598. return (options.sparse_linear_algebra_library_type == CUDA_SPARSE);
  599. }
  600. return false;
  601. }
  602. #endif
  603. } // namespace
  604. bool Solver::Options::IsValid(std::string* error) const {
  605. if (!CommonOptionsAreValid(*this, error)) {
  606. return false;
  607. }
  608. if (minimizer_type == TRUST_REGION &&
  609. !TrustRegionOptionsAreValid(*this, error)) {
  610. return false;
  611. }
  612. // We do not know if the problem is bounds constrained or not, if it
  613. // is then the trust region solver will also use the line search
  614. // solver to do a projection onto the box constraints, so make sure
  615. // that the line search options are checked independent of what
  616. // minimizer algorithm is being used.
  617. return LineSearchOptionsAreValid(*this, error);
  618. }
  619. Solver::~Solver() = default;
  620. void Solver::Solve(const Solver::Options& options,
  621. Problem* problem,
  622. Solver::Summary* summary) {
  623. using internal::PreprocessedProblem;
  624. using internal::Preprocessor;
  625. using internal::ProblemImpl;
  626. using internal::Program;
  627. using internal::WallTimeInSeconds;
  628. CHECK(problem != nullptr);
  629. CHECK(summary != nullptr);
  630. double start_time = WallTimeInSeconds();
  631. *summary = Summary();
  632. if (!options.IsValid(&summary->message)) {
  633. LOG(ERROR) << "Terminating: " << summary->message;
  634. return;
  635. }
  636. ProblemImpl* problem_impl = problem->mutable_impl();
  637. Program* program = problem_impl->mutable_program();
  638. PreSolveSummarize(options, problem_impl, summary);
  639. #ifndef CERES_NO_CUDA
  640. if (IsCudaRequired(options)) {
  641. if (!problem_impl->context()->InitCuda(&summary->message)) {
  642. LOG(ERROR) << "Terminating: " << summary->message;
  643. return;
  644. }
  645. }
  646. #endif // CERES_NO_CUDA
  647. // If gradient_checking is enabled, wrap all cost functions in a
  648. // gradient checker and install a callback that terminates if any gradient
  649. // error is detected.
  650. std::unique_ptr<internal::ProblemImpl> gradient_checking_problem;
  651. internal::GradientCheckingIterationCallback gradient_checking_callback;
  652. Solver::Options modified_options = options;
  653. if (options.check_gradients) {
  654. modified_options.callbacks.push_back(&gradient_checking_callback);
  655. gradient_checking_problem = CreateGradientCheckingProblemImpl(
  656. problem_impl,
  657. options.gradient_check_numeric_derivative_relative_step_size,
  658. options.gradient_check_relative_precision,
  659. &gradient_checking_callback);
  660. problem_impl = gradient_checking_problem.get();
  661. program = problem_impl->mutable_program();
  662. }
  663. // Make sure that all the parameter blocks states are set to the
  664. // values provided by the user.
  665. program->SetParameterBlockStatePtrsToUserStatePtrs();
  666. // The main thread also does work so we only need to launch num_threads - 1.
  667. problem_impl->context()->EnsureMinimumThreads(options.num_threads - 1);
  668. auto preprocessor = Preprocessor::Create(modified_options.minimizer_type);
  669. PreprocessedProblem pp;
  670. const bool status =
  671. preprocessor->Preprocess(modified_options, problem_impl, &pp);
  672. // We check the linear_solver_options.type rather than
  673. // modified_options.linear_solver_type because, depending on the
  674. // lack of a Schur structure, the preprocessor may change the linear
  675. // solver type.
  676. if (IsSchurType(pp.linear_solver_options.type)) {
  677. // TODO(sameeragarwal): We can likely eliminate the duplicate call
  678. // to DetectStructure here and inside the linear solver, by
  679. // calling this in the preprocessor.
  680. int row_block_size;
  681. int e_block_size;
  682. int f_block_size;
  683. DetectStructure(*static_cast<internal::BlockSparseMatrix*>(
  684. pp.minimizer_options.jacobian.get())
  685. ->block_structure(),
  686. pp.linear_solver_options.elimination_groups[0],
  687. &row_block_size,
  688. &e_block_size,
  689. &f_block_size);
  690. summary->schur_structure_given =
  691. SchurStructureToString(row_block_size, e_block_size, f_block_size);
  692. internal::GetBestSchurTemplateSpecialization(
  693. &row_block_size, &e_block_size, &f_block_size);
  694. summary->schur_structure_used =
  695. SchurStructureToString(row_block_size, e_block_size, f_block_size);
  696. }
  697. summary->fixed_cost = pp.fixed_cost;
  698. summary->preprocessor_time_in_seconds = WallTimeInSeconds() - start_time;
  699. if (status) {
  700. const double minimizer_start_time = WallTimeInSeconds();
  701. Minimize(&pp, summary);
  702. summary->minimizer_time_in_seconds =
  703. WallTimeInSeconds() - minimizer_start_time;
  704. } else {
  705. summary->message = pp.error;
  706. }
  707. const double postprocessor_start_time = WallTimeInSeconds();
  708. problem_impl = problem->mutable_impl();
  709. program = problem_impl->mutable_program();
  710. // On exit, ensure that the parameter blocks again point at the user
  711. // provided values and the parameter blocks are numbered according
  712. // to their position in the original user provided program.
  713. program->SetParameterBlockStatePtrsToUserStatePtrs();
  714. program->SetParameterOffsetsAndIndex();
  715. PostSolveSummarize(pp, summary);
  716. summary->postprocessor_time_in_seconds =
  717. WallTimeInSeconds() - postprocessor_start_time;
  718. // If the gradient checker reported an error, we want to report FAILURE
  719. // instead of USER_FAILURE and provide the error log.
  720. if (gradient_checking_callback.gradient_error_detected()) {
  721. summary->termination_type = FAILURE;
  722. summary->message = gradient_checking_callback.error_log();
  723. }
  724. summary->total_time_in_seconds = WallTimeInSeconds() - start_time;
  725. }
  726. void Solve(const Solver::Options& options,
  727. Problem* problem,
  728. Solver::Summary* summary) {
  729. Solver solver;
  730. solver.Solve(options, problem, summary);
  731. }
  732. std::string Solver::Summary::BriefReport() const {
  733. return StringPrintf(
  734. "Ceres Solver Report: "
  735. "Iterations: %d, "
  736. "Initial cost: %e, "
  737. "Final cost: %e, "
  738. "Termination: %s",
  739. num_successful_steps + num_unsuccessful_steps,
  740. initial_cost,
  741. final_cost,
  742. TerminationTypeToString(termination_type));
  743. }
  744. std::string Solver::Summary::FullReport() const {
  745. using internal::VersionString;
  746. // NOTE operator+ is not usable for concatenating a string and a string_view.
  747. std::string report =
  748. std::string{"\nSolver Summary (v "}.append(VersionString()) + ")\n\n";
  749. StringAppendF(&report, "%45s %21s\n", "Original", "Reduced");
  750. StringAppendF(&report,
  751. "Parameter blocks % 25d% 25d\n",
  752. num_parameter_blocks,
  753. num_parameter_blocks_reduced);
  754. StringAppendF(&report,
  755. "Parameters % 25d% 25d\n",
  756. num_parameters,
  757. num_parameters_reduced);
  758. if (num_effective_parameters_reduced != num_parameters_reduced) {
  759. StringAppendF(&report,
  760. "Effective parameters% 25d% 25d\n",
  761. num_effective_parameters,
  762. num_effective_parameters_reduced);
  763. }
  764. StringAppendF(&report,
  765. "Residual blocks % 25d% 25d\n",
  766. num_residual_blocks,
  767. num_residual_blocks_reduced);
  768. StringAppendF(&report,
  769. "Residuals % 25d% 25d\n",
  770. num_residuals,
  771. num_residuals_reduced);
  772. if (minimizer_type == TRUST_REGION) {
  773. // TRUST_SEARCH HEADER
  774. StringAppendF(
  775. &report, "\nMinimizer %19s\n", "TRUST_REGION");
  776. if (linear_solver_type_used == DENSE_NORMAL_CHOLESKY ||
  777. linear_solver_type_used == DENSE_SCHUR ||
  778. linear_solver_type_used == DENSE_QR) {
  779. const char* mixed_precision_suffix =
  780. (mixed_precision_solves_used ? "(Mixed Precision)" : "");
  781. StringAppendF(&report,
  782. "\nDense linear algebra library %15s %s\n",
  783. DenseLinearAlgebraLibraryTypeToString(
  784. dense_linear_algebra_library_type),
  785. mixed_precision_suffix);
  786. }
  787. StringAppendF(&report,
  788. "Trust region strategy %19s",
  789. TrustRegionStrategyTypeToString(trust_region_strategy_type));
  790. if (trust_region_strategy_type == DOGLEG) {
  791. if (dogleg_type == TRADITIONAL_DOGLEG) {
  792. StringAppendF(&report, " (TRADITIONAL)");
  793. } else {
  794. StringAppendF(&report, " (SUBSPACE)");
  795. }
  796. }
  797. const bool used_sparse_linear_algebra_library =
  798. linear_solver_type_used == SPARSE_NORMAL_CHOLESKY ||
  799. linear_solver_type_used == SPARSE_SCHUR ||
  800. linear_solver_type_used == CGNR ||
  801. (linear_solver_type_used == ITERATIVE_SCHUR &&
  802. (preconditioner_type_used == CLUSTER_JACOBI ||
  803. preconditioner_type_used == CLUSTER_TRIDIAGONAL));
  804. const bool linear_solver_ordering_required =
  805. linear_solver_type_used == SPARSE_SCHUR ||
  806. (linear_solver_type_used == ITERATIVE_SCHUR &&
  807. (preconditioner_type_used == CLUSTER_JACOBI ||
  808. preconditioner_type_used == CLUSTER_TRIDIAGONAL)) ||
  809. (linear_solver_type_used == CGNR && preconditioner_type_used == SUBSET);
  810. if (used_sparse_linear_algebra_library) {
  811. const char* mixed_precision_suffix =
  812. (mixed_precision_solves_used ? "(Mixed Precision)" : "");
  813. if (linear_solver_ordering_required) {
  814. StringAppendF(
  815. &report,
  816. "\nSparse linear algebra library %15s + %s %s\n",
  817. SparseLinearAlgebraLibraryTypeToString(
  818. sparse_linear_algebra_library_type),
  819. LinearSolverOrderingTypeToString(linear_solver_ordering_type),
  820. mixed_precision_suffix);
  821. } else {
  822. StringAppendF(&report,
  823. "\nSparse linear algebra library %15s %s\n",
  824. SparseLinearAlgebraLibraryTypeToString(
  825. sparse_linear_algebra_library_type),
  826. mixed_precision_suffix);
  827. }
  828. }
  829. StringAppendF(&report, "\n");
  830. StringAppendF(&report, "%45s %21s\n", "Given", "Used");
  831. StringAppendF(&report,
  832. "Linear solver %25s%25s\n",
  833. LinearSolverTypeToString(linear_solver_type_given),
  834. LinearSolverTypeToString(linear_solver_type_used));
  835. if (IsIterativeSolver(linear_solver_type_given)) {
  836. StringAppendF(&report,
  837. "Preconditioner %25s%25s\n",
  838. PreconditionerTypeToString(preconditioner_type_given),
  839. PreconditionerTypeToString(preconditioner_type_used));
  840. }
  841. if (preconditioner_type_used == CLUSTER_JACOBI ||
  842. preconditioner_type_used == CLUSTER_TRIDIAGONAL) {
  843. StringAppendF(
  844. &report,
  845. "Visibility clustering%24s%25s\n",
  846. VisibilityClusteringTypeToString(visibility_clustering_type),
  847. VisibilityClusteringTypeToString(visibility_clustering_type));
  848. }
  849. StringAppendF(&report,
  850. "Threads % 25d% 25d\n",
  851. num_threads_given,
  852. num_threads_used);
  853. std::string given;
  854. StringifyOrdering(linear_solver_ordering_given, &given);
  855. std::string used;
  856. StringifyOrdering(linear_solver_ordering_used, &used);
  857. StringAppendF(&report,
  858. "Linear solver ordering %22s %24s\n",
  859. given.c_str(),
  860. used.c_str());
  861. if (IsSchurType(linear_solver_type_used)) {
  862. StringAppendF(&report,
  863. "Schur structure %22s %24s\n",
  864. schur_structure_given.c_str(),
  865. schur_structure_used.c_str());
  866. }
  867. if (inner_iterations_given) {
  868. StringAppendF(&report,
  869. "Use inner iterations %20s %20s\n",
  870. inner_iterations_given ? "True" : "False",
  871. inner_iterations_used ? "True" : "False");
  872. }
  873. if (inner_iterations_used) {
  874. std::string given;
  875. StringifyOrdering(inner_iteration_ordering_given, &given);
  876. std::string used;
  877. StringifyOrdering(inner_iteration_ordering_used, &used);
  878. StringAppendF(&report,
  879. "Inner iteration ordering %20s %24s\n",
  880. given.c_str(),
  881. used.c_str());
  882. }
  883. } else {
  884. // LINE_SEARCH HEADER
  885. StringAppendF(&report, "\nMinimizer %19s\n", "LINE_SEARCH");
  886. std::string line_search_direction_string;
  887. if (line_search_direction_type == LBFGS) {
  888. line_search_direction_string = StringPrintf("LBFGS (%d)", max_lbfgs_rank);
  889. } else if (line_search_direction_type == NONLINEAR_CONJUGATE_GRADIENT) {
  890. line_search_direction_string = NonlinearConjugateGradientTypeToString(
  891. nonlinear_conjugate_gradient_type);
  892. } else {
  893. line_search_direction_string =
  894. LineSearchDirectionTypeToString(line_search_direction_type);
  895. }
  896. StringAppendF(&report,
  897. "Line search direction %19s\n",
  898. line_search_direction_string.c_str());
  899. const std::string line_search_type_string = StringPrintf(
  900. "%s %s",
  901. LineSearchInterpolationTypeToString(line_search_interpolation_type),
  902. LineSearchTypeToString(line_search_type));
  903. StringAppendF(&report,
  904. "Line search type %19s\n",
  905. line_search_type_string.c_str());
  906. StringAppendF(&report, "\n");
  907. StringAppendF(&report, "%45s %21s\n", "Given", "Used");
  908. StringAppendF(&report,
  909. "Threads % 25d% 25d\n",
  910. num_threads_given,
  911. num_threads_used);
  912. }
  913. StringAppendF(&report, "\nCost:\n");
  914. StringAppendF(&report, "Initial % 30e\n", initial_cost);
  915. if (termination_type != FAILURE && termination_type != USER_FAILURE) {
  916. StringAppendF(&report, "Final % 30e\n", final_cost);
  917. StringAppendF(&report, "Change % 30e\n", initial_cost - final_cost);
  918. }
  919. StringAppendF(&report,
  920. "\nMinimizer iterations % 16d\n",
  921. num_successful_steps + num_unsuccessful_steps);
  922. // Successful/Unsuccessful steps only matter in the case of the
  923. // trust region solver. Line search terminates when it encounters
  924. // the first unsuccessful step.
  925. if (minimizer_type == TRUST_REGION) {
  926. StringAppendF(&report,
  927. "Successful steps % 14d\n",
  928. num_successful_steps);
  929. StringAppendF(&report,
  930. "Unsuccessful steps % 14d\n",
  931. num_unsuccessful_steps);
  932. }
  933. if (inner_iterations_used) {
  934. StringAppendF(&report,
  935. "Steps with inner iterations % 14d\n",
  936. num_inner_iteration_steps);
  937. }
  938. const bool line_search_used =
  939. (minimizer_type == LINE_SEARCH ||
  940. (minimizer_type == TRUST_REGION && is_constrained));
  941. if (line_search_used) {
  942. StringAppendF(&report,
  943. "Line search steps % 14d\n",
  944. num_line_search_steps);
  945. }
  946. StringAppendF(&report, "\nTime (in seconds):\n");
  947. StringAppendF(
  948. &report, "Preprocessor %25.6f\n", preprocessor_time_in_seconds);
  949. StringAppendF(&report,
  950. "\n Residual only evaluation %18.6f (%d)\n",
  951. residual_evaluation_time_in_seconds,
  952. num_residual_evaluations);
  953. if (line_search_used) {
  954. StringAppendF(&report,
  955. " Line search cost evaluation %10.6f\n",
  956. line_search_cost_evaluation_time_in_seconds);
  957. }
  958. StringAppendF(&report,
  959. " Jacobian & residual evaluation %12.6f (%d)\n",
  960. jacobian_evaluation_time_in_seconds,
  961. num_jacobian_evaluations);
  962. if (line_search_used) {
  963. StringAppendF(&report,
  964. " Line search gradient evaluation %6.6f\n",
  965. line_search_gradient_evaluation_time_in_seconds);
  966. }
  967. if (minimizer_type == TRUST_REGION) {
  968. StringAppendF(&report,
  969. " Linear solver %23.6f (%d)\n",
  970. linear_solver_time_in_seconds,
  971. num_linear_solves);
  972. }
  973. if (inner_iterations_used) {
  974. StringAppendF(&report,
  975. " Inner iterations %23.6f\n",
  976. inner_iteration_time_in_seconds);
  977. }
  978. if (line_search_used) {
  979. StringAppendF(&report,
  980. " Line search polynomial minimization %.6f\n",
  981. line_search_polynomial_minimization_time_in_seconds);
  982. }
  983. StringAppendF(
  984. &report, "Minimizer %25.6f\n\n", minimizer_time_in_seconds);
  985. StringAppendF(
  986. &report, "Postprocessor %24.6f\n", postprocessor_time_in_seconds);
  987. StringAppendF(
  988. &report, "Total %25.6f\n\n", total_time_in_seconds);
  989. StringAppendF(&report,
  990. "Termination: %25s (%s)\n",
  991. TerminationTypeToString(termination_type),
  992. message.c_str());
  993. return report;
  994. }
  995. bool Solver::Summary::IsSolutionUsable() const {
  996. return internal::IsSolutionUsable(*this);
  997. }
  998. } // namespace ceres