Blame docs/html/_sources/interfacing_with_autodiff.txt

Packit ea1746
.. default-domain:: cpp
Packit ea1746
Packit ea1746
.. cpp:namespace:: ceres
Packit ea1746
Packit ea1746
.. _chapter-interfacing_with_automatic_differentiation:
Packit ea1746
Packit ea1746
Interfacing with Automatic Differentiation
Packit ea1746
==========================================
Packit ea1746
Packit ea1746
Automatic differentiation is straightforward to use in cases where an
Packit ea1746
explicit expression for the cost function is available. But this is
Packit ea1746
not always possible. Often one has to interface with external routines
Packit ea1746
or data. In this chapter we will consider a number of different ways
Packit ea1746
of doing so.
Packit ea1746
Packit ea1746
To do this, we will consider the problem of finding parameters
Packit ea1746
:math:`\theta` and :math:`t` that solve an optimization problem of the
Packit ea1746
form:
Packit ea1746
Packit ea1746
.. math::
Packit ea1746
   \min & \quad \sum_i \left \|y_i - f\left (\|q_{i}\|^2\right) q_i
Packit ea1746
   \right \|^2\\
Packit ea1746
   \text{such that} & \quad q_i = R(\theta) x_i + t
Packit ea1746
Packit ea1746
Here, :math:`R` is a two dimensional rotation matrix parameterized
Packit ea1746
using the angle :math:`\theta` and :math:`t` is a two dimensional
Packit ea1746
vector. :math:`f` is an external distortion function.
Packit ea1746
Packit ea1746
We begin by considering the case, where we have a templated function
Packit ea1746
:code:`TemplatedComputeDistortion` that can compute the function
Packit ea1746
:math:`f`. Then the implementation of the corresponding residual
Packit ea1746
functor is straightforward and will look as follows:
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
   :emphasize-lines: 21
Packit ea1746
Packit ea1746
   template <typename T> T TemplatedComputeDistortion(const T r2) {
Packit ea1746
     const double k1 = 0.0082;
Packit ea1746
     const double k2 = 0.000023;
Packit ea1746
     return 1.0 + k1 * y2 + k2 * r2 * r2;
Packit ea1746
   }
Packit ea1746
Packit ea1746
   struct Affine2DWithDistortion {
Packit ea1746
     Affine2DWithDistortion(const double x_in[2], const double y_in[2]) {
Packit ea1746
       x[0] = x_in[0];
Packit ea1746
       x[1] = x_in[1];
Packit ea1746
       y[0] = y_in[0];
Packit ea1746
       y[1] = y_in[1];
Packit ea1746
     }
Packit ea1746
Packit ea1746
     template <typename T>
Packit ea1746
     bool operator()(const T* theta,
Packit ea1746
                     const T* t,
Packit ea1746
                     T* residuals) const {
Packit ea1746
       const T q_0 =  cos(theta[0]) * x[0] - sin(theta[0]) * x[1] + t[0];
Packit ea1746
       const T q_1 =  sin(theta[0]) * x[0] + cos(theta[0]) * x[1] + t[1];
Packit ea1746
       const T f = TemplatedComputeDistortion(q_0 * q_0 + q_1 * q_1);
Packit ea1746
       residuals[0] = y[0] - f * q_0;
Packit ea1746
       residuals[1] = y[1] - f * q_1;
Packit ea1746
       return true;
Packit ea1746
     }
Packit ea1746
Packit ea1746
     double x[2];
Packit ea1746
     double y[2];
Packit ea1746
   };
Packit ea1746
Packit ea1746
So far so good, but let us now consider three ways of defining
Packit ea1746
:math:`f` which are not directly amenable to being used with automatic
Packit ea1746
differentiation:
Packit ea1746
Packit ea1746
#. A non-templated function that evaluates its value.
Packit ea1746
#. A function that evaluates its value and derivative.
Packit ea1746
#. A function that is defined as a table of values to be interpolated.
Packit ea1746
Packit ea1746
We will consider them in turn below.
Packit ea1746
Packit ea1746
A function that returns its value
Packit ea1746
----------------------------------
Packit ea1746
Packit ea1746
Suppose we were given a function :code:`ComputeDistortionValue` with
Packit ea1746
the following signature
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
Packit ea1746
   double ComputeDistortionValue(double r2);
Packit ea1746
Packit ea1746
that computes the value of :math:`f`. The actual implementation of the
Packit ea1746
function does not matter. Interfacing this function with
Packit ea1746
:code:`Affine2DWithDistortion` is a three step process:
Packit ea1746
Packit ea1746
1. Wrap :code:`ComputeDistortionValue` into a functor
Packit ea1746
   :code:`ComputeDistortionValueFunctor`.
Packit ea1746
2. Numerically differentiate :code:`ComputeDistortionValueFunctor`
Packit ea1746
   using :class:`NumericDiffCostFunction` to create a
Packit ea1746
   :class:`CostFunction`.
Packit ea1746
3. Wrap the resulting :class:`CostFunction` object using
Packit ea1746
   :class:`CostFunctionToFunctor`. The resulting object is a functor
Packit ea1746
   with a templated :code:`operator()` method, which pipes the
Packit ea1746
   Jacobian computed by :class:`NumericDiffCostFunction` into the
Packit ea1746
   approproate :code:`Jet` objects.
Packit ea1746
Packit ea1746
An implementation of the above three steps looks as follows:
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
   :emphasize-lines: 15,16,17,18,19,20, 29
Packit ea1746
Packit ea1746
   struct ComputeDistortionValueFunctor {
Packit ea1746
     bool operator()(const double* r2, double* value) const {
Packit ea1746
       *value = ComputeDistortionValue(r2[0]);
Packit ea1746
       return true;
Packit ea1746
     }
Packit ea1746
   };
Packit ea1746
Packit ea1746
   struct Affine2DWithDistortion {
Packit ea1746
     Affine2DWithDistortion(const double x_in[2], const double y_in[2]) {
Packit ea1746
       x[0] = x_in[0];
Packit ea1746
       x[1] = x_in[1];
Packit ea1746
       y[0] = y_in[0];
Packit ea1746
       y[1] = y_in[1];
Packit ea1746
Packit ea1746
       compute_distortion.reset(new ceres::CostFunctionToFunctor<1, 1>(
Packit ea1746
            new ceres::NumericDiffCostFunction
Packit ea1746
                                               ceres::CENTRAL,
Packit ea1746
                                               1,
Packit ea1746
                                               1>(
Packit ea1746
               new ComputeDistortionValueFunctor)));
Packit ea1746
     }
Packit ea1746
Packit ea1746
     template <typename T>
Packit ea1746
     bool operator()(const T* theta, const T* t, T* residuals) const {
Packit ea1746
       const T q_0 = cos(theta[0]) * x[0] - sin(theta[0]) * x[1] + t[0];
Packit ea1746
       const T q_1 = sin(theta[0]) * x[0] + cos(theta[0]) * x[1] + t[1];
Packit ea1746
       const T r2 = q_0 * q_0 + q_1 * q_1;
Packit ea1746
       T f;
Packit ea1746
       (*compute_distortion)(&r2, &f);
Packit ea1746
       residuals[0] = y[0] - f * q_0;
Packit ea1746
       residuals[1] = y[1] - f * q_1;
Packit ea1746
       return true;
Packit ea1746
     }
Packit ea1746
Packit ea1746
     double x[2];
Packit ea1746
     double y[2];
Packit ea1746
     std::unique_ptr<ceres::CostFunctionToFunctor<1, 1> > compute_distortion;
Packit ea1746
   };
