g2o
Loading...
Searching...
No Matches
Public Types | Static Public Member Functions | Static Protected Member Functions | List of all members
g2o::AutoDifferentiation< Edge, EstimateAccess > Class Template Reference

Implementation of Automatic Differentiation for edges in g2o. More...

#include <auto_differentiation.h>

Public Types

template<int EdgeDimension, int VertexDimension>
using ADJacobianType = typename Eigen::Matrix< double, EdgeDimension, VertexDimension, Eigen::RowMajor >
 type for the Jacobians during AD
 

Static Public Member Functions

static void computeError (Edge *that)
 helper for computing the error based on the functor in the edge
 
static void linearize (Edge *that)
 

Static Protected Member Functions

template<std::size_t... Ints>
static void computeErrorNs (Edge *that, std::index_sequence< Ints... >)
 packed version to call the functor that evaluates the error function
 
template<std::size_t... Ints>
static void linearizeOplusNs (Edge *that, std::index_sequence< Ints... >)
 
template<typename A , typename B >
static EIGEN_STRONG_INLINE void assign (const Eigen::MatrixBase< A > &a, const Eigen::MatrixBase< B > &b)
 helper function to perform a = b
 

Detailed Description

template<typename Edge, typename EstimateAccess = EstimateAccessor<Edge>>
class g2o::AutoDifferentiation< Edge, EstimateAccess >

Implementation of Automatic Differentiation for edges in g2o.

This class implements an interface to Automatic Differentiation, see, for example, https://en.wikipedia.org/wiki/Automatic_differentiation for the idea behind it.

Pre-condition: Your estimate type in your vertices provides a method called data() which returns a raw pointer to the data representing the estimate. This can, for example, be achieved by using Eigen's vector underneath as the container for the data. An SE2 vertex might look as follows

class VertexFlatSE2 : public g2o::BaseVertex<3, g2o::Vector3> { public: virtual void setToOriginImpl() { _estimate.setZero(); } virtual void oplusImpl(const double* update) { _estimate += Eigen::Map<const g2o::Vector3>(update); _estimate(2) = g2o::normalize_theta(_estimate(2)); } virtual bool read(std::istream&) { return false; } virtual bool write(std::ostream&) const { return false; } };

If this is not the case for your edge, then you can provide a functor object as second template argument which does this conversion for you. See EstimateAccessor above. Such a functor has to provide a templatized function data(Edge*) that returns the raw-pointer to the underlying data. The raw-pointer should point to memory that is either owned by the functor itself or is owned by the edge, the vertex, or sth else. It has to to be valid throughout the lifetime of the functor object. See, for example, the functor EstimateAccessorGet which uses the potentially implemented method getEstimateData() on vertices to obtain the estimate in a raw array. This array is then buffered and passed on to compute the error or its Jacobian.

To use automatic differentiation on your own edge you need to implement the following steps:

  1. Implement an operator() that computes your error function: The function is required to have the following declaration template <typename T> bool operator()(const T* v1Estimate, const T* v2Estimate, T* error) const {} The example above assumes a binary edge. If your edge has more or less vertices, the number of vEstimate parameters differs. Let's assume that your edge connects N vertices, then your operator() will consume N+1 pointers. Whereas the last pointer is the output of your error function. Note the template on the operator(). This is required to be able to evaluate your error function with double pointer, i.e., to purely evaluate the error. But also we will pass a more complex class to it during the numerical computation of the Jacobian.
  2. Integrate the operator(): To this end, we provide the macro "G2O_MAKE_AUTO_AD_FUNCTIONS" which you can include into the public section of your edge class. See below for the macro. If you use the macro, you do not need to implement computeError() and linearizeOPlus() in your edge. Both methods will be ready for integration into the g2o framework. You may, however, decide against the macro and provide the implementation on your own if this suits your edge class better.

Example integration: g2o/examples/bal/bal_example.cpp This provides a self-contained example for integration of AD into an optimization problem.

Further documentation on the underlying implementation: Jet: EXTERNAL/ceres/jet.h AutoDiff: EXTERNAL/ceres/autodiff.h

Definition at line 155 of file auto_differentiation.h.

Member Typedef Documentation

◆ ADJacobianType

template<typename Edge , typename EstimateAccess = EstimateAccessor<Edge>>
template<int EdgeDimension, int VertexDimension>
using g2o::AutoDifferentiation< Edge, EstimateAccess >::ADJacobianType = typename Eigen::Matrix<double, EdgeDimension, VertexDimension, Eigen::RowMajor>

type for the Jacobians during AD

Definition at line 159 of file auto_differentiation.h.

Member Function Documentation

◆ assign()

template<typename Edge , typename EstimateAccess = EstimateAccessor<Edge>>
template<typename A , typename B >
static EIGEN_STRONG_INLINE void g2o::AutoDifferentiation< Edge, EstimateAccess >::assign ( const Eigen::MatrixBase< A > &  a,
const Eigen::MatrixBase< B > &  b 
)
inlinestaticprotected

helper function to perform a = b

Definition at line 253 of file auto_differentiation.h.

254 {
255 Eigen::MatrixBase<A>& aux = const_cast<Eigen::MatrixBase<A>&>(a);
256 aux = b;
257 }

Referenced by g2o::AutoDifferentiation< Edge, EstimateAccess >::linearizeOplusNs().

◆ computeError()

template<typename Edge , typename EstimateAccess = EstimateAccessor<Edge>>
static void g2o::AutoDifferentiation< Edge, EstimateAccess >::computeError ( Edge *  that)
inlinestatic

helper for computing the error based on the functor in the edge

Definition at line 164 of file auto_differentiation.h.

