solver.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. // 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. #include "ceres/solver.h"
  32. #include <vector>
  33. #include "ceres/problem.h"
  34. #include "ceres/problem_impl.h"
  35. #include "ceres/program.h"
  36. #include "ceres/solver_impl.h"
  37. #include "ceres/stringprintf.h"
  38. #include "ceres/wall_time.h"
  39. namespace ceres {
  40. namespace {
  41. void StringifyOrdering(const vector<int>& ordering, string* report) {
  42. if (ordering.size() == 0) {
  43. internal::StringAppendF(report, "AUTOMATIC");
  44. return;
  45. }
  46. for (int i = 0; i < ordering.size() - 1; ++i) {
  47. internal::StringAppendF(report, "%d, ", ordering[i]);
  48. }
  49. internal::StringAppendF(report, "%d", ordering.back());
  50. }
  51. } // namespace
  52. Solver::Options::~Options() {
  53. delete linear_solver_ordering;
  54. delete inner_iteration_ordering;
  55. }
  56. Solver::~Solver() {}
  57. void Solver::Solve(const Solver::Options& options,
  58. Problem* problem,
  59. Solver::Summary* summary) {
  60. double start_time_seconds = internal::WallTimeInSeconds();
  61. internal::ProblemImpl* problem_impl =
  62. CHECK_NOTNULL(problem)->problem_impl_.get();
  63. internal::SolverImpl::Solve(options, problem_impl, summary);
  64. summary->total_time_in_seconds =
  65. internal::WallTimeInSeconds() - start_time_seconds;
  66. }
  67. void Solve(const Solver::Options& options,
  68. Problem* problem,
  69. Solver::Summary* summary) {
  70. Solver solver;
  71. solver.Solve(options, problem, summary);
  72. }
  73. Solver::Summary::Summary()
  74. // Invalid values for most fields, to ensure that we are not
  75. // accidentally reporting default values.
  76. : minimizer_type(TRUST_REGION),
  77. termination_type(DID_NOT_RUN),
  78. initial_cost(-1.0),
  79. final_cost(-1.0),
  80. fixed_cost(-1.0),
  81. num_successful_steps(-1),
  82. num_unsuccessful_steps(-1),
  83. num_inner_iteration_steps(-1),
  84. preprocessor_time_in_seconds(-1.0),
  85. minimizer_time_in_seconds(-1.0),
  86. postprocessor_time_in_seconds(-1.0),
  87. total_time_in_seconds(-1.0),
  88. linear_solver_time_in_seconds(-1.0),
  89. residual_evaluation_time_in_seconds(-1.0),
  90. jacobian_evaluation_time_in_seconds(-1.0),
  91. inner_iteration_time_in_seconds(-1.0),
  92. num_parameter_blocks(-1),
  93. num_parameters(-1),
  94. num_effective_parameters(-1),
  95. num_residual_blocks(-1),
  96. num_residuals(-1),
  97. num_parameter_blocks_reduced(-1),
  98. num_parameters_reduced(-1),
  99. num_effective_parameters_reduced(-1),
  100. num_residual_blocks_reduced(-1),
  101. num_residuals_reduced(-1),
  102. num_threads_given(-1),
  103. num_threads_used(-1),
  104. num_linear_solver_threads_given(-1),
  105. num_linear_solver_threads_used(-1),
  106. linear_solver_type_given(SPARSE_NORMAL_CHOLESKY),
  107. linear_solver_type_used(SPARSE_NORMAL_CHOLESKY),
  108. inner_iterations_given(false),
  109. inner_iterations_used(false),
  110. preconditioner_type(IDENTITY),
  111. visibility_clustering_type(CANONICAL_VIEWS),
  112. trust_region_strategy_type(LEVENBERG_MARQUARDT),
  113. dense_linear_algebra_library_type(EIGEN),
  114. sparse_linear_algebra_library_type(SUITE_SPARSE),
  115. line_search_direction_type(LBFGS),
  116. line_search_type(ARMIJO) {
  117. }
  118. string Solver::Summary::BriefReport() const {
  119. string report = "Ceres Solver Report: ";
  120. if (termination_type == DID_NOT_RUN) {
  121. CHECK(!error.empty())
  122. << "Solver terminated with DID_NOT_RUN but the solver did not "
  123. << "return a reason. This is a Ceres error. Please report this "
  124. << "to the Ceres team";
  125. return report + "Termination: DID_NOT_RUN, because " + error;
  126. }
  127. internal::StringAppendF(&report, "Iterations: %d",
  128. num_successful_steps + num_unsuccessful_steps);
  129. internal::StringAppendF(&report, ", Initial cost: %e", initial_cost);
  130. // If the solver failed or was aborted, then the final_cost has no
  131. // meaning.
  132. if (termination_type != NUMERICAL_FAILURE &&
  133. termination_type != USER_ABORT) {
  134. internal::StringAppendF(&report, ", Final cost: %e", final_cost);
  135. }
  136. internal::StringAppendF(&report, ", Termination: %s.",
  137. SolverTerminationTypeToString(termination_type));
  138. return report;
  139. };
  140. using internal::StringAppendF;
  141. using internal::StringPrintf;
  142. string Solver::Summary::FullReport() const {
  143. string report =
  144. "\n"
  145. "Ceres Solver Report\n"
  146. "-------------------\n";
  147. if (termination_type == DID_NOT_RUN) {
  148. StringAppendF(&report, " Original\n");
  149. StringAppendF(&report, "Parameter blocks % 10d\n", num_parameter_blocks);
  150. StringAppendF(&report, "Parameters % 10d\n", num_parameters);
  151. if (num_effective_parameters != num_parameters) {
  152. StringAppendF(&report, "Effective parameters% 10d\n", num_parameters);
  153. }
  154. StringAppendF(&report, "Residual blocks % 10d\n",
  155. num_residual_blocks);
  156. StringAppendF(&report, "Residuals % 10d\n\n",
  157. num_residuals);
  158. } else {
  159. StringAppendF(&report, "%45s %21s\n", "Original", "Reduced");
  160. StringAppendF(&report, "Parameter blocks % 25d% 25d\n",
  161. num_parameter_blocks, num_parameter_blocks_reduced);
  162. StringAppendF(&report, "Parameters % 25d% 25d\n",
  163. num_parameters, num_parameters_reduced);
  164. if (num_effective_parameters_reduced != num_parameters_reduced) {
  165. StringAppendF(&report, "Effective parameters% 25d% 25d\n",
  166. num_effective_parameters, num_effective_parameters_reduced);
  167. }
  168. StringAppendF(&report, "Residual blocks % 25d% 25d\n",
  169. num_residual_blocks, num_residual_blocks_reduced);
  170. StringAppendF(&report, "Residual % 25d% 25d\n",
  171. num_residuals, num_residuals_reduced);
  172. }
  173. if (minimizer_type == TRUST_REGION) {
  174. // TRUST_SEARCH HEADER
  175. StringAppendF(&report, "\nMinimizer %19s\n",
  176. "TRUST_REGION");
  177. if (linear_solver_type_used == DENSE_NORMAL_CHOLESKY ||
  178. linear_solver_type_used == DENSE_SCHUR ||
  179. linear_solver_type_used == DENSE_QR) {
  180. StringAppendF(&report, "\nDense linear algebra library %15s\n",
  181. DenseLinearAlgebraLibraryTypeToString(
  182. dense_linear_algebra_library_type));
  183. }
  184. if (linear_solver_type_used == SPARSE_NORMAL_CHOLESKY ||
  185. linear_solver_type_used == SPARSE_SCHUR ||
  186. (linear_solver_type_used == ITERATIVE_SCHUR &&
  187. (preconditioner_type == CLUSTER_JACOBI ||
  188. preconditioner_type == CLUSTER_TRIDIAGONAL))) {
  189. StringAppendF(&report, "\nSparse linear algebra library %15s\n",
  190. SparseLinearAlgebraLibraryTypeToString(
  191. sparse_linear_algebra_library_type));
  192. }
  193. StringAppendF(&report, "Trust region strategy %19s",
  194. TrustRegionStrategyTypeToString(
  195. trust_region_strategy_type));
  196. if (trust_region_strategy_type == DOGLEG) {
  197. if (dogleg_type == TRADITIONAL_DOGLEG) {
  198. StringAppendF(&report, " (TRADITIONAL)");
  199. } else {
  200. StringAppendF(&report, " (SUBSPACE)");
  201. }
  202. }
  203. StringAppendF(&report, "\n");
  204. StringAppendF(&report, "\n");
  205. StringAppendF(&report, "%45s %21s\n", "Given", "Used");
  206. StringAppendF(&report, "Linear solver %25s%25s\n",
  207. LinearSolverTypeToString(linear_solver_type_given),
  208. LinearSolverTypeToString(linear_solver_type_used));
  209. if (linear_solver_type_given == CGNR ||
  210. linear_solver_type_given == ITERATIVE_SCHUR) {
  211. StringAppendF(&report, "Preconditioner %25s%25s\n",
  212. PreconditionerTypeToString(preconditioner_type),
  213. PreconditionerTypeToString(preconditioner_type));
  214. }
  215. if (preconditioner_type == CLUSTER_JACOBI ||
  216. preconditioner_type == CLUSTER_TRIDIAGONAL) {
  217. StringAppendF(&report, "Visibility clustering%24s%25s\n",
  218. VisibilityClusteringTypeToString(
  219. visibility_clustering_type),
  220. VisibilityClusteringTypeToString(
  221. visibility_clustering_type));
  222. }
  223. 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);
  228. if (IsSchurType(linear_solver_type_used)) {
  229. string given;
  230. StringifyOrdering(linear_solver_ordering_given, &given);
  231. string used;
  232. StringifyOrdering(linear_solver_ordering_used, &used);
  233. StringAppendF(&report,
  234. "Linear solver ordering %22s %24s\n",
  235. given.c_str(),
  236. used.c_str());
  237. }
  238. if (inner_iterations_given) {
  239. StringAppendF(&report,
  240. "Use inner iterations %20s %20s\n",
  241. inner_iterations_given ? "True" : "False",
  242. inner_iterations_used ? "True" : "False");
  243. }
  244. if (inner_iterations_used) {
  245. string given;
  246. StringifyOrdering(inner_iteration_ordering_given, &given);
  247. string used;
  248. StringifyOrdering(inner_iteration_ordering_used, &used);
  249. StringAppendF(&report,
  250. "Inner iteration ordering %20s %24s\n",
  251. given.c_str(),
  252. used.c_str());
  253. }
  254. } else {
  255. // LINE_SEARCH HEADER
  256. StringAppendF(&report, "\nMinimizer %19s\n", "LINE_SEARCH");
  257. string line_search_direction_string;
  258. if (line_search_direction_type == LBFGS) {
  259. line_search_direction_string = StringPrintf("LBFGS (%d)", max_lbfgs_rank);
  260. } else if (line_search_direction_type == NONLINEAR_CONJUGATE_GRADIENT) {
  261. line_search_direction_string =
  262. NonlinearConjugateGradientTypeToString(
  263. nonlinear_conjugate_gradient_type);
  264. } else {
  265. line_search_direction_string =
  266. LineSearchDirectionTypeToString(line_search_direction_type);
  267. }
  268. StringAppendF(&report, "Line search direction %19s\n",
  269. line_search_direction_string.c_str());
  270. const string line_search_type_string =
  271. StringPrintf("%s %s",
  272. LineSearchInterpolationTypeToString(
  273. line_search_interpolation_type),
  274. LineSearchTypeToString(line_search_type));
  275. StringAppendF(&report, "Line search type %19s\n",
  276. line_search_type_string.c_str());
  277. StringAppendF(&report, "\n");
  278. StringAppendF(&report, "%45s %21s\n", "Given", "Used");
  279. StringAppendF(&report, "Threads % 25d% 25d\n",
  280. num_threads_given, num_threads_used);
  281. }
  282. if (termination_type == DID_NOT_RUN) {
  283. CHECK(!error.empty())
  284. << "Solver terminated with DID_NOT_RUN but the solver did not "
  285. << "return a reason. This is a Ceres error. Please report this "
  286. << "to the Ceres team";
  287. StringAppendF(&report, "Termination: %20s\n",
  288. "DID_NOT_RUN");
  289. StringAppendF(&report, "Reason: %s\n", error.c_str());
  290. return report;
  291. }
  292. StringAppendF(&report, "\nCost:\n");
  293. StringAppendF(&report, "Initial % 30e\n", initial_cost);
  294. if (termination_type != NUMERICAL_FAILURE &&
  295. termination_type != USER_ABORT) {
  296. StringAppendF(&report, "Final % 30e\n", final_cost);
  297. StringAppendF(&report, "Change % 30e\n",
  298. initial_cost - final_cost);
  299. }
  300. StringAppendF(&report, "\nMinimizer iterations % 16d\n",
  301. num_successful_steps + num_unsuccessful_steps);
  302. // Successful/Unsuccessful steps only matter in the case of the
  303. // trust region solver. Line search terminates when it encounters
  304. // the first unsuccessful step.
  305. if (minimizer_type == TRUST_REGION) {
  306. StringAppendF(&report, "Successful steps % 14d\n",
  307. num_successful_steps);
  308. StringAppendF(&report, "Unsuccessful steps % 14d\n",
  309. num_unsuccessful_steps);
  310. }
  311. if (inner_iterations_used) {
  312. StringAppendF(&report, "Steps with inner iterations % 14d\n",
  313. num_inner_iteration_steps);
  314. }
  315. StringAppendF(&report, "\nTime (in seconds):\n");
  316. StringAppendF(&report, "Preprocessor %25.3f\n",
  317. preprocessor_time_in_seconds);
  318. StringAppendF(&report, "\n Residual evaluation %23.3f\n",
  319. residual_evaluation_time_in_seconds);
  320. StringAppendF(&report, " Jacobian evaluation %23.3f\n",
  321. jacobian_evaluation_time_in_seconds);
  322. if (minimizer_type == TRUST_REGION) {
  323. StringAppendF(&report, " Linear solver %23.3f\n",
  324. linear_solver_time_in_seconds);
  325. }
  326. if (inner_iterations_used) {
  327. StringAppendF(&report, " Inner iterations %23.3f\n",
  328. inner_iteration_time_in_seconds);
  329. }
  330. StringAppendF(&report, "Minimizer %25.3f\n\n",
  331. minimizer_time_in_seconds);
  332. StringAppendF(&report, "Postprocessor %24.3f\n",
  333. postprocessor_time_in_seconds);
  334. StringAppendF(&report, "Total %25.3f\n\n",
  335. total_time_in_seconds);
  336. StringAppendF(&report, "Termination: %25s\n",
  337. SolverTerminationTypeToString(termination_type));
  338. return report;
  339. };
  340. } // namespace ceres