| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #ifndef CERES_PUBLIC_INTERNAL_NUMERIC_DIFF_H_ |
| | #define CERES_PUBLIC_INTERNAL_NUMERIC_DIFF_H_ |
| |
|
| | #include <cstring> |
| | #include <utility> |
| |
|
| | #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 { |
| |
|
| | |
| | |
| | |
| | template <typename CostFunctor, |
| | NumericDiffMethodType kMethod, |
| | int kNumResiduals, |
| | typename ParameterDims, |
| | int kParameterBlock, |
| | int kParameterBlockSize> |
| | struct NumericDiff { |
| | |
| | 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::ColMajor; |
| | using Eigen::Map; |
| | using Eigen::Matrix; |
| | using Eigen::RowMajor; |
| |
|
| | DCHECK(jacobian); |
| |
|
| | 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); |
| |
|
| | using ResidualVector = Matrix<double, kNumResiduals, 1>; |
| | using ParameterVector = Matrix<double, kParameterBlockSize, 1>; |
| |
|
| | |
| | |
| | |
| | |
| | using JacobianMatrix = |
| | Matrix<double, |
| | kNumResiduals, |
| | kParameterBlockSize, |
| | (kParameterBlockSize == 1) ? ColMajor : RowMajor>; |
| |
|
| | 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); |
| |
|
| | |
| | |
| | |
| | |
| | double min_step_size = std::sqrt(std::numeric_limits<double>::epsilon()); |
| |
|
| | |
| | |
| | if (kMethod == RIDDERS) { |
| | min_step_size = |
| | (std::max)(min_step_size, options.ridders_relative_initial_step_size); |
| | } |
| |
|
| | |
| | |
| | FixedArray<double> temp_residual_array(num_residuals_internal); |
| | FixedArray<double> residual_array(num_residuals_internal); |
| | Map<ResidualVector> residuals(residual_array.data(), |
| | 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.data(), |
| | residual_array.data())) { |
| | 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.data(), |
| | residual_array.data())) { |
| | 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; |
| |
|
| | using ResidualVector = Matrix<double, kNumResiduals, 1>; |
| | using ParameterVector = Matrix<double, kParameterBlockSize, 1>; |
| |
|
| | 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); |
| |
|
| | |
| | x_plus_delta(parameter_index) = x(parameter_index) + delta; |
| |
|
| | if (!VariadicEvaluate<ParameterDims>( |
| | *functor, parameters, residuals.data())) { |
| | return false; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | double one_over_delta = 1.0 / delta; |
| | if (kMethod == CENTRAL || kMethod == RIDDERS) { |
| | |
| | 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 { |
| | |
| | residuals -= |
| | Map<const ResidualVector>(residuals_at_eval_point, num_residuals); |
| | } |
| |
|
| | |
| | x_plus_delta(parameter_index) = x(parameter_index); |
| |
|
| | |
| | residuals *= one_over_delta; |
| |
|
| | return true; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | 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::aligned_allocator; |
| | using Eigen::Map; |
| | using Eigen::Matrix; |
| |
|
| | using ResidualVector = Matrix<double, kNumResiduals, 1>; |
| | using ResidualCandidateMatrix = |
| | Matrix<double, kNumResiduals, Eigen::Dynamic>; |
| | using ParameterVector = Matrix<double, kParameterBlockSize, 1>; |
| |
|
| | 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); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | double current_step_size = |
| | delta * pow(options.ridders_step_shrink_factor, |
| | options.max_num_ridders_extrapolations / 2); |
| |
|
| | |
| | |
| | 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; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | double norm_error = (std::numeric_limits<double>::max)(); |
| |
|
| | |
| | |
| | |
| | |
| | for (int i = 0; i < options.max_num_ridders_extrapolations; ++i) { |
| | |
| | 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())) { |
| | |
| | return false; |
| | } |
| |
|
| | |
| | if (i == 0) { |
| | residuals = current_candidates->col(0); |
| | } |
| |
|
| | |
| | current_step_size /= options.ridders_step_shrink_factor; |
| |
|
| | |
| | double richardson_factor = options.ridders_step_shrink_factor * |
| | options.ridders_step_shrink_factor; |
| | for (int k = 1; k <= i; ++k) { |
| | |
| | |
| | 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; |
| |
|
| | |
| | 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 (candidate_error <= norm_error) { |
| | norm_error = candidate_error; |
| | residuals = current_candidates->col(k); |
| |
|
| | |
| | if (norm_error < options.ridders_epsilon) { |
| | break; |
| | } |
| | } |
| | } |
| |
|
| | |
| | if (norm_error < options.ridders_epsilon) { |
| | break; |
| | } |
| |
|
| | |
| | |
| | if (i > 0) { |
| | double tableau_error = |
| | (current_candidates->col(i) - previous_candidates->col(i - 1)) |
| | .norm(); |
| |
|
| | |
| | if (tableau_error >= 2 * norm_error) { |
| | break; |
| | } |
| | } |
| |
|
| | std::swap(current_candidates, previous_candidates); |
| | } |
| | return true; |
| | } |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | 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, |
| | std::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 (jacobians[ParameterIdx] != nullptr) { |
| | 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, |
| | std::integer_sequence<int, Ns...>, |
| | ParameterIdx + 1>:: |
| | template Apply<method, kNumResiduals>(functor, |
| | residuals_at_eval_point, |
| | options, |
| | num_residuals, |
| | parameters, |
| | jacobians); |
| | } |
| | }; |
| |
|
| | |
| | template <typename ParameterDims, int ParameterIdx> |
| | struct EvaluateJacobianForParameterBlocks<ParameterDims, |
| | std::integer_sequence<int>, |
| | ParameterIdx> { |
| | template <NumericDiffMethodType method, |
| | int kNumResiduals, |
| | typename CostFunctor> |
| | static bool Apply(const CostFunctor* , |
| | const double* , |
| | const NumericDiffOptions& , |
| | int , |
| | double** , |
| | double** ) { |
| | return true; |
| | } |
| | }; |
| |
|
| | } |
| | } |
| |
|
| | #endif |
| |
|