levenberg_marquardt_strategy.cc 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // Ceres Solver - A fast non-linear least squares minimizer
  2. // Copyright 2023 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: sameeragarwal@google.com (Sameer Agarwal)
  30. #include "ceres/levenberg_marquardt_strategy.h"
  31. #include <algorithm>
  32. #include <cmath>
  33. #include "Eigen/Core"
  34. #include "ceres/array_utils.h"
  35. #include "ceres/internal/eigen.h"
  36. #include "ceres/linear_least_squares_problems.h"
  37. #include "ceres/linear_solver.h"
  38. #include "ceres/parallel_vector_ops.h"
  39. #include "ceres/sparse_matrix.h"
  40. #include "ceres/trust_region_strategy.h"
  41. #include "ceres/types.h"
  42. #include "glog/logging.h"
  43. namespace ceres::internal {
  44. LevenbergMarquardtStrategy::LevenbergMarquardtStrategy(
  45. const TrustRegionStrategy::Options& options)
  46. : linear_solver_(options.linear_solver),
  47. radius_(options.initial_radius),
  48. max_radius_(options.max_radius),
  49. min_diagonal_(options.min_lm_diagonal),
  50. max_diagonal_(options.max_lm_diagonal),
  51. decrease_factor_(2.0),
  52. reuse_diagonal_(false),
  53. context_(options.context),
  54. num_threads_(options.num_threads) {
  55. CHECK(linear_solver_ != nullptr);
  56. CHECK_GT(min_diagonal_, 0.0);
  57. CHECK_LE(min_diagonal_, max_diagonal_);
  58. CHECK_GT(max_radius_, 0.0);
  59. }
  60. LevenbergMarquardtStrategy::~LevenbergMarquardtStrategy() = default;
  61. TrustRegionStrategy::Summary LevenbergMarquardtStrategy::ComputeStep(
  62. const TrustRegionStrategy::PerSolveOptions& per_solve_options,
  63. SparseMatrix* jacobian,
  64. const double* residuals,
  65. double* step) {
  66. CHECK(jacobian != nullptr);
  67. CHECK(residuals != nullptr);
  68. CHECK(step != nullptr);
  69. const int num_parameters = jacobian->num_cols();
  70. if (!reuse_diagonal_) {
  71. if (diagonal_.rows() != num_parameters) {
  72. diagonal_.resize(num_parameters, 1);
  73. }
  74. jacobian->SquaredColumnNorm(diagonal_.data(), context_, num_threads_);
  75. ParallelAssign(context_,
  76. num_threads_,
  77. diagonal_,
  78. diagonal_.array().max(min_diagonal_).min(max_diagonal_));
  79. }
  80. if (lm_diagonal_.size() == 0) {
  81. lm_diagonal_.resize(num_parameters);
  82. }
  83. ParallelAssign(
  84. context_, num_threads_, lm_diagonal_, (diagonal_ / radius_).cwiseSqrt());
  85. LinearSolver::PerSolveOptions solve_options;
  86. solve_options.D = lm_diagonal_.data();
  87. solve_options.q_tolerance = per_solve_options.eta;
  88. // Disable r_tolerance checking. Since we only care about
  89. // termination via the q_tolerance. As Nash and Sofer show,
  90. // r_tolerance based termination is essentially useless in
  91. // Truncated Newton methods.
  92. solve_options.r_tolerance = -1.0;
  93. // Invalidate the output array lm_step, so that we can detect if
  94. // the linear solver generated numerical garbage. This is known
  95. // to happen for the DENSE_QR and then DENSE_SCHUR solver when
  96. // the Jacobian is severely rank deficient and mu is too small.
  97. InvalidateArray(num_parameters, step);
  98. // Instead of solving Jx = -r, solve Jy = r.
  99. // Then x can be found as x = -y, but the inputs jacobian and residuals
  100. // do not need to be modified.
  101. LinearSolver::Summary linear_solver_summary =
  102. linear_solver_->Solve(jacobian, residuals, solve_options, step);
  103. if (linear_solver_summary.termination_type ==
  104. LinearSolverTerminationType::FATAL_ERROR) {
  105. LOG(WARNING) << "Linear solver fatal error: "
  106. << linear_solver_summary.message;
  107. } else if (linear_solver_summary.termination_type ==
  108. LinearSolverTerminationType::FAILURE) {
  109. LOG(WARNING) << "Linear solver failure. Failed to compute a step: "
  110. << linear_solver_summary.message;
  111. } else if (!IsArrayValid(num_parameters, step)) {
  112. LOG(WARNING) << "Linear solver failure. Failed to compute a finite step.";
  113. linear_solver_summary.termination_type =
  114. LinearSolverTerminationType::FAILURE;
  115. } else {
  116. VectorRef step_vec(step, num_parameters);
  117. ParallelAssign(context_, num_threads_, step_vec, -step_vec);
  118. }
  119. reuse_diagonal_ = true;
  120. if (per_solve_options.dump_format_type == CONSOLE ||
  121. (per_solve_options.dump_format_type != CONSOLE &&
  122. !per_solve_options.dump_filename_base.empty())) {
  123. if (!DumpLinearLeastSquaresProblem(per_solve_options.dump_filename_base,
  124. per_solve_options.dump_format_type,
  125. jacobian,
  126. solve_options.D,
  127. residuals,
  128. step,
  129. 0)) {
  130. LOG(ERROR) << "Unable to dump trust region problem."
  131. << " Filename base: " << per_solve_options.dump_filename_base;
  132. }
  133. }
  134. TrustRegionStrategy::Summary summary;
  135. summary.residual_norm = linear_solver_summary.residual_norm;
  136. summary.num_iterations = linear_solver_summary.num_iterations;
  137. summary.termination_type = linear_solver_summary.termination_type;
  138. return summary;
  139. }
  140. void LevenbergMarquardtStrategy::StepAccepted(double step_quality) {
  141. CHECK_GT(step_quality, 0.0);
  142. radius_ =
  143. radius_ / std::max(1.0 / 3.0, 1.0 - pow(2.0 * step_quality - 1.0, 3));
  144. radius_ = std::min(max_radius_, radius_);
  145. decrease_factor_ = 2.0;
  146. reuse_diagonal_ = false;
  147. }
  148. void LevenbergMarquardtStrategy::StepRejected(double /*step_quality*/) {
  149. radius_ = radius_ / decrease_factor_;
  150. decrease_factor_ *= 2.0;
  151. reuse_diagonal_ = true;
  152. }
  153. double LevenbergMarquardtStrategy::Radius() const { return radius_; }
  154. } // namespace ceres::internal