From 40f5188b20588122f3b55d652f876ebf1a8884d9 Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 3 Dec 2014 19:43:52 +0100 Subject: [PATCH] OK, that should about do it... --- GTSAM-Concepts.md | 162 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 113 insertions(+), 49 deletions(-) diff --git a/GTSAM-Concepts.md b/GTSAM-Concepts.md index 4540a0fb6..0693a758d 100644 --- a/GTSAM-Concepts.md +++ b/GTSAM-Concepts.md @@ -16,33 +16,56 @@ Manifold To optimize over continuous types, we assume they are manifolds. This is central to GTSAM and hence discussed in some more detail below. -[Manifolds](http://en.wikipedia.org/wiki/Manifold#Charts.2C_atlases.2C_and_transition_maps) and [charts](http://en.wikipedia.org/wiki/Manifold#Charts.2C_atlases.2C_and_transition_maps) are intimately linked concepts. We are only interested here in [differentiable manifolds](http://en.wikipedia.org/wiki/Differentiable_manifold#Definition), continuous spaces that can be locally approximated *at any point* using a local vector space, called the [tangent space](http://en.wikipedia.org/wiki/Tangent_space). A chart is an invertible map from the manifold to the vector space. +[Manifolds](http://en.wikipedia.org/wiki/Manifold#Charts.2C_atlases.2C_and_transition_maps) and [charts](http://en.wikipedia.org/wiki/Manifold#Charts.2C_atlases.2C_and_transition_maps) are intimately linked concepts. We are only interested here in [differentiable manifolds](http://en.wikipedia.org/wiki/Differentiable_manifold#Definition), continuous spaces that can be locally approximated *at any point* using a local vector space, called the [tangent space](http://en.wikipedia.org/wiki/Tangent_space). A *chart* is an invertible map from the manifold to the vector space. -In GTSAM we assume that a manifold type can yield such a chart at any point, and we require that a functor `defaultChart` is available that, when called for any point on the manifold, returns a Chart type. +In GTSAM we assume that a manifold type can yield such a chart at any point, and we require that a functor `defaultChart` is available that, when called for any point on the manifold, returns a Chart type. Hence, the functor itself can be seen as an *Atlas*. -* values: `dimension` -* types: `Vector`, type that lives in tangent space - * - * `DefaultChart` is the *type* of the chart returned by the functor `defaultChart` -* functor `defaultChart`, returns a `DefaultChart` -* invariants: `defaultChart::result_type == DefaultChart::type` +In detail, we ask the following are defined for a MANIFOLD type: + +* values: + * `dimension`, an int that indicates the dimensionality *n* of the manifold. In Eigen-fashion, we also support manifolds whose dimenionality is only defined at runtime, by specifying the value -1. +* functors: + * `defaultChart`, returns the default chart at a point p +* types: + * `TangentVector`, type that lives in tangent space. This will almost always be an `Eigen::Matrix`. Anything else? Chart ----- +A given chart is implemented using a small class that defines the chart itself (from manifold to tangent space) and its inverse. -* types: `Manifold`, a pointer back to the type -* values: `retract`, `local` +* types: + * `Manifold`, a pointer back to the type +* valid expressions: + * `Chart chart(p)` constructor + * `v = chart.local(q)`, the chart, from manifold to tangent space, think of it as *p (-) q* + * `p = chart.retract(v)`, the inverse chart, from tangent space to manifold, think of it as *p (+) v* -Are these values? They are just methods. Anything else? +For many differential manifolds, an obvious mapping is the `exponential map`, which associates staright lines in the tangent space with geodesics on the manifold (and it's inverse, the log map). However, there are two cases in which we deviate from this: + + * Sometimes, most notably for *SO(3)* and *SE(3)*, the exponential map is unnecessarily expensive for use in optimiazation. Hence, the `defaultChart` functor returns a chart that is much cheaper to evaluate. + * While vector spaces (see below) are in principle also manifolds, it is overkill to think about charts etc. Really, we should simply think about vector addition and subtraction. Hence, while a `defaultChart` functor is defined by default for every vector space, GTSAM will never call it. Group ----- +A [group](http://en.wikipedia.org/wiki/Group_(mathematics)) should be well known from grade school :-), and provides a type with a composition operation that is closed, associative, has an identity element, and an inverse for each element. -* values: `identity` -* values: `compose`, `inverse`, (`between`) +* values: + * `identity` +* valid expressions: + * `compose(p,q)` + * `inverse(p)` + * `between(p,q)` +* invariants: + * `compose(p,inverse(p)) == identity` + * `compose(p,between(p,q)) == q` + * `between(p,q) == compose(inverse(p),q)` + +We do *not* at this time support more than one composition operator per type. Although mathematically possible, it is hardly ever needed, and the machinery to support it would be burdensome and counter-intuitive. + +Also, a type should provide either multiplication or addition operators depending on the flavor of the operation. To distinguish between the two, we will use a tag (see below). Lie Group --------- @@ -52,36 +75,49 @@ Implements both MANIFOLD and GROUP Vector Space ------------ -Lie Group where compose == `+` +Trivial Lie Group where + + * `identity == 0` + * `inverse(p) == -p` + * `compose(p,q) == p+q` + * `between(p,q) == q-p` + * `chart.retract(q) == p-q` + * `chart.retract(v) == p+v` + +This considerably simplifies certain operations. Testable -------- Unit tests heavily depend on the following two functions being defined for all types that need to be tested: -* functions: `print`, `equals` +* valid expressions: + * `print(p,s)` where s is an optional string + * `equals(p,q,tol)` where tol is an optional tolerance Implementation ============== -GTSAM Types start with Uppercase, e.g., `gtsam::Point2`, and are models of the TESTABLE, MANIFOLD, GROUP, LIE_GROUP, and VECTOR_SPACE concepts. `gtsam::traits` is our way to associate these concepts with types, and we also define a limited number of `gtsam::tags` to select the correct implementation of certain functions at compile time (tag dispatching). +GTSAM Types start with Uppercase, e.g., `gtsam::Point2`, and are models of the TESTABLE, MANIFOLD, GROUP, LIE_GROUP, and VECTOR_SPACE concepts. -traits +`gtsam::traits` is our way to associate these concepts with types, and we also define a limited number of `gtsam::tags` to select the correct implementation of certain functions at compile time (tag dispatching). + +Traits ------ -We will not use Eigen-style or STL-style traits, that define many properties at once. Rather, we use boost::mpl style meta-programming functions to facilitate meta-programming. +We will not use Eigen-style or STL-style traits, that define *many* properties at once. Rather, we use boost::mpl style meta-programming functions to facilitate meta-programming, which return a single type or value for every trait. -Traits allow us to play with types that are outside GTSAM control, e.g., `Eigen::VectorXd`. +Traits allow us to play with types that are outside GTSAM control, e.g., `Eigen::VectorXd`. However, for GTSAM types, it is perfectly acceptable (and even desired) to define associated types as internal types, as well, rather than having to use traits internally. -The naming conventions are as follows: +The conventions for `gtsam::traits` are as follows: -* Types: `gtsam::traits::SomeAssociatedType::type`, i.e., they are MixedCase and define a `type`, for example: +* Types: `gtsam::traits::SomeAssociatedType::type`, i.e., they are MixedCase and define a *single* `type`, for example: template<> gtsam::traits::TangentVector { typedef Vector2 type; } -* Values: `gtsam::traits::someValue::value`, i.e., they are mixedCase starting with a lowercase letter and define a `value`, but also a `value_type`. For example: +* Values: `gtsam::traits::someValue::value`, i.e., they are mixedCase starting with a lowercase letter and define a `value`, *and* a `value_type`. For example: template<> gtsam::traits::dimension { @@ -89,7 +125,7 @@ The naming conventions are as follows: typedef const int value_type; // const ? } -* Functors: `gtsam::traits::someFunctor::type`, i.e., they are mixedCase starting with a lowercase letter and define a functor `type`. The funcor itself should define a `result_type`. Example +* Functors: `gtsam::traits::someFunctor::type`, i.e., they are mixedCase starting with a lowercase letter and define a functor (i.e., no *type*). The functor itself should define a `result_type`. Example struct Point2::retract { typedef Point2 result_type; @@ -101,53 +137,62 @@ The naming conventions are as follows: } template<> - gtsam::traits::retract { - typedef Point2::retract type; - } - - The above is still up in the air. Do we need the type indirection? Could we just inherit the trait like so - - template<> - gtsam::traits::retract : Point2::retract { - } + gtsam::traits::retract : Point2::retract {} - In which case we could just say `gtsam::traits::retract(p)(v)`. + By *inherting* the trait from the functor, we can just use the [currying](http://en.wikipedia.org/wiki/Currying) style `gtsam::traits::retract(p)(v)`. Note that, although technically a functor is a type, in spirit it is a free function and hence starts with a lowercase letter. -tags +Tags ---- -Concepts are associated with a tag. +Algebraic structure concepts are associated with the following tags -* `gtsam::tags::manifold_tag` -* `gtsam::tags::group_tag` -* `gtsam::tags::lie_group_tag` -* `gtsam::tags::vector_space_tag` +* `gtsam::traits::manifold_tag` +* `gtsam::traits::group_tag` +* `gtsam::traits::lie_group_tag` +* `gtsam::traits::vector_space_tag` -Can be queried `gtsam::traits::structure_tag` +which should be queryable by `gtsam::traits::structure` +The group composition operation can be of two flavors: +* `gtsam::traits::additive_group_tag` +* `gtsam::traits::multiplicative_group_tag` + +which should be queryable by `gtsam::traits::group_flavor` Examples -------- -An example of implementing a Manifold is here: +An example of implementing a Manifold type is here: // GTSAM type class Rot2 { - ... + typedef Vector2 TangentVector; class Chart { - ... + Chart(const Rot2& R); + TangentVector local(const Rot2& R) const; + Rot2 retract(const TangentVector& v) const; } + Rot2 operator*(const Rot2&) const; + Rot2 transpose() const; } - namespace gtsam { - namespace traits { + namespace gtsam { namespace traits { template<> - struct DefaultChart { - typedef Rot2::Chart type; + struct dimension { + static const int value = 2; + typedef int value_type; } + template<> + struct TangentVector { + typedef Rot2::TangentVector type; + } + + template<> + struct defaultChart : Rot2::Chart {} + template<> struct Manifold { typedef Rot2 type; @@ -157,7 +202,26 @@ An example of implementing a Manifold is here: struct Vector { typedef Vector2 type; } - } - + + }} + +But Rot2 is in fact also a Lie Group, after we define + + Rot2 inverse(const Rot2& p) { return p.transpose();} + Rot2 operator*(const Rot2& p, const Rot2& q) { return p*q;} + Rot2 compose(const Rot2& p, const Rot2& q) { return p*q;} + Rot2 between(const Rot2& p, const Rot2& q) { return p*q;} + +The only traits that needs to be implemented are the tags: + + namespace gtsam { namespace traits { + + template<> + struct structure : lie_group_tag {} + + template<> + struct group_flavor : multiplicative_group_tag {} + + }}