// Ceres Solver - A fast non-linear least squares minimizer // Copyright 2019 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) #ifndef CERES_PUBLIC_NUMERIC_DIFF_FIRST_ORDER_FUNCTION_H_ #define CERES_PUBLIC_NUMERIC_DIFF_FIRST_ORDER_FUNCTION_H_ #include #include #include "ceres/first_order_function.h" #include "ceres/internal/eigen.h" #include "ceres/internal/fixed_array.h" #include "ceres/internal/numeric_diff.h" #include "ceres/internal/parameter_dims.h" #include "ceres/internal/variadic_evaluate.h" #include "ceres/numeric_diff_options.h" #include "ceres/types.h" #include "glog/logging.h" namespace ceres { // Creates FirstOrderFunctions as needed by the GradientProblem // framework, with gradients computed via numeric differentiation. For // more information on numeric differentiation, see the wikipedia // article at https://en.wikipedia.org/wiki/Numerical_differentiation // // To get an numerically differentiated cost function, you must define // a class with an operator() (a functor) that computes the cost. // // The function must write the computed value in the last argument // (the only non-const one) and return true to indicate success. // // For example, consider a scalar error e = x'y - a, where both x and y are // two-dimensional column vector parameters, the prime sign indicates // transposition, and a is a constant. // // To write an numerically-differentiable cost function for the above model, // first define the object // // class QuadraticCostFunctor { // public: // explicit QuadraticCostFunctor(double a) : a_(a) {} // bool operator()(const double* const xy, double* cost) const { // constexpr int kInputVectorLength = 2; // const double* const x = xy; // const double* const y = xy + kInputVectorLength; // *cost = x[0] * y[0] + x[1] * y[1] - a_; // return true; // } // // private: // double a_; // }; // // // Note that in the declaration of operator() the input parameters xy // come first, and are passed as const pointers to array of // doubles. The output cost is the last parameter. // // Then given this class definition, the numerically differentiated // first order function with central differences used for computing the // derivative can be constructed as follows. // // FirstOrderFunction* function // = new NumericDiffFirstOrderFunction( // new QuadraticCostFunctor(1.0)); ^ ^ ^ // | | | // Finite Differencing Scheme -+ | | // Dimension of xy ------------------------+ // // // In the instantiation above, the template parameters following // "QuadraticCostFunctor", "CENTRAL, 4", describe the finite // differencing scheme as "central differencing" and the functor as // computing its cost from a 4 dimensional input. // // If the size of the parameter vector is not known at compile time, then an // alternate construction syntax can be used: // // FirstOrderFunction* function // = new NumericDiffFirstOrderFunction( // new QuadraticCostFunctor(1.0), 4); // // Note that instead of passing 4 as a template argument, it is now passed as // the second argument to the constructor. template class NumericDiffFirstOrderFunction final : public FirstOrderFunction { public: // Constructor for the case where the parameter size is known at compile time. explicit NumericDiffFirstOrderFunction( FirstOrderFunctor* functor, Ownership ownership = TAKE_OWNERSHIP, const NumericDiffOptions& options = NumericDiffOptions()) : functor_(functor), num_parameters_(kNumParameters), ownership_(ownership), options_(options) { static_assert(kNumParameters != DYNAMIC, "Number of parameters must be static when defined via the " "template parameter. Use the other constructor for " "dynamically sized functions."); static_assert(kNumParameters > 0, "kNumParameters must be positive"); } // Constructor for the case where the parameter size is specified at run time. explicit NumericDiffFirstOrderFunction( FirstOrderFunctor* functor, int num_parameters, Ownership ownership = TAKE_OWNERSHIP, const NumericDiffOptions& options = NumericDiffOptions()) : functor_(functor), num_parameters_(num_parameters), ownership_(ownership), options_(options) { static_assert( kNumParameters == DYNAMIC, "Template parameter must be DYNAMIC when using this constructor. If " "you want to provide the number of parameters statically use the other " "constructor."); CHECK_GT(num_parameters, 0); } ~NumericDiffFirstOrderFunction() override { if (ownership_ != TAKE_OWNERSHIP) { functor_.release(); } } bool Evaluate(const double* const parameters, double* cost, double* gradient) const override { // Get the function value (cost) at the the point to evaluate. if (!(*functor_)(parameters, cost)) { return false; } if (gradient == nullptr) { return true; } // Create a copy of the parameters which will get mutated. internal::FixedArray parameters_copy(num_parameters_); std::copy_n(parameters, num_parameters_, parameters_copy.data()); double* parameters_ptr = parameters_copy.data(); constexpr int kNumResiduals = 1; if constexpr (kNumParameters == DYNAMIC) { internal::FirstOrderFunctorAdapter fofa(*functor_); return internal::NumericDiff< internal::FirstOrderFunctorAdapter, kMethod, kNumResiduals, internal::DynamicParameterDims, 0, DYNAMIC>::EvaluateJacobianForParameterBlock(&fofa, cost, options_, kNumResiduals, 0, num_parameters_, ¶meters_ptr, gradient); } else { return internal::EvaluateJacobianForParameterBlocks< internal::StaticParameterDims>:: template Apply(functor_.get(), cost, options_, kNumResiduals, ¶meters_ptr, &gradient); } } int NumParameters() const override { return num_parameters_; } const FirstOrderFunctor& functor() const { return *functor_; } private: std::unique_ptr functor_; const int num_parameters_; const Ownership ownership_; const NumericDiffOptions options_; }; } // namespace ceres #endif // CERES_PUBLIC_NUMERIC_DIFF_FIRST_ORDER_FUNCTION_H_