Packit ea1746
Packit ea1746
Packit ea1746
A function that returns its value and derivative
Packit ea1746
------------------------------------------------
Packit ea1746
Packit ea1746
Now suppose we are given a function :code:`ComputeDistortionValue`
Packit ea1746
thatis able to compute its value and optionally its Jacobian on demand
Packit ea1746
and has the following signature:
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
Packit ea1746
   void ComputeDistortionValueAndJacobian(double r2,
Packit ea1746
                                          double* value,
Packit ea1746
                                          double* jacobian);
Packit ea1746
Packit ea1746
Again, the actual implementation of the function does not
Packit ea1746
matter. Interfacing this function with :code:`Affine2DWithDistortion`
Packit ea1746
is a two step process:
Packit ea1746
Packit ea1746
1. Wrap :code:`ComputeDistortionValueAndJacobian` into a
Packit ea1746
   :class:`CostFunction` object which we call
Packit ea1746
   :code:`ComputeDistortionFunction`.
Packit ea1746
2. Wrap the resulting :class:`ComputeDistortionFunction` object using
Packit ea1746
   :class:`CostFunctionToFunctor`. The resulting object is a functor
Packit ea1746
   with a templated :code:`operator()` method, which pipes the
Packit ea1746
   Jacobian computed by :class:`NumericDiffCostFunction` into the
Packit ea1746
   approproate :code:`Jet` objects.
Packit ea1746
Packit ea1746
The resulting code will look as follows:
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
   :emphasize-lines: 21,22, 33
Packit ea1746
Packit ea1746
   class ComputeDistortionFunction : public ceres::SizedCostFunction<1, 1> {
Packit ea1746
    public:
Packit ea1746
     virtual bool Evaluate(double const* const* parameters,
Packit ea1746
                           double* residuals,
Packit ea1746
                           double** jacobians) const {
Packit ea1746
       if (!jacobians) {
Packit ea1746
         ComputeDistortionValueAndJacobian(parameters[0][0], residuals, NULL);
Packit ea1746
       } else {
Packit ea1746
         ComputeDistortionValueAndJacobian(parameters[0][0], residuals, jacobians[0]);
Packit ea1746
       }
Packit ea1746
       return true;
Packit ea1746
     }
Packit ea1746
   };
Packit ea1746
Packit ea1746
   struct Affine2DWithDistortion {
Packit ea1746
     Affine2DWithDistortion(const double x_in[2], const double y_in[2]) {
Packit ea1746
       x[0] = x_in[0];
Packit ea1746
       x[1] = x_in[1];
Packit ea1746
       y[0] = y_in[0];
Packit ea1746
       y[1] = y_in[1];
Packit ea1746
       compute_distortion.reset(
Packit ea1746
           new ceres::CostFunctionToFunctor<1, 1>(new ComputeDistortionFunction));
Packit ea1746
     }
Packit ea1746
Packit ea1746
     template <typename T>
Packit ea1746
     bool operator()(const T* theta,
Packit ea1746
                     const T* t,
Packit ea1746
                     T* residuals) const {
Packit ea1746
       const T q_0 =  cos(theta[0]) * x[0] - sin(theta[0]) * x[1] + t[0];
Packit ea1746
       const T q_1 =  sin(theta[0]) * x[0] + cos(theta[0]) * x[1] + t[1];
Packit ea1746
       const T r2 = q_0 * q_0 + q_1 * q_1;
Packit ea1746
       T f;
Packit ea1746
       (*compute_distortion)(&r2, &f);
Packit ea1746
       residuals[0] = y[0] - f * q_0;
Packit ea1746
       residuals[1] = y[1] - f * q_1;
Packit ea1746
       return true;
Packit ea1746
     }
Packit ea1746
Packit ea1746
     double x[2];
Packit ea1746
     double y[2];
Packit ea1746
     std::unique_ptr<ceres::CostFunctionToFunctor<1, 1> > compute_distortion;
Packit ea1746
   };
