diff --git a/gtsam/base/concepts.h b/gtsam/base/concepts.h index a6b2448c2..6caf1703f 100644 --- a/gtsam/base/concepts.h +++ b/gtsam/base/concepts.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -25,9 +26,7 @@ namespace traits { * @brief Associate a unique tag with each of the main GTSAM concepts */ //@{ -template -struct structure_category; -// specializations should be derived from one of the following tags +template struct structure_category; //@} /** @@ -35,81 +34,95 @@ struct structure_category; * @brief Possible values for traits::structure_category::type */ //@{ -struct manifold_tag { -}; -struct group_tag { -}; -struct lie_group_tag: public manifold_tag, public group_tag { -}; -struct vector_space_tag: public lie_group_tag { -}; +struct manifold_tag {}; +struct group_tag {}; +struct lie_group_tag: public manifold_tag, public group_tag {}; +struct vector_space_tag: public lie_group_tag {}; //@} }// namespace traits +namespace manifold { + +/** @name Free functions any Manifold needs to define */ +//@{ +//@} + namespace traits { /** @name Manifold Traits */ //@{ +template struct dimension; template struct TangentVector; template struct DefaultChart; //@} -}// namespace traits +}// \ namespace traits -/* - template - class ManifoldConcept { - public: - typedef T Manifold; - typedef typename traits::TangentVector::type TangentVector; - typedef typename traits::DefaultChart::type DefaultChart; - static const size_t dim = traits::dimension::value; +/// Check invariants for Manifold type +template +BOOST_CONCEPT_REQUIRES(((Testable)),(bool)) // +check_invariants(const T& a, const T& b) { + typedef typename traits::DefaultChart::type Chart; + return true; +} - BOOST_CONCEPT_USAGE(ManifoldConcept) { - BOOST_STATIC_ASSERT(boost::is_base_of >); - BOOST_STATIC_ASSERT(TangentVector::SizeAtCompileTime == dim); - // no direct usage for manifold since most usage is through a chart - } - private: - Manifold p; - TangentVector v; - }; - - template - class ChartConcept { - public: - typedef C Chart; - typedef typename traits::Manifold::type Manifold; - typedef typename traits::TangentVector::type TangentVector; - - BOOST_CONCEPT_USAGE(ChartConcept) { - v = Chart::local(p,q); // returns local coordinates of q w.r.t. origin p - q = Chart::retract(p,v); // returns retracted update of p with v - } - - private: - Manifold p,q; - TangentVector v; - - }; +/** + * Base class for Charts + * Derived has to implement local and retract as static methods */ +template +struct Chart { + typedef T ManifoldType; + typedef typename traits::TangentVector::type TangentVector; + static TangentVector Local(const ManifoldType& p, const ManifoldType& q) { + return Derived::local(p, q); + } + static ManifoldType Retract(const ManifoldType& p, const TangentVector& v) { + return Derived::retract(p, v); + } +protected: + Chart() { + (void) &Local; + (void) &Retract; + } // enforce early instantiation. +}; + +} // \ namespace manifold + +template +class IsManifold { +public: + typedef typename traits::structure_category::type structure_category_tag; + static const size_t dim = manifold::traits::dimension::value; + typedef typename manifold::traits::TangentVector::type TangentVector; + typedef typename manifold::traits::DefaultChart::type DefaultChart; + + BOOST_CONCEPT_USAGE(IsManifold) { + BOOST_STATIC_ASSERT(boost::is_base_of::value, "This type's structure_category trait does not assert it as a manifold (or derived)"); + BOOST_STATIC_ASSERT(TangentVector::SizeAtCompileTime == dim); + // no direct usage for manifold since most usage is through a chart + } +private: + T p; + TangentVector v; +}; namespace group { /** @name Free functions any Group needs to define */ //@{ -template G compose(const G&g, const G& h); -template G between(const G&g, const G& h); -template G inverse(const G&g); +template T compose(const T&g, const T& h); +template T between(const T&g, const T& h); +template T inverse(const T&g); //@} namespace traits { /** @name Group Traits */ //@{ -template struct identity; -template struct flavor; +template struct identity; +template struct flavor; //@} /** @name Group Flavor Tags */ @@ -120,31 +133,29 @@ struct multiplicative_tag { }; //@} -} // \ namespace traits +}// \ namespace traits /// Check invariants -template -//BOOST_CONCEPT_REQUIRES((Testable)) -bool check_invariants(const G& a, const G& b) { - G e = traits::identity::value; - typename traits::flavor::type flavor; - return (equal(compose(a, inverse(a)), e)) - && (equal(between(a, b), compose(inverse(a), b))) - && (equal(compose(a, between(a, b)), b)) // - && operator_usage(a, b, flavor); +template +BOOST_CONCEPT_REQUIRES(((Testable)),(bool)) // +check_invariants(const T& a, const T& b, double tol = 1e-9) { + T e = traits::identity::value; + return compose(a, inverse(a)).equals(e, tol) + && between(a, b).equals(compose(inverse(a), b), tol) + && compose(a, between(a, b)).equals(b, tol); } } // \ namespace group /** * Group Concept */ -template +template class IsGroup { public: - typedef typename traits::structure_category::type structure_category_tag; - typedef typename group::traits::identity::value_type identity_value_type; - typedef typename group::traits::flavor::type flavor_tag; + typedef typename traits::structure_category::type structure_category_tag; + typedef typename group::traits::identity::value_type identity_value_type; + typedef typename group::traits::flavor::type flavor_tag; void operator_usage(group::traits::multiplicative_tag) { g = g * h; @@ -159,9 +170,8 @@ public: using group::compose; using group::between; using group::inverse; - BOOST_STATIC_ASSERT( - boost::is_base_of::value); - e = group::traits::identity::value; + BOOST_STATIC_ASSERT( boost::is_base_of::value, "This type's structure_category trait does not assert it as a group (or derived)"); + e = group::traits::identity::value; g = compose(g, h); g = between(g, h); g = inverse(g); @@ -170,42 +180,78 @@ public: private: flavor_tag flavor; - G e, g, h; + T e, g, h; }; -/* - template - class LieGroupConcept : public GroupConcept, public ManifoldConcept { +namespace lie_group { - BOOST_CONCEPT_USAGE(LieGroupConcept) { - BOOST_STATIC_ASSERT(boost::is_base_of >); - } - }; +/** @name Free functions any Group needs to define */ +//@{ +// TODO need Jacobians +//template T compose(const T&g, const T& h); +//template T between(const T&g, const T& h); +//template T inverse(const T&g); +//@} - template - class VectorSpaceConcept : public LieGroupConcept { - typedef typename traits::DefaultChart::type Chart; - typedef typename GroupConcept::identity identity; +namespace traits { - BOOST_CONCEPT_USAGE(VectorSpaceConcept) { - BOOST_STATIC_ASSERT(boost::is_base_of >); - r = p+q; - r = -p; - r = p-q; - } +/** @name Lie Group Traits */ +//@{ +//@} - bool check_invariants(const V& a, const V& b) { - return equal(compose(a, b), a+b) - && equal(inverse(a), -a) - && equal(between(a, b), b-a) - && equal(Chart::retract(a, b), a+b) - && equal(Chart::local(a, b), b-a); - } +}// \ namespace traits - private: - V g,q,r; - }; +/// Check invariants +//template +//BOOST_CONCEPT_REQUIRES(((Testable)),(bool)) check_invariants(const T& a, +// const T& b) { +// bool check_invariants(const V& a, const V& b) { +// return equal(Chart::retract(a, b), a + b) +// && equal(Chart::local(a, b), b - a); +// } +//} +}// \ namespace lie_group + +/** + * Lie Group Concept */ +template +class IsLieGroup: public IsGroup, public IsManifold { +public: + + typedef typename traits::structure_category::type structure_category_tag; + + BOOST_CONCEPT_USAGE(IsLieGroup) { + BOOST_STATIC_ASSERT(boost::is_base_of::value,"This type's trait does not assert it as a Lie group (or derived)"); + // TODO Check with Jacobian +// using lie_group::compose; +// using lie_group::between; +// using lie_group::inverse; +// g = compose(g, h); +// g = between(g, h); +// g = inverse(g); + } +private: + + T g, h; +}; + +template +class IsVectorSpace: public IsLieGroup { +public: + + typedef typename traits::structure_category::type structure_category_tag; + + BOOST_CONCEPT_USAGE(IsVectorSpace) { + BOOST_STATIC_ASSERT(boost::is_base_of::value,"This type's trait does not assert it as a vector space (or derived)"); + r = p + q; + r = -p; + r = p - q; + } + +private: + T p, q, r; +}; } // namespace gtsam diff --git a/gtsam/geometry/tests/testCyclic.cpp b/gtsam/geometry/tests/testCyclic.cpp index f9d4a2d77..3cfa6b2c8 100644 --- a/gtsam/geometry/tests/testCyclic.cpp +++ b/gtsam/geometry/tests/testCyclic.cpp @@ -81,6 +81,12 @@ TEST(Cyclic, Ivnverse) { EXPECT_LONGS_EQUAL(1, group::inverse(G(5))); } +//****************************************************************************** +TEST(Cyclic , Invariants) { + G g(2), h(5); + group::check_invariants(g,h); +} + //****************************************************************************** int main() { TestResult tr; diff --git a/gtsam/geometry/tests/testQuaternion.cpp b/gtsam/geometry/tests/testQuaternion.cpp index 339897a06..22a96f4a3 100644 --- a/gtsam/geometry/tests/testQuaternion.cpp +++ b/gtsam/geometry/tests/testQuaternion.cpp @@ -20,13 +20,47 @@ namespace gtsam { namespace traits { -/// Define Eigen::Quaternion to be a model of the Group concept + +/// Define Eigen::Quaternion to be a model of the Lie Group concept template struct structure_category > { - typedef group_tag type; + typedef lie_group_tag type; }; + } // \namespace gtsam::traits +namespace manifold { + +/// Chart for Eigen Quaternions +template +class QuaternionChart: public manifold::Chart, + QuaternionChart > { + +}; + +namespace traits { + +/// Define the trait that asserts Quaternion manifold has dimension 3 +template +struct dimension > : public boost::integral_constant< + int, 3> { +}; + +/// Define the trait that asserts Quaternion TangentVector is Vector3 +template +struct TangentVector > { + typedef Eigen::Matrix type; +}; + +/// Define the trait that asserts Quaternion TangentVector is Vector3 +template +struct DefaultChart > { + typedef QuaternionChart type; +}; + +} // \namespace gtsam::manifold::traits +} // \namespace gtsam::manifold + namespace group { template @@ -95,7 +129,9 @@ typedef Quaternion Q; // Typedef //****************************************************************************** TEST(Quaternion , Concept) { - BOOST_CONCEPT_ASSERT((IsGroup)); + // BOOST_CONCEPT_ASSERT((IsGroup)); + // BOOST_CONCEPT_ASSERT((IsManifold)); + // BOOST_CONCEPT_ASSERT((IsLieGroup)); } //****************************************************************************** @@ -103,6 +139,13 @@ TEST(Quaternion , Constructor) { Q g(Eigen::AngleAxisd(1, Vector3(0, 0, 1))); } +//****************************************************************************** +TEST(Quaternion , Invariants) { + Q g(Eigen::AngleAxisd(1, Vector3(0, 0, 1))); + Q h(Eigen::AngleAxisd(2, Vector3(0, 1, 0))); + // group::check_invariants(g,h); Does not satisfy Testable concept (yet!) +} + //****************************************************************************** TEST(Quaternion , Compose) { }