164 {
165 static_assert(Edge::Dimension > 0,
166 "Dynamically sized edges are not supported");
167 computeErrorNs(that, std::make_index_sequence<Edge::_nr_of_vertices>());
168 }
static void computeErrorNs(Edge *that, std::index_sequence< Ints... >)
packed version to call the functor that evaluates the error function

References g2o::AutoDifferentiation< Edge, EstimateAccess >::computeErrorNs().

◆ computeErrorNs()

template<typename Edge , typename EstimateAccess = EstimateAccessor<Edge>>
template<std::size_t... Ints>
static void g2o::AutoDifferentiation< Edge, EstimateAccess >::computeErrorNs ( Edge *  that,
std::index_sequence< Ints... >   
)
inlinestaticprotected

packed version to call the functor that evaluates the error function

Definition at line 186 of file auto_differentiation.h.

186 {
187 static_assert(
188 std::min({Edge::template VertexXnType<Ints>::Dimension...}) > 0,
189 "Dynamically sized vertices are not supported");
190 EstimateAccess estimateAccess;
191 (*that)(estimateAccess.template data<Ints>(that)..., that->errorData());
192 }

Referenced by g2o::AutoDifferentiation< Edge, EstimateAccess >::computeError().

◆ linearize()

template<typename Edge , typename EstimateAccess = EstimateAccessor<Edge>>
static void g2o::AutoDifferentiation< Edge, EstimateAccess >::linearize ( Edge *  that)
inlinestatic

Linearize (compute the Jacobians) for the given edge. Stores the Jacobians in the members of the edge. A vertex that is fixed will obtain a Jacobian with all elements set to zero. In the particular case that all vertices are fixed, we terminate early and do not start evaluation of the Jacobian.

Definition at line 177 of file auto_differentiation.h.

177 {
178 static_assert(Edge::Dimension > 0,
179 "Dynamically sized edges are not supported");
180 linearizeOplusNs(that, std::make_index_sequence<Edge::_nr_of_vertices>());
181 }
static void linearizeOplusNs(Edge *that, std::index_sequence< Ints... >)

References g2o::AutoDifferentiation< Edge, EstimateAccess >::linearizeOplusNs().

◆ linearizeOplusNs()

template<typename Edge , typename EstimateAccess = EstimateAccessor<Edge>>
template<std::size_t... Ints>
static void g2o::AutoDifferentiation< Edge, EstimateAccess >::linearizeOplusNs ( Edge *  that,
std::index_sequence< Ints... >   
)
inlinestaticprotected

packed version of the code to linearize using AD

Definition at line 198 of file auto_differentiation.h.

198 {
199 static_assert(
200 std::min({Edge::template VertexXnType<Ints>::Dimension...}) > 0,
201 "Dynamically sized vertices are not supported");
202 // all vertices are fixed, no need to compute anything here
203 if (that->allVerticesFixed()) {
204 (void(that->template jacobianOplusXn<Ints>().setZero()), ...);
205 return;
206 }
207
208 // tuple containing the Jacobians
209 std::tuple<ADJacobianType<Edge::Dimension,
210 Edge::template VertexXnType<Ints>::Dimension>...>
211 ad_jacobians;
212
213 // setting up the pointer to the parameters and the Jacobians for calling
214 // AD.
215 EstimateAccess estimateAccess;
216 double* parameters[] = {estimateAccess.template data<Ints>(that)...};
217 // double* parameters[] = { /* trivial case would be */
218 // const_cast<double*>(that->template
219 // vertexXn<Ints>()->estimate().data())...};
220
221 // pointers to the Jacobians, set to NULL if vertex is fixed to skip
222 // computation
223 double* jacobians[] = {
224 that->template vertexXn<Ints>()->fixed()
225 ? nullptr
226 : const_cast<double*>(std::get<Ints>(ad_jacobians).data())...};
227 // Calls the automatic differentiation for evaluation of the Jacobians.
228 double errorValue[Edge::Dimension];
229 using AutoDiffDims = ceres::internal::StaticParameterDims<
230 Edge::template VertexXnType<Ints>::Dimension...>;
231 bool diffState =
232 ceres::internal::AutoDifferentiate<Edge::Dimension, AutoDiffDims, Edge,
233 double>(
234 *that, parameters, Edge::Dimension, errorValue, jacobians);
235
236 assert(diffState && "Error during Automatic Differentiation");
237 if (!diffState) { // something went wrong during AD
238 (void(std::get<Ints>(ad_jacobians).setZero()), ...);
239 return;
240 }
241 // copy over the Jacobians (convert row-major -> column-major) for non-fixed
242 // vertices
243 (void(that->template vertexXn<Ints>()->fixed()
244 ? (that->template jacobianOplusXn<Ints>().setZero(), 0)
245 : (assign(that->template jacobianOplusXn<Ints>(),
246 std::get<Ints>(ad_jacobians)),
247 0)),
248 ...);
249 }
static EIGEN_STRONG_INLINE void assign(const Eigen::MatrixBase< A > &a, const Eigen::MatrixBase< B > &b)
helper function to perform a = b
typename Eigen::Matrix< double, EdgeDimension, VertexDimension, Eigen::RowMajor > ADJacobianType
type for the Jacobians during AD
bool AutoDifferentiate(const Functor &functor, T const *const *parameters, int dynamic_num_outputs, T *function_value, T **jacobians)
Definition autodiff.h:295
ParameterDims< false, Ns... > StaticParameterDims
Definition jet.h:876

References g2o::AutoDifferentiation< Edge, EstimateAccess >::assign(), and g2o::ceres::internal::AutoDifferentiate().

Referenced by g2o::AutoDifferentiation< Edge, EstimateAccess >::linearize().


The documentation for this class was generated from the following file: