/* ---------------------------------------------------------------------------- * GTSAM Copyright 2010, Georgia Tech Research Corporation, * Atlanta, Georgia 30332-0415 * All Rights Reserved * Authors: Frank Dellaert, et al. (see THANKS for the full author list) * See LICENSE for the license information * -------------------------------------------------------------------------- */ /** * @file numericalDerivative.h * @brief Some functions to compute numerical derivatives * @author Frank Dellaert */ // \callgraph #pragma once #include #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" #endif #include #ifdef __GNUC__ #pragma GCC diagnostic pop #endif #include #include #include #include namespace gtsam { /* * Note that all of these functions have two versions, a boost.function version and a * standard C++ function pointer version. This allows reformulating the arguments of * a function to fit the correct structure, which is useful for situations like * member functions and functions with arguments not involved in the derivative: * * Usage of the boost bind version to rearrange arguments: * for a function with one relevant param and an optional derivative: * Foo bar(const Obj& a, boost::optional H1) * Use boost.bind to restructure: * boost::bind(bar, _1, boost::none) * This syntax will fix the optional argument to boost::none, while using the first argument provided * * For member functions, such as below, with an instantiated copy instanceOfSomeClass * Foo SomeClass::bar(const Obj& a) * Use boost bind as follows to create a function pointer that uses the member function: * boost::bind(&SomeClass::bar, ref(instanceOfSomeClass), _1) * * For additional details, see the documentation: * http://www.boost.org/doc/libs/release/libs/bind/bind.html */ // a quick helper struct to get the appropriate fixed sized matrix from two value types namespace internal { template struct FixedSizeMatrix { typedef Eigen::Matrix::dimension, traits::dimension> type; }; } /** * Numerically compute gradient of scalar function * Class X is the input argument * The class X needs to have dim, expmap, logmap */ template typename internal::FixedSizeMatrix::type numericalGradient(boost::function h, const X& x, double delta = 1e-5) { double factor = 1.0 / (2.0 * delta); BOOST_STATIC_ASSERT_MSG( (boost::is_base_of::structure_category>::value), "Template argument X must be a manifold type."); static const int N = traits::dimension; BOOST_STATIC_ASSERT_MSG(N>0, "Template argument X must be fixed-size type."); typedef typename traits::TangentVector TangentX; // Prepare a tangent vector to perturb x with, only works for fixed size TangentX d; d.setZero(); Vector g = zero(N); // Can be fixed size for (int j = 0; j < N; j++) { d(j) = delta; double hxplus = h(traits::Retract(x, d)); d(j) = -delta; double hxmin = h(traits::Retract(x, d)); d(j) = 0; g(j) = (hxplus - hxmin) * factor; } return g; } /** * @brief New-style numerical derivatives using manifold_traits * @brief Computes numerical derivative in argument 1 of unary function * @param h unary function yielding m-vector * @param x n-dimensional value at which to evaluate h * @param delta increment for numerical derivative * Class Y is the output argument * Class X is the input argument * @return m*n Jacobian computed via central differencing */ template // TODO Should compute fixed-size matrix typename internal::FixedSizeMatrix::type numericalDerivative11(boost::function h, const X& x, double delta = 1e-5) { typedef typename internal::FixedSizeMatrix::type Matrix; BOOST_STATIC_ASSERT_MSG( (boost::is_base_of::structure_category>::value), "Template argument Y must be a manifold type."); typedef traits TraitsY; typedef typename TraitsY::TangentVector TangentY; BOOST_STATIC_ASSERT_MSG( (boost::is_base_of::structure_category>::value), "Template argument X must be a manifold type."); static const int N = traits::dimension; BOOST_STATIC_ASSERT_MSG(N>0, "Template argument X must be fixed-size type."); typedef traits TraitsX; typedef typename TraitsX::TangentVector TangentX; // get value at x, and corresponding chart const Y hx = h(x); // Bit of a hack for now to find number of rows const TangentY zeroY = TraitsY::Local(hx, hx); const size_t m = zeroY.size(); // Prepare a tangent vector to perturb x with, only works for fixed size TangentX dx; dx.setZero(); // Fill in Jacobian H Matrix H = zeros(m, N); const double factor = 1.0 / (2.0 * delta); for (int j = 0; j < N; j++) { dx(j) = delta; const TangentY dy1 = TraitsY::Local(hx, h(TraitsX::Retract(x, dx))); dx(j) = -delta; const TangentY dy2 = TraitsY::Local(hx, h(TraitsX::Retract(x, dx))); dx(j) = 0; H.col(j) << (dy1 - dy2) * factor; } return H; } /** use a raw C++ function pointer */ template typename internal::FixedSizeMatrix::type numericalDerivative11(Y (*h)(const X&), const X& x, double delta = 1e-5) { return numericalDerivative11(boost::bind(h, _1), x, delta); } /** * Compute numerical derivative in argument 1 of binary function * @param h binary function yielding m-vector * @param x1 n-dimensional first argument value * @param x2 second argument value * @param delta increment for numerical derivative * @return m*n Jacobian computed via central differencing */ template typename internal::FixedSizeMatrix::type numericalDerivative21(const boost::function& h, const X1& x1, const X2& x2, double delta = 1e-5) { BOOST_STATIC_ASSERT_MSG( (boost::is_base_of::structure_category>::value), "Template argument Y must be a manifold type."); BOOST_STATIC_ASSERT_MSG( (boost::is_base_of::structure_category>::value), "Template argument X1 must be a manifold type."); return numericalDerivative11(boost::bind(h, _1, x2), x1, delta); } /** use a raw C++ function pointer */ template typename internal::FixedSizeMatrix::type numericalDerivative21(Y (*h)(const X1&, const X2&), const X1& x1, const X2& x2, double delta = 1e-5) { return numericalDerivative21(boost::bind(h, _1, _2), x1, x2, delta); } /** * Compute numerical derivative in argument 2 of binary function * @param h binary function yielding m-vector * @param x1 first argument value * @param x2 n-dimensional second argument value * @param delta increment for numerical derivative * @return m*n Jacobian computed via central differencing */ template typename internal::FixedSizeMatrix::type numericalDerivative22(boost::function h, const X1& x1, const X2& x2, double delta = 1e-5) { // BOOST_STATIC_ASSERT_MSG( (boost::is_base_of::structure_category>::value), // "Template argument X1 must be a manifold type."); BOOST_STATIC_ASSERT_MSG( (boost::is_base_of::structure_category>::value), "Template argument X2 must be a manifold type."); return numericalDerivative11(boost::bind(h, x1, _1), x2, delta); } /** use a raw C++ function pointer */ template typename internal::FixedSizeMatrix::type numericalDerivative22(Y (*h)(const X1&, const X2&), const X1& x1, const X2& x2, double delta = 1e-5) { return numericalDerivative22(boost::bind(h, _1, _2), x1, x2, delta); } /** * Compute numerical derivative in argument 1 of ternary function * @param h ternary function yielding m-vector * @param x1 n-dimensional first argument value * @param x2 second argument value * @param x3 third argument value * @param delta increment for numerical derivative * @return m*n Jacobian computed via central differencing * All classes Y,X1,X2,X3 need dim, expmap, logmap */ template typename internal::FixedSizeMatrix::type numericalDerivative31( boost::function h, const X1& x1, const X2& x2, const X3& x3, double delta = 1e-5) { BOOST_STATIC_ASSERT_MSG( (boost::is_base_of::structure_category>::value), "Template argument Y must be a manifold type."); BOOST_STATIC_ASSERT_MSG( (boost::is_base_of::structure_category>::value), "Template argument X1 must be a manifold type."); return numericalDerivative11(boost::bind(h, _1, x2, x3), x1, delta); } template typename internal::FixedSizeMatrix::type numericalDerivative31(Y (*h)(const X1&, const X2&, const X3&), const X1& x1, const X2& x2, const X3& x3, double delta = 1e-5) { return numericalDerivative31(boost::bind(h, _1, _2, _3), x1, x2, x3, delta); } /** * Compute numerical derivative in argument 2 of ternary function * @param h ternary function yielding m-vector * @param x1 n-dimensional first argument value * @param x2 second argument value * @param x3 third argument value * @param delta increment for numerical derivative * @return m*n Jacobian computed via central differencing * All classes Y,X1,X2,X3 need dim, expmap, logmap */ template typename internal::FixedSizeMatrix::type numericalDerivative32( boost::function h, const X1& x1, const X2& x2, const X3& x3, double delta = 1e-5) { BOOST_STATIC_ASSERT_MSG( (boost::is_base_of::structure_category>::value), "Template argument Y must be a manifold type."); BOOST_STATIC_ASSERT_MSG( (boost::is_base_of::structure_category>::value), "Template argument X2 must be a manifold type."); return numericalDerivative11(boost::bind(h, x1, _1, x3), x2, delta); } template inline typename internal::FixedSizeMatrix::type numericalDerivative32(Y (*h)(const X1&, const X2&, const X3&), const X1& x1, const X2& x2, const X3& x3, double delta = 1e-5) { return numericalDerivative32(boost::bind(h, _1, _2, _3), x1, x2, x3, delta); } /** * Compute numerical derivative in argument 3 of ternary function * @param h ternary function yielding m-vector * @param x1 n-dimensional first argument value * @param x2 second argument value * @param x3 third argument value * @param delta increment for numerical derivative * @return m*n Jacobian computed via central differencing * All classes Y,X1,X2,X3 need dim, expmap, logmap */ template typename internal::FixedSizeMatrix::type numericalDerivative33( boost::function h, const X1& x1, const X2& x2, const X3& x3, double delta = 1e-5) { BOOST_STATIC_ASSERT_MSG( (boost::is_base_of::structure_category>::value), "Template argument Y must be a manifold type."); BOOST_STATIC_ASSERT_MSG( (boost::is_base_of::structure_category>::value), "Template argument X3 must be a manifold type."); return numericalDerivative11(boost::bind(h, x1, x2, _1), x3, delta); } template inline typename internal::FixedSizeMatrix::type numericalDerivative33(Y (*h)(const X1&, const X2&, const X3&), const X1& x1, const X2& x2, const X3& x3, double delta = 1e-5) { return numericalDerivative33(boost::bind(h, _1, _2, _3), x1, x2, x3, delta); } /** * Compute numerical derivative in argument 1 of 4-argument function * @param h ternary function yielding m-vector * @param x1 n-dimensional first argument value * @param x2 second argument value * @param x3 third argument value * @param x4 fourth argument value * @param delta increment for numerical derivative * @return m*n Jacobian computed via central differencing */ template typename internal::FixedSizeMatrix::type numericalDerivative41( boost::function h, const X1& x1, const X2& x2, const X3& x3, const X4& x4, double delta = 1e-5) { BOOST_STATIC_ASSERT_MSG( (boost::is_base_of::structure_category>::value), "Template argument Y must be a manifold type."); BOOST_STATIC_ASSERT_MSG( (boost::is_base_of::structure_category>::value), "Template argument X1 must be a manifold type."); return numericalDerivative11(boost::bind(h, _1, x2, x3, x4), x1, delta); } /** * Compute numerical derivative in argument 2 of 4-argument function * @param h ternary function yielding m-vector * @param x1 n-dimensional first argument value * @param x2 second argument value * @param x3 third argument value * @param x4 fourth argument value * @param delta increment for numerical derivative * @return m*n Jacobian computed via central differencing */ template typename internal::FixedSizeMatrix::type numericalDerivative42( boost::function h, const X1& x1, const X2& x2, const X3& x3, const X4& x4, double delta = 1e-5) { BOOST_STATIC_ASSERT_MSG( (boost::is_base_of::structure_category>::value), "Template argument Y must be a manifold type."); BOOST_STATIC_ASSERT_MSG( (boost::is_base_of::structure_category>::value), "Template argument X1 must be a manifold type."); return numericalDerivative11(boost::bind(h, x1, _1, x3, x4), x2, delta); } /** * Compute numerical Hessian matrix. Requires a single-argument Lie->scalar * function. This is implemented simply as the derivative of the gradient. * @param f A function taking a Lie object as input and returning a scalar * @param x The center point for computing the Hessian * @param delta The numerical derivative step size * @return n*n Hessian matrix computed via central differencing */ template inline typename internal::FixedSizeMatrix::type numericalHessian(boost::function f, const X& x, double delta = 1e-5) { BOOST_STATIC_ASSERT_MSG( (boost::is_base_of::structure_category>::value), "Template argument X must be a manifold type."); typedef Eigen::Matrix::dimension, 1> VectorD; typedef boost::function F; typedef boost::function G; G ng = static_cast(numericalGradient ); return numericalDerivative11(boost::bind(ng, f, _1, delta), x, delta); } template inline typename internal::FixedSizeMatrix::type numericalHessian(double (*f)(const X&), const X& x, double delta = 1e-5) { return numericalHessian(boost::function(f), x, delta); } /** Helper class that computes the derivative of f w.r.t. x1, centered about * x1_, as a function of x2 */ template class G_x1 { const boost::function& f_; X1 x1_; double delta_; public: typedef typename internal::FixedSizeMatrix::type Vector; G_x1(const boost::function& f, const X1& x1, double delta) : f_(f), x1_(x1), delta_(delta) { } Vector operator()(const X2& x2) { return numericalGradient(boost::bind(f_, _1, x2), x1_, delta_); } }; template inline typename internal::FixedSizeMatrix::type numericalHessian212( boost::function f, const X1& x1, const X2& x2, double delta = 1e-5) { typedef typename internal::FixedSizeMatrix::type Vector; G_x1 g_x1(f, x1, delta); return numericalDerivative11( boost::function( boost::bind(boost::ref(g_x1), _1)), x2, delta); } template inline typename internal::FixedSizeMatrix::type numericalHessian212(double (*f)(const X1&, const X2&), const X1& x1, const X2& x2, double delta = 1e-5) { return numericalHessian212(boost::function(f), x1, x2, delta); } template inline typename internal::FixedSizeMatrix::type numericalHessian211( boost::function f, const X1& x1, const X2& x2, double delta = 1e-5) { typedef typename internal::FixedSizeMatrix::type Vector; Vector (*numGrad)(boost::function, const X1&, double) = &numericalGradient; boost::function f2(boost::bind(f, _1, x2)); return numericalDerivative11( boost::function(boost::bind(numGrad, f2, _1, delta)), x1, delta); } template inline typename internal::FixedSizeMatrix::type numericalHessian211(double (*f)(const X1&, const X2&), const X1& x1, const X2& x2, double delta = 1e-5) { return numericalHessian211(boost::function(f), x1, x2, delta); } template inline typename internal::FixedSizeMatrix::type numericalHessian222( boost::function f, const X1& x1, const X2& x2, double delta = 1e-5) { typedef typename internal::FixedSizeMatrix::type Vector; Vector (*numGrad)(boost::function, const X2&, double) = &numericalGradient; boost::function f2(boost::bind(f, x1, _1)); return numericalDerivative11( boost::function(boost::bind(numGrad, f2, _1, delta)), x2, delta); } template inline typename internal::FixedSizeMatrix::type numericalHessian222(double (*f)(const X1&, const X2&), const X1& x1, const X2& x2, double delta = 1e-5) { return numericalHessian222(boost::function(f), x1, x2, delta); } /** * Numerical Hessian for tenary functions */ /* **************************************************************** */ template inline typename internal::FixedSizeMatrix::type numericalHessian311( boost::function f, const X1& x1, const X2& x2, const X3& x3, double delta = 1e-5) { typedef typename internal::FixedSizeMatrix::type Vector; Vector (*numGrad)(boost::function, const X1&, double) = &numericalGradient; boost::function f2(boost::bind(f, _1, x2, x3)); return numericalDerivative11( boost::function(boost::bind(numGrad, f2, _1, delta)), x1, delta); } template inline typename internal::FixedSizeMatrix::type numericalHessian311(double (*f)(const X1&, const X2&, const X3&), const X1& x1, const X2& x2, const X3& x3, double delta = 1e-5) { return numericalHessian311( boost::function(f), x1, x2, x3, delta); } /* **************************************************************** */ template inline typename internal::FixedSizeMatrix::type numericalHessian322( boost::function f, const X1& x1, const X2& x2, const X3& x3, double delta = 1e-5) { typedef typename internal::FixedSizeMatrix::type Vector; Vector (*numGrad)(boost::function, const X2&, double) = &numericalGradient; boost::function f2(boost::bind(f, x1, _1, x3)); return numericalDerivative11( boost::function(boost::bind(numGrad, f2, _1, delta)), x2, delta); } template inline typename internal::FixedSizeMatrix::type numericalHessian322(double (*f)(const X1&, const X2&, const X3&), const X1& x1, const X2& x2, const X3& x3, double delta = 1e-5) { return numericalHessian322( boost::function(f), x1, x2, x3, delta); } /* **************************************************************** */ template inline typename internal::FixedSizeMatrix::type numericalHessian333( boost::function f, const X1& x1, const X2& x2, const X3& x3, double delta = 1e-5) { typedef typename internal::FixedSizeMatrix::type Vector; Vector (*numGrad)(boost::function, const X3&, double) = &numericalGradient; boost::function f2(boost::bind(f, x1, x2, _1)); return numericalDerivative11( boost::function(boost::bind(numGrad, f2, _1, delta)), x3, delta); } template inline typename internal::FixedSizeMatrix::type numericalHessian333(double (*f)(const X1&, const X2&, const X3&), const X1& x1, const X2& x2, const X3& x3, double delta = 1e-5) { return numericalHessian333( boost::function(f), x1, x2, x3, delta); } /* **************************************************************** */ template inline typename internal::FixedSizeMatrix::type numericalHessian312( boost::function f, const X1& x1, const X2& x2, const X3& x3, double delta = 1e-5) { return numericalHessian212( boost::function(boost::bind(f, _1, _2, x3)), x1, x2, delta); } template inline typename internal::FixedSizeMatrix::type numericalHessian313( boost::function f, const X1& x1, const X2& x2, const X3& x3, double delta = 1e-5) { return numericalHessian212( boost::function(boost::bind(f, _1, x2, _2)), x1, x3, delta); } template inline typename internal::FixedSizeMatrix::type numericalHessian323( boost::function f, const X1& x1, const X2& x2, const X3& x3, double delta = 1e-5) { return numericalHessian212( boost::function(boost::bind(f, x1, _1, _2)), x2, x3, delta); } /* **************************************************************** */ template inline typename internal::FixedSizeMatrix::type numericalHessian312(double (*f)(const X1&, const X2&, const X3&), const X1& x1, const X2& x2, const X3& x3, double delta = 1e-5) { return numericalHessian312( boost::function(f), x1, x2, x3, delta); } template inline typename internal::FixedSizeMatrix::type numericalHessian313(double (*f)(const X1&, const X2&, const X3&), const X1& x1, const X2& x2, const X3& x3, double delta = 1e-5) { return numericalHessian313( boost::function(f), x1, x2, x3, delta); } template inline typename internal::FixedSizeMatrix::type numericalHessian323(double (*f)(const X1&, const X2&, const X3&), const X1& x1, const X2& x2, const X3& x3, double delta = 1e-5) { return numericalHessian323( boost::function(f), x1, x2, x3, delta); } } // namespace gtsam