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