blob: ff7a2c345e4e7cede3a94e78cc6e2c2052d66978 [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2015 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
35#ifndef CERES_PUBLIC_INTERNAL_NUMERIC_DIFF_H_
36#define CERES_PUBLIC_INTERNAL_NUMERIC_DIFF_H_
37
38#include <cstring>
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080039#include <utility>
Austin Schuh70cc9552019-01-21 19:46:48 -080040
41#include "Eigen/Dense"
42#include "Eigen/StdVector"
43#include "ceres/cost_function.h"
44#include "ceres/internal/fixed_array.h"
45#include "ceres/internal/variadic_evaluate.h"
46#include "ceres/numeric_diff_options.h"
47#include "ceres/types.h"
48#include "glog/logging.h"
49
Austin Schuh70cc9552019-01-21 19:46:48 -080050namespace ceres {
51namespace internal {
52
53// This is split from the main class because C++ doesn't allow partial template
54// specializations for member functions. The alternative is to repeat the main
55// class for differing numbers of parameters, which is also unfortunate.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080056template <typename CostFunctor,
57 NumericDiffMethodType kMethod,
58 int kNumResiduals,
59 typename ParameterDims,
60 int kParameterBlock,
Austin Schuh70cc9552019-01-21 19:46:48 -080061 int kParameterBlockSize>
62struct NumericDiff {
63 // Mutates parameters but must restore them before return.
64 static bool EvaluateJacobianForParameterBlock(
65 const CostFunctor* functor,
66 const double* residuals_at_eval_point,
67 const NumericDiffOptions& options,
68 int num_residuals,
69 int parameter_block_index,
70 int parameter_block_size,
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080071 double** parameters,
72 double* jacobian) {
73 using Eigen::ColMajor;
Austin Schuh70cc9552019-01-21 19:46:48 -080074 using Eigen::Map;
75 using Eigen::Matrix;
76 using Eigen::RowMajor;
Austin Schuh70cc9552019-01-21 19:46:48 -080077
78 DCHECK(jacobian);
79
80 const int num_residuals_internal =
81 (kNumResiduals != ceres::DYNAMIC ? kNumResiduals : num_residuals);
82 const int parameter_block_index_internal =
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080083 (kParameterBlock != ceres::DYNAMIC ? kParameterBlock
84 : parameter_block_index);
Austin Schuh70cc9552019-01-21 19:46:48 -080085 const int parameter_block_size_internal =
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080086 (kParameterBlockSize != ceres::DYNAMIC ? kParameterBlockSize
87 : parameter_block_size);
Austin Schuh70cc9552019-01-21 19:46:48 -080088
89 typedef Matrix<double, kNumResiduals, 1> ResidualVector;
90 typedef Matrix<double, kParameterBlockSize, 1> ParameterVector;
91
92 // The convoluted reasoning for choosing the Row/Column major
93 // ordering of the matrix is an artifact of the restrictions in
94 // Eigen that prevent it from creating RowMajor matrices with a
95 // single column. In these cases, we ask for a ColMajor matrix.
96 typedef Matrix<double,
97 kNumResiduals,
98 kParameterBlockSize,
99 (kParameterBlockSize == 1) ? ColMajor : RowMajor>
100 JacobianMatrix;
101
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800102 Map<JacobianMatrix> parameter_jacobian(
103 jacobian, num_residuals_internal, parameter_block_size_internal);
Austin Schuh70cc9552019-01-21 19:46:48 -0800104
105 Map<ParameterVector> x_plus_delta(
106 parameters[parameter_block_index_internal],
107 parameter_block_size_internal);
108 ParameterVector x(x_plus_delta);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800109 ParameterVector step_size =
110 x.array().abs() * ((kMethod == RIDDERS)
111 ? options.ridders_relative_initial_step_size
112 : options.relative_step_size);
Austin Schuh70cc9552019-01-21 19:46:48 -0800113
114 // It is not a good idea to make the step size arbitrarily
115 // small. This will lead to problems with round off and numerical
116 // instability when dividing by the step size. The general
117 // recommendation is to not go down below sqrt(epsilon).
118 double min_step_size = std::sqrt(std::numeric_limits<double>::epsilon());
119
120 // For Ridders' method, the initial step size is required to be large,
121 // thus ridders_relative_initial_step_size is used.
122 if (kMethod == RIDDERS) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800123 min_step_size =
124 std::max(min_step_size, options.ridders_relative_initial_step_size);
Austin Schuh70cc9552019-01-21 19:46:48 -0800125 }
126
127 // For each parameter in the parameter block, use finite differences to
128 // compute the derivative for that parameter.
129 FixedArray<double> temp_residual_array(num_residuals_internal);
130 FixedArray<double> residual_array(num_residuals_internal);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800131 Map<ResidualVector> residuals(residual_array.data(),
Austin Schuh70cc9552019-01-21 19:46:48 -0800132 num_residuals_internal);
133
134 for (int j = 0; j < parameter_block_size_internal; ++j) {
135 const double delta = std::max(min_step_size, step_size(j));
136
137 if (kMethod == RIDDERS) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800138 if (!EvaluateRiddersJacobianColumn(functor,
139 j,
140 delta,
Austin Schuh70cc9552019-01-21 19:46:48 -0800141 options,
142 num_residuals_internal,
143 parameter_block_size_internal,
144 x.data(),
145 residuals_at_eval_point,
146 parameters,
147 x_plus_delta.data(),
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800148 temp_residual_array.data(),
149 residual_array.data())) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800150 return false;
151 }
152 } else {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800153 if (!EvaluateJacobianColumn(functor,
154 j,
155 delta,
Austin Schuh70cc9552019-01-21 19:46:48 -0800156 num_residuals_internal,
157 parameter_block_size_internal,
158 x.data(),
159 residuals_at_eval_point,
160 parameters,
161 x_plus_delta.data(),
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800162 temp_residual_array.data(),
163 residual_array.data())) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800164 return false;
165 }
166 }
167
168 parameter_jacobian.col(j).matrix() = residuals;
169 }
170 return true;
171 }
172
173 static bool EvaluateJacobianColumn(const CostFunctor* functor,
174 int parameter_index,
175 double delta,
176 int num_residuals,
177 int parameter_block_size,
178 const double* x_ptr,
179 const double* residuals_at_eval_point,
180 double** parameters,
181 double* x_plus_delta_ptr,
182 double* temp_residuals_ptr,
183 double* residuals_ptr) {
184 using Eigen::Map;
185 using Eigen::Matrix;
186
187 typedef Matrix<double, kNumResiduals, 1> ResidualVector;
188 typedef Matrix<double, kParameterBlockSize, 1> ParameterVector;
189
190 Map<const ParameterVector> x(x_ptr, parameter_block_size);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800191 Map<ParameterVector> x_plus_delta(x_plus_delta_ptr, parameter_block_size);
Austin Schuh70cc9552019-01-21 19:46:48 -0800192
193 Map<ResidualVector> residuals(residuals_ptr, num_residuals);
194 Map<ResidualVector> temp_residuals(temp_residuals_ptr, num_residuals);
195
196 // Mutate 1 element at a time and then restore.
197 x_plus_delta(parameter_index) = x(parameter_index) + delta;
198
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800199 if (!VariadicEvaluate<ParameterDims>(
200 *functor, parameters, residuals.data())) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800201 return false;
202 }
203
204 // Compute this column of the jacobian in 3 steps:
205 // 1. Store residuals for the forward part.
206 // 2. Subtract residuals for the backward (or 0) part.
207 // 3. Divide out the run.
208 double one_over_delta = 1.0 / delta;
209 if (kMethod == CENTRAL || kMethod == RIDDERS) {
210 // Compute the function on the other side of x(parameter_index).
211 x_plus_delta(parameter_index) = x(parameter_index) - delta;
212
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800213 if (!VariadicEvaluate<ParameterDims>(
214 *functor, parameters, temp_residuals.data())) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800215 return false;
216 }
217
218 residuals -= temp_residuals;
219 one_over_delta /= 2;
220 } else {
221 // Forward difference only; reuse existing residuals evaluation.
222 residuals -=
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800223 Map<const ResidualVector>(residuals_at_eval_point, num_residuals);
Austin Schuh70cc9552019-01-21 19:46:48 -0800224 }
225
226 // Restore x_plus_delta.
227 x_plus_delta(parameter_index) = x(parameter_index);
228
229 // Divide out the run to get slope.
230 residuals *= one_over_delta;
231
232 return true;
233 }
234
235 // This numeric difference implementation uses adaptive differentiation
236 // on the parameters to obtain the Jacobian matrix. The adaptive algorithm
237 // is based on Ridders' method for adaptive differentiation, which creates
238 // a Romberg tableau from varying step sizes and extrapolates the
239 // intermediate results to obtain the current computational error.
240 //
241 // References:
242 // C.J.F. Ridders, Accurate computation of F'(x) and F'(x) F"(x), Advances
243 // in Engineering Software (1978), Volume 4, Issue 2, April 1982,
244 // Pages 75-76, ISSN 0141-1195,
245 // http://dx.doi.org/10.1016/S0141-1195(82)80057-0.
246 static bool EvaluateRiddersJacobianColumn(
247 const CostFunctor* functor,
248 int parameter_index,
249 double delta,
250 const NumericDiffOptions& options,
251 int num_residuals,
252 int parameter_block_size,
253 const double* x_ptr,
254 const double* residuals_at_eval_point,
255 double** parameters,
256 double* x_plus_delta_ptr,
257 double* temp_residuals_ptr,
258 double* residuals_ptr) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800259 using Eigen::aligned_allocator;
Austin Schuh70cc9552019-01-21 19:46:48 -0800260 using Eigen::Map;
261 using Eigen::Matrix;
Austin Schuh70cc9552019-01-21 19:46:48 -0800262
263 typedef Matrix<double, kNumResiduals, 1> ResidualVector;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800264 typedef Matrix<double, kNumResiduals, Eigen::Dynamic>
265 ResidualCandidateMatrix;
Austin Schuh70cc9552019-01-21 19:46:48 -0800266 typedef Matrix<double, kParameterBlockSize, 1> ParameterVector;
267
268 Map<const ParameterVector> x(x_ptr, parameter_block_size);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800269 Map<ParameterVector> x_plus_delta(x_plus_delta_ptr, parameter_block_size);
Austin Schuh70cc9552019-01-21 19:46:48 -0800270
271 Map<ResidualVector> residuals(residuals_ptr, num_residuals);
272 Map<ResidualVector> temp_residuals(temp_residuals_ptr, num_residuals);
273
274 // In order for the algorithm to converge, the step size should be
275 // initialized to a value that is large enough to produce a significant
276 // change in the function.
277 // As the derivative is estimated, the step size decreases.
278 // By default, the step sizes are chosen so that the middle column
279 // of the Romberg tableau uses the input delta.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800280 double current_step_size =
281 delta * pow(options.ridders_step_shrink_factor,
282 options.max_num_ridders_extrapolations / 2);
Austin Schuh70cc9552019-01-21 19:46:48 -0800283
284 // Double-buffering temporary differential candidate vectors
285 // from previous step size.
286 ResidualCandidateMatrix stepsize_candidates_a(
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800287 num_residuals, options.max_num_ridders_extrapolations);
Austin Schuh70cc9552019-01-21 19:46:48 -0800288 ResidualCandidateMatrix stepsize_candidates_b(
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800289 num_residuals, options.max_num_ridders_extrapolations);
Austin Schuh70cc9552019-01-21 19:46:48 -0800290 ResidualCandidateMatrix* current_candidates = &stepsize_candidates_a;
291 ResidualCandidateMatrix* previous_candidates = &stepsize_candidates_b;
292
293 // Represents the computational error of the derivative. This variable is
294 // initially set to a large value, and is set to the difference between
295 // current and previous finite difference extrapolations.
296 // norm_error is supposed to decrease as the finite difference tableau
297 // generation progresses, serving both as an estimate for differentiation
298 // error and as a measure of differentiation numerical stability.
299 double norm_error = std::numeric_limits<double>::max();
300
301 // Loop over decreasing step sizes until:
302 // 1. Error is smaller than a given value (ridders_epsilon),
303 // 2. Maximal order of extrapolation reached, or
304 // 3. Extrapolation becomes numerically unstable.
305 for (int i = 0; i < options.max_num_ridders_extrapolations; ++i) {
306 // Compute the numerical derivative at this step size.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800307 if (!EvaluateJacobianColumn(functor,
308 parameter_index,
309 current_step_size,
Austin Schuh70cc9552019-01-21 19:46:48 -0800310 num_residuals,
311 parameter_block_size,
312 x.data(),
313 residuals_at_eval_point,
314 parameters,
315 x_plus_delta.data(),
316 temp_residuals.data(),
317 current_candidates->col(0).data())) {
318 // Something went wrong; bail.
319 return false;
320 }
321
322 // Store initial results.
323 if (i == 0) {
324 residuals = current_candidates->col(0);
325 }
326
327 // Shrink differentiation step size.
328 current_step_size /= options.ridders_step_shrink_factor;
329
330 // Extrapolation factor for Richardson acceleration method (see below).
331 double richardson_factor = options.ridders_step_shrink_factor *
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800332 options.ridders_step_shrink_factor;
Austin Schuh70cc9552019-01-21 19:46:48 -0800333 for (int k = 1; k <= i; ++k) {
334 // Extrapolate the various orders of finite differences using
335 // the Richardson acceleration method.
336 current_candidates->col(k) =
337 (richardson_factor * current_candidates->col(k - 1) -
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800338 previous_candidates->col(k - 1)) /
339 (richardson_factor - 1.0);
Austin Schuh70cc9552019-01-21 19:46:48 -0800340
341 richardson_factor *= options.ridders_step_shrink_factor *
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800342 options.ridders_step_shrink_factor;
Austin Schuh70cc9552019-01-21 19:46:48 -0800343
344 // Compute the difference between the previous value and the current.
345 double candidate_error = std::max(
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800346 (current_candidates->col(k) - current_candidates->col(k - 1))
347 .norm(),
348 (current_candidates->col(k) - previous_candidates->col(k - 1))
349 .norm());
Austin Schuh70cc9552019-01-21 19:46:48 -0800350
351 // If the error has decreased, update results.
352 if (candidate_error <= norm_error) {
353 norm_error = candidate_error;
354 residuals = current_candidates->col(k);
355
356 // If the error is small enough, stop.
357 if (norm_error < options.ridders_epsilon) {
358 break;
359 }
360 }
361 }
362
363 // After breaking out of the inner loop, declare convergence.
364 if (norm_error < options.ridders_epsilon) {
365 break;
366 }
367
368 // Check to see if the current gradient estimate is numerically unstable.
369 // If so, bail out and return the last stable result.
370 if (i > 0) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800371 double tableau_error =
372 (current_candidates->col(i) - previous_candidates->col(i - 1))
373 .norm();
Austin Schuh70cc9552019-01-21 19:46:48 -0800374
375 // Compare current error to the chosen candidate's error.
376 if (tableau_error >= 2 * norm_error) {
377 break;
378 }
379 }
380
381 std::swap(current_candidates, previous_candidates);
382 }
383 return true;
384 }
385};
386
387// This function calls NumericDiff<...>::EvaluateJacobianForParameterBlock for
388// each parameter block.
389//
390// Example:
391// A call to
392// EvaluateJacobianForParameterBlocks<StaticParameterDims<2, 3>>(
393// functor,
394// residuals_at_eval_point,
395// options,
396// num_residuals,
397// parameters,
398// jacobians);
399// will result in the following calls to
400// NumericDiff<...>::EvaluateJacobianForParameterBlock:
401//
402// if (jacobians[0] != nullptr) {
403// if (!NumericDiff<
404// CostFunctor,
405// method,
406// kNumResiduals,
407// StaticParameterDims<2, 3>,
408// 0,
409// 2>::EvaluateJacobianForParameterBlock(functor,
410// residuals_at_eval_point,
411// options,
412// num_residuals,
413// 0,
414// 2,
415// parameters,
416// jacobians[0])) {
417// return false;
418// }
419// }
420// if (jacobians[1] != nullptr) {
421// if (!NumericDiff<
422// CostFunctor,
423// method,
424// kNumResiduals,
425// StaticParameterDims<2, 3>,
426// 1,
427// 3>::EvaluateJacobianForParameterBlock(functor,
428// residuals_at_eval_point,
429// options,
430// num_residuals,
431// 1,
432// 3,
433// parameters,
434// jacobians[1])) {
435// return false;
436// }
437// }
438template <typename ParameterDims,
439 typename Parameters = typename ParameterDims::Parameters,
440 int ParameterIdx = 0>
441struct EvaluateJacobianForParameterBlocks;
442
443template <typename ParameterDims, int N, int... Ns, int ParameterIdx>
444struct EvaluateJacobianForParameterBlocks<ParameterDims,
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800445 std::integer_sequence<int, N, Ns...>,
Austin Schuh70cc9552019-01-21 19:46:48 -0800446 ParameterIdx> {
447 template <NumericDiffMethodType method,
448 int kNumResiduals,
449 typename CostFunctor>
450 static bool Apply(const CostFunctor* functor,
451 const double* residuals_at_eval_point,
452 const NumericDiffOptions& options,
453 int num_residuals,
454 double** parameters,
455 double** jacobians) {
456 if (jacobians[ParameterIdx] != nullptr) {
457 if (!NumericDiff<
458 CostFunctor,
459 method,
460 kNumResiduals,
461 ParameterDims,
462 ParameterIdx,
463 N>::EvaluateJacobianForParameterBlock(functor,
464 residuals_at_eval_point,
465 options,
466 num_residuals,
467 ParameterIdx,
468 N,
469 parameters,
470 jacobians[ParameterIdx])) {
471 return false;
472 }
473 }
474
475 return EvaluateJacobianForParameterBlocks<ParameterDims,
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800476 std::integer_sequence<int, Ns...>,
Austin Schuh70cc9552019-01-21 19:46:48 -0800477 ParameterIdx + 1>::
478 template Apply<method, kNumResiduals>(functor,
479 residuals_at_eval_point,
480 options,
481 num_residuals,
482 parameters,
483 jacobians);
484 }
485};
486
487// End of 'recursion'. Nothing more to do.
488template <typename ParameterDims, int ParameterIdx>
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800489struct EvaluateJacobianForParameterBlocks<ParameterDims,
490 std::integer_sequence<int>,
Austin Schuh70cc9552019-01-21 19:46:48 -0800491 ParameterIdx> {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800492 template <NumericDiffMethodType method,
493 int kNumResiduals,
Austin Schuh70cc9552019-01-21 19:46:48 -0800494 typename CostFunctor>
495 static bool Apply(const CostFunctor* /* NOT USED*/,
496 const double* /* NOT USED*/,
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800497 const NumericDiffOptions& /* NOT USED*/,
498 int /* NOT USED*/,
499 double** /* NOT USED*/,
500 double** /* NOT USED*/) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800501 return true;
502 }
503};
504
505} // namespace internal
506} // namespace ceres
507
508#endif // CERES_PUBLIC_INTERNAL_NUMERIC_DIFF_H_