g2o
Loading...
Searching...
No Matches
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
 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
Initial value:
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>
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 {
256 aux = b;
257 }
Implementation of Automatic Differentiation for edges in g2o.

Referenced by linearizeOplusNs().

◆ computeError()

template<typename Edge, typename EstimateAccess = EstimateAccessor<Edge>>
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");
168 }
static void computeErrorNs(Edge *that, std::index_sequence< Ints... >)
packed version to call the functor that evaluates the error function

References computeErrorNs().

◆ computeErrorNs()

template<typename Edge, typename EstimateAccess = EstimateAccessor<Edge>>
template<std::size_t... Ints>
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(
189 "Dynamically sized vertices are not supported");
191 (*that)(estimateAccess.template data<Ints>(that)..., that->errorData());
192 }

Referenced by computeError().

◆ linearize()

template<typename Edge, typename EstimateAccess = EstimateAccessor<Edge>>
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");
181 }
static void linearizeOplusNs(Edge *that, std::index_sequence< Ints... >)

References linearizeOplusNs().

◆ linearizeOplusNs()

template<typename Edge, typename EstimateAccess = EstimateAccessor<Edge>>
template<std::size_t... Ints>
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(
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
212
213 // setting up the pointer to the parameters and the Jacobians for calling
214 // AD.
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.
231 bool diffState =
233 double>(
235
236 assert(diffState && "Error during Automatic Differentiation");
237 if (!diffState) { // something went wrong during AD
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>(),
247 0)),
248 ...);
249 }
typename Eigen::Matrix< double, EdgeDimension, VertexDimension, Eigen::RowMajor > ADJacobianType
type for the Jacobians during AD
static EIGEN_STRONG_INLINE void assign(const Eigen::MatrixBase< A > &a, const Eigen::MatrixBase< B > &b)
helper function to perform a = b

References assign(), and g2o::ceres::internal::AutoDifferentiate().

Referenced by linearize().


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