Packit ea1746
Packit ea1746
Packit ea1746
A function that is defined as a table of values
Packit ea1746
-----------------------------------------------
Packit ea1746
Packit ea1746
The third and final case we will consider is where the function
Packit ea1746
:math:`f` is defined as a table of values on the interval :math:`[0,
Packit ea1746
100)`, with a value for each integer.
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
Packit ea1746
   vector<double> distortion_values;
Packit ea1746
Packit ea1746
There are many ways of interpolating a table of values. Perhaps the
Packit ea1746
simplest and most common method is linear interpolation. But it is not
Packit ea1746
a great idea to use linear interpolation because the interpolating
Packit ea1746
function is not differentiable at the sample points.
Packit ea1746
Packit ea1746
A simple (well behaved) differentiable interpolation is the `Cubic
Packit ea1746
Hermite Spline
Packit ea1746
<http://en.wikipedia.org/wiki/Cubic_Hermite_spline>`_. Ceres Solver
Packit ea1746
ships with routines to perform Cubic & Bi-Cubic interpolation that is
Packit ea1746
automatic differentiation friendly.
Packit ea1746
Packit ea1746
Using Cubic interpolation requires first constructing a
Packit ea1746
:class:`Grid1D` object to wrap the table of values and then
Packit ea1746
constructing a :class:`CubicInterpolator` object using it.
Packit ea1746
Packit ea1746
The resulting code will look as follows:
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
   :emphasize-lines: 10,11,12,13, 24, 32,33
Packit ea1746
Packit ea1746
   struct Affine2DWithDistortion {
Packit ea1746
     Affine2DWithDistortion(const double x_in[2],
Packit ea1746
                            const double y_in[2],
Packit ea1746
                            const std::vector<double>& distortion_values) {
Packit ea1746
       x[0] = x_in[0];
Packit ea1746
       x[1] = x_in[1];
Packit ea1746
       y[0] = y_in[0];
Packit ea1746
       y[1] = y_in[1];
Packit ea1746
Packit ea1746
       grid.reset(new ceres::Grid1D<double, 1>(
Packit ea1746
           &distortion_values[0], 0, distortion_values.size()));
Packit ea1746
       compute_distortion.reset(
Packit ea1746
           new ceres::CubicInterpolator<ceres::Grid1D<double, 1> >(*grid));
Packit ea1746
     }
Packit ea1746
Packit ea1746
     template <typename T>
Packit ea1746
     bool operator()(const T* theta,
Packit ea1746
                     const T* t,
Packit ea1746
                     T* residuals) const {
Packit ea1746
       const T q_0 =  cos(theta[0]) * x[0] - sin(theta[0]) * x[1] + t[0];
Packit ea1746
       const T q_1 =  sin(theta[0]) * x[0] + cos(theta[0]) * x[1] + t[1];
Packit ea1746
       const T r2 = q_0 * q_0 + q_1 * q_1;
Packit ea1746
       T f;
Packit ea1746
       compute_distortion->Evaluate(r2, &f);
Packit ea1746
       residuals[0] = y[0] - f * q_0;
Packit ea1746
       residuals[1] = y[1] - f * q_1;
Packit ea1746
       return true;
Packit ea1746
     }
Packit ea1746
Packit ea1746
     double x[2];
Packit ea1746
     double y[2];
Packit ea1746
     std::unique_ptr<ceres::Grid1D<double, 1> > grid;
Packit ea1746
     std::unique_ptr<ceres::CubicInterpolator<ceres::Grid1D<double, 1> > > compute_distortion;
Packit ea1746
   };
Packit ea1746
Packit ea1746
In the above example we used :class:`Grid1D` and
Packit ea1746
:class:`CubicInterpolator` to interpolate a one dimensional table of
Packit ea1746
values. :class:`Grid2D` combined with :class:`CubicInterpolator` lets
Packit ea1746
the user to interpolate two dimensional tables of values. Note that
Packit ea1746
neither :class:`Grid1D` or :class:`Grid2D` are limited to scalar
Packit ea1746
valued functions, they also work with vector valued functions.