numeric_diff.h 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  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. // mierle@gmail.com (Keir Mierle)
  31. // tbennun@gmail.com (Tal Ben-Nun)
  32. //
  33. // Finite differencing routines used by NumericDiffCostFunction.
  34. #ifndef CERES_PUBLIC_INTERNAL_NUMERIC_DIFF_H_
  35. #define CERES_PUBLIC_INTERNAL_NUMERIC_DIFF_H_
  36. #include <cstring>
  37. #include <utility>
  38. #include "Eigen/Dense"
  39. #include "Eigen/StdVector"
  40. #include "ceres/cost_function.h"
  41. #include "ceres/internal/fixed_array.h"
  42. #include "ceres/internal/variadic_evaluate.h"
  43. #include "ceres/numeric_diff_options.h"
  44. #include "ceres/types.h"
  45. #include "glog/logging.h"
  46. namespace ceres::internal {
  47. // This is split from the main class because C++ doesn't allow partial template
  48. // specializations for member functions. The alternative is to repeat the main
  49. // class for differing numbers of parameters, which is also unfortunate.
  50. template <typename CostFunctor,
  51. NumericDiffMethodType kMethod,
  52. int kNumResiduals,
  53. typename ParameterDims,
  54. int kParameterBlock,
  55. int kParameterBlockSize>
  56. struct NumericDiff {
  57. // Mutates parameters but must restore them before return.
  58. static bool EvaluateJacobianForParameterBlock(
  59. const CostFunctor* functor,
  60. const double* residuals_at_eval_point,
  61. const NumericDiffOptions& options,
  62. int num_residuals,
  63. int parameter_block_index,
  64. int parameter_block_size,
  65. double** parameters,
  66. double* jacobian) {
  67. using Eigen::ColMajor;
  68. using Eigen::Map;
  69. using Eigen::Matrix;
  70. using Eigen::RowMajor;
  71. DCHECK(jacobian);
  72. const int num_residuals_internal =
  73. (kNumResiduals != ceres::DYNAMIC ? kNumResiduals : num_residuals);
  74. const int parameter_block_index_internal =
  75. (kParameterBlock != ceres::DYNAMIC ? kParameterBlock
  76. : parameter_block_index);
  77. const int parameter_block_size_internal =
  78. (kParameterBlockSize != ceres::DYNAMIC ? kParameterBlockSize
  79. : parameter_block_size);
  80. using ResidualVector = Matrix<double, kNumResiduals, 1>;
  81. using ParameterVector = Matrix<double, kParameterBlockSize, 1>;
  82. // The convoluted reasoning for choosing the Row/Column major
  83. // ordering of the matrix is an artifact of the restrictions in
  84. // Eigen that prevent it from creating RowMajor matrices with a
  85. // single column. In these cases, we ask for a ColMajor matrix.
  86. using JacobianMatrix =
  87. Matrix<double,
  88. kNumResiduals,
  89. kParameterBlockSize,
  90. (kParameterBlockSize == 1) ? ColMajor : RowMajor>;
  91. Map<JacobianMatrix> parameter_jacobian(
  92. jacobian, num_residuals_internal, parameter_block_size_internal);
  93. Map<ParameterVector> x_plus_delta(
  94. parameters[parameter_block_index_internal],
  95. parameter_block_size_internal);
  96. ParameterVector x(x_plus_delta);
  97. ParameterVector step_size =
  98. x.array().abs() * ((kMethod == RIDDERS)
  99. ? options.ridders_relative_initial_step_size
  100. : options.relative_step_size);
  101. // It is not a good idea to make the step size arbitrarily
  102. // small. This will lead to problems with round off and numerical
  103. // instability when dividing by the step size. The general
  104. // recommendation is to not go down below sqrt(epsilon).
  105. double min_step_size = std::sqrt(std::numeric_limits<double>::epsilon());
  106. // For Ridders' method, the initial step size is required to be large,
  107. // thus ridders_relative_initial_step_size is used.
  108. if (kMethod == RIDDERS) {
  109. min_step_size =
  110. (std::max)(min_step_size, options.ridders_relative_initial_step_size);
  111. }
  112. // For each parameter in the parameter block, use finite differences to
  113. // compute the derivative for that parameter.
  114. FixedArray<double> temp_residual_array(num_residuals_internal);
  115. FixedArray<double> residual_array(num_residuals_internal);
  116. Map<ResidualVector> residuals(residual_array.data(),
  117. num_residuals_internal);
  118. for (int j = 0; j < parameter_block_size_internal; ++j) {
  119. const double delta = (std::max)(min_step_size, step_size(j));
  120. if (kMethod == RIDDERS) {
  121. if (!EvaluateRiddersJacobianColumn(functor,
  122. j,
  123. delta,
  124. options,
  125. num_residuals_internal,
  126. parameter_block_size_internal,
  127. x.data(),
  128. residuals_at_eval_point,
  129. parameters,
  130. x_plus_delta.data(),
  131. temp_residual_array.data(),
  132. residual_array.data())) {
  133. return false;
  134. }
  135. } else {
  136. if (!EvaluateJacobianColumn(functor,
  137. j,
  138. delta,
  139. num_residuals_internal,
  140. parameter_block_size_internal,
  141. x.data(),
  142. residuals_at_eval_point,
  143. parameters,
  144. x_plus_delta.data(),
  145. temp_residual_array.data(),
  146. residual_array.data())) {
  147. return false;
  148. }
  149. }
  150. parameter_jacobian.col(j).matrix() = residuals;
  151. }
  152. return true;
  153. }
  154. static bool EvaluateJacobianColumn(const CostFunctor* functor,
  155. int parameter_index,
  156. double delta,
  157. int num_residuals,
  158. int parameter_block_size,
  159. const double* x_ptr,
  160. const double* residuals_at_eval_point,
  161. double** parameters,
  162. double* x_plus_delta_ptr,
  163. double* temp_residuals_ptr,
  164. double* residuals_ptr) {
  165. using Eigen::Map;
  166. using Eigen::Matrix;
  167. using ResidualVector = Matrix<double, kNumResiduals, 1>;
  168. using ParameterVector = Matrix<double, kParameterBlockSize, 1>;
  169. Map<const ParameterVector> x(x_ptr, parameter_block_size);
  170. Map<ParameterVector> x_plus_delta(x_plus_delta_ptr, parameter_block_size);
  171. Map<ResidualVector> residuals(residuals_ptr, num_residuals);
  172. Map<ResidualVector> temp_residuals(temp_residuals_ptr, num_residuals);
  173. // Mutate 1 element at a time and then restore.
  174. x_plus_delta(parameter_index) = x(parameter_index) + delta;
  175. if (!VariadicEvaluate<ParameterDims>(
  176. *functor, parameters, residuals.data())) {
  177. return false;
  178. }
  179. // Compute this column of the jacobian in 3 steps:
  180. // 1. Store residuals for the forward part.
  181. // 2. Subtract residuals for the backward (or 0) part.
  182. // 3. Divide out the run.
  183. double one_over_delta = 1.0 / delta;
  184. if (kMethod == CENTRAL || kMethod == RIDDERS) {
  185. // Compute the function on the other side of x(parameter_index).
  186. x_plus_delta(parameter_index) = x(parameter_index) - delta;
  187. if (!VariadicEvaluate<ParameterDims>(
  188. *functor, parameters, temp_residuals.data())) {
  189. return false;
  190. }
  191. residuals -= temp_residuals;
  192. one_over_delta /= 2;
  193. } else {
  194. // Forward difference only; reuse existing residuals evaluation.
  195. residuals -=
  196. Map<const ResidualVector>(residuals_at_eval_point, num_residuals);
  197. }
  198. // Restore x_plus_delta.
  199. x_plus_delta(parameter_index) = x(parameter_index);
  200. // Divide out the run to get slope.
  201. residuals *= one_over_delta;
  202. return true;
  203. }
  204. // This numeric difference implementation uses adaptive differentiation
  205. // on the parameters to obtain the Jacobian matrix. The adaptive algorithm
  206. // is based on Ridders' method for adaptive differentiation, which creates
  207. // a Romberg tableau from varying step sizes and extrapolates the
  208. // intermediate results to obtain the current computational error.
  209. //
  210. // References:
  211. // C.J.F. Ridders, Accurate computation of F'(x) and F'(x) F"(x), Advances
  212. // in Engineering Software (1978), Volume 4, Issue 2, April 1982,
  213. // Pages 75-76, ISSN 0141-1195,
  214. // http://dx.doi.org/10.1016/S0141-1195(82)80057-0.
  215. static bool EvaluateRiddersJacobianColumn(
  216. const CostFunctor* functor,
  217. int parameter_index,
  218. double delta,
  219. const NumericDiffOptions& options,
  220. int num_residuals,
  221. int parameter_block_size,
  222. const double* x_ptr,
  223. const double* residuals_at_eval_point,
  224. double** parameters,
  225. double* x_plus_delta_ptr,
  226. double* temp_residuals_ptr,
  227. double* residuals_ptr) {
  228. using Eigen::aligned_allocator;
  229. using Eigen::Map;
  230. using Eigen::Matrix;
  231. using ResidualVector = Matrix<double, kNumResiduals, 1>;
  232. using ResidualCandidateMatrix =
  233. Matrix<double, kNumResiduals, Eigen::Dynamic>;
  234. using ParameterVector = Matrix<double, kParameterBlockSize, 1>;
  235. Map<const ParameterVector> x(x_ptr, parameter_block_size);
  236. Map<ParameterVector> x_plus_delta(x_plus_delta_ptr, parameter_block_size);
  237. Map<ResidualVector> residuals(residuals_ptr, num_residuals);
  238. Map<ResidualVector> temp_residuals(temp_residuals_ptr, num_residuals);
  239. // In order for the algorithm to converge, the step size should be
  240. // initialized to a value that is large enough to produce a significant
  241. // change in the function.
  242. // As the derivative is estimated, the step size decreases.
  243. // By default, the step sizes are chosen so that the middle column
  244. // of the Romberg tableau uses the input delta.
  245. double current_step_size =
  246. delta * pow(options.ridders_step_shrink_factor,
  247. options.max_num_ridders_extrapolations / 2);
  248. // Double-buffering temporary differential candidate vectors
  249. // from previous step size.
  250. ResidualCandidateMatrix stepsize_candidates_a(
  251. num_residuals, options.max_num_ridders_extrapolations);
  252. ResidualCandidateMatrix stepsize_candidates_b(
  253. num_residuals, options.max_num_ridders_extrapolations);
  254. ResidualCandidateMatrix* current_candidates = &stepsize_candidates_a;
  255. ResidualCandidateMatrix* previous_candidates = &stepsize_candidates_b;
  256. // Represents the computational error of the derivative. This variable is
  257. // initially set to a large value, and is set to the difference between
  258. // current and previous finite difference extrapolations.
  259. // norm_error is supposed to decrease as the finite difference tableau
  260. // generation progresses, serving both as an estimate for differentiation
  261. // error and as a measure of differentiation numerical stability.
  262. double norm_error = (std::numeric_limits<double>::max)();
  263. // Loop over decreasing step sizes until:
  264. // 1. Error is smaller than a given value (ridders_epsilon),
  265. // 2. Maximal order of extrapolation reached, or
  266. // 3. Extrapolation becomes numerically unstable.
  267. for (int i = 0; i < options.max_num_ridders_extrapolations; ++i) {
  268. // Compute the numerical derivative at this step size.
  269. if (!EvaluateJacobianColumn(functor,
  270. parameter_index,
  271. current_step_size,
  272. num_residuals,
  273. parameter_block_size,
  274. x.data(),
  275. residuals_at_eval_point,
  276. parameters,
  277. x_plus_delta.data(),
  278. temp_residuals.data(),
  279. current_candidates->col(0).data())) {
  280. // Something went wrong; bail.
  281. return false;
  282. }
  283. // Store initial results.
  284. if (i == 0) {
  285. residuals = current_candidates->col(0);
  286. }
  287. // Shrink differentiation step size.
  288. current_step_size /= options.ridders_step_shrink_factor;
  289. // Extrapolation factor for Richardson acceleration method (see below).
  290. double richardson_factor = options.ridders_step_shrink_factor *
  291. options.ridders_step_shrink_factor;
  292. for (int k = 1; k <= i; ++k) {
  293. // Extrapolate the various orders of finite differences using
  294. // the Richardson acceleration method.
  295. current_candidates->col(k) =
  296. (richardson_factor * current_candidates->col(k - 1) -
  297. previous_candidates->col(k - 1)) /
  298. (richardson_factor - 1.0);
  299. richardson_factor *= options.ridders_step_shrink_factor *
  300. options.ridders_step_shrink_factor;
  301. // Compute the difference between the previous value and the current.
  302. double candidate_error = (std::max)(
  303. (current_candidates->col(k) - current_candidates->col(k - 1))
  304. .norm(),
  305. (current_candidates->col(k) - previous_candidates->col(k - 1))
  306. .norm());
  307. // If the error has decreased, update results.
  308. if (candidate_error <= norm_error) {
  309. norm_error = candidate_error;
  310. residuals = current_candidates->col(k);
  311. // If the error is small enough, stop.
  312. if (norm_error < options.ridders_epsilon) {
  313. break;
  314. }
  315. }
  316. }
  317. // After breaking out of the inner loop, declare convergence.
  318. if (norm_error < options.ridders_epsilon) {
  319. break;
  320. }
  321. // Check to see if the current gradient estimate is numerically unstable.
  322. // If so, bail out and return the last stable result.
  323. if (i > 0) {
  324. double tableau_error =
  325. (current_candidates->col(i) - previous_candidates->col(i - 1))
  326. .norm();
  327. // Compare current error to the chosen candidate's error.
  328. if (tableau_error >= 2 * norm_error) {
  329. break;
  330. }
  331. }
  332. std::swap(current_candidates, previous_candidates);
  333. }
  334. return true;
  335. }
  336. };
  337. // This function calls NumericDiff<...>::EvaluateJacobianForParameterBlock for
  338. // each parameter block.
  339. //
  340. // Example:
  341. // A call to
  342. // EvaluateJacobianForParameterBlocks<StaticParameterDims<2, 3>>(
  343. // functor,
  344. // residuals_at_eval_point,
  345. // options,
  346. // num_residuals,
  347. // parameters,
  348. // jacobians);
  349. // will result in the following calls to
  350. // NumericDiff<...>::EvaluateJacobianForParameterBlock:
  351. //
  352. // if (jacobians[0] != nullptr) {
  353. // if (!NumericDiff<
  354. // CostFunctor,
  355. // method,
  356. // kNumResiduals,
  357. // StaticParameterDims<2, 3>,
  358. // 0,
  359. // 2>::EvaluateJacobianForParameterBlock(functor,
  360. // residuals_at_eval_point,
  361. // options,
  362. // num_residuals,
  363. // 0,
  364. // 2,
  365. // parameters,
  366. // jacobians[0])) {
  367. // return false;
  368. // }
  369. // }
  370. // if (jacobians[1] != nullptr) {
  371. // if (!NumericDiff<
  372. // CostFunctor,
  373. // method,
  374. // kNumResiduals,
  375. // StaticParameterDims<2, 3>,
  376. // 1,
  377. // 3>::EvaluateJacobianForParameterBlock(functor,
  378. // residuals_at_eval_point,
  379. // options,
  380. // num_residuals,
  381. // 1,
  382. // 3,
  383. // parameters,
  384. // jacobians[1])) {
  385. // return false;
  386. // }
  387. // }
  388. template <typename ParameterDims,
  389. typename Parameters = typename ParameterDims::Parameters,
  390. int ParameterIdx = 0>
  391. struct EvaluateJacobianForParameterBlocks;
  392. template <typename ParameterDims, int N, int... Ns, int ParameterIdx>
  393. struct EvaluateJacobianForParameterBlocks<ParameterDims,
  394. std::integer_sequence<int, N, Ns...>,
  395. ParameterIdx> {
  396. template <NumericDiffMethodType method,
  397. int kNumResiduals,
  398. typename CostFunctor>
  399. static bool Apply(const CostFunctor* functor,
  400. const double* residuals_at_eval_point,
  401. const NumericDiffOptions& options,
  402. int num_residuals,
  403. double** parameters,
  404. double** jacobians) {
  405. if (jacobians[ParameterIdx] != nullptr) {
  406. if (!NumericDiff<
  407. CostFunctor,
  408. method,
  409. kNumResiduals,
  410. ParameterDims,
  411. ParameterIdx,
  412. N>::EvaluateJacobianForParameterBlock(functor,
  413. residuals_at_eval_point,
  414. options,
  415. num_residuals,
  416. ParameterIdx,
  417. N,
  418. parameters,
  419. jacobians[ParameterIdx])) {
  420. return false;
  421. }
  422. }
  423. return EvaluateJacobianForParameterBlocks<ParameterDims,
  424. std::integer_sequence<int, Ns...>,
  425. ParameterIdx + 1>::
  426. template Apply<method, kNumResiduals>(functor,
  427. residuals_at_eval_point,
  428. options,
  429. num_residuals,
  430. parameters,
  431. jacobians);
  432. }
  433. };
  434. // End of 'recursion'. Nothing more to do.
  435. template <typename ParameterDims, int ParameterIdx>
  436. struct EvaluateJacobianForParameterBlocks<ParameterDims,
  437. std::integer_sequence<int>,
  438. ParameterIdx> {
  439. template <NumericDiffMethodType method,
  440. int kNumResiduals,
  441. typename CostFunctor>
  442. static bool Apply(const CostFunctor* /* NOT USED*/,
  443. const double* /* NOT USED*/,
  444. const NumericDiffOptions& /* NOT USED*/,
  445. int /* NOT USED*/,
  446. double** /* NOT USED*/,
  447. double** /* NOT USED*/) {
  448. return true;
  449. }
  450. };
  451. } // namespace ceres::internal
  452. #endif // CERES_PUBLIC_INTERNAL_NUMERIC_DIFF_H_