| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 | // Ceres Solver - A fast non-linear least squares minimizer// Copyright 2015 Google Inc. All rights reserved.// http://ceres-solver.org///// Redistribution and use in source and binary forms, with or without// modification, are permitted provided that the following conditions are met://// * Redistributions of source code must retain the above copyright notice,//   this list of conditions and the following disclaimer.// * Redistributions in binary form must reproduce the above copyright notice,//   this list of conditions and the following disclaimer in the documentation//   and/or other materials provided with the distribution.// * Neither the name of Google Inc. nor the names of its contributors may be//   used to endorse or promote products derived from this software without//   specific prior written permission.//// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE// POSSIBILITY OF SUCH DAMAGE.//// Author: sameeragarwal@google.com (Sameer Agarwal)//         mierle@gmail.com (Keir Mierle)//         tbennun@gmail.com (Tal Ben-Nun)//// Finite differencing routines used by NumericDiffCostFunction.#ifndef CERES_PUBLIC_INTERNAL_NUMERIC_DIFF_H_#define CERES_PUBLIC_INTERNAL_NUMERIC_DIFF_H_#include <cstring>#include "Eigen/Dense"#include "Eigen/StdVector"#include "ceres/cost_function.h"#include "ceres/internal/fixed_array.h"#include "ceres/internal/variadic_evaluate.h"#include "ceres/numeric_diff_options.h"#include "ceres/types.h"#include "glog/logging.h"namespace ceres {namespace internal {// This is split from the main class because C++ doesn't allow partial template// specializations for member functions. The alternative is to repeat the main// class for differing numbers of parameters, which is also unfortunate.template <typename CostFunctor, NumericDiffMethodType kMethod,          int kNumResiduals, typename ParameterDims, int kParameterBlock,          int kParameterBlockSize>struct NumericDiff {  // Mutates parameters but must restore them before return.  static bool EvaluateJacobianForParameterBlock(      const CostFunctor* functor,      const double* residuals_at_eval_point,      const NumericDiffOptions& options,      int num_residuals,      int parameter_block_index,      int parameter_block_size,      double **parameters,      double *jacobian) {    using Eigen::Map;    using Eigen::Matrix;    using Eigen::RowMajor;    using Eigen::ColMajor;    const int num_residuals_internal =        (kNumResiduals != ceres::DYNAMIC ? kNumResiduals : num_residuals);    const int parameter_block_index_internal =        (kParameterBlock != ceres::DYNAMIC ? kParameterBlock :                                             parameter_block_index);    const int parameter_block_size_internal =        (kParameterBlockSize != ceres::DYNAMIC ? kParameterBlockSize :                                                 parameter_block_size);    typedef Matrix<double, kNumResiduals, 1> ResidualVector;    typedef Matrix<double, kParameterBlockSize, 1> ParameterVector;    // The convoluted reasoning for choosing the Row/Column major    // ordering of the matrix is an artifact of the restrictions in    // Eigen that prevent it from creating RowMajor matrices with a    // single column. In these cases, we ask for a ColMajor matrix.    typedef Matrix<double,                   kNumResiduals,                   kParameterBlockSize,                   (kParameterBlockSize == 1) ? ColMajor : RowMajor>        JacobianMatrix;    Map<JacobianMatrix> parameter_jacobian(jacobian,                                           num_residuals_internal,                                           parameter_block_size_internal);    Map<ParameterVector> x_plus_delta(        parameters[parameter_block_index_internal],        parameter_block_size_internal);    ParameterVector x(x_plus_delta);    ParameterVector step_size = x.array().abs() *        ((kMethod == RIDDERS) ? options.ridders_relative_initial_step_size :        options.relative_step_size);    // It is not a good idea to make the step size arbitrarily    // small. This will lead to problems with round off and numerical    // instability when dividing by the step size. The general    // recommendation is to not go down below sqrt(epsilon).    double min_step_size = std::sqrt(std::numeric_limits<double>::epsilon());    // For Ridders' method, the initial step size is required to be large,    // thus ridders_relative_initial_step_size is used.    if (kMethod == RIDDERS) {      min_step_size = std::max(min_step_size,                               options.ridders_relative_initial_step_size);    }    // For each parameter in the parameter block, use finite differences to    // compute the derivative for that parameter.    FixedArray<double> temp_residual_array(num_residuals_internal);    FixedArray<double> residual_array(num_residuals_internal);    Map<ResidualVector> residuals(residual_array.get(),                                  num_residuals_internal);    for (int j = 0; j < parameter_block_size_internal; ++j) {      const double delta = std::max(min_step_size, step_size(j));      if (kMethod == RIDDERS) {        if (!EvaluateRiddersJacobianColumn(functor, j, delta,                                           options,                                           num_residuals_internal,                                           parameter_block_size_internal,                                           x.data(),                                           residuals_at_eval_point,                                           parameters,                                           x_plus_delta.data(),                                           temp_residual_array.get(),                                           residual_array.get())) {          return false;        }      } else {        if (!EvaluateJacobianColumn(functor, j, delta,                                    num_residuals_internal,                                    parameter_block_size_internal,                                    x.data(),                                    residuals_at_eval_point,                                    parameters,                                    x_plus_delta.data(),                                    temp_residual_array.get(),                                    residual_array.get())) {          return false;        }      }      parameter_jacobian.col(j).matrix() = residuals;    }    return true;  }  static bool EvaluateJacobianColumn(const CostFunctor* functor,                                     int parameter_index,                                     double delta,                                     int num_residuals,                                     int parameter_block_size,                                     const double* x_ptr,                                     const double* residuals_at_eval_point,                                     double** parameters,                                     double* x_plus_delta_ptr,                                     double* temp_residuals_ptr,                                     double* residuals_ptr) {    using Eigen::Map;    using Eigen::Matrix;    typedef Matrix<double, kNumResiduals, 1> ResidualVector;    typedef Matrix<double, kParameterBlockSize, 1> ParameterVector;    Map<const ParameterVector> x(x_ptr, parameter_block_size);    Map<ParameterVector> x_plus_delta(x_plus_delta_ptr,                                      parameter_block_size);    Map<ResidualVector> residuals(residuals_ptr, num_residuals);    Map<ResidualVector> temp_residuals(temp_residuals_ptr, num_residuals);    // Mutate 1 element at a time and then restore.    x_plus_delta(parameter_index) = x(parameter_index) + delta;    if (!VariadicEvaluate<ParameterDims>(*functor,                                         parameters,                                         residuals.data())) {      return false;    }    // Compute this column of the jacobian in 3 steps:    // 1. Store residuals for the forward part.    // 2. Subtract residuals for the backward (or 0) part.    // 3. Divide out the run.    double one_over_delta = 1.0 / delta;    if (kMethod == CENTRAL || kMethod == RIDDERS) {      // Compute the function on the other side of x(parameter_index).      x_plus_delta(parameter_index) = x(parameter_index) - delta;      if (!VariadicEvaluate<ParameterDims>(*functor,                                           parameters,                                           temp_residuals.data())) {        return false;      }      residuals -= temp_residuals;      one_over_delta /= 2;    } else {      // Forward difference only; reuse existing residuals evaluation.      residuals -=          Map<const ResidualVector>(residuals_at_eval_point,                                    num_residuals);    }    // Restore x_plus_delta.    x_plus_delta(parameter_index) = x(parameter_index);    // Divide out the run to get slope.    residuals *= one_over_delta;    return true;  }  // This numeric difference implementation uses adaptive differentiation  // on the parameters to obtain the Jacobian matrix. The adaptive algorithm  // is based on Ridders' method for adaptive differentiation, which creates  // a Romberg tableau from varying step sizes and extrapolates the  // intermediate results to obtain the current computational error.  //  // References:  // C.J.F. Ridders, Accurate computation of F'(x) and F'(x) F"(x), Advances  // in Engineering Software (1978), Volume 4, Issue 2, April 1982,  // Pages 75-76, ISSN 0141-1195,  // http://dx.doi.org/10.1016/S0141-1195(82)80057-0.  static bool EvaluateRiddersJacobianColumn(      const CostFunctor* functor,      int parameter_index,      double delta,      const NumericDiffOptions& options,      int num_residuals,      int parameter_block_size,      const double* x_ptr,      const double* residuals_at_eval_point,      double** parameters,      double* x_plus_delta_ptr,      double* temp_residuals_ptr,      double* residuals_ptr) {    using Eigen::Map;    using Eigen::Matrix;    using Eigen::aligned_allocator;    typedef Matrix<double, kNumResiduals, 1> ResidualVector;    typedef Matrix<double, kNumResiduals, Eigen::Dynamic> ResidualCandidateMatrix;    typedef Matrix<double, kParameterBlockSize, 1> ParameterVector;    Map<const ParameterVector> x(x_ptr, parameter_block_size);    Map<ParameterVector> x_plus_delta(x_plus_delta_ptr,                                      parameter_block_size);    Map<ResidualVector> residuals(residuals_ptr, num_residuals);    Map<ResidualVector> temp_residuals(temp_residuals_ptr, num_residuals);    // In order for the algorithm to converge, the step size should be    // initialized to a value that is large enough to produce a significant    // change in the function.    // As the derivative is estimated, the step size decreases.    // By default, the step sizes are chosen so that the middle column    // of the Romberg tableau uses the input delta.    double current_step_size = delta *        pow(options.ridders_step_shrink_factor,            options.max_num_ridders_extrapolations / 2);    // Double-buffering temporary differential candidate vectors    // from previous step size.    ResidualCandidateMatrix stepsize_candidates_a(        num_residuals,        options.max_num_ridders_extrapolations);    ResidualCandidateMatrix stepsize_candidates_b(        num_residuals,        options.max_num_ridders_extrapolations);    ResidualCandidateMatrix* current_candidates = &stepsize_candidates_a;    ResidualCandidateMatrix* previous_candidates = &stepsize_candidates_b;    // Represents the computational error of the derivative. This variable is    // initially set to a large value, and is set to the difference between    // current and previous finite difference extrapolations.    // norm_error is supposed to decrease as the finite difference tableau    // generation progresses, serving both as an estimate for differentiation    // error and as a measure of differentiation numerical stability.    double norm_error = std::numeric_limits<double>::max();    // Loop over decreasing step sizes until:    //  1. Error is smaller than a given value (ridders_epsilon),    //  2. Maximal order of extrapolation reached, or    //  3. Extrapolation becomes numerically unstable.    for (int i = 0; i < options.max_num_ridders_extrapolations; ++i) {      // Compute the numerical derivative at this step size.      if (!EvaluateJacobianColumn(functor, parameter_index, current_step_size,                                  num_residuals,                                  parameter_block_size,                                  x.data(),                                  residuals_at_eval_point,                                  parameters,                                  x_plus_delta.data(),                                  temp_residuals.data(),                                  current_candidates->col(0).data())) {        // Something went wrong; bail.        return false;      }      // Store initial results.      if (i == 0) {        residuals = current_candidates->col(0);      }      // Shrink differentiation step size.      current_step_size /= options.ridders_step_shrink_factor;      // Extrapolation factor for Richardson acceleration method (see below).      double richardson_factor = options.ridders_step_shrink_factor *          options.ridders_step_shrink_factor;      for (int k = 1; k <= i; ++k) {        // Extrapolate the various orders of finite differences using        // the Richardson acceleration method.        current_candidates->col(k) =            (richardson_factor * current_candidates->col(k - 1) -             previous_candidates->col(k - 1)) / (richardson_factor - 1.0);        richardson_factor *= options.ridders_step_shrink_factor *            options.ridders_step_shrink_factor;        // Compute the difference between the previous value and the current.        double candidate_error = std::max(            (current_candidates->col(k) -             current_candidates->col(k - 1)).norm(),            (current_candidates->col(k) -             previous_candidates->col(k - 1)).norm());        // If the error has decreased, update results.        if (candidate_error <= norm_error) {          norm_error = candidate_error;          residuals = current_candidates->col(k);          // If the error is small enough, stop.          if (norm_error < options.ridders_epsilon) {            break;          }        }      }      // After breaking out of the inner loop, declare convergence.      if (norm_error < options.ridders_epsilon) {        break;      }      // Check to see if the current gradient estimate is numerically unstable.      // If so, bail out and return the last stable result.      if (i > 0) {        double tableau_error = (current_candidates->col(i) -            previous_candidates->col(i - 1)).norm();        // Compare current error to the chosen candidate's error.        if (tableau_error >= 2 * norm_error) {          break;        }      }      std::swap(current_candidates, previous_candidates);    }    return true;  }};// This function calls NumericDiff<...>::EvaluateJacobianForParameterBlock for// each parameter block.//// Example:// A call to // EvaluateJacobianForParameterBlocks<StaticParameterDims<2, 3>>(//        functor,//        residuals_at_eval_point,//        options,//        num_residuals,//        parameters,//        jacobians);// will result in the following calls to// NumericDiff<...>::EvaluateJacobianForParameterBlock://// if (!NumericDiff<//          CostFunctor, method, kNumResiduals, ParameterDims, 0,//          2>::EvaluateJacobianForParameterBlock(functor,//                                                residuals_at_eval_point,//                                                options,//                                                num_residuals,//                                                0,//                                                2,//                                                parameters,//                                                jacobians[0])) {//   return false;// }// if (!NumericDiff<//          CostFunctor, method, kNumResiduals, ParameterDims, 1,//          3>::EvaluateJacobianForParameterBlock(functor,//                                                residuals_at_eval_point,//                                                options,//                                                num_residuals,//                                                1,//                                                3,//                                                parameters,//                                                jacobians[1])) {//   return false;// }template <typename ParameterDims,          typename Parameters = typename ParameterDims::Parameters,          int ParameterIdx = 0>struct EvaluateJacobianForParameterBlocks;template <typename ParameterDims, int N, int... Ns, int ParameterIdx>struct EvaluateJacobianForParameterBlocks<    ParameterDims, integer_sequence<int, N, Ns...>, ParameterIdx> {  template <NumericDiffMethodType method, int kNumResiduals,            typename CostFunctor>  static bool Apply(const CostFunctor* functor,                    const double* residuals_at_eval_point,                    const NumericDiffOptions& options, int num_residuals,                    double** parameters, double** jacobians) {    if (!NumericDiff<            CostFunctor, method, kNumResiduals, ParameterDims, ParameterIdx,            N>::EvaluateJacobianForParameterBlock(functor,                                                  residuals_at_eval_point,                                                  options,                                                  num_residuals,                                                  ParameterIdx,                                                  N,                                                  parameters,                                                  jacobians[ParameterIdx])) {      return false;    }    return EvaluateJacobianForParameterBlocks<        ParameterDims, integer_sequence<int, Ns...>, ParameterIdx + 1>::        template Apply<method, kNumResiduals>(functor,                                              residuals_at_eval_point,                                              options,                                              num_residuals,                                              parameters, jacobians);  }};// End of 'recursion'. Nothing more to do.template <typename ParameterDims, int ParameterIdx>struct EvaluateJacobianForParameterBlocks<ParameterDims, integer_sequence<int>,                                          ParameterIdx> {  template <NumericDiffMethodType method, int kNumResiduals,            typename CostFunctor>  static bool Apply(const CostFunctor* /* NOT USED*/,                    const double* /* NOT USED*/,                    const NumericDiffOptions& /* NOT USED*/, int /* NOT USED*/,                    double** /* NOT USED*/, double** /* NOT USED*/) {    return true;  }};}  // namespace internal}  // namespace ceres#endif  // CERES_PUBLIC_INTERNAL_NUMERIC_DIFF_H_
 |