From 0c7ea68f2fa003314ee85114d3c044a18d614f9f Mon Sep 17 00:00:00 2001 From: dellaert Date: Sat, 11 Oct 2014 17:05:53 +0200 Subject: [PATCH 01/35] Now overwriting linearize as preparation for direct VericalBlockMatrix --- gtsam/nonlinear/NonlinearFactor.cpp | 2 +- gtsam_unstable/nonlinear/ExpressionFactor.h | 31 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/gtsam/nonlinear/NonlinearFactor.cpp b/gtsam/nonlinear/NonlinearFactor.cpp index d2e1febd0..7d229a1ea 100644 --- a/gtsam/nonlinear/NonlinearFactor.cpp +++ b/gtsam/nonlinear/NonlinearFactor.cpp @@ -124,7 +124,7 @@ boost::shared_ptr NoiseModelFactor::linearize( boost::dynamic_pointer_cast(this->noiseModel_); if (constrained) { // Create a factor of reduced row dimension d_ - size_t d_ = terms[0].second.rows() - constrained->dim(); + size_t d_ = b.size() - constrained->dim(); Vector zero_ = zero(d_); Vector b_ = concatVectors(2, &b, &zero_); noiseModel::Constrained::shared_ptr model = constrained->unit(d_); diff --git a/gtsam_unstable/nonlinear/ExpressionFactor.h b/gtsam_unstable/nonlinear/ExpressionFactor.h index 14e9c54ba..b1a16d2a3 100644 --- a/gtsam_unstable/nonlinear/ExpressionFactor.h +++ b/gtsam_unstable/nonlinear/ExpressionFactor.h @@ -60,6 +60,37 @@ public: } } + virtual boost::shared_ptr linearize(const Values& x) const { + + // Only linearize if the factor is active + if (!this->active(x)) + return boost::shared_ptr(); + + // Evaluate error to get Jacobians and RHS vector b + Augmented augmented = expression_.augmented(x); + Vector b = - measurement_.localCoordinates(augmented.value()); + // check(noiseModel_, b); // TODO: use, but defined in NonlinearFactor.cpp + + // Whiten the corresponding system now + // TODO ! this->noiseModel_->WhitenSystem(A, b); + + // Terms, needed to create JacobianFactor below, are already here! + const JacobianMap& terms = augmented.jacobians(); + + // TODO pass unwhitened + noise model to Gaussian factor + // For now, only linearized constrained factors have noise model at linear level!!! + noiseModel::Constrained::shared_ptr constrained = // + boost::dynamic_pointer_cast(this->noiseModel_); + if (constrained) { + // Create a factor of reduced row dimension d_ + size_t d_ = b.size() - constrained->dim(); + Vector zero_ = zero(d_); + Vector b_ = concatVectors(2, &b, &zero_); + noiseModel::Constrained::shared_ptr model = constrained->unit(d_); + return boost::make_shared(terms, b_, model); + } else + return boost::make_shared(terms, b); + } }; // ExpressionFactor From ed62271f8174667f4fe5e8c77969b34697e0d7aa Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 12 Oct 2014 10:52:07 +0200 Subject: [PATCH 02/35] Dealing with constrained noise model --- gtsam_unstable/nonlinear/ExpressionFactor.h | 7 +- .../nonlinear/tests/testExpressionFactor.cpp | 75 ++++++++----------- 2 files changed, 33 insertions(+), 49 deletions(-) diff --git a/gtsam_unstable/nonlinear/ExpressionFactor.h b/gtsam_unstable/nonlinear/ExpressionFactor.h index b1a16d2a3..5a678c0e3 100644 --- a/gtsam_unstable/nonlinear/ExpressionFactor.h +++ b/gtsam_unstable/nonlinear/ExpressionFactor.h @@ -82,12 +82,7 @@ public: noiseModel::Constrained::shared_ptr constrained = // boost::dynamic_pointer_cast(this->noiseModel_); if (constrained) { - // Create a factor of reduced row dimension d_ - size_t d_ = b.size() - constrained->dim(); - Vector zero_ = zero(d_); - Vector b_ = concatVectors(2, &b, &zero_); - noiseModel::Constrained::shared_ptr model = constrained->unit(d_); - return boost::make_shared(terms, b_, model); + return boost::make_shared(terms, b, constrained->unit()); } else return boost::make_shared(terms, b); } diff --git a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp index 02d9b4e9d..eb4f1e52e 100644 --- a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -33,80 +34,68 @@ using namespace gtsam; Point2 measured(-17, 30); SharedNoiseModel model = noiseModel::Unit::Create(2); +namespace leaf { +// Create some values +struct MyValues: public Values { + MyValues() { + insert(2, Point2(3, 5)); + } +} values; + +// Create leaf +Point2_ p(2); +} + /* ************************************************************************* */ // Leaf TEST(ExpressionFactor, leaf) { + using namespace leaf; - // Create some values - Values values; - values.insert(2, Point2(3, 5)); - - JacobianFactor expected( // - 2, (Matrix(2, 2) << 1, 0, 0, 1), // - (Vector(2) << -3, -5)); - - // Create leaf - Point2_ p(2); + // Create old-style factor to create expected value and derivatives + PriorFactor old(2, Point2(0, 0), model); // Concise version ExpressionFactor f(model, Point2(0, 0), p); + EXPECT_DOUBLES_EQUAL(old.error(values), f.error(values), 1e-9); EXPECT_LONGS_EQUAL(2, f.dim()); - boost::shared_ptr gf = f.linearize(values); - boost::shared_ptr jf = // - boost::dynamic_pointer_cast(gf); - EXPECT( assert_equal(expected, *jf, 1e-9)); + boost::shared_ptr gf2 = f.linearize(values); + EXPECT( assert_equal(*old.linearize(values), *gf2, 1e-9)); } /* ************************************************************************* */ // non-zero noise model TEST(ExpressionFactor, model) { + using namespace leaf; SharedNoiseModel model = noiseModel::Diagonal::Sigmas(Vector2(0.1, 0.01)); - // Create some values - Values values; - values.insert(2, Point2(3, 5)); - - JacobianFactor expected( // - 2, (Matrix(2, 2) << 10, 0, 0, 100), // - (Vector(2) << -30, -500)); - - // Create leaf - Point2_ p(2); + // Create old-style factor to create expected value and derivatives + PriorFactor old(2, Point2(0, 0), model); // Concise version ExpressionFactor f(model, Point2(0, 0), p); + EXPECT_DOUBLES_EQUAL(old.error(values), f.error(values), 1e-9); EXPECT_LONGS_EQUAL(2, f.dim()); - boost::shared_ptr gf = f.linearize(values); - boost::shared_ptr jf = // - boost::dynamic_pointer_cast(gf); - EXPECT( assert_equal(expected, *jf, 1e-9)); + boost::shared_ptr gf2 = f.linearize(values); + EXPECT( assert_equal(*old.linearize(values), *gf2, 1e-9)); } /* ************************************************************************* */ // Constrained noise model TEST(ExpressionFactor, constrained) { + using namespace leaf; - SharedDiagonal model = noiseModel::Constrained::MixedSigmas(Vector2(0.2,0)); + SharedDiagonal model = noiseModel::Constrained::MixedSigmas(Vector2(0.2, 0)); - // Create some values - Values values; - values.insert(2, Point2(3, 5)); - - vector > terms; - terms.push_back(make_pair(2, (Matrix(2, 2) << 1, 0, 0, 1))); - JacobianFactor expected(terms, (Vector(2) << -3, -5), model); - - // Create leaf - Point2_ p(2); + // Create old-style factor to create expected value and derivatives + PriorFactor old(2, Point2(0, 0), model); // Concise version ExpressionFactor f(model, Point2(0, 0), p); + EXPECT_DOUBLES_EQUAL(old.error(values), f.error(values), 1e-9); EXPECT_LONGS_EQUAL(2, f.dim()); - boost::shared_ptr gf = f.linearize(values); - boost::shared_ptr jf = // - boost::dynamic_pointer_cast(gf); - EXPECT( assert_equal(expected, *jf, 1e-9)); + boost::shared_ptr gf2 = f.linearize(values); + EXPECT( assert_equal(*old.linearize(values), *gf2, 1e-9)); } /* ************************************************************************* */ From fea2eb0b5f404eb4c46a12efc0974f3b8a234e76 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 12 Oct 2014 11:05:43 +0200 Subject: [PATCH 03/35] Inlined VerticalBlockMatrix construction --- gtsam_unstable/nonlinear/ExpressionFactor.h | 35 +++++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/gtsam_unstable/nonlinear/ExpressionFactor.h b/gtsam_unstable/nonlinear/ExpressionFactor.h index 5a678c0e3..9ca54924d 100644 --- a/gtsam_unstable/nonlinear/ExpressionFactor.h +++ b/gtsam_unstable/nonlinear/ExpressionFactor.h @@ -68,7 +68,7 @@ public: // Evaluate error to get Jacobians and RHS vector b Augmented augmented = expression_.augmented(x); - Vector b = - measurement_.localCoordinates(augmented.value()); + Vector b = -measurement_.localCoordinates(augmented.value()); // check(noiseModel_, b); // TODO: use, but defined in NonlinearFactor.cpp // Whiten the corresponding system now @@ -76,15 +76,44 @@ public: // Terms, needed to create JacobianFactor below, are already here! const JacobianMap& terms = augmented.jacobians(); + size_t n = terms.size(); + + // Get dimensions of matrices + std::vector dimensions; + dimensions.reserve(n); + std::vector keys; + keys.reserve(n); + for (JacobianMap::const_iterator it = terms.begin(); it != terms.end(); + ++it) { + const std::pair& term = *it; + Key key = term.first; + const Matrix& Ai = term.second; + dimensions.push_back(Ai.cols()); + keys.push_back(key); + } + + // Construct block matrix + VerticalBlockMatrix Ab(dimensions, b.size(), true); + + // Check and add terms + DenseIndex i = 0; // For block index + for (JacobianMap::const_iterator it = terms.begin(); it != terms.end(); + ++it) { + const std::pair& term = *it; + const Matrix& Ai = term.second; + Ab(i++) = Ai; + } + + Ab(n).col(0) = b; // TODO pass unwhitened + noise model to Gaussian factor // For now, only linearized constrained factors have noise model at linear level!!! noiseModel::Constrained::shared_ptr constrained = // boost::dynamic_pointer_cast(this->noiseModel_); if (constrained) { - return boost::make_shared(terms, b, constrained->unit()); + return boost::make_shared(keys, Ab, constrained->unit()); } else - return boost::make_shared(terms, b); + return boost::make_shared(keys, Ab); } }; // ExpressionFactor From 7a5f48f6ddf27185fce0d9ff5b05d8f50435e130 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 12 Oct 2014 12:20:12 +0200 Subject: [PATCH 04/35] Fixed typo in assert --- gtsam_unstable/nonlinear/Expression-inl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index c40dfb405..5ee1ca272 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -370,7 +370,7 @@ public: /// Move terms to array, destroys content void move(std::vector& H) { - assert(H.size()==jacobains.size()); + assert(H.size()==jacobians_.size()); size_t j = 0; JacobianMap::iterator it = jacobians_.begin(); for (; it != jacobians_.end(); ++it) From dc541f1051161e8e6faa0e005dce21b88be2f4d7 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 12 Oct 2014 18:52:12 +0200 Subject: [PATCH 05/35] made traceSize an instance variable --- gtsam_unstable/nonlinear/Expression-inl.h | 45 +++++++++-------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index 5ee1ca272..08a0e0bc6 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -392,7 +392,11 @@ class ExpressionNode { protected: - ExpressionNode() { + size_t traceSize_; + + /// Constructor, traceSize is size of the execution trace of expression rooted here + ExpressionNode(size_t traceSize = 0) : + traceSize_(traceSize) { } public: @@ -404,17 +408,17 @@ public: /// Return keys that play in this expression as a set virtual std::set keys() const = 0; + // Return size needed for memory buffer in traceExecution + size_t traceSize() const { + return traceSize_; + } + /// Return value virtual T value(const Values& values) const = 0; /// Return value and derivatives virtual Augmented forward(const Values& values) const = 0; - // Return size needed for memory buffer in traceExecution - virtual size_t traceSize() const { - return 0; - } - /// Construct an execution trace for reverse AD virtual T traceExecution(const Values& values, ExecutionTrace& trace, char* raw) const = 0; @@ -519,8 +523,9 @@ private: boost::shared_ptr > expressionA1_; /// Constructor with a unary function f, and input argument e - UnaryExpression(Function f, const Expression& e) : - function_(f), expressionA1_(e.root()) { + UnaryExpression(Function f, const Expression& e1) : + ExpressionNode(sizeof(Record) + e1.traceSize()), // + function_(f), expressionA1_(e1.root()) { } friend class Expression ; @@ -551,11 +556,6 @@ public: typedef boost::mpl::vector > Arguments; typedef typename GenerateRecord::type Record; - // Return size needed for memory buffer in traceExecution - virtual size_t traceSize() const { - return sizeof(Record) + expressionA1_->traceSize(); - } - /// Construct an execution trace for reverse AD virtual T traceExecution(const Values& values, ExecutionTrace& trace, char* raw) const { @@ -592,7 +592,8 @@ private: /// Constructor with a binary function f, and two input arguments BinaryExpression(Function f, // const Expression& e1, const Expression& e2) : - function_(f), expressionA1_(e1.root()), expressionA2_(e2.root()) { + ExpressionNode(sizeof(Record) + e1.traceSize() + e2.traceSize()), function_( + f), expressionA1_(e1.root()), expressionA2_(e2.root()) { } friend class Expression ; @@ -632,12 +633,6 @@ public: typedef boost::mpl::vector, Numbered > Arguments; typedef typename GenerateRecord::type Record; - // Return size needed for memory buffer in traceExecution - virtual size_t traceSize() const { - return sizeof(Record) + expressionA1_->traceSize() - + expressionA2_->traceSize(); - } - /// Construct an execution trace for reverse AD /// The raw buffer is [Record | A1 raw | A2 raw] virtual T traceExecution(const Values& values, ExecutionTrace& trace, @@ -682,7 +677,9 @@ private: Function f, // const Expression& e1, const Expression& e2, const Expression& e3) : - function_(f), expressionA1_(e1.root()), expressionA2_(e2.root()), expressionA3_( + ExpressionNode( + sizeof(Record) + e1.traceSize() + e2.traceSize() + e3.traceSize()), function_( + f), expressionA1_(e1.root()), expressionA2_(e2.root()), expressionA3_( e3.root()) { } @@ -729,12 +726,6 @@ public: typedef boost::mpl::vector, Numbered, Numbered > Arguments; typedef typename GenerateRecord::type Record; - // Return size needed for memory buffer in traceExecution - virtual size_t traceSize() const { - return sizeof(Record) + expressionA1_->traceSize() - + expressionA2_->traceSize() + expressionA2_->traceSize(); - } - /// Construct an execution trace for reverse AD virtual T traceExecution(const Values& values, ExecutionTrace& trace, char* raw) const { From 4d1eb05c7d645452ab1732ae1685d978f4a6efb7 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 12 Oct 2014 20:16:08 +0200 Subject: [PATCH 06/35] Passing JacobianMap as an argument now.. --- gtsam_unstable/nonlinear/Expression-inl.h | 16 ++++++---- gtsam_unstable/nonlinear/Expression.h | 29 ++++++++++--------- gtsam_unstable/nonlinear/ExpressionFactor.h | 13 +++++---- .../nonlinear/tests/testExpression.cpp | 21 ++++++++++---- 4 files changed, 48 insertions(+), 31 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index 08a0e0bc6..ac97de40e 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -45,6 +45,16 @@ class Expression; typedef std::map JacobianMap; +/// Move terms to array, destroys content +void move(JacobianMap& jacobians, std::vector& H) { + assert(H.size()==jacobians.size()); + size_t j = 0; + JacobianMap::iterator it = jacobians.begin(); + for (; it != jacobians.end(); ++it) + it->second.swap(H[j++]); +} + + //----------------------------------------------------------------------------- /** * The CallRecord class stores the Jacobians of applying a function @@ -370,11 +380,7 @@ public: /// Move terms to array, destroys content void move(std::vector& H) { - assert(H.size()==jacobians_.size()); - size_t j = 0; - JacobianMap::iterator it = jacobians_.begin(); - for (; it != jacobians_.end(); ++it) - it->second.swap(H[j++]); + move(jacobians_, H); } }; diff --git a/gtsam_unstable/nonlinear/Expression.h b/gtsam_unstable/nonlinear/Expression.h index afd5a60e0..147804fb1 100644 --- a/gtsam_unstable/nonlinear/Expression.h +++ b/gtsam_unstable/nonlinear/Expression.h @@ -108,14 +108,11 @@ public: return root_->keys(); } - /// Return value and optional derivatives - T value(const Values& values) const { - return root_->value(values); - } - /// Return value and derivatives, forward AD version - Augmented forward(const Values& values) const { - return root_->forward(values); + T forward(const Values& values, JacobianMap& jacobians) const { + Augmented augmented = root_->forward(values); + jacobians = augmented.jacobians(); + return augmented.value(); } // Return size needed for memory buffer in traceExecution @@ -130,22 +127,26 @@ public: } /// Return value and derivatives, reverse AD version - Augmented reverse(const Values& values) const { + T reverse(const Values& values, JacobianMap& jacobians) const { size_t size = traceSize(); char raw[size]; ExecutionTrace trace; T value(traceExecution(values, trace, raw)); - Augmented augmented(value); - trace.startReverseAD(augmented.jacobians()); - return augmented; + trace.startReverseAD(jacobians); + return value; + } + + /// Return value + T value(const Values& values) const { + return root_->value(values); } /// Return value and derivatives - Augmented augmented(const Values& values) const { + T value(const Values& values, JacobianMap& jacobians) const { #ifdef EXPRESSION_FORWARD_AD - return forward(values); + return forward(values, jacobians); #else - return reverse(values); + return reverse(values, jacobians); #endif } diff --git a/gtsam_unstable/nonlinear/ExpressionFactor.h b/gtsam_unstable/nonlinear/ExpressionFactor.h index 9ca54924d..5f78df004 100644 --- a/gtsam_unstable/nonlinear/ExpressionFactor.h +++ b/gtsam_unstable/nonlinear/ExpressionFactor.h @@ -50,10 +50,11 @@ public: boost::optional&> H = boost::none) const { if (H) { assert(H->size()==size()); - Augmented augmented = expression_.augmented(x); + JacobianMap jacobians; + T value = expression_.value(x, jacobians); // move terms to H, which is pre-allocated to correct size - augmented.move(*H); - return measurement_.localCoordinates(augmented.value()); + move(jacobians, *H); + return measurement_.localCoordinates(value); } else { const T& value = expression_.value(x); return measurement_.localCoordinates(value); @@ -67,15 +68,15 @@ public: return boost::shared_ptr(); // Evaluate error to get Jacobians and RHS vector b - Augmented augmented = expression_.augmented(x); - Vector b = -measurement_.localCoordinates(augmented.value()); + JacobianMap terms; + T value = expression_.value(x, terms); + Vector b = -measurement_.localCoordinates(value); // check(noiseModel_, b); // TODO: use, but defined in NonlinearFactor.cpp // Whiten the corresponding system now // TODO ! this->noiseModel_->WhitenSystem(A, b); // Terms, needed to create JacobianFactor below, are already here! - const JacobianMap& terms = augmented.jacobians(); size_t n = terms.size(); // Get dimensions of matrices diff --git a/gtsam_unstable/nonlinear/tests/testExpression.cpp b/gtsam_unstable/nonlinear/tests/testExpression.cpp index e14912a65..38297f156 100644 --- a/gtsam_unstable/nonlinear/tests/testExpression.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpression.cpp @@ -44,10 +44,11 @@ static const Rot3 someR = Rot3::RzRyRx(1, 2, 3); TEST(Expression, constant) { Expression R(someR); Values values; - Augmented a = R.augmented(values); - EXPECT(assert_equal(someR, a.value())); + JacobianMap actualMap; + Rot3 actual = R.value(values, actualMap); + EXPECT(assert_equal(someR, actual)); JacobianMap expected; - EXPECT(a.jacobians() == expected); + EXPECT(actualMap == expected); } /* ************************************************************************* */ @@ -56,11 +57,19 @@ TEST(Expression, leaf) { Expression R(100); Values values; values.insert(100, someR); - Augmented a = R.augmented(values); - EXPECT(assert_equal(someR, a.value())); + JacobianMap expected; expected[100] = eye(3); - EXPECT(a.jacobians() == expected); + + JacobianMap actualMap1; + Rot3 actual1 = R.forward(values, actualMap1); + EXPECT(assert_equal(someR, actual1)); + EXPECT(actualMap1 == expected); + + JacobianMap actualMap2; + Rot3 actual2 = R.reverse(values, actualMap2); + EXPECT(assert_equal(someR, actual2)); + EXPECT(actualMap2 == expected); } /* ************************************************************************* */ From 107bcd8bb4294bf4d20d3e114a1baab59eeb873c Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 12 Oct 2014 22:04:40 +0200 Subject: [PATCH 07/35] Going forwards, we default to reverse :-) --- gtsam_unstable/nonlinear/Expression.h | 10 ++-------- .../nonlinear/tests/testExpression.cpp | 17 +++-------------- .../nonlinear/tests/testExpressionFactor.cpp | 9 +++++++++ 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression.h b/gtsam_unstable/nonlinear/Expression.h index 147804fb1..913a2b037 100644 --- a/gtsam_unstable/nonlinear/Expression.h +++ b/gtsam_unstable/nonlinear/Expression.h @@ -109,10 +109,8 @@ public: } /// Return value and derivatives, forward AD version - T forward(const Values& values, JacobianMap& jacobians) const { - Augmented augmented = root_->forward(values); - jacobians = augmented.jacobians(); - return augmented.value(); + Augmented forward(const Values& values) const { + return root_->forward(values); } // Return size needed for memory buffer in traceExecution @@ -143,11 +141,7 @@ public: /// Return value and derivatives T value(const Values& values, JacobianMap& jacobians) const { -#ifdef EXPRESSION_FORWARD_AD - return forward(values, jacobians); -#else return reverse(values, jacobians); -#endif } const boost::shared_ptr >& root() const { diff --git a/gtsam_unstable/nonlinear/tests/testExpression.cpp b/gtsam_unstable/nonlinear/tests/testExpression.cpp index 38297f156..bf13749b9 100644 --- a/gtsam_unstable/nonlinear/tests/testExpression.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpression.cpp @@ -61,10 +61,9 @@ TEST(Expression, leaf) { JacobianMap expected; expected[100] = eye(3); - JacobianMap actualMap1; - Rot3 actual1 = R.forward(values, actualMap1); - EXPECT(assert_equal(someR, actual1)); - EXPECT(actualMap1 == expected); + Augmented actual1 = R.forward(values); + EXPECT(assert_equal(someR, actual1.value())); + EXPECT(actual1.jacobians() == expected); JacobianMap actualMap2; Rot3 actual2 = R.reverse(values, actualMap2); @@ -126,16 +125,6 @@ TEST(Expression, keys_tree) { EXPECT(expectedKeys == tree::uv_hat.keys()); } /* ************************************************************************* */ -// keys -TEST(Expression, block_tree) { -// // Check VerticalBlockMatrix -// size_t dimensions[3] = { 6, 3, 5 }; -// Matrix matrix(2, 14); -// VerticalBlockMatrix expected(dimensions, matrix), actual = -// tree::uv_hat.verticalBlockMatrix(); -// EXPECT( assert_equal(expected, *jf, 1e-9)); -} -/* ************************************************************************* */ TEST(Expression, compose1) { diff --git a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp index eb4f1e52e..015a4ca6e 100644 --- a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp @@ -260,6 +260,15 @@ TEST(ExpressionFactor, tree) { Point2_ xy_hat(PinholeCamera::project_to_camera, p_cam); Point2_ uv_hat(K, &Cal3_S2::uncalibrate, xy_hat); + // Compare reverse and forward + { + JacobianMap expectedMap; // via reverse + Point2 expectedValue = uv_hat.reverse(values, expectedMap); + Augmented actual = uv_hat.forward(values); + EXPECT(assert_equal(expectedValue, actual.value())); + EXPECT(actual.jacobians() == expectedMap); + } + // Create factor and check value, dimension, linearization ExpressionFactor f(model, measured, uv_hat); EXPECT_DOUBLES_EQUAL(expected_error, f.error(values), 1e-9); From 408be628d20106ff2ac646c6488fb86841ccfea5 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 12 Oct 2014 22:17:21 +0200 Subject: [PATCH 08/35] Small change in meta-programming, big improvement in clarity --- gtsam_unstable/nonlinear/Expression-inl.h | 22 ++++++------------- .../nonlinear/tests/testExpressionFactor.cpp | 13 +++++------ 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index ac97de40e..30ab3ca4c 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -65,6 +65,7 @@ void move(JacobianMap& jacobians, std::vector& H) { */ template struct CallRecord { + static size_t const N = 0; virtual void print(const std::string& indent) const { } virtual void startReverseAD(JacobianMap& jacobians) const { @@ -205,12 +206,11 @@ struct Argument { * C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost * and Beyond. Pearson Education. */ -template -struct Record: Argument, More { +template +struct Record: Argument, More { typedef T return_type; - typedef typename AN::type A; - const static size_t N = AN::value; + static size_t const N = More::N + 1; typedef Argument This; /// Print to std::cout @@ -242,14 +242,6 @@ struct Record: Argument, More { } }; -/// Meta-function for generating a numbered type -template -struct Numbered { - typedef A type; - typedef size_t value_type; - static const size_t value = N; -}; - /// Recursive Record class Generator template struct GenerateRecord { @@ -559,7 +551,7 @@ public: } /// CallRecord structure for reverse AD - typedef boost::mpl::vector > Arguments; + typedef boost::mpl::vector Arguments; typedef typename GenerateRecord::type Record; /// Construct an execution trace for reverse AD @@ -636,7 +628,7 @@ public: } /// CallRecord structure for reverse AD - typedef boost::mpl::vector, Numbered > Arguments; + typedef boost::mpl::vector Arguments; typedef typename GenerateRecord::type Record; /// Construct an execution trace for reverse AD @@ -729,7 +721,7 @@ public: } /// CallRecord structure for reverse AD - typedef boost::mpl::vector, Numbered, Numbered > Arguments; + typedef boost::mpl::vector Arguments; typedef typename GenerateRecord::type Record; /// Construct an execution trace for reverse AD diff --git a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp index 015a4ca6e..9b8c8bac3 100644 --- a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp @@ -262,11 +262,11 @@ TEST(ExpressionFactor, tree) { // Compare reverse and forward { - JacobianMap expectedMap; // via reverse - Point2 expectedValue = uv_hat.reverse(values, expectedMap); - Augmented actual = uv_hat.forward(values); - EXPECT(assert_equal(expectedValue, actual.value())); - EXPECT(actual.jacobians() == expectedMap); + JacobianMap expectedMap; // via reverse + Point2 expectedValue = uv_hat.reverse(values, expectedMap); + Augmented actual = uv_hat.forward(values); + EXPECT(assert_equal(expectedValue, actual.value())); + EXPECT(actual.jacobians() == expectedMap); } // Create factor and check value, dimension, linearization @@ -435,8 +435,7 @@ namespace mpl = boost::mpl; #include template struct Incomplete; -typedef mpl::vector, Numbered, - Numbered > MyTypes; +typedef mpl::vector MyTypes; typedef GenerateRecord::type Generated; //Incomplete incomplete; //BOOST_MPL_ASSERT((boost::is_same< Matrix25, Generated::JacobianTA >)); From ef21a4ba4aac1dae15b7bb040889c0344c788f22 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 12 Oct 2014 23:03:33 +0200 Subject: [PATCH 09/35] Major re-org in preparation of recursive Functional nodes --- gtsam_unstable/nonlinear/Expression-inl.h | 418 ++++++++++-------- .../nonlinear/tests/testExpressionFactor.cpp | 9 +- 2 files changed, 232 insertions(+), 195 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index 30ab3ca4c..fccb517c3 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -54,6 +54,114 @@ void move(JacobianMap& jacobians, std::vector& H) { it->second.swap(H[j++]); } +//----------------------------------------------------------------------------- +/** + * Value and Jacobians + */ +template +class Augmented { + +private: + + T value_; + JacobianMap jacobians_; + + typedef std::pair Pair; + + /// Insert terms into jacobians_, adding if already exists + void add(const JacobianMap& terms) { + BOOST_FOREACH(const Pair& term, terms) { + JacobianMap::iterator it = jacobians_.find(term.first); + if (it != jacobians_.end()) + it->second += term.second; + else + jacobians_[term.first] = term.second; + } + } + + /// Insert terms into jacobians_, premultiplying by H, adding if already exists + void add(const Matrix& H, const JacobianMap& terms) { + BOOST_FOREACH(const Pair& term, terms) { + JacobianMap::iterator it = jacobians_.find(term.first); + if (it != jacobians_.end()) + it->second += H * term.second; + else + jacobians_[term.first] = H * term.second; + } + } + +public: + + /// Construct value that does not depend on anything + Augmented(const T& t) : + value_(t) { + } + + /// Construct value dependent on a single key + Augmented(const T& t, Key key) : + value_(t) { + size_t n = t.dim(); + jacobians_[key] = Eigen::MatrixXd::Identity(n, n); + } + + /// Construct value, pre-multiply jacobians by dTdA + Augmented(const T& t, const Matrix& dTdA, const JacobianMap& jacobians) : + value_(t) { + add(dTdA, jacobians); + } + + /// Construct value, pre-multiply jacobians + Augmented(const T& t, const Matrix& dTdA1, const JacobianMap& jacobians1, + const Matrix& dTdA2, const JacobianMap& jacobians2) : + value_(t) { + add(dTdA1, jacobians1); + add(dTdA2, jacobians2); + } + + /// Construct value, pre-multiply jacobians + Augmented(const T& t, const Matrix& dTdA1, const JacobianMap& jacobians1, + const Matrix& dTdA2, const JacobianMap& jacobians2, const Matrix& dTdA3, + const JacobianMap& jacobians3) : + value_(t) { + add(dTdA1, jacobians1); + add(dTdA2, jacobians2); + add(dTdA3, jacobians3); + } + + /// Return value + const T& value() const { + return value_; + } + + /// Return jacobians + const JacobianMap& jacobians() const { + return jacobians_; + } + + /// Return jacobians + JacobianMap& jacobians() { + return jacobians_; + } + + /// Not dependent on any key + bool constant() const { + return jacobians_.empty(); + } + + /// debugging + void print(const KeyFormatter& keyFormatter = DefaultKeyFormatter) { + BOOST_FOREACH(const Pair& term, jacobians_) + std::cout << "(" << keyFormatter(term.first) << ", " << term.second.rows() + << "x" << term.second.cols() << ") "; + std::cout << std::endl; + } + + /// Move terms to array, destroys content + void move(std::vector& H) { + move(jacobians_, H); + } + +}; //----------------------------------------------------------------------------- /** @@ -188,195 +296,6 @@ struct Select<2, A> { } }; -//----------------------------------------------------------------------------- -/** - * Record the evaluation of a single argument in a functional expression - * Building block for Recursive Record Class - */ -template -struct Argument { - typedef Eigen::Matrix JacobianTA; - ExecutionTrace trace; - JacobianTA dTdA; -}; - -/** - * Recursive Record Class for Functional Expressions - * Abrahams, David; Gurtovoy, Aleksey (2004-12-10). - * C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost - * and Beyond. Pearson Education. - */ -template -struct Record: Argument, More { - - typedef T return_type; - static size_t const N = More::N + 1; - typedef Argument This; - - /// Print to std::cout - virtual void print(const std::string& indent) const { - More::print(indent); - static const Eigen::IOFormat matlab(0, 1, " ", "; ", "", "", "[", "]"); - std::cout << This::dTdA.format(matlab) << std::endl; - This::trace.print(indent); - } - - /// Start the reverse AD process - virtual void startReverseAD(JacobianMap& jacobians) const { - More::startReverseAD(jacobians); - Select::reverseAD(This::trace, This::dTdA, jacobians); - } - - /// Given df/dT, multiply in dT/dA and continue reverse AD process - virtual void reverseAD(const Matrix& dFdT, JacobianMap& jacobians) const { - More::reverseAD(dFdT, jacobians); - This::trace.reverseAD(dFdT * This::dTdA, jacobians); - } - - /// Version specialized to 2-dimensional output - typedef Eigen::Matrix Jacobian2T; - virtual void reverseAD2(const Jacobian2T& dFdT, - JacobianMap& jacobians) const { - More::reverseAD2(dFdT, jacobians); - This::trace.reverseAD2(dFdT * This::dTdA, jacobians); - } -}; - -/// Recursive Record class Generator -template -struct GenerateRecord { - typedef typename boost::mpl::fold, - Record >::type type; -}; - -/// Access Argument -template -Argument& argument(Record& record) { - return static_cast&>(record); -} - -/// Access Trace -template -ExecutionTrace& getTrace(Record* record) { - return argument(*record).trace; -} - -/// Access Jacobian -template -Eigen::Matrix& jacobian( - Record* record) { - return argument(*record).dTdA; -} - -//----------------------------------------------------------------------------- -/** - * Value and Jacobians - */ -template -class Augmented { - -private: - - T value_; - JacobianMap jacobians_; - - typedef std::pair Pair; - - /// Insert terms into jacobians_, adding if already exists - void add(const JacobianMap& terms) { - BOOST_FOREACH(const Pair& term, terms) { - JacobianMap::iterator it = jacobians_.find(term.first); - if (it != jacobians_.end()) - it->second += term.second; - else - jacobians_[term.first] = term.second; - } - } - - /// Insert terms into jacobians_, premultiplying by H, adding if already exists - void add(const Matrix& H, const JacobianMap& terms) { - BOOST_FOREACH(const Pair& term, terms) { - JacobianMap::iterator it = jacobians_.find(term.first); - if (it != jacobians_.end()) - it->second += H * term.second; - else - jacobians_[term.first] = H * term.second; - } - } - -public: - - /// Construct value that does not depend on anything - Augmented(const T& t) : - value_(t) { - } - - /// Construct value dependent on a single key - Augmented(const T& t, Key key) : - value_(t) { - size_t n = t.dim(); - jacobians_[key] = Eigen::MatrixXd::Identity(n, n); - } - - /// Construct value, pre-multiply jacobians by dTdA - Augmented(const T& t, const Matrix& dTdA, const JacobianMap& jacobians) : - value_(t) { - add(dTdA, jacobians); - } - - /// Construct value, pre-multiply jacobians - Augmented(const T& t, const Matrix& dTdA1, const JacobianMap& jacobians1, - const Matrix& dTdA2, const JacobianMap& jacobians2) : - value_(t) { - add(dTdA1, jacobians1); - add(dTdA2, jacobians2); - } - - /// Construct value, pre-multiply jacobians - Augmented(const T& t, const Matrix& dTdA1, const JacobianMap& jacobians1, - const Matrix& dTdA2, const JacobianMap& jacobians2, const Matrix& dTdA3, - const JacobianMap& jacobians3) : - value_(t) { - add(dTdA1, jacobians1); - add(dTdA2, jacobians2); - add(dTdA3, jacobians3); - } - - /// Return value - const T& value() const { - return value_; - } - - /// Return jacobians - const JacobianMap& jacobians() const { - return jacobians_; - } - - /// Return jacobians - JacobianMap& jacobians() { - return jacobians_; - } - - /// Not dependent on any key - bool constant() const { - return jacobians_.empty(); - } - - /// debugging - void print(const KeyFormatter& keyFormatter = DefaultKeyFormatter) { - BOOST_FOREACH(const Pair& term, jacobians_) - std::cout << "(" << keyFormatter(term.first) << ", " << term.second.rows() - << "x" << term.second.cols() << ") "; - std::cout << std::endl; - } - - /// Move terms to array, destroys content - void move(std::vector& H) { - move(jacobians_, H); - } - -}; - //----------------------------------------------------------------------------- /** * Expression node. The superclass for objects that do the heavy lifting @@ -388,6 +307,10 @@ public: template class ExpressionNode { +public: + + static size_t const N = 0; // number of arguments + protected: size_t traceSize_; @@ -505,6 +428,123 @@ public: }; +//----------------------------------------------------------------------------- +/** + * Building block for Recursive Record Class + * Records the evaluation of a single argument in a functional expression + * The integer argument N is to guarantee a unique type signature, + * so we are guaranteed to be able to extract their values by static cast. + */ +template +struct JacobianTrace { + typedef Eigen::Matrix JacobianTA; + ExecutionTrace trace; + JacobianTA dTdA; +}; + +/** + * Recursive Record Class for Functional Expressions + * Abrahams, David; Gurtovoy, Aleksey (2004-12-10). + * C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost + * and Beyond. Pearson Education. + */ +template +struct Record: JacobianTrace, Base { + + typedef T return_type; + static size_t const N = Base::N + 1; + typedef JacobianTrace This; + + /// Print to std::cout + virtual void print(const std::string& indent) const { + Base::print(indent); + static const Eigen::IOFormat matlab(0, 1, " ", "; ", "", "", "[", "]"); + std::cout << This::dTdA.format(matlab) << std::endl; + This::trace.print(indent); + } + + /// Start the reverse AD process + virtual void startReverseAD(JacobianMap& jacobians) const { + Base::startReverseAD(jacobians); + Select::reverseAD(This::trace, This::dTdA, jacobians); + } + + /// Given df/dT, multiply in dT/dA and continue reverse AD process + virtual void reverseAD(const Matrix& dFdT, JacobianMap& jacobians) const { + Base::reverseAD(dFdT, jacobians); + This::trace.reverseAD(dFdT * This::dTdA, jacobians); + } + + /// Version specialized to 2-dimensional output + typedef Eigen::Matrix Jacobian2T; + virtual void reverseAD2(const Jacobian2T& dFdT, + JacobianMap& jacobians) const { + Base::reverseAD2(dFdT, jacobians); + This::trace.reverseAD2(dFdT * This::dTdA, jacobians); + } +}; + +/// Recursive Record class Generator +template +struct GenerateRecord { + typedef typename boost::mpl::fold, + Record >::type type; +}; + +/// Access JacobianTrace +template +JacobianTrace& jacobianTrace( + Record& record) { + return static_cast&>(record); +} + +/// Access Trace +template +ExecutionTrace& getTrace(Record* record) { + return jacobianTrace(*record).trace; +} + +/// Access Jacobian +template +Eigen::Matrix& jacobian( + Record* record) { + return jacobianTrace(*record).dTdA; +} + +//----------------------------------------------------------------------------- +/** + * Building block for Recursive FunctionalNode Class + */ +template +struct Argument { + boost::shared_ptr > expressionA_; +}; + +/** + * Recursive Definition of Functional ExpressionNode + */ +template +struct FunctionalNode: Argument, Base { + + typedef T return_type; + static size_t const N = Base::N + 1; + typedef Argument This; + +}; + +/// Recursive GenerateFunctionalNode class Generator +template +struct GenerateFunctionalNode { + typedef typename boost::mpl::fold, + Record >::type type; +}; + +/// Access Argument +template +Argument& argument(Record& record) { + return static_cast&>(record); +} + //----------------------------------------------------------------------------- /// Unary Function Expression template diff --git a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp index 9b8c8bac3..82b03c0e4 100644 --- a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp @@ -175,10 +175,8 @@ TEST(ExpressionFactor, binary) { // Check matrices boost::optional r = trace.record(); CHECK(r); - EXPECT( - assert_equal(expected25, (Matrix) static_cast*> (*r)->dTdA, 1e-9)); - EXPECT( - assert_equal(expected22, (Matrix) static_cast*> (*r)->dTdA, 1e-9)); + EXPECT(assert_equal(expected25, (Matrix) jacobian(*r), 1e-9)); + EXPECT(assert_equal(expected22, (Matrix) jacobian(*r), 1e-9)); } /* ************************************************************************* */ // Unary(Binary(Leaf,Leaf)) @@ -224,8 +222,7 @@ TEST(ExpressionFactor, shallow) { // Check matrices boost::optional r = trace.record(); CHECK(r); - EXPECT( - assert_equal(expected23, (Matrix)static_cast*>(*r)->dTdA, 1e-9)); + EXPECT(assert_equal(expected23, (Matrix)jacobian(*r), 1e-9)); // Linearization ExpressionFactor f2(model, measured, expression); From 55cc4ba56c01f292f538e9fc2384ee36d9cb2a82 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 12 Oct 2014 23:31:48 +0200 Subject: [PATCH 10/35] Switched names of fold result and meta-function that is folded over --- gtsam_unstable/nonlinear/Expression-inl.h | 50 ++++++++++--------- .../nonlinear/tests/testExpressionFactor.cpp | 2 +- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index fccb517c3..3f090c881 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -449,7 +449,7 @@ struct JacobianTrace { * and Beyond. Pearson Education. */ template -struct Record: JacobianTrace, Base { +struct GenerateRecord: JacobianTrace, Base { typedef T return_type; static size_t const N = Base::N + 1; @@ -486,9 +486,8 @@ struct Record: JacobianTrace, Base { /// Recursive Record class Generator template -struct GenerateRecord { - typedef typename boost::mpl::fold, - Record >::type type; +struct Record: public boost::mpl::fold, + GenerateRecord >::type { }; /// Access JacobianTrace @@ -517,14 +516,14 @@ Eigen::Matrix& jacobian( */ template struct Argument { - boost::shared_ptr > expressionA_; + boost::shared_ptr > expression; }; /** * Recursive Definition of Functional ExpressionNode */ template -struct FunctionalNode: Argument, Base { +struct GenerateFunctionalNode: Argument, Base { typedef T return_type; static size_t const N = Base::N + 1; @@ -534,9 +533,8 @@ struct FunctionalNode: Argument, Base { /// Recursive GenerateFunctionalNode class Generator template -struct GenerateFunctionalNode { - typedef typename boost::mpl::fold, - Record >::type type; +struct FunctionalNode: public boost::mpl::fold, + GenerateFunctionalNode >::type { }; /// Access Argument @@ -545,10 +543,17 @@ Argument& argument(Record& record) { return static_cast&>(record); } +/// Access Expression +template +ExecutionTrace& expression(Record* record) { + return argument(*record).expression; +} + //----------------------------------------------------------------------------- + /// Unary Function Expression template -class UnaryExpression: public ExpressionNode { +class UnaryExpression: public FunctionalNode > { public: @@ -562,8 +567,8 @@ private: /// Constructor with a unary function f, and input argument e UnaryExpression(Function f, const Expression& e1) : - ExpressionNode(sizeof(Record) + e1.traceSize()), // function_(f), expressionA1_(e1.root()) { + ExpressionNode::traceSize_ = sizeof(Record) + e1.traceSize(); } friend class Expression ; @@ -592,7 +597,7 @@ public: /// CallRecord structure for reverse AD typedef boost::mpl::vector Arguments; - typedef typename GenerateRecord::type Record; + typedef Record Record; /// Construct an execution trace for reverse AD virtual T traceExecution(const Values& values, ExecutionTrace& trace, @@ -630,8 +635,9 @@ private: /// Constructor with a binary function f, and two input arguments BinaryExpression(Function f, // const Expression& e1, const Expression& e2) : - ExpressionNode(sizeof(Record) + e1.traceSize() + e2.traceSize()), function_( - f), expressionA1_(e1.root()), expressionA2_(e2.root()) { + function_(f), expressionA1_(e1.root()), expressionA2_(e2.root()) { + ExpressionNode::traceSize_ = // + sizeof(Record) + e1.traceSize() + e2.traceSize(); } friend class Expression ; @@ -669,7 +675,7 @@ public: /// CallRecord structure for reverse AD typedef boost::mpl::vector Arguments; - typedef typename GenerateRecord::type Record; + typedef Record Record; /// Construct an execution trace for reverse AD /// The raw buffer is [Record | A1 raw | A2 raw] @@ -711,14 +717,12 @@ private: boost::shared_ptr > expressionA3_; /// Constructor with a ternary function f, and three input arguments - TernaryExpression( - Function f, // - const Expression& e1, const Expression& e2, - const Expression& e3) : - ExpressionNode( - sizeof(Record) + e1.traceSize() + e2.traceSize() + e3.traceSize()), function_( - f), expressionA1_(e1.root()), expressionA2_(e2.root()), expressionA3_( + TernaryExpression(Function f, const Expression& e1, + const Expression& e2, const Expression& e3) : + function_(f), expressionA1_(e1.root()), expressionA2_(e2.root()), expressionA3_( e3.root()) { + ExpressionNode::traceSize_ = // + sizeof(Record) + e1.traceSize() + e2.traceSize() + e3.traceSize(); } friend class Expression ; @@ -762,7 +766,7 @@ public: /// CallRecord structure for reverse AD typedef boost::mpl::vector Arguments; - typedef typename GenerateRecord::type Record; + typedef Record Record; /// Construct an execution trace for reverse AD virtual T traceExecution(const Values& values, ExecutionTrace& trace, diff --git a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp index 82b03c0e4..a7923b157 100644 --- a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp @@ -433,7 +433,7 @@ namespace mpl = boost::mpl; template struct Incomplete; typedef mpl::vector MyTypes; -typedef GenerateRecord::type Generated; +typedef Record Generated; //Incomplete incomplete; //BOOST_MPL_ASSERT((boost::is_same< Matrix25, Generated::JacobianTA >)); BOOST_MPL_ASSERT((boost::is_same< Matrix2, Generated::Jacobian2T >)); From 8100d89094d31566774634b9eac33e7c4fca2f2d Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 12 Oct 2014 23:57:08 +0200 Subject: [PATCH 11/35] So much better as methods --- gtsam_unstable/nonlinear/Expression-inl.h | 66 ++++++++++--------- .../nonlinear/tests/testExpressionFactor.cpp | 7 +- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index 3f090c881..6fef90d38 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -488,28 +488,27 @@ struct GenerateRecord: JacobianTrace, Base { template struct Record: public boost::mpl::fold, GenerateRecord >::type { + + /// Access JacobianTrace + template + JacobianTrace& jacobianTrace() { + return static_cast&>(*this); + } + + /// Access Trace + template + ExecutionTrace& trace() { + return jacobianTrace().trace; + } + + /// Access Jacobian + template + Eigen::Matrix& jacobian() { + return jacobianTrace().dTdA; + } + }; -/// Access JacobianTrace -template -JacobianTrace& jacobianTrace( - Record& record) { - return static_cast&>(record); -} - -/// Access Trace -template -ExecutionTrace& getTrace(Record* record) { - return jacobianTrace(*record).trace; -} - -/// Access Jacobian -template -Eigen::Matrix& jacobian( - Record* record) { - return jacobianTrace(*record).dTdA; -} - //----------------------------------------------------------------------------- /** * Building block for Recursive FunctionalNode Class @@ -606,9 +605,10 @@ public: trace.setFunction(record); raw = (char*) (record + 1); - A1 a1 = expressionA1_->traceExecution(values, getTrace(record), raw); + A1 a1 = expressionA1_->traceExecution(values, + record->template trace(), raw); - return function_(a1, jacobian(record)); + return function_(a1, record->template jacobian()); } }; @@ -685,11 +685,14 @@ public: trace.setFunction(record); raw = (char*) (record + 1); - A1 a1 = expressionA1_->traceExecution(values, getTrace(record), raw); + A1 a1 = expressionA1_->traceExecution(values, + record->template trace(), raw); raw = raw + expressionA1_->traceSize(); - A2 a2 = expressionA2_->traceExecution(values, getTrace(record), raw); + A2 a2 = expressionA2_->traceExecution(values, + record->template trace(), raw); - return function_(a1, a2, jacobian(record), jacobian(record)); + return function_(a1, a2, record->template jacobian(), + record->template jacobian()); } }; @@ -775,14 +778,17 @@ public: trace.setFunction(record); raw = (char*) (record + 1); - A1 a1 = expressionA1_->traceExecution(values, getTrace(record), raw); + A1 a1 = expressionA1_->traceExecution(values, + record->template trace(), raw); raw = raw + expressionA1_->traceSize(); - A2 a2 = expressionA2_->traceExecution(values, getTrace(record), raw); + A2 a2 = expressionA2_->traceExecution(values, + record->template trace(), raw); raw = raw + expressionA2_->traceSize(); - A3 a3 = expressionA3_->traceExecution(values, getTrace(record), raw); + A3 a3 = expressionA3_->traceExecution(values, + record->template trace(), raw); - return function_(a1, a2, a3, jacobian(record), - jacobian(record), jacobian(record)); + return function_(a1, a2, a3, record->template jacobian(), + record->template jacobian(), record->template jacobian()); } }; diff --git a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp index a7923b157..16eb2fd7b 100644 --- a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp @@ -175,8 +175,9 @@ TEST(ExpressionFactor, binary) { // Check matrices boost::optional r = trace.record(); CHECK(r); - EXPECT(assert_equal(expected25, (Matrix) jacobian(*r), 1e-9)); - EXPECT(assert_equal(expected22, (Matrix) jacobian(*r), 1e-9)); + EXPECT( + assert_equal(expected25, (Matrix) (*r)-> jacobian(), 1e-9)); + EXPECT( assert_equal(expected22, (Matrix) (*r)->jacobian(), 1e-9)); } /* ************************************************************************* */ // Unary(Binary(Leaf,Leaf)) @@ -222,7 +223,7 @@ TEST(ExpressionFactor, shallow) { // Check matrices boost::optional r = trace.record(); CHECK(r); - EXPECT(assert_equal(expected23, (Matrix)jacobian(*r), 1e-9)); + EXPECT(assert_equal(expected23, (Matrix)(*r)->jacobian(), 1e-9)); // Linearization ExpressionFactor f2(model, measured, expression); From a9d9fcd241ae117179bc09ed7baa698a09cf22a7 Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 13 Oct 2014 00:31:03 +0200 Subject: [PATCH 12/35] FunctionalNode inherited for all three functional ExpressionNode sub-classes --- gtsam_unstable/nonlinear/Expression-inl.h | 121 +++++++++++----------- 1 file changed, 58 insertions(+), 63 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index 6fef90d38..a765177aa 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -489,22 +489,16 @@ template struct Record: public boost::mpl::fold, GenerateRecord >::type { - /// Access JacobianTrace - template - JacobianTrace& jacobianTrace() { - return static_cast&>(*this); - } - /// Access Trace template ExecutionTrace& trace() { - return jacobianTrace().trace; + return static_cast&>(*this).trace; } /// Access Jacobian template Eigen::Matrix& jacobian() { - return jacobianTrace().dTdA; + return static_cast&>(*this).dTdA; } }; @@ -534,20 +528,21 @@ struct GenerateFunctionalNode: Argument, Base { template struct FunctionalNode: public boost::mpl::fold, GenerateFunctionalNode >::type { + + /// Access Expression + template + boost::shared_ptr > expression() { + return static_cast &>(*this).expression; + } + + /// Access Expression, const version + template + boost::shared_ptr > expression() const { + return static_cast const &>(*this).expression; + } + }; -/// Access Argument -template -Argument& argument(Record& record) { - return static_cast&>(record); -} - -/// Access Expression -template -ExecutionTrace& expression(Record* record) { - return argument(*record).expression; -} - //----------------------------------------------------------------------------- /// Unary Function Expression @@ -562,11 +557,11 @@ public: private: Function function_; - boost::shared_ptr > expressionA1_; /// Constructor with a unary function f, and input argument e UnaryExpression(Function f, const Expression& e1) : - function_(f), expressionA1_(e1.root()) { + function_(f) { + this->template expression() = e1.root(); ExpressionNode::traceSize_ = sizeof(Record) + e1.traceSize(); } @@ -576,18 +571,18 @@ public: /// Return keys that play in this expression virtual std::set keys() const { - return expressionA1_->keys(); + return this->template expression()->keys(); } /// Return value virtual T value(const Values& values) const { - return function_(this->expressionA1_->value(values), boost::none); + return function_(this->template expression()->value(values), boost::none); } /// Return value and derivatives virtual Augmented forward(const Values& values) const { using boost::none; - Augmented argument = this->expressionA1_->forward(values); + Augmented argument = this->template expression()->forward(values); JacobianTA dTdA; T t = function_(argument.value(), argument.constant() ? none : boost::optional(dTdA)); @@ -605,7 +600,7 @@ public: trace.setFunction(record); raw = (char*) (record + 1); - A1 a1 = expressionA1_->traceExecution(values, + A1 a1 = this-> template expression()->traceExecution(values, record->template trace(), raw); return function_(a1, record->template jacobian()); @@ -616,7 +611,7 @@ public: /// Binary Expression template -class BinaryExpression: public ExpressionNode { +class BinaryExpression: public FunctionalNode > { public: @@ -629,13 +624,13 @@ public: private: Function function_; - boost::shared_ptr > expressionA1_; - boost::shared_ptr > expressionA2_; - /// Constructor with a binary function f, and two input arguments - BinaryExpression(Function f, // - const Expression& e1, const Expression& e2) : - function_(f), expressionA1_(e1.root()), expressionA2_(e2.root()) { + /// Constructor with a ternary function f, and three input arguments + BinaryExpression(Function f, const Expression& e1, + const Expression& e2) : + function_(f) { + this->template expression() = e1.root(); + this->template expression() = e2.root(); ExpressionNode::traceSize_ = // sizeof(Record) + e1.traceSize() + e2.traceSize(); } @@ -647,8 +642,8 @@ public: /// Return keys that play in this expression virtual std::set keys() const { - std::set keys1 = expressionA1_->keys(); - std::set keys2 = expressionA2_->keys(); + std::set keys1 = this->template expression()->keys(); + std::set keys2 = this->template expression()->keys(); keys1.insert(keys2.begin(), keys2.end()); return keys1; } @@ -656,15 +651,16 @@ public: /// Return value virtual T value(const Values& values) const { using boost::none; - return function_(this->expressionA1_->value(values), - this->expressionA2_->value(values), none, none); + return function_(this->template expression()->value(values), + this->template expression()->value(values), + none, none); } /// Return value and derivatives virtual Augmented forward(const Values& values) const { using boost::none; - Augmented a1 = this->expressionA1_->forward(values); - Augmented a2 = this->expressionA2_->forward(values); + Augmented a1 = this->template expression()->forward(values); + Augmented a2 = this->template expression()->forward(values); JacobianTA1 dTdA1; JacobianTA2 dTdA2; T t = function_(a1.value(), a2.value(), @@ -678,30 +674,29 @@ public: typedef Record Record; /// Construct an execution trace for reverse AD - /// The raw buffer is [Record | A1 raw | A2 raw] virtual T traceExecution(const Values& values, ExecutionTrace& trace, char* raw) const { Record* record = new (raw) Record(); trace.setFunction(record); raw = (char*) (record + 1); - A1 a1 = expressionA1_->traceExecution(values, + A1 a1 = this->template expression()->traceExecution(values, record->template trace(), raw); - raw = raw + expressionA1_->traceSize(); - A2 a2 = expressionA2_->traceExecution(values, + raw = raw + this->template expression()->traceSize(); + A2 a2 = this->template expression()->traceExecution(values, record->template trace(), raw); + raw = raw + this->template expression()->traceSize(); return function_(a1, a2, record->template jacobian(), record->template jacobian()); } - }; //----------------------------------------------------------------------------- /// Ternary Expression template -class TernaryExpression: public ExpressionNode { +class TernaryExpression: public FunctionalNode > { public: @@ -715,15 +710,14 @@ public: private: Function function_; - boost::shared_ptr > expressionA1_; - boost::shared_ptr > expressionA2_; - boost::shared_ptr > expressionA3_; /// Constructor with a ternary function f, and three input arguments TernaryExpression(Function f, const Expression& e1, const Expression& e2, const Expression& e3) : - function_(f), expressionA1_(e1.root()), expressionA2_(e2.root()), expressionA3_( - e3.root()) { + function_(f) { + this->template expression() = e1.root(); + this->template expression() = e2.root(); + this->template expression() = e3.root(); ExpressionNode::traceSize_ = // sizeof(Record) + e1.traceSize() + e2.traceSize() + e3.traceSize(); } @@ -734,9 +728,9 @@ public: /// Return keys that play in this expression virtual std::set keys() const { - std::set keys1 = expressionA1_->keys(); - std::set keys2 = expressionA2_->keys(); - std::set keys3 = expressionA3_->keys(); + std::set keys1 = this->template expression()->keys(); + std::set keys2 = this->template expression()->keys(); + std::set keys3 = this->template expression()->keys(); keys2.insert(keys3.begin(), keys3.end()); keys1.insert(keys2.begin(), keys2.end()); return keys1; @@ -745,17 +739,18 @@ public: /// Return value virtual T value(const Values& values) const { using boost::none; - return function_(this->expressionA1_->value(values), - this->expressionA2_->value(values), this->expressionA3_->value(values), + return function_(this->template expression()->value(values), + this->template expression()->value(values), + this->template expression()->value(values), none, none, none); } /// Return value and derivatives virtual Augmented forward(const Values& values) const { using boost::none; - Augmented a1 = this->expressionA1_->forward(values); - Augmented a2 = this->expressionA2_->forward(values); - Augmented a3 = this->expressionA3_->forward(values); + Augmented a1 = this->template expression()->forward(values); + Augmented a2 = this->template expression()->forward(values); + Augmented a3 = this->template expression()->forward(values); JacobianTA1 dTdA1; JacobianTA2 dTdA2; JacobianTA3 dTdA3; @@ -778,13 +773,13 @@ public: trace.setFunction(record); raw = (char*) (record + 1); - A1 a1 = expressionA1_->traceExecution(values, + A1 a1 = this->template expression()->traceExecution(values, record->template trace(), raw); - raw = raw + expressionA1_->traceSize(); - A2 a2 = expressionA2_->traceExecution(values, + raw = raw + this->template expression()->traceSize(); + A2 a2 = this->template expression()->traceExecution(values, record->template trace(), raw); - raw = raw + expressionA2_->traceSize(); - A3 a3 = expressionA3_->traceExecution(values, + raw = raw + this->template expression()->traceSize(); + A3 a3 = this->template expression()->traceExecution(values, record->template trace(), raw); return function_(a1, a2, a3, record->template jacobian(), From 2e8d868cd2bbab637078051ca51cde70e689e9fd Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 13 Oct 2014 00:37:46 +0200 Subject: [PATCH 13/35] keys have been implemented --- gtsam_unstable/nonlinear/Expression-inl.h | 43 +++++++---------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index a765177aa..f9401bf0f 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -327,7 +327,11 @@ public: } /// Return keys that play in this expression as a set - virtual std::set keys() const = 0; + virtual std::set keys() const { + std::set keys; + return keys; + } + // Return size needed for memory buffer in traceExecution size_t traceSize() const { @@ -362,12 +366,6 @@ class ConstantExpression: public ExpressionNode { public: - /// Return keys that play in this expression, i.e., the empty set - virtual std::set keys() const { - std::set keys; - return keys; - } - /// Return value virtual T value(const Values& values) const { return constant_; @@ -522,6 +520,14 @@ struct GenerateFunctionalNode: Argument, Base { static size_t const N = Base::N + 1; typedef Argument This; + /// Return keys that play in this expression + virtual std::set keys() const { + std::set keys = Base::keys(); + std::set myKeys = This::expression->keys(); + keys.insert(myKeys.begin(), myKeys.end()); + return keys; + } + }; /// Recursive GenerateFunctionalNode class Generator @@ -569,11 +575,6 @@ private: public: - /// Return keys that play in this expression - virtual std::set keys() const { - return this->template expression()->keys(); - } - /// Return value virtual T value(const Values& values) const { return function_(this->template expression()->value(values), boost::none); @@ -640,14 +641,6 @@ private: public: - /// Return keys that play in this expression - virtual std::set keys() const { - std::set keys1 = this->template expression()->keys(); - std::set keys2 = this->template expression()->keys(); - keys1.insert(keys2.begin(), keys2.end()); - return keys1; - } - /// Return value virtual T value(const Values& values) const { using boost::none; @@ -726,16 +719,6 @@ private: public: - /// Return keys that play in this expression - virtual std::set keys() const { - std::set keys1 = this->template expression()->keys(); - std::set keys2 = this->template expression()->keys(); - std::set keys3 = this->template expression()->keys(); - keys2.insert(keys3.begin(), keys3.end()); - keys1.insert(keys2.begin(), keys2.end()); - return keys1; - } - /// Return value virtual T value(const Values& values) const { using boost::none; From 7f621af54a70353cc55de9c11aef8689416e22b1 Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 13 Oct 2014 00:57:11 +0200 Subject: [PATCH 14/35] Fixed bug --- gtsam_unstable/nonlinear/Expression-inl.h | 28 +++++++++++------------ 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index f9401bf0f..75407fb54 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -332,7 +332,6 @@ public: return keys; } - // Return size needed for memory buffer in traceExecution size_t traceSize() const { return traceSize_; @@ -537,8 +536,8 @@ struct FunctionalNode: public boost::mpl::fold, /// Access Expression template - boost::shared_ptr > expression() { - return static_cast &>(*this).expression; + void reset(const boost::shared_ptr >& ptr) { + static_cast &>(*this).expression = ptr; } /// Access Expression, const version @@ -567,7 +566,7 @@ private: /// Constructor with a unary function f, and input argument e UnaryExpression(Function f, const Expression& e1) : function_(f) { - this->template expression() = e1.root(); + this->template reset(e1.root()); ExpressionNode::traceSize_ = sizeof(Record) + e1.traceSize(); } @@ -630,8 +629,8 @@ private: BinaryExpression(Function f, const Expression& e1, const Expression& e2) : function_(f) { - this->template expression() = e1.root(); - this->template expression() = e2.root(); + this->template reset(e1.root()); + this->template reset(e2.root()); ExpressionNode::traceSize_ = // sizeof(Record) + e1.traceSize() + e2.traceSize(); } @@ -645,8 +644,8 @@ public: virtual T value(const Values& values) const { using boost::none; return function_(this->template expression()->value(values), - this->template expression()->value(values), - none, none); + this->template expression()->value(values), + none, none); } /// Return value and derivatives @@ -678,7 +677,6 @@ public: raw = raw + this->template expression()->traceSize(); A2 a2 = this->template expression()->traceExecution(values, record->template trace(), raw); - raw = raw + this->template expression()->traceSize(); return function_(a1, a2, record->template jacobian(), record->template jacobian()); @@ -708,9 +706,9 @@ private: TernaryExpression(Function f, const Expression& e1, const Expression& e2, const Expression& e3) : function_(f) { - this->template expression() = e1.root(); - this->template expression() = e2.root(); - this->template expression() = e3.root(); + this->template reset(e1.root()); + this->template reset(e2.root()); + this->template reset(e3.root()); ExpressionNode::traceSize_ = // sizeof(Record) + e1.traceSize() + e2.traceSize() + e3.traceSize(); } @@ -723,9 +721,9 @@ public: virtual T value(const Values& values) const { using boost::none; return function_(this->template expression()->value(values), - this->template expression()->value(values), - this->template expression()->value(values), - none, none, none); + this->template expression()->value(values), + this->template expression()->value(values), + none, none, none); } /// Return value and derivatives From 7848d749280929f36d1d1bf97d132a6806c4f7c3 Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 13 Oct 2014 08:49:12 +0200 Subject: [PATCH 15/35] Detailed explanation of recursive class composition pattern. Jacobian type now defined in argument. --- gtsam_unstable/nonlinear/Expression-inl.h | 79 +++++++++++++++++------ 1 file changed, 61 insertions(+), 18 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index 75407fb54..2393493d0 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -441,9 +441,6 @@ struct JacobianTrace { /** * Recursive Record Class for Functional Expressions - * Abrahams, David; Gurtovoy, Aleksey (2004-12-10). - * C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost - * and Beyond. Pearson Education. */ template struct GenerateRecord: JacobianTrace, Base { @@ -501,23 +498,64 @@ struct Record: public boost::mpl::fold, }; //----------------------------------------------------------------------------- +// Below we use the "Class Composition" technique described in the book +// C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost +// and Beyond. Abrahams, David; Gurtovoy, Aleksey. Pearson Education. +// to recursively generate a class, that will be the base for function nodes. +// The class generated, for two arguments A1, A2, and A3 will be +// +// struct Base1 : Argument, ExpressionNode { +// ... storage related to A1 ... +// ... methods that work on A1 ... +// }; +// +// struct Base2 : Argument, Base1 { +// ... storage related to A2 ... +// ... methods that work on A2 and (recursively) on A2 ... +// }; +// +// struct Base2 : Argument, Base2 { +// ... storage related to A3 ... +// ... methods that work on A3 and (recursively) on A2 and A3 ... +// }; +// +// struct FunctionalNode : Base3 { +// Provides convenience access to storage in hierarchy by using +// static_cast &>(*this) +// } +// +// All this magic happens when we generate the Base3 base class of FunctionalNode +// by invoking boost::mpl::fold over the meta-function GenerateFunctionalNode +//----------------------------------------------------------------------------- + /** * Building block for Recursive FunctionalNode Class + * The integer argument N is to guarantee a unique type signature, + * so we are guaranteed to be able to extract their values by static cast. */ template struct Argument { + /// Fixed size Jacobian type for the argument A + typedef Eigen::Matrix JacobianTA; + + /// Expression that will generate value/derivatives for argument boost::shared_ptr > expression; }; +/// meta-function to access JacobianTA type +template +struct Jacobian { + typedef typename Argument::JacobianTA type; +}; + /** * Recursive Definition of Functional ExpressionNode */ template struct GenerateFunctionalNode: Argument, Base { - typedef T return_type; - static size_t const N = Base::N + 1; - typedef Argument This; + static size_t const N = Base::N + 1; ///< Number of arguments in hierarchy + typedef Argument This; ///< The storage we have direct access to /// Return keys that play in this expression virtual std::set keys() const { @@ -529,18 +567,20 @@ struct GenerateFunctionalNode: Argument, Base { }; -/// Recursive GenerateFunctionalNode class Generator +/** + * Recursive GenerateFunctionalNode class Generator + */ template struct FunctionalNode: public boost::mpl::fold, GenerateFunctionalNode >::type { - /// Access Expression + /// Reset expression shared pointer template void reset(const boost::shared_ptr >& ptr) { static_cast &>(*this).expression = ptr; } - /// Access Expression, const version + /// Access Expression shared pointer template boost::shared_ptr > expression() const { return static_cast const &>(*this).expression; @@ -554,10 +594,13 @@ struct FunctionalNode: public boost::mpl::fold, template class UnaryExpression: public FunctionalNode > { + /// The automatically generated Base class + typedef FunctionalNode > Base; + public: - typedef Eigen::Matrix JacobianTA; - typedef boost::function)> Function; + typedef typename Jacobian::type JacobianTA1; + typedef boost::function)> Function; private: @@ -583,9 +626,9 @@ public: virtual Augmented forward(const Values& values) const { using boost::none; Augmented argument = this->template expression()->forward(values); - JacobianTA dTdA; + JacobianTA1 dTdA; T t = function_(argument.value(), - argument.constant() ? none : boost::optional(dTdA)); + argument.constant() ? none : boost::optional(dTdA)); return Augmented(t, dTdA, argument.jacobians()); } @@ -615,8 +658,8 @@ class BinaryExpression: public FunctionalNode > { public: - typedef Eigen::Matrix JacobianTA1; - typedef Eigen::Matrix JacobianTA2; + typedef typename Jacobian::type JacobianTA1; + typedef typename Jacobian::type JacobianTA2; typedef boost::function< T(const A1&, const A2&, boost::optional, boost::optional)> Function; @@ -691,9 +734,9 @@ class TernaryExpression: public FunctionalNode public: - typedef Eigen::Matrix JacobianTA1; - typedef Eigen::Matrix JacobianTA2; - typedef Eigen::Matrix JacobianTA3; + typedef typename Jacobian::type JacobianTA1; + typedef typename Jacobian::type JacobianTA2; + typedef typename Jacobian::type JacobianTA3; typedef boost::function< T(const A1&, const A2&, const A3&, boost::optional, boost::optional, boost::optional)> Function; From 7fde47c48b739a316afe0c57b078102c92743802 Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 13 Oct 2014 09:25:06 +0200 Subject: [PATCH 16/35] No more JacobianTA typedefs -> all use Jacobian now. --- gtsam_unstable/nonlinear/Expression-inl.h | 71 ++++++++----------- gtsam_unstable/nonlinear/Expression.h | 5 +- .../nonlinear/tests/testExpressionFactor.cpp | 4 +- 3 files changed, 35 insertions(+), 45 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index 2393493d0..86a2bfa96 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -277,9 +277,9 @@ public: }; /// Primary template calls the generic Matrix reverseAD pipeline -template +template struct Select { - typedef Eigen::Matrix Jacobian; + typedef Eigen::Matrix Jacobian; static void reverseAD(const ExecutionTrace& trace, const Jacobian& dTdA, JacobianMap& jacobians) { trace.reverseAD(dTdA, jacobians); @@ -426,6 +426,13 @@ public: }; //----------------------------------------------------------------------------- +/// meta-function to generate fixed-size JacobianTA type +template +struct Jacobian { + typedef Eigen::Matrix type; + typedef boost::optional optional; +}; + /** * Building block for Recursive Record Class * Records the evaluation of a single argument in a functional expression @@ -434,9 +441,8 @@ public: */ template struct JacobianTrace { - typedef Eigen::Matrix JacobianTA; ExecutionTrace trace; - JacobianTA dTdA; + typename Jacobian::type dTdA; }; /** @@ -491,7 +497,7 @@ struct Record: public boost::mpl::fold, /// Access Jacobian template - Eigen::Matrix& jacobian() { + typename Jacobian::type& jacobian() { return static_cast&>(*this).dTdA; } @@ -535,19 +541,10 @@ struct Record: public boost::mpl::fold, */ template struct Argument { - /// Fixed size Jacobian type for the argument A - typedef Eigen::Matrix JacobianTA; - /// Expression that will generate value/derivatives for argument boost::shared_ptr > expression; }; -/// meta-function to access JacobianTA type -template -struct Jacobian { - typedef typename Argument::JacobianTA type; -}; - /** * Recursive Definition of Functional ExpressionNode */ @@ -599,8 +596,7 @@ class UnaryExpression: public FunctionalNode > { public: - typedef typename Jacobian::type JacobianTA1; - typedef boost::function)> Function; + typedef boost::function::optional)> Function; private: @@ -625,11 +621,10 @@ public: /// Return value and derivatives virtual Augmented forward(const Values& values) const { using boost::none; - Augmented argument = this->template expression()->forward(values); - JacobianTA1 dTdA; - T t = function_(argument.value(), - argument.constant() ? none : boost::optional(dTdA)); - return Augmented(t, dTdA, argument.jacobians()); + Augmented a1 = this->template expression()->forward(values); + typename Jacobian::type dTdA1; + T t = function_(a1.value(), a1.constant() ? none : typename Jacobian::optional(dTdA1)); + return Augmented(t, dTdA1, a1.jacobians()); } /// CallRecord structure for reverse AD @@ -658,11 +653,9 @@ class BinaryExpression: public FunctionalNode > { public: - typedef typename Jacobian::type JacobianTA1; - typedef typename Jacobian::type JacobianTA2; typedef boost::function< - T(const A1&, const A2&, boost::optional, - boost::optional)> Function; + T(const A1&, const A2&, typename Jacobian::optional, + typename Jacobian::optional)> Function; private: @@ -696,11 +689,11 @@ public: using boost::none; Augmented a1 = this->template expression()->forward(values); Augmented a2 = this->template expression()->forward(values); - JacobianTA1 dTdA1; - JacobianTA2 dTdA2; + typename Jacobian::type dTdA1; + typename Jacobian::type dTdA2; T t = function_(a1.value(), a2.value(), - a1.constant() ? none : boost::optional(dTdA1), - a2.constant() ? none : boost::optional(dTdA2)); + a1.constant() ? none : typename Jacobian::optional(dTdA1), + a2.constant() ? none : typename Jacobian::optional(dTdA2)); return Augmented(t, dTdA1, a1.jacobians(), dTdA2, a2.jacobians()); } @@ -734,12 +727,10 @@ class TernaryExpression: public FunctionalNode public: - typedef typename Jacobian::type JacobianTA1; - typedef typename Jacobian::type JacobianTA2; - typedef typename Jacobian::type JacobianTA3; typedef boost::function< - T(const A1&, const A2&, const A3&, boost::optional, - boost::optional, boost::optional)> Function; + T(const A1&, const A2&, const A3&, typename Jacobian::optional, + typename Jacobian::optional, + typename Jacobian::optional)> Function; private: @@ -775,13 +766,13 @@ public: Augmented a1 = this->template expression()->forward(values); Augmented a2 = this->template expression()->forward(values); Augmented a3 = this->template expression()->forward(values); - JacobianTA1 dTdA1; - JacobianTA2 dTdA2; - JacobianTA3 dTdA3; + typename Jacobian::type dTdA1; + typename Jacobian::type dTdA2; + typename Jacobian::type dTdA3; T t = function_(a1.value(), a2.value(), a3.value(), - a1.constant() ? none : boost::optional(dTdA1), - a2.constant() ? none : boost::optional(dTdA2), - a3.constant() ? none : boost::optional(dTdA3)); + a1.constant() ? none : typename Jacobian::optional(dTdA1), + a2.constant() ? none : typename Jacobian::optional(dTdA2), + a3.constant() ? none : typename Jacobian::optional(dTdA3)); return Augmented(t, dTdA1, a1.jacobians(), dTdA2, a2.jacobians(), dTdA3, a3.jacobians()); } diff --git a/gtsam_unstable/nonlinear/Expression.h b/gtsam_unstable/nonlinear/Expression.h index 913a2b037..502826f61 100644 --- a/gtsam_unstable/nonlinear/Expression.h +++ b/gtsam_unstable/nonlinear/Expression.h @@ -76,9 +76,8 @@ public: /// Construct a unary method expression template Expression(const Expression& expression1, - T (A1::*method)(const A2&, - boost::optional::JacobianTA1&>, - boost::optional::JacobianTA2&>) const, + T (A1::*method)(const A2&, typename Jacobian::optional, + typename Jacobian::optional) const, const Expression& expression2) { root_.reset( new BinaryExpression(boost::bind(method, _1, _2, _3, _4), diff --git a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp index 16eb2fd7b..c92853d33 100644 --- a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp @@ -150,8 +150,8 @@ TEST(ExpressionFactor, binary) { EXPECT_LONGS_EQUAL(8, sizeof(double)); EXPECT_LONGS_EQUAL(16, sizeof(ExecutionTrace)); EXPECT_LONGS_EQUAL(16, sizeof(ExecutionTrace)); - EXPECT_LONGS_EQUAL(2*5*8, sizeof(Binary::JacobianTA1)); - EXPECT_LONGS_EQUAL(2*2*8, sizeof(Binary::JacobianTA2)); + EXPECT_LONGS_EQUAL(2*5*8, sizeof(Jacobian::type)); + EXPECT_LONGS_EQUAL(2*2*8, sizeof(Jacobian::type)); size_t expectedRecordSize = 16 + 2 * 16 + 80 + 32; EXPECT_LONGS_EQUAL(expectedRecordSize, sizeof(Binary::Record)); From bc9e11f43c0c6536bc3d7f340190e5752bf70b3a Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 13 Oct 2014 10:10:46 +0200 Subject: [PATCH 17/35] Pre-big collapse: prototype recursively defined inner Record2 type --- gtsam_unstable/nonlinear/Expression-inl.h | 125 +++++++++++++++++----- 1 file changed, 99 insertions(+), 26 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index 86a2bfa96..e9addeec7 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -307,10 +307,6 @@ struct Select<2, A> { template class ExpressionNode { -public: - - static size_t const N = 0; // number of arguments - protected: size_t traceSize_; @@ -510,7 +506,7 @@ struct Record: public boost::mpl::fold, // to recursively generate a class, that will be the base for function nodes. // The class generated, for two arguments A1, A2, and A3 will be // -// struct Base1 : Argument, ExpressionNode { +// struct Base1 : Argument, FunctionalBase { // ... storage related to A1 ... // ... methods that work on A1 ... // }; @@ -535,7 +531,21 @@ struct Record: public boost::mpl::fold, //----------------------------------------------------------------------------- /** - * Building block for Recursive FunctionalNode Class + * Base case for recursive FunctionalNode class + */ +template +struct FunctionalBase: ExpressionNode { + static size_t const N = 0; // number of arguments + + typedef CallRecord Record2; + + /// Construct an execution trace for reverse AD + void trace(const Values& values, Record2* record, char*& raw) const { + } +}; + +/** + * Building block for recursive FunctionalNode class * The integer argument N is to guarantee a unique type signature, * so we are guaranteed to be able to extract their values by static cast. */ @@ -562,34 +572,91 @@ struct GenerateFunctionalNode: Argument, Base { return keys; } + /** + * Recursive Record Class for Functional Expressions + */ + struct Record2: JacobianTrace, Base::Record2 { + + typedef T return_type; + typedef JacobianTrace This; + + /// Print to std::cout + virtual void print(const std::string& indent) const { + Base::Record2::print(indent); + static const Eigen::IOFormat matlab(0, 1, " ", "; ", "", "", "[", "]"); + std::cout << This::dTdA.format(matlab) << std::endl; + This::trace.print(indent); + } + + /// Start the reverse AD process + virtual void startReverseAD(JacobianMap& jacobians) const { + Base::Record2::startReverseAD(jacobians); + Select::reverseAD(This::trace, This::dTdA, jacobians); + } + + /// Given df/dT, multiply in dT/dA and continue reverse AD process + virtual void reverseAD(const Matrix& dFdT, JacobianMap& jacobians) const { + Base::Record2::reverseAD(dFdT, jacobians); + This::trace.reverseAD(dFdT * This::dTdA, jacobians); + } + + /// Version specialized to 2-dimensional output + typedef Eigen::Matrix Jacobian2T; + virtual void reverseAD2(const Jacobian2T& dFdT, + JacobianMap& jacobians) const { + Base::Record2::reverseAD2(dFdT, jacobians); + This::trace.reverseAD2(dFdT * This::dTdA, jacobians); + } + }; + + /// Construct an execution trace for reverse AD + void trace(const Values& values, Record2* record, char*& raw) const { + Base::trace(values, record, raw); + A a = This::expression->traceExecution(values, record->Record2::This::trace, raw); + raw = raw + This::expression->traceSize(); + } }; /** * Recursive GenerateFunctionalNode class Generator */ template -struct FunctionalNode: public boost::mpl::fold, - GenerateFunctionalNode >::type { +struct FunctionalNode { + typedef typename boost::mpl::fold, + GenerateFunctionalNode >::type Base; - /// Reset expression shared pointer - template - void reset(const boost::shared_ptr >& ptr) { - static_cast &>(*this).expression = ptr; - } + struct type: public Base { - /// Access Expression shared pointer - template - boost::shared_ptr > expression() const { - return static_cast const &>(*this).expression; - } + /// Reset expression shared pointer + template + void reset(const boost::shared_ptr >& ptr) { + static_cast &>(*this).expression = ptr; + } + /// Access Expression shared pointer + template + boost::shared_ptr > expression() const { + return static_cast const &>(*this).expression; + } + + /// Construct an execution trace for reverse AD + virtual T traceExecution(const Values& values, ExecutionTrace& trace, + char* raw) const { + typename Base::Record2* record = new (raw) typename Base::Record2(); + trace.setFunction(record); + raw = (char*) (record + 1); + + this->trace(values, record, raw); + + return T(); // TODO + } + }; }; - //----------------------------------------------------------------------------- /// Unary Function Expression template -class UnaryExpression: public FunctionalNode > { +class UnaryExpression: public FunctionalNode >::type { /// The automatically generated Base class typedef FunctionalNode > Base; @@ -636,10 +703,11 @@ public: char* raw) const { Record* record = new (raw) Record(); trace.setFunction(record); - raw = (char*) (record + 1); - A1 a1 = this-> template expression()->traceExecution(values, + + A1 a1 = this->template expression()->traceExecution(values, record->template trace(), raw); + raw = raw + this->template expression()->traceSize(); return function_(a1, record->template jacobian()); } @@ -649,7 +717,7 @@ public: /// Binary Expression template -class BinaryExpression: public FunctionalNode > { +class BinaryExpression: public FunctionalNode >::type { public: @@ -706,13 +774,15 @@ public: char* raw) const { Record* record = new (raw) Record(); trace.setFunction(record); - raw = (char*) (record + 1); + A1 a1 = this->template expression()->traceExecution(values, record->template trace(), raw); raw = raw + this->template expression()->traceSize(); + A2 a2 = this->template expression()->traceExecution(values, record->template trace(), raw); + raw = raw + this->template expression()->traceSize(); return function_(a1, a2, record->template jacobian(), record->template jacobian()); @@ -723,7 +793,7 @@ public: /// Ternary Expression template -class TernaryExpression: public FunctionalNode > { +class TernaryExpression: public FunctionalNode >::type { public: @@ -786,16 +856,19 @@ public: char* raw) const { Record* record = new (raw) Record(); trace.setFunction(record); - raw = (char*) (record + 1); + A1 a1 = this->template expression()->traceExecution(values, record->template trace(), raw); raw = raw + this->template expression()->traceSize(); + A2 a2 = this->template expression()->traceExecution(values, record->template trace(), raw); raw = raw + this->template expression()->traceSize(); + A3 a3 = this->template expression()->traceExecution(values, record->template trace(), raw); + raw = raw + this->template expression()->traceSize(); return function_(a1, a2, a3, record->template jacobian(), record->template jacobian(), record->template jacobian()); From da0e5fe52fbe01aa9b6cc2ced3ed94e3b7598572 Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 13 Oct 2014 10:50:05 +0200 Subject: [PATCH 18/35] The great collapse: instead of two recursively defined classes, there is now only one. The Record class is now a (recursive) inner class. --- gtsam_unstable/nonlinear/Expression-inl.h | 163 ++++++------------ .../nonlinear/tests/testExpressionFactor.cpp | 62 +++---- 2 files changed, 76 insertions(+), 149 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index e9addeec7..e4606c243 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -38,6 +38,9 @@ struct TestBinaryExpression; namespace MPL = boost::mpl::placeholders; +class ExpressionFactorBinaryTest; +// Forward declare for testing + namespace gtsam { template @@ -421,84 +424,6 @@ public: }; -//----------------------------------------------------------------------------- -/// meta-function to generate fixed-size JacobianTA type -template -struct Jacobian { - typedef Eigen::Matrix type; - typedef boost::optional optional; -}; - -/** - * Building block for Recursive Record Class - * Records the evaluation of a single argument in a functional expression - * The integer argument N is to guarantee a unique type signature, - * so we are guaranteed to be able to extract their values by static cast. - */ -template -struct JacobianTrace { - ExecutionTrace trace; - typename Jacobian::type dTdA; -}; - -/** - * Recursive Record Class for Functional Expressions - */ -template -struct GenerateRecord: JacobianTrace, Base { - - typedef T return_type; - static size_t const N = Base::N + 1; - typedef JacobianTrace This; - - /// Print to std::cout - virtual void print(const std::string& indent) const { - Base::print(indent); - static const Eigen::IOFormat matlab(0, 1, " ", "; ", "", "", "[", "]"); - std::cout << This::dTdA.format(matlab) << std::endl; - This::trace.print(indent); - } - - /// Start the reverse AD process - virtual void startReverseAD(JacobianMap& jacobians) const { - Base::startReverseAD(jacobians); - Select::reverseAD(This::trace, This::dTdA, jacobians); - } - - /// Given df/dT, multiply in dT/dA and continue reverse AD process - virtual void reverseAD(const Matrix& dFdT, JacobianMap& jacobians) const { - Base::reverseAD(dFdT, jacobians); - This::trace.reverseAD(dFdT * This::dTdA, jacobians); - } - - /// Version specialized to 2-dimensional output - typedef Eigen::Matrix Jacobian2T; - virtual void reverseAD2(const Jacobian2T& dFdT, - JacobianMap& jacobians) const { - Base::reverseAD2(dFdT, jacobians); - This::trace.reverseAD2(dFdT * This::dTdA, jacobians); - } -}; - -/// Recursive Record class Generator -template -struct Record: public boost::mpl::fold, - GenerateRecord >::type { - - /// Access Trace - template - ExecutionTrace& trace() { - return static_cast&>(*this).trace; - } - - /// Access Jacobian - template - typename Jacobian::type& jacobian() { - return static_cast&>(*this).dTdA; - } - -}; - //----------------------------------------------------------------------------- // Below we use the "Class Composition" technique described in the book // C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost @@ -530,6 +455,13 @@ struct Record: public boost::mpl::fold, // by invoking boost::mpl::fold over the meta-function GenerateFunctionalNode //----------------------------------------------------------------------------- +/// meta-function to generate fixed-size JacobianTA type +template +struct Jacobian { + typedef Eigen::Matrix type; + typedef boost::optional optional; +}; + /** * Base case for recursive FunctionalNode class */ @@ -537,10 +469,10 @@ template struct FunctionalBase: ExpressionNode { static size_t const N = 0; // number of arguments - typedef CallRecord Record2; + typedef CallRecord Record; /// Construct an execution trace for reverse AD - void trace(const Values& values, Record2* record, char*& raw) const { + void trace(const Values& values, Record* record, char*& raw) const { } }; @@ -555,6 +487,16 @@ struct Argument { boost::shared_ptr > expression; }; +/** + * Building block for Recursive Record Class + * Records the evaluation of a single argument in a functional expression + */ +template +struct JacobianTrace { + ExecutionTrace trace; + typename Jacobian::type dTdA; +}; + /** * Recursive Definition of Functional ExpressionNode */ @@ -572,17 +514,15 @@ struct GenerateFunctionalNode: Argument, Base { return keys; } - /** - * Recursive Record Class for Functional Expressions - */ - struct Record2: JacobianTrace, Base::Record2 { + /// Recursive Record Class for Functional Expressions + struct Record: JacobianTrace, Base::Record { typedef T return_type; typedef JacobianTrace This; /// Print to std::cout virtual void print(const std::string& indent) const { - Base::Record2::print(indent); + Base::Record::print(indent); static const Eigen::IOFormat matlab(0, 1, " ", "; ", "", "", "[", "]"); std::cout << This::dTdA.format(matlab) << std::endl; This::trace.print(indent); @@ -590,13 +530,13 @@ struct GenerateFunctionalNode: Argument, Base { /// Start the reverse AD process virtual void startReverseAD(JacobianMap& jacobians) const { - Base::Record2::startReverseAD(jacobians); + Base::Record::startReverseAD(jacobians); Select::reverseAD(This::trace, This::dTdA, jacobians); } /// Given df/dT, multiply in dT/dA and continue reverse AD process virtual void reverseAD(const Matrix& dFdT, JacobianMap& jacobians) const { - Base::Record2::reverseAD(dFdT, jacobians); + Base::Record::reverseAD(dFdT, jacobians); This::trace.reverseAD(dFdT * This::dTdA, jacobians); } @@ -604,15 +544,16 @@ struct GenerateFunctionalNode: Argument, Base { typedef Eigen::Matrix Jacobian2T; virtual void reverseAD2(const Jacobian2T& dFdT, JacobianMap& jacobians) const { - Base::Record2::reverseAD2(dFdT, jacobians); + Base::Record::reverseAD2(dFdT, jacobians); This::trace.reverseAD2(dFdT * This::dTdA, jacobians); } }; /// Construct an execution trace for reverse AD - void trace(const Values& values, Record2* record, char*& raw) const { + void trace(const Values& values, Record* record, char*& raw) const { Base::trace(values, record, raw); - A a = This::expression->traceExecution(values, record->Record2::This::trace, raw); + A a = This::expression->traceExecution(values, record->Record::This::trace, + raw); raw = raw + This::expression->traceSize(); } }; @@ -639,10 +580,27 @@ struct FunctionalNode { return static_cast const &>(*this).expression; } + /// Provide convenience access to Record storage + struct Record: public Base::Record { + + /// Access Trace + template + ExecutionTrace& trace() { + return static_cast&>(*this).trace; + } + + /// Access Jacobian + template + typename Jacobian::type& jacobian() { + return static_cast&>(*this).dTdA; + } + + }; + /// Construct an execution trace for reverse AD virtual T traceExecution(const Values& values, ExecutionTrace& trace, char* raw) const { - typename Base::Record2* record = new (raw) typename Base::Record2(); + Record* record = new (raw) Record(); trace.setFunction(record); raw = (char*) (record + 1); @@ -658,12 +616,11 @@ struct FunctionalNode { template class UnaryExpression: public FunctionalNode >::type { - /// The automatically generated Base class - typedef FunctionalNode > Base; - public: typedef boost::function::optional)> Function; + typedef typename FunctionalNode >::type Base; + typedef typename Base::Record Record; private: @@ -694,10 +651,6 @@ public: return Augmented(t, dTdA1, a1.jacobians()); } - /// CallRecord structure for reverse AD - typedef boost::mpl::vector Arguments; - typedef Record Record; - /// Construct an execution trace for reverse AD virtual T traceExecution(const Values& values, ExecutionTrace& trace, char* raw) const { @@ -724,6 +677,8 @@ public: typedef boost::function< T(const A1&, const A2&, typename Jacobian::optional, typename Jacobian::optional)> Function; + typedef typename FunctionalNode >::type Base; + typedef typename Base::Record Record; private: @@ -740,7 +695,7 @@ private: } friend class Expression ; - friend struct ::TestBinaryExpression; + friend class ::ExpressionFactorBinaryTest; public: @@ -765,10 +720,6 @@ public: return Augmented(t, dTdA1, a1.jacobians(), dTdA2, a2.jacobians()); } - /// CallRecord structure for reverse AD - typedef boost::mpl::vector Arguments; - typedef Record Record; - /// Construct an execution trace for reverse AD virtual T traceExecution(const Values& values, ExecutionTrace& trace, char* raw) const { @@ -801,6 +752,8 @@ public: T(const A1&, const A2&, const A3&, typename Jacobian::optional, typename Jacobian::optional, typename Jacobian::optional)> Function; + typedef typename FunctionalNode >::type Base; + typedef typename Base::Record Record; private: @@ -847,10 +800,6 @@ public: a3.jacobians()); } - /// CallRecord structure for reverse AD - typedef boost::mpl::vector Arguments; - typedef Record Record; - /// Construct an execution trace for reverse AD virtual T traceExecution(const Values& values, ExecutionTrace& trace, char* raw) const { diff --git a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp index c92853d33..25dd35218 100644 --- a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp @@ -48,7 +48,7 @@ Point2_ p(2); /* ************************************************************************* */ // Leaf -TEST(ExpressionFactor, leaf) { +TEST(ExpressionFactor, Leaf) { using namespace leaf; // Create old-style factor to create expected value and derivatives @@ -64,7 +64,7 @@ TEST(ExpressionFactor, leaf) { /* ************************************************************************* */ // non-zero noise model -TEST(ExpressionFactor, model) { +TEST(ExpressionFactor, Model) { using namespace leaf; SharedNoiseModel model = noiseModel::Diagonal::Sigmas(Vector2(0.1, 0.01)); @@ -82,7 +82,7 @@ TEST(ExpressionFactor, model) { /* ************************************************************************* */ // Constrained noise model -TEST(ExpressionFactor, constrained) { +TEST(ExpressionFactor, Constrained) { using namespace leaf; SharedDiagonal model = noiseModel::Constrained::MixedSigmas(Vector2(0.2, 0)); @@ -100,7 +100,7 @@ TEST(ExpressionFactor, constrained) { /* ************************************************************************* */ // Unary(Leaf)) -TEST(ExpressionFactor, unary) { +TEST(ExpressionFactor, Unary) { // Create some values Values values; @@ -121,25 +121,21 @@ TEST(ExpressionFactor, unary) { boost::dynamic_pointer_cast(gf); EXPECT( assert_equal(expected, *jf, 1e-9)); } + /* ************************************************************************* */ -struct TestBinaryExpression { - static Point2 myUncal(const Cal3_S2& K, const Point2& p, - boost::optional Dcal, boost::optional Dp) { - return K.uncalibrate(p, Dcal, Dp); - } - Cal3_S2_ K_; - Point2_ p_; - BinaryExpression binary_; - TestBinaryExpression() : - K_(1), p_(2), binary_(myUncal, K_, p_) { - } -}; -/* ************************************************************************* */ +static Point2 myUncal(const Cal3_S2& K, const Point2& p, + boost::optional Dcal, boost::optional Dp) { + return K.uncalibrate(p, Dcal, Dp); +} + // Binary(Leaf,Leaf) -TEST(ExpressionFactor, binary) { +TEST(ExpressionFactor, Binary) { typedef BinaryExpression Binary; - TestBinaryExpression tester; + + Cal3_S2_ K_(1); + Point2_ p_(2); + Binary binary(myUncal, K_, p_); // Create some values Values values; @@ -156,14 +152,14 @@ TEST(ExpressionFactor, binary) { EXPECT_LONGS_EQUAL(expectedRecordSize, sizeof(Binary::Record)); // Check size - size_t size = tester.binary_.traceSize(); + size_t size = binary.traceSize(); CHECK(size); EXPECT_LONGS_EQUAL(expectedRecordSize, size); // Use Variable Length Array, allocated on stack by gcc // Note unclear for Clang: http://clang.llvm.org/compatibility.html#vla char raw[size]; ExecutionTrace trace; - Point2 value = tester.binary_.traceExecution(values, trace, raw); + Point2 value = binary.traceExecution(values, trace, raw); // trace.print(); // Expected Jacobians @@ -181,7 +177,7 @@ TEST(ExpressionFactor, binary) { } /* ************************************************************************* */ // Unary(Binary(Leaf,Leaf)) -TEST(ExpressionFactor, shallow) { +TEST(ExpressionFactor, Shallow) { // Create some values Values values; @@ -434,27 +430,9 @@ namespace mpl = boost::mpl; template struct Incomplete; typedef mpl::vector MyTypes; -typedef Record Generated; +typedef FunctionalNode::type Generated; //Incomplete incomplete; -//BOOST_MPL_ASSERT((boost::is_same< Matrix25, Generated::JacobianTA >)); -BOOST_MPL_ASSERT((boost::is_same< Matrix2, Generated::Jacobian2T >)); - -Generated generated; - -typedef mpl::vector1 OneType; -typedef mpl::pop_front::type Empty; -typedef mpl::pop_front::type Bad; -//typedef ProtoTrace UnaryTrace; -//BOOST_MPL_ASSERT((boost::is_same< UnaryTrace::A, Point3 >)); - -#include -#include -#include -//#include - -typedef struct { -} Expected0; -BOOST_MPL_ASSERT((boost::is_same< Expected0, Expected0 >)); +BOOST_MPL_ASSERT((boost::is_same< Matrix2, Generated::Record::Jacobian2T >)); /* ************************************************************************* */ int main() { From 74269902d7fb4b367facff478b3321043ce0c465 Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 13 Oct 2014 11:37:47 +0200 Subject: [PATCH 19/35] Big collapse now realized all the way through --- gtsam_unstable/nonlinear/Expression-inl.h | 73 ++++++++----------- .../nonlinear/tests/testExpressionFactor.cpp | 10 ++- 2 files changed, 38 insertions(+), 45 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index e4606c243..0bc552985 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -493,6 +493,7 @@ struct Argument { */ template struct JacobianTrace { + A value; ExecutionTrace trace; typename Jacobian::type dTdA; }; @@ -552,8 +553,8 @@ struct GenerateFunctionalNode: Argument, Base { /// Construct an execution trace for reverse AD void trace(const Values& values, Record* record, char*& raw) const { Base::trace(values, record, raw); - A a = This::expression->traceExecution(values, record->Record::This::trace, - raw); + record->Record::This::value = This::expression->traceExecution(values, + record->Record::This::trace, raw); raw = raw + This::expression->traceSize(); } }; @@ -583,6 +584,12 @@ struct FunctionalNode { /// Provide convenience access to Record storage struct Record: public Base::Record { + /// Access Value + template + const A& value() const { + return static_cast const &>(*this).value; + } + /// Access Trace template ExecutionTrace& trace() { @@ -598,15 +605,18 @@ struct FunctionalNode { }; /// Construct an execution trace for reverse AD - virtual T traceExecution(const Values& values, ExecutionTrace& trace, - char* raw) const { + Record* trace(const Values& values, char* raw) const { + + // Create the record and advance the pointer Record* record = new (raw) Record(); - trace.setFunction(record); raw = (char*) (record + 1); - this->trace(values, record, raw); + // Record the traces for all arguments + // After this, the raw pointer is set to after what was written + Base::trace(values, record, raw); - return T(); // TODO + // Return the record for this function evaluation + return record; } }; }; @@ -647,22 +657,20 @@ public: using boost::none; Augmented a1 = this->template expression()->forward(values); typename Jacobian::type dTdA1; - T t = function_(a1.value(), a1.constant() ? none : typename Jacobian::optional(dTdA1)); + T t = function_(a1.value(), + a1.constant() ? none : typename Jacobian::optional(dTdA1)); return Augmented(t, dTdA1, a1.jacobians()); } /// Construct an execution trace for reverse AD virtual T traceExecution(const Values& values, ExecutionTrace& trace, char* raw) const { - Record* record = new (raw) Record(); + + Record* record = Base::trace(values, raw); trace.setFunction(record); - raw = (char*) (record + 1); - A1 a1 = this->template expression()->traceExecution(values, - record->template trace(), raw); - raw = raw + this->template expression()->traceSize(); - - return function_(a1, record->template jacobian()); + return function_(record->template value(), + record->template jacobian()); } }; @@ -723,19 +731,12 @@ public: /// Construct an execution trace for reverse AD virtual T traceExecution(const Values& values, ExecutionTrace& trace, char* raw) const { - Record* record = new (raw) Record(); + + Record* record = Base::trace(values, raw); trace.setFunction(record); - raw = (char*) (record + 1); - A1 a1 = this->template expression()->traceExecution(values, - record->template trace(), raw); - raw = raw + this->template expression()->traceSize(); - - A2 a2 = this->template expression()->traceExecution(values, - record->template trace(), raw); - raw = raw + this->template expression()->traceSize(); - - return function_(a1, a2, record->template jacobian(), + return function_(record->template value(), + record->template value(), record->template jacobian(), record->template jacobian()); } }; @@ -803,23 +804,13 @@ public: /// Construct an execution trace for reverse AD virtual T traceExecution(const Values& values, ExecutionTrace& trace, char* raw) const { - Record* record = new (raw) Record(); + + Record* record = Base::trace(values, raw); trace.setFunction(record); - raw = (char*) (record + 1); - A1 a1 = this->template expression()->traceExecution(values, - record->template trace(), raw); - raw = raw + this->template expression()->traceSize(); - - A2 a2 = this->template expression()->traceExecution(values, - record->template trace(), raw); - raw = raw + this->template expression()->traceSize(); - - A3 a3 = this->template expression()->traceExecution(values, - record->template trace(), raw); - raw = raw + this->template expression()->traceSize(); - - return function_(a1, a2, a3, record->template jacobian(), + return function_( + record->template value(), record->template value(), + record->template value(), record->template jacobian(), record->template jacobian(), record->template jacobian()); } diff --git a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp index 25dd35218..8e57e7400 100644 --- a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp @@ -144,11 +144,13 @@ TEST(ExpressionFactor, Binary) { // traceRaw will fill raw with [Trace | Binary::Record] EXPECT_LONGS_EQUAL(8, sizeof(double)); + EXPECT_LONGS_EQUAL(24, sizeof(Point2)); + EXPECT_LONGS_EQUAL(48, sizeof(Cal3_S2)); EXPECT_LONGS_EQUAL(16, sizeof(ExecutionTrace)); EXPECT_LONGS_EQUAL(16, sizeof(ExecutionTrace)); EXPECT_LONGS_EQUAL(2*5*8, sizeof(Jacobian::type)); EXPECT_LONGS_EQUAL(2*2*8, sizeof(Jacobian::type)); - size_t expectedRecordSize = 16 + 2 * 16 + 80 + 32; + size_t expectedRecordSize = 24 + 24 + 48 + 2 * 16 + 80 + 32; EXPECT_LONGS_EQUAL(expectedRecordSize, sizeof(Binary::Record)); // Check size @@ -200,10 +202,10 @@ TEST(ExpressionFactor, Shallow) { // traceExecution of shallow tree typedef UnaryExpression Unary; typedef BinaryExpression Binary; - EXPECT_LONGS_EQUAL(80, sizeof(Unary::Record)); - EXPECT_LONGS_EQUAL(272, sizeof(Binary::Record)); + EXPECT_LONGS_EQUAL(112, sizeof(Unary::Record)); + EXPECT_LONGS_EQUAL(432, sizeof(Binary::Record)); size_t expectedTraceSize = sizeof(Unary::Record) + sizeof(Binary::Record); - LONGS_EQUAL(352, expectedTraceSize); + LONGS_EQUAL(112+432, expectedTraceSize); size_t size = expression.traceSize(); CHECK(size); EXPECT_LONGS_EQUAL(expectedTraceSize, size); From c11d7885e132ba824eec73b191fc638a0b5d978d Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 13 Oct 2014 11:55:16 +0200 Subject: [PATCH 20/35] Comments --- gtsam_unstable/nonlinear/Expression-inl.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index 0bc552985..6f78832b9 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -552,10 +552,16 @@ struct GenerateFunctionalNode: Argument, Base { /// Construct an execution trace for reverse AD void trace(const Values& values, Record* record, char*& raw) const { - Base::trace(values, record, raw); + Base::trace(values, record, raw); // recurse + // Write an Expression execution trace in record->trace + // Iff Constant or Leaf, this will not write to raw, only to trace. + // Iff the expression is functional, write all Records in raw buffer + // Return value of type T is recorded in record->value record->Record::This::value = This::expression->traceExecution(values, record->Record::This::trace, raw); - raw = raw + This::expression->traceSize(); + // raw is never modified by traceExecution, but if traceExecution has + // written in the buffer, the next caller expects we advance the pointer + raw += This::expression->traceSize(); } }; @@ -590,12 +596,6 @@ struct FunctionalNode { return static_cast const &>(*this).value; } - /// Access Trace - template - ExecutionTrace& trace() { - return static_cast&>(*this).trace; - } - /// Access Jacobian template typename Jacobian::type& jacobian() { From 1c1695353e908d561f178bf460541f48a3e03465 Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 13 Oct 2014 13:04:37 +0200 Subject: [PATCH 21/35] Now we can apply ExecutionTrace and Expression as meta-functions --- gtsam_unstable/nonlinear/Expression-inl.h | 31 ++++++++++--------- gtsam_unstable/nonlinear/Expression.h | 2 ++ .../nonlinear/tests/testExpressionFactor.cpp | 24 +++++++++++++- 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index 6f78832b9..ba41ab485 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -201,7 +201,7 @@ template class ExecutionTrace { enum { Constant, Leaf, Function - } type; + } kind; union { Key key; CallRecord* ptr; @@ -209,25 +209,25 @@ class ExecutionTrace { public: /// Pointer always starts out as a Constant ExecutionTrace() : - type(Constant) { + kind(Constant) { } /// Change pointer to a Leaf Record void setLeaf(Key key) { - type = Leaf; + kind = Leaf; content.key = key; } /// Take ownership of pointer to a Function Record void setFunction(CallRecord* record) { - type = Function; + kind = Function; content.ptr = record; } /// Print void print(const std::string& indent = "") const { - if (type == Constant) + if (kind == Constant) std::cout << indent << "Constant" << std::endl; - else if (type == Leaf) + else if (kind == Leaf) std::cout << indent << "Leaf, key = " << content.key << std::endl; - else if (type == Function) { + else if (kind == Function) { std::cout << indent << "Function" << std::endl; content.ptr->print(indent + " "); } @@ -235,7 +235,7 @@ public: /// Return record pointer, quite unsafe, used only for testing template boost::optional record() { - if (type != Function) + if (kind != Function) return boost::none; else { Record* p = dynamic_cast(content.ptr); @@ -245,38 +245,41 @@ public: // *** This is the main entry point for reverseAD, called from Expression::augmented *** // Called only once, either inserts identity into Jacobians (Leaf) or starts AD (Function) void startReverseAD(JacobianMap& jacobians) const { - if (type == Leaf) { + if (kind == Leaf) { // This branch will only be called on trivial Leaf expressions, i.e. Priors size_t n = T::Dim(); jacobians[content.key] = Eigen::MatrixXd::Identity(n, n); - } else if (type == Function) + } else if (kind == Function) // This is the more typical entry point, starting the AD pipeline // It is inside the startReverseAD that the correctly dimensioned pipeline is chosen. content.ptr->startReverseAD(jacobians); } // Either add to Jacobians (Leaf) or propagate (Function) void reverseAD(const Matrix& dTdA, JacobianMap& jacobians) const { - if (type == Leaf) { + if (kind == Leaf) { JacobianMap::iterator it = jacobians.find(content.key); if (it != jacobians.end()) it->second += dTdA; else jacobians[content.key] = dTdA; - } else if (type == Function) + } else if (kind == Function) content.ptr->reverseAD(dTdA, jacobians); } // Either add to Jacobians (Leaf) or propagate (Function) typedef Eigen::Matrix Jacobian2T; void reverseAD2(const Jacobian2T& dTdA, JacobianMap& jacobians) const { - if (type == Leaf) { + if (kind == Leaf) { JacobianMap::iterator it = jacobians.find(content.key); if (it != jacobians.end()) it->second += dTdA; else jacobians[content.key] = dTdA; - } else if (type == Function) + } else if (kind == Function) content.ptr->reverseAD2(dTdA, jacobians); } + + /// Define type so we can apply it as a meta-function + typedef ExecutionTrace type; }; /// Primary template calls the generic Matrix reverseAD pipeline diff --git a/gtsam_unstable/nonlinear/Expression.h b/gtsam_unstable/nonlinear/Expression.h index 502826f61..6b28667a7 100644 --- a/gtsam_unstable/nonlinear/Expression.h +++ b/gtsam_unstable/nonlinear/Expression.h @@ -147,6 +147,8 @@ public: return root_; } + /// Define type so we can apply it as a meta-function + typedef Expression type; }; // http://stackoverflow.com/questions/16260445/boost-bind-to-operator diff --git a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp index 8e57e7400..04ade90be 100644 --- a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp @@ -429,13 +429,35 @@ TEST(ExpressionFactor, composeTernary) { namespace mpl = boost::mpl; #include +#include +#include template struct Incomplete; -typedef mpl::vector MyTypes; +// Check generation of FunctionalNode +typedef mpl::vector MyTypes; typedef FunctionalNode::type Generated; //Incomplete incomplete; BOOST_MPL_ASSERT((boost::is_same< Matrix2, Generated::Record::Jacobian2T >)); +// Try generating vectors of ExecutionTrace +typedef mpl::vector, ExecutionTrace > ExpectedTraces; + +typedef mpl::transform >::type MyTraces; +BOOST_MPL_ASSERT((boost::mpl::equal< ExpectedTraces, MyTraces >)); + +template +struct MakeTrace { + typedef ExecutionTrace type; +}; +typedef mpl::transform >::type MyTraces1; +BOOST_MPL_ASSERT((boost::mpl::equal< ExpectedTraces, MyTraces1 >)); + +// Try generating vectors of Expression types +typedef mpl::vector, Expression > ExpectedExpressions; + +typedef mpl::transform >::type Expressions; +BOOST_MPL_ASSERT((boost::mpl::equal< ExpectedExpressions, Expressions >)); + /* ************************************************************************* */ int main() { TestResult tr; From a52ff529412112f4d505401b389a2c98b7a0cd91 Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 13 Oct 2014 13:34:00 +0200 Subject: [PATCH 22/35] Try some meta-transforms --- .../nonlinear/tests/testExpressionFactor.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp index 04ade90be..f3099778f 100644 --- a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp @@ -429,7 +429,6 @@ TEST(ExpressionFactor, composeTernary) { namespace mpl = boost::mpl; #include -#include #include template struct Incomplete; @@ -443,20 +442,29 @@ BOOST_MPL_ASSERT((boost::is_same< Matrix2, Generated::Record::Jacobian2T >)); typedef mpl::vector, ExecutionTrace > ExpectedTraces; typedef mpl::transform >::type MyTraces; -BOOST_MPL_ASSERT((boost::mpl::equal< ExpectedTraces, MyTraces >)); +BOOST_MPL_ASSERT((mpl::equal< ExpectedTraces, MyTraces >)); template struct MakeTrace { typedef ExecutionTrace type; }; typedef mpl::transform >::type MyTraces1; -BOOST_MPL_ASSERT((boost::mpl::equal< ExpectedTraces, MyTraces1 >)); +BOOST_MPL_ASSERT((mpl::equal< ExpectedTraces, MyTraces1 >)); // Try generating vectors of Expression types typedef mpl::vector, Expression > ExpectedExpressions; - typedef mpl::transform >::type Expressions; -BOOST_MPL_ASSERT((boost::mpl::equal< ExpectedExpressions, Expressions >)); +BOOST_MPL_ASSERT((mpl::equal< ExpectedExpressions, Expressions >)); + +// Try generating vectors of Jacobian types +typedef mpl::vector ExpectedJacobians; +typedef mpl::transform >::type Jacobians; +BOOST_MPL_ASSERT((mpl::equal< ExpectedJacobians, Jacobians >)); + +// Try accessing a Jacobian +typedef mpl::int_<1> one; +typedef mpl::at::type Jacobian23; // base zero ! +BOOST_MPL_ASSERT((boost::is_same< Matrix23, Jacobian23>)); /* ************************************************************************* */ int main() { From ba0b68110f5ae428574cc69554454f1b9476792b Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 13 Oct 2014 13:56:51 +0200 Subject: [PATCH 23/35] Boost Fusion needed to access values :-( --- .../nonlinear/tests/testExpressionFactor.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp index f3099778f..45936fc8e 100644 --- a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp @@ -462,10 +462,24 @@ typedef mpl::transform >::type Jacobians; BOOST_MPL_ASSERT((mpl::equal< ExpectedJacobians, Jacobians >)); // Try accessing a Jacobian -typedef mpl::int_<1> one; -typedef mpl::at::type Jacobian23; // base zero ! +typedef mpl::at_c::type Jacobian23; // base zero ! BOOST_MPL_ASSERT((boost::is_same< Matrix23, Jacobian23>)); +#include +#include +#include + +// Create a value and access it +TEST(ExpressionFactor, JacobiansValue) { + Matrix23 expected; + ExpectedJacobians jacobians; + using boost::fusion::at_c; + + Matrix23 actual = at_c<1>(jacobians); + CHECK(actual.cols() == expected.cols()); + CHECK(actual.rows() == expected.rows()); +} + /* ************************************************************************* */ int main() { TestResult tr; From dda91df6e10988c8ae7add0f6d452e45c4897e6f Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 13 Oct 2014 18:32:58 +0200 Subject: [PATCH 24/35] On the way to full fusion: Optional meta-function now separate from Jacobian. --- gtsam_unstable/nonlinear/Expression-inl.h | 59 ++++++++++++------- gtsam_unstable/nonlinear/Expression.h | 6 +- .../nonlinear/tests/testExpressionFactor.cpp | 8 +-- 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index ba41ab485..53b38bba4 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -35,6 +35,12 @@ struct TestBinaryExpression; #include #include #include +#include +#include + +// Boost Fusion includes allow us to create/access values from MPL vectors +#include +#include namespace MPL = boost::mpl::placeholders; @@ -462,7 +468,13 @@ public: template struct Jacobian { typedef Eigen::Matrix type; - typedef boost::optional optional; +}; + +/// meta-function to generate JacobianTA optional reference +template +struct Optional { + typedef Eigen::Matrix Jacobian; + typedef boost::optional type; }; /** @@ -573,11 +585,17 @@ struct GenerateFunctionalNode: Argument, Base { */ template struct FunctionalNode { + typedef typename boost::mpl::fold, GenerateFunctionalNode >::type Base; struct type: public Base { + // Argument types and derived, note these are base 0 ! + typedef TYPES Arguments; + typedef typename boost::mpl::transform >::type Jacobians; + typedef typename boost::mpl::transform >::type Optionals; + /// Reset expression shared pointer template void reset(const boost::shared_ptr >& ptr) { @@ -631,7 +649,7 @@ class UnaryExpression: public FunctionalNode >::type { public: - typedef boost::function::optional)> Function; + typedef boost::function::type)> Function; typedef typename FunctionalNode >::type Base; typedef typename Base::Record Record; @@ -661,7 +679,7 @@ public: Augmented a1 = this->template expression()->forward(values); typename Jacobian::type dTdA1; T t = function_(a1.value(), - a1.constant() ? none : typename Jacobian::optional(dTdA1)); + a1.constant() ? none : typename Optional::type(dTdA1)); return Augmented(t, dTdA1, a1.jacobians()); } @@ -686,8 +704,8 @@ class BinaryExpression: public FunctionalNode >::t public: typedef boost::function< - T(const A1&, const A2&, typename Jacobian::optional, - typename Jacobian::optional)> Function; + T(const A1&, const A2&, typename Optional::type, + typename Optional::type)> Function; typedef typename FunctionalNode >::type Base; typedef typename Base::Record Record; @@ -726,8 +744,8 @@ public: typename Jacobian::type dTdA1; typename Jacobian::type dTdA2; T t = function_(a1.value(), a2.value(), - a1.constant() ? none : typename Jacobian::optional(dTdA1), - a2.constant() ? none : typename Jacobian::optional(dTdA2)); + a1.constant() ? none : typename Optional::type(dTdA1), + a2.constant() ? none : typename Optional::type(dTdA2)); return Augmented(t, dTdA1, a1.jacobians(), dTdA2, a2.jacobians()); } @@ -753,9 +771,8 @@ class TernaryExpression: public FunctionalNode public: typedef boost::function< - T(const A1&, const A2&, const A3&, typename Jacobian::optional, - typename Jacobian::optional, - typename Jacobian::optional)> Function; + T(const A1&, const A2&, const A3&, typename Optional::type, + typename Optional::type, typename Optional::type)> Function; typedef typename FunctionalNode >::type Base; typedef typename Base::Record Record; @@ -793,15 +810,17 @@ public: Augmented a1 = this->template expression()->forward(values); Augmented a2 = this->template expression()->forward(values); Augmented a3 = this->template expression()->forward(values); - typename Jacobian::type dTdA1; - typename Jacobian::type dTdA2; - typename Jacobian::type dTdA3; - T t = function_(a1.value(), a2.value(), a3.value(), - a1.constant() ? none : typename Jacobian::optional(dTdA1), - a2.constant() ? none : typename Jacobian::optional(dTdA2), - a3.constant() ? none : typename Jacobian::optional(dTdA3)); - return Augmented(t, dTdA1, a1.jacobians(), dTdA2, a2.jacobians(), dTdA3, - a3.jacobians()); + + typedef typename Base::Jacobians Jacobians; + + using boost::fusion::at_c; + Jacobians H; + typename boost::mpl::at_c::type H1; + typename boost::mpl::at_c::type H2; + typename boost::mpl::at_c::type H3; + T t = function_(a1.value(), a2.value(), a3.value(),H1,H2,H3); + return Augmented(t, H1, a1.jacobians(), H2, a2.jacobians(), + H3, a3.jacobians()); } /// Construct an execution trace for reverse AD @@ -819,5 +838,5 @@ public: }; //----------------------------------------------------------------------------- -} + } diff --git a/gtsam_unstable/nonlinear/Expression.h b/gtsam_unstable/nonlinear/Expression.h index 6b28667a7..23621f2bb 100644 --- a/gtsam_unstable/nonlinear/Expression.h +++ b/gtsam_unstable/nonlinear/Expression.h @@ -61,7 +61,7 @@ public: /// Construct a nullary method expression template Expression(const Expression& expression, - T (A::*method)(boost::optional) const) { + T (A::*method)(typename Optional::type) const) { root_.reset( new UnaryExpression(boost::bind(method, _1, _2), expression)); } @@ -76,8 +76,8 @@ public: /// Construct a unary method expression template Expression(const Expression& expression1, - T (A1::*method)(const A2&, typename Jacobian::optional, - typename Jacobian::optional) const, + T (A1::*method)(const A2&, typename Optional::type, + typename Optional::type) const, const Expression& expression2) { root_.reset( new BinaryExpression(boost::bind(method, _1, _2, _3, _4), diff --git a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp index 45936fc8e..62ad55294 100644 --- a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp @@ -465,16 +465,14 @@ BOOST_MPL_ASSERT((mpl::equal< ExpectedJacobians, Jacobians >)); typedef mpl::at_c::type Jacobian23; // base zero ! BOOST_MPL_ASSERT((boost::is_same< Matrix23, Jacobian23>)); -#include -#include -#include - // Create a value and access it TEST(ExpressionFactor, JacobiansValue) { Matrix23 expected; - ExpectedJacobians jacobians; + Jacobians jacobians; using boost::fusion::at_c; + at_c<1>(jacobians) << 1,2,3,4,5,6; + Matrix23 actual = at_c<1>(jacobians); CHECK(actual.cols() == expected.cols()); CHECK(actual.rows() == expected.rows()); From 70f0caf0e3e0f07387f26a115217453145cd15f1 Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 13 Oct 2014 22:50:47 +0200 Subject: [PATCH 25/35] Experimenting w Fusion --- .../nonlinear/tests/testExpressionFactor.cpp | 72 ++++++++++++++++--- 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp index 62ad55294..d7fe87c87 100644 --- a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp @@ -425,7 +425,6 @@ TEST(ExpressionFactor, composeTernary) { } /* ************************************************************************* */ - namespace mpl = boost::mpl; #include @@ -441,43 +440,96 @@ BOOST_MPL_ASSERT((boost::is_same< Matrix2, Generated::Record::Jacobian2T >)); // Try generating vectors of ExecutionTrace typedef mpl::vector, ExecutionTrace > ExpectedTraces; -typedef mpl::transform >::type MyTraces; +typedef mpl::transform >::type MyTraces; BOOST_MPL_ASSERT((mpl::equal< ExpectedTraces, MyTraces >)); -template +template struct MakeTrace { typedef ExecutionTrace type; }; -typedef mpl::transform >::type MyTraces1; +typedef mpl::transform >::type MyTraces1; BOOST_MPL_ASSERT((mpl::equal< ExpectedTraces, MyTraces1 >)); // Try generating vectors of Expression types typedef mpl::vector, Expression > ExpectedExpressions; -typedef mpl::transform >::type Expressions; +typedef mpl::transform >::type Expressions; BOOST_MPL_ASSERT((mpl::equal< ExpectedExpressions, Expressions >)); // Try generating vectors of Jacobian types -typedef mpl::vector ExpectedJacobians; -typedef mpl::transform >::type Jacobians; +typedef mpl::vector ExpectedJacobians; +typedef mpl::transform >::type Jacobians; BOOST_MPL_ASSERT((mpl::equal< ExpectedJacobians, Jacobians >)); // Try accessing a Jacobian -typedef mpl::at_c::type Jacobian23; // base zero ! +typedef mpl::at_c::type Jacobian23; // base zero ! BOOST_MPL_ASSERT((boost::is_same< Matrix23, Jacobian23>)); // Create a value and access it TEST(ExpressionFactor, JacobiansValue) { + using boost::fusion::at_c; Matrix23 expected; Jacobians jacobians; - using boost::fusion::at_c; - at_c<1>(jacobians) << 1,2,3,4,5,6; + at_c<1>(jacobians) << 1, 2, 3, 4, 5, 6; Matrix23 actual = at_c<1>(jacobians); CHECK(actual.cols() == expected.cols()); CHECK(actual.rows() == expected.rows()); } +/* ************************************************************************* */ +#include + +struct triple { + template struct result; // says we will provide result + + template + struct result { + typedef double type; // result for int argument + }; + + template + struct result { + typedef double type; // result for double argument + }; + + // actual function + template + typename result::type operator()(T& x) const { + return (double) x; + } +}; + + +// Test out polymorphic transform +TEST(ExpressionFactor, Triple) { + typedef boost::fusion::vector IntDouble; + IntDouble H = boost::fusion::make_vector(1, 2.0); + + // Only works if I use Double2 + typedef boost::fusion::result_of::transform::type Result; + typedef boost::fusion::vector Double2; + Double2 D = boost::fusion::transform(H, triple()); + + using boost::fusion::at_c; + DOUBLES_EQUAL(1.0, at_c<0>(D), 1e-9); + DOUBLES_EQUAL(2.0, at_c<1>(D), 1e-9); +} + +/* ************************************************************************* */ +#include +#include + +// Test out polymorphic transform +TEST(ExpressionFactor, Invoke) { + std::plus add; + assert(invoke(add,boost::fusion::make_vector(1,1)) == 2); + + // Creating a Pose3 (is there another way?) + boost::fusion::vector pair; + Pose3 pose = boost::fusion::invoke(boost::value_factory(), pair); +} + /* ************************************************************************* */ int main() { TestResult tr; From ef5bf03c81dcbad11118d9895fe2c25bdf76144c Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 13 Oct 2014 23:04:30 +0200 Subject: [PATCH 26/35] Clean up --- gtsam_unstable/nonlinear/Expression-inl.h | 40 +++++++++---------- .../nonlinear/tests/testExpressionFactor.cpp | 23 +++++++++-- 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index 53b38bba4..f0301ba4a 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -38,10 +38,6 @@ struct TestBinaryExpression; #include #include -// Boost Fusion includes allow us to create/access values from MPL vectors -#include -#include - namespace MPL = boost::mpl::placeholders; class ExpressionFactorBinaryTest; @@ -677,10 +673,13 @@ public: virtual Augmented forward(const Values& values) const { using boost::none; Augmented a1 = this->template expression()->forward(values); - typename Jacobian::type dTdA1; - T t = function_(a1.value(), - a1.constant() ? none : typename Optional::type(dTdA1)); - return Augmented(t, dTdA1, a1.jacobians()); + + // Declare Jacobians + using boost::mpl::at_c; + typename at_c::type H1; + + T t = function_(a1.value(), H1); + return Augmented(t, H1, a1.jacobians()); } /// Construct an execution trace for reverse AD @@ -741,12 +740,14 @@ public: using boost::none; Augmented a1 = this->template expression()->forward(values); Augmented a2 = this->template expression()->forward(values); - typename Jacobian::type dTdA1; - typename Jacobian::type dTdA2; - T t = function_(a1.value(), a2.value(), - a1.constant() ? none : typename Optional::type(dTdA1), - a2.constant() ? none : typename Optional::type(dTdA2)); - return Augmented(t, dTdA1, a1.jacobians(), dTdA2, a2.jacobians()); + + // Declare Jacobians + using boost::mpl::at_c; + typename at_c::type H1; + typename at_c::type H2; + + T t = function_(a1.value(), a2.value(),H1,H2); + return Augmented(t, H1, a1.jacobians(), H2, a2.jacobians()); } /// Construct an execution trace for reverse AD @@ -811,13 +812,12 @@ public: Augmented a2 = this->template expression()->forward(values); Augmented a3 = this->template expression()->forward(values); - typedef typename Base::Jacobians Jacobians; + // Declare Jacobians + using boost::mpl::at_c; + typename at_c::type H1; + typename at_c::type H2; + typename at_c::type H3; - using boost::fusion::at_c; - Jacobians H; - typename boost::mpl::at_c::type H1; - typename boost::mpl::at_c::type H2; - typename boost::mpl::at_c::type H3; T t = function_(a1.value(), a2.value(), a3.value(),H1,H2,H3); return Augmented(t, H1, a1.jacobians(), H2, a2.jacobians(), H3, a3.jacobians()); diff --git a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp index d7fe87c87..3a7ad5c72 100644 --- a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp @@ -464,6 +464,11 @@ BOOST_MPL_ASSERT((mpl::equal< ExpectedJacobians, Jacobians >)); typedef mpl::at_c::type Jacobian23; // base zero ! BOOST_MPL_ASSERT((boost::is_same< Matrix23, Jacobian23>)); +/* ************************************************************************* */ +// Boost Fusion includes allow us to create/access values from MPL vectors +#include +#include + // Create a value and access it TEST(ExpressionFactor, JacobiansValue) { using boost::fusion::at_c; @@ -478,7 +483,11 @@ TEST(ExpressionFactor, JacobiansValue) { } /* ************************************************************************* */ +// Test out polymorphic transform + #include +#include +#include struct triple { template struct result; // says we will provide result @@ -488,6 +497,16 @@ struct triple { typedef double type; // result for int argument }; + template + struct result { + typedef double type; // result for int argument + }; + + template + struct result { + typedef double type; // result for double argument + }; + template struct result { typedef double type; // result for double argument @@ -495,13 +514,11 @@ struct triple { // actual function template - typename result::type operator()(T& x) const { + typename result::type operator()(const T& x) const { return (double) x; } }; - -// Test out polymorphic transform TEST(ExpressionFactor, Triple) { typedef boost::fusion::vector IntDouble; IntDouble H = boost::fusion::make_vector(1, 2.0); From 0a41b0a027bbfb2c3256f0f58c923043b7e37bcb Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 14 Oct 2014 08:53:05 +0200 Subject: [PATCH 27/35] Moved meta-programming tests to testExpressionMeta.cpp --- .cproject | 106 ++++++------ .../nonlinear/tests/testExpressionFactor.cpp | 123 -------------- .../nonlinear/tests/testExpressionMeta.cpp | 158 ++++++++++++++++++ 3 files changed, 210 insertions(+), 177 deletions(-) create mode 100644 gtsam_unstable/nonlinear/tests/testExpressionMeta.cpp diff --git a/.cproject b/.cproject index 7d302b39a..0665eaf06 100644 --- a/.cproject +++ b/.cproject @@ -600,7 +600,6 @@ make - tests/testBayesTree.run true false @@ -608,7 +607,6 @@ make - testBinaryBayesNet.run true false @@ -656,7 +654,6 @@ make - testSymbolicBayesNet.run true false @@ -664,7 +661,6 @@ make - tests/testSymbolicFactor.run true false @@ -672,7 +668,6 @@ make - testSymbolicFactorGraph.run true false @@ -688,7 +683,6 @@ make - tests/testBayesTree true false @@ -1040,7 +1034,6 @@ make - testErrors.run true false @@ -1270,46 +1263,6 @@ true true - - make - -j5 - testBTree.run - true - true - true - - - make - -j5 - testDSF.run - true - true - true - - - make - -j5 - testDSFMap.run - true - true - true - - - make - -j5 - testDSFVector.run - true - true - true - - - make - -j5 - testFixedVector.run - true - true - true - make -j2 @@ -1392,6 +1345,7 @@ make + testSimulated2DOriented.run true false @@ -1431,6 +1385,7 @@ make + testSimulated2D.run true false @@ -1438,6 +1393,7 @@ make + testSimulated3D.run true false @@ -1451,6 +1407,46 @@ true true + + make + -j5 + testBTree.run + true + true + true + + + make + -j5 + testDSF.run + true + true + true + + + make + -j5 + testDSFMap.run + true + true + true + + + make + -j5 + testDSFVector.run + true + true + true + + + make + -j5 + testFixedVector.run + true + true + true + make -j5 @@ -1708,7 +1704,6 @@ cpack - -G DEB true false @@ -1716,7 +1711,6 @@ cpack - -G RPM true false @@ -1724,7 +1718,6 @@ cpack - -G TGZ true false @@ -1732,7 +1725,6 @@ cpack - --config CPackSourceConfig.cmake true false @@ -2459,7 +2451,6 @@ make - testGraph.run true false @@ -2467,7 +2458,6 @@ make - testJunctionTree.run true false @@ -2475,7 +2465,6 @@ make - testSymbolicBayesNetB.run true false @@ -2561,6 +2550,14 @@ true true + + make + -j5 + testExpressionMeta.run + true + true + true + make -j2 @@ -2963,6 +2960,7 @@ make + tests/testGaussianISAM2 true false diff --git a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp index 3a7ad5c72..5867f9dcf 100644 --- a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp @@ -424,129 +424,6 @@ TEST(ExpressionFactor, composeTernary) { EXPECT( assert_equal(expected, *jf,1e-9)); } -/* ************************************************************************* */ -namespace mpl = boost::mpl; - -#include -#include -template struct Incomplete; - -// Check generation of FunctionalNode -typedef mpl::vector MyTypes; -typedef FunctionalNode::type Generated; -//Incomplete incomplete; -BOOST_MPL_ASSERT((boost::is_same< Matrix2, Generated::Record::Jacobian2T >)); - -// Try generating vectors of ExecutionTrace -typedef mpl::vector, ExecutionTrace > ExpectedTraces; - -typedef mpl::transform >::type MyTraces; -BOOST_MPL_ASSERT((mpl::equal< ExpectedTraces, MyTraces >)); - -template -struct MakeTrace { - typedef ExecutionTrace type; -}; -typedef mpl::transform >::type MyTraces1; -BOOST_MPL_ASSERT((mpl::equal< ExpectedTraces, MyTraces1 >)); - -// Try generating vectors of Expression types -typedef mpl::vector, Expression > ExpectedExpressions; -typedef mpl::transform >::type Expressions; -BOOST_MPL_ASSERT((mpl::equal< ExpectedExpressions, Expressions >)); - -// Try generating vectors of Jacobian types -typedef mpl::vector ExpectedJacobians; -typedef mpl::transform >::type Jacobians; -BOOST_MPL_ASSERT((mpl::equal< ExpectedJacobians, Jacobians >)); - -// Try accessing a Jacobian -typedef mpl::at_c::type Jacobian23; // base zero ! -BOOST_MPL_ASSERT((boost::is_same< Matrix23, Jacobian23>)); - -/* ************************************************************************* */ -// Boost Fusion includes allow us to create/access values from MPL vectors -#include -#include - -// Create a value and access it -TEST(ExpressionFactor, JacobiansValue) { - using boost::fusion::at_c; - Matrix23 expected; - Jacobians jacobians; - - at_c<1>(jacobians) << 1, 2, 3, 4, 5, 6; - - Matrix23 actual = at_c<1>(jacobians); - CHECK(actual.cols() == expected.cols()); - CHECK(actual.rows() == expected.rows()); -} - -/* ************************************************************************* */ -// Test out polymorphic transform - -#include -#include -#include - -struct triple { - template struct result; // says we will provide result - - template - struct result { - typedef double type; // result for int argument - }; - - template - struct result { - typedef double type; // result for int argument - }; - - template - struct result { - typedef double type; // result for double argument - }; - - template - struct result { - typedef double type; // result for double argument - }; - - // actual function - template - typename result::type operator()(const T& x) const { - return (double) x; - } -}; - -TEST(ExpressionFactor, Triple) { - typedef boost::fusion::vector IntDouble; - IntDouble H = boost::fusion::make_vector(1, 2.0); - - // Only works if I use Double2 - typedef boost::fusion::result_of::transform::type Result; - typedef boost::fusion::vector Double2; - Double2 D = boost::fusion::transform(H, triple()); - - using boost::fusion::at_c; - DOUBLES_EQUAL(1.0, at_c<0>(D), 1e-9); - DOUBLES_EQUAL(2.0, at_c<1>(D), 1e-9); -} - -/* ************************************************************************* */ -#include -#include - -// Test out polymorphic transform -TEST(ExpressionFactor, Invoke) { - std::plus add; - assert(invoke(add,boost::fusion::make_vector(1,1)) == 2); - - // Creating a Pose3 (is there another way?) - boost::fusion::vector pair; - Pose3 pose = boost::fusion::invoke(boost::value_factory(), pair); -} - /* ************************************************************************* */ int main() { TestResult tr; diff --git a/gtsam_unstable/nonlinear/tests/testExpressionMeta.cpp b/gtsam_unstable/nonlinear/tests/testExpressionMeta.cpp new file mode 100644 index 000000000..19a39d52f --- /dev/null +++ b/gtsam_unstable/nonlinear/tests/testExpressionMeta.cpp @@ -0,0 +1,158 @@ +/* ---------------------------------------------------------------------------- + + * GTSAM Copyright 2010, Georgia Tech Research Corporation, + * Atlanta, Georgia 30332-0415 + * All Rights Reserved + * Authors: Frank Dellaert, et al. (see THANKS for the full author list) + + * See LICENSE for the license information + + * -------------------------------------------------------------------------- */ + +/** + * @file testExpressionMeta.cpp + * @date October 14, 2014 + * @author Frank Dellaert + * @brief Test meta-programming constructs for Expressions + */ + +#include +#include +#include +#include + +#include + +using namespace std; +using namespace gtsam; + +/* ************************************************************************* */ +namespace mpl = boost::mpl; + +#include +#include +template struct Incomplete; + +// Check generation of FunctionalNode +typedef mpl::vector MyTypes; +typedef FunctionalNode::type Generated; +//Incomplete incomplete; +BOOST_MPL_ASSERT((boost::is_same< Matrix2, Generated::Record::Jacobian2T >)); + +// Try generating vectors of ExecutionTrace +typedef mpl::vector, ExecutionTrace > ExpectedTraces; + +typedef mpl::transform >::type MyTraces; +BOOST_MPL_ASSERT((mpl::equal< ExpectedTraces, MyTraces >)); + +template +struct MakeTrace { + typedef ExecutionTrace type; +}; +typedef mpl::transform >::type MyTraces1; +BOOST_MPL_ASSERT((mpl::equal< ExpectedTraces, MyTraces1 >)); + +// Try generating vectors of Expression types +typedef mpl::vector, Expression > ExpectedExpressions; +typedef mpl::transform >::type Expressions; +BOOST_MPL_ASSERT((mpl::equal< ExpectedExpressions, Expressions >)); + +// Try generating vectors of Jacobian types +typedef mpl::vector ExpectedJacobians; +typedef mpl::transform >::type Jacobians; +BOOST_MPL_ASSERT((mpl::equal< ExpectedJacobians, Jacobians >)); + +// Try accessing a Jacobian +typedef mpl::at_c::type Jacobian23; // base zero ! +BOOST_MPL_ASSERT((boost::is_same< Matrix23, Jacobian23>)); + +/* ************************************************************************* */ +// Boost Fusion includes allow us to create/access values from MPL vectors +#include +#include + +// Create a value and access it +TEST(ExpressionFactor, JacobiansValue) { + using boost::fusion::at_c; + Matrix23 expected; + Jacobians jacobians; + + at_c<1>(jacobians) << 1, 2, 3, 4, 5, 6; + + Matrix23 actual = at_c<1>(jacobians); + CHECK(actual.cols() == expected.cols()); + CHECK(actual.rows() == expected.rows()); +} + +/* ************************************************************************* */ +// Test out polymorphic transform + +#include +#include +#include + +struct triple { + template struct result; // says we will provide result + + template + struct result { + typedef double type; // result for int argument + }; + + template + struct result { + typedef double type; // result for int argument + }; + + template + struct result { + typedef double type; // result for double argument + }; + + template + struct result { + typedef double type; // result for double argument + }; + + // actual function + template + typename result::type operator()(const T& x) const { + return (double) x; + } +}; + +TEST(ExpressionFactor, Triple) { + typedef boost::fusion::vector IntDouble; + IntDouble H = boost::fusion::make_vector(1, 2.0); + + // Only works if I use Double2 + typedef boost::fusion::result_of::transform::type Result; + typedef boost::fusion::vector Double2; + Double2 D = boost::fusion::transform(H, triple()); + + using boost::fusion::at_c; + DOUBLES_EQUAL(1.0, at_c<0>(D), 1e-9); + DOUBLES_EQUAL(2.0, at_c<1>(D), 1e-9); +} + +/* ************************************************************************* */ +#include +#include + +// Test out polymorphic transform +TEST(ExpressionFactor, Invoke) { + std::plus add; + assert(invoke(add,boost::fusion::make_vector(1,1)) == 2); + + // Creating a Pose3 (is there another way?) + boost::fusion::vector pair; + Pose3 pose = boost::fusion::invoke(boost::value_factory(), pair); +} + +/* ************************************************************************* */ +int main() { + TestResult tr; + return TestRegistry::runAllTests(tr); +} +/* ************************************************************************* */ + From 781cc6daa9725f0018da3bdf9a5c586712c4fd06 Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 14 Oct 2014 08:59:01 +0200 Subject: [PATCH 28/35] keys now from expression_ --- gtsam_unstable/nonlinear/ExpressionFactor.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/gtsam_unstable/nonlinear/ExpressionFactor.h b/gtsam_unstable/nonlinear/ExpressionFactor.h index 5f78df004..a9bac6065 100644 --- a/gtsam_unstable/nonlinear/ExpressionFactor.h +++ b/gtsam_unstable/nonlinear/ExpressionFactor.h @@ -82,18 +82,14 @@ public: // Get dimensions of matrices std::vector dimensions; dimensions.reserve(n); - std::vector keys; - keys.reserve(n); for (JacobianMap::const_iterator it = terms.begin(); it != terms.end(); ++it) { const std::pair& term = *it; - Key key = term.first; const Matrix& Ai = term.second; dimensions.push_back(Ai.cols()); - keys.push_back(key); } - // Construct block matrix + // Construct block matrix, is of right size but un-initialized VerticalBlockMatrix Ab(dimensions, b.size(), true); // Check and add terms @@ -107,6 +103,9 @@ public: Ab(n).col(0) = b; + // Get keys to construct factor + std::set keys = expression_.keys(); + // TODO pass unwhitened + noise model to Gaussian factor // For now, only linearized constrained factors have noise model at linear level!!! noiseModel::Constrained::shared_ptr constrained = // From d8d94d0c34a25f93bc02af64e5672a225e03f927 Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 14 Oct 2014 09:53:47 +0200 Subject: [PATCH 29/35] dimensions implemented and tested --- gtsam_unstable/nonlinear/Expression-inl.h | 21 ++++++ gtsam_unstable/nonlinear/Expression.h | 5 ++ .../nonlinear/tests/testExpression.cpp | 68 +++++++++++-------- 3 files changed, 64 insertions(+), 30 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index f0301ba4a..f9a0c91bf 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -336,6 +336,12 @@ public: return keys; } + /// Return dimensions for each argument + virtual std::map dimensions() const { + std::map map; + return map; + } + // Return size needed for memory buffer in traceExecution size_t traceSize() const { return traceSize_; @@ -410,6 +416,13 @@ public: return keys; } + /// Return dimensions for each argument + virtual std::map dimensions() const { + std::map map; + map[key_] = T::dimension; + return map; + } + /// Return value virtual T value(const Values& values) const { return values.at(key_); @@ -526,6 +539,14 @@ struct GenerateFunctionalNode: Argument, Base { return keys; } + /// Return dimensions for each argument + virtual std::map dimensions() const { + std::map map = Base::dimensions(); + std::map myMap = This::expression->dimensions(); + map.insert(myMap.begin(), myMap.end()); + return map; + } + /// Recursive Record Class for Functional Expressions struct Record: JacobianTrace, Base::Record { diff --git a/gtsam_unstable/nonlinear/Expression.h b/gtsam_unstable/nonlinear/Expression.h index 23621f2bb..2f6367734 100644 --- a/gtsam_unstable/nonlinear/Expression.h +++ b/gtsam_unstable/nonlinear/Expression.h @@ -107,6 +107,11 @@ public: return root_->keys(); } + /// Return dimensions for each argument, as a map (allows order to change later) + std::map dimensions() const { + return root_->dimensions(); + } + /// Return value and derivatives, forward AD version Augmented forward(const Values& values) const { return root_->forward(values); diff --git a/gtsam_unstable/nonlinear/tests/testExpression.cpp b/gtsam_unstable/nonlinear/tests/testExpression.cpp index bf13749b9..e6fd12ab4 100644 --- a/gtsam_unstable/nonlinear/tests/testExpression.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpression.cpp @@ -26,9 +26,15 @@ #include +#include +using boost::assign::list_of; +using boost::assign::map_list_of; + using namespace std; using namespace gtsam; +typedef pair Pair; + /* ************************************************************************* */ template @@ -94,13 +100,18 @@ Expression p_cam(x, &Pose3::transform_to, p); } /* ************************************************************************* */ // keys -TEST(Expression, keys_binary) { - - // Check keys - set expectedKeys; - expectedKeys.insert(1); - expectedKeys.insert(2); - EXPECT(expectedKeys == binary::p_cam.keys()); +TEST(Expression, BinaryKeys) { + set expected = list_of(1)(2); + EXPECT(expected == binary::p_cam.keys()); +} +/* ************************************************************************* */ +// dimensions +TEST(Expression, BinaryDimensions) { + map expected = map_list_of(1, 6)(2, 3), // + actual = binary::p_cam.dimensions(); + EXPECT_LONGS_EQUAL(expected.size(),actual.size()); + BOOST_FOREACH(Pair pair, actual) + EXPECT_LONGS_EQUAL(expected[pair.first],pair.second); } /* ************************************************************************* */ // Binary(Leaf,Unary(Binary(Leaf,Leaf))) @@ -115,14 +126,18 @@ Expression uv_hat(uncalibrate, K, projection); } /* ************************************************************************* */ // keys -TEST(Expression, keys_tree) { - - // Check keys - set expectedKeys; - expectedKeys.insert(1); - expectedKeys.insert(2); - expectedKeys.insert(3); - EXPECT(expectedKeys == tree::uv_hat.keys()); +TEST(Expression, TreeKeys) { + set expected = list_of(1)(2)(3); + EXPECT(expected == tree::uv_hat.keys()); +} +/* ************************************************************************* */ +// dimensions +TEST(Expression, TreeDimensions) { + map expected = map_list_of(1, 6)(2, 3)(3, 5), // + actual = tree::uv_hat.dimensions(); + EXPECT_LONGS_EQUAL(expected.size(),actual.size()); + BOOST_FOREACH(Pair pair, actual) + EXPECT_LONGS_EQUAL(expected[pair.first],pair.second); } /* ************************************************************************* */ @@ -133,10 +148,8 @@ TEST(Expression, compose1) { Expression R3 = R1 * R2; // Check keys - set expectedKeys; - expectedKeys.insert(1); - expectedKeys.insert(2); - EXPECT(expectedKeys == R3.keys()); + set expected = list_of(1)(2); + EXPECT(expected == R3.keys()); } /* ************************************************************************* */ @@ -148,9 +161,8 @@ TEST(Expression, compose2) { Expression R3 = R1 * R2; // Check keys - set expectedKeys; - expectedKeys.insert(1); - EXPECT(expectedKeys == R3.keys()); + set expected = list_of(1); + EXPECT(expected == R3.keys()); } /* ************************************************************************* */ @@ -162,9 +174,8 @@ TEST(Expression, compose3) { Expression R3 = R1 * R2; // Check keys - set expectedKeys; - expectedKeys.insert(3); - EXPECT(expectedKeys == R3.keys()); + set expected = list_of(3); + EXPECT(expected == R3.keys()); } /* ************************************************************************* */ @@ -189,11 +200,8 @@ TEST(Expression, ternary) { Expression ABC(composeThree, A, B, C); // Check keys - set expectedKeys; - expectedKeys.insert(1); - expectedKeys.insert(2); - expectedKeys.insert(3); - EXPECT(expectedKeys == ABC.keys()); + set expected = list_of(1)(2)(3); + EXPECT(expected == ABC.keys()); } /* ************************************************************************* */ From 4c76f390097d15ddc567a33c71a2ff6a5f25e051 Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 14 Oct 2014 09:55:34 +0200 Subject: [PATCH 30/35] Now uses dimensions --- gtsam_unstable/nonlinear/ExpressionFactor.h | 32 ++++++++++----------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/gtsam_unstable/nonlinear/ExpressionFactor.h b/gtsam_unstable/nonlinear/ExpressionFactor.h index a9bac6065..0e5e2da70 100644 --- a/gtsam_unstable/nonlinear/ExpressionFactor.h +++ b/gtsam_unstable/nonlinear/ExpressionFactor.h @@ -21,6 +21,9 @@ #include #include +#include +#include + namespace gtsam { /** @@ -63,6 +66,8 @@ public: virtual boost::shared_ptr linearize(const Values& x) const { + using namespace boost::adaptors; + // Only linearize if the factor is active if (!this->active(x)) return boost::shared_ptr(); @@ -76,21 +81,16 @@ public: // Whiten the corresponding system now // TODO ! this->noiseModel_->WhitenSystem(A, b); - // Terms, needed to create JacobianFactor below, are already here! - size_t n = terms.size(); - // Get dimensions of matrices - std::vector dimensions; - dimensions.reserve(n); - for (JacobianMap::const_iterator it = terms.begin(); it != terms.end(); - ++it) { - const std::pair& term = *it; - const Matrix& Ai = term.second; - dimensions.push_back(Ai.cols()); - } + std::map map = expression_.dimensions(); + size_t n = map.size(); + + // Get actual diemnsions. TODO: why can't we pass map | map_values directly? + std::vector dims(n); + boost::copy(map | map_values, dims.begin()); // Construct block matrix, is of right size but un-initialized - VerticalBlockMatrix Ab(dimensions, b.size(), true); + VerticalBlockMatrix Ab(dims, b.size(), true); // Check and add terms DenseIndex i = 0; // For block index @@ -101,19 +101,17 @@ public: Ab(i++) = Ai; } + // Fill in RHS Ab(n).col(0) = b; - // Get keys to construct factor - std::set keys = expression_.keys(); - // TODO pass unwhitened + noise model to Gaussian factor // For now, only linearized constrained factors have noise model at linear level!!! noiseModel::Constrained::shared_ptr constrained = // boost::dynamic_pointer_cast(this->noiseModel_); if (constrained) { - return boost::make_shared(keys, Ab, constrained->unit()); + return boost::make_shared(map | map_keys, Ab, constrained->unit()); } else - return boost::make_shared(keys, Ab); + return boost::make_shared(map | map_keys, Ab); } }; // ExpressionFactor From 027759300d007fdd5dbbd6fad429f9a636f3db55 Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 14 Oct 2014 11:13:09 +0200 Subject: [PATCH 31/35] size_t argument for check --- gtsam/nonlinear/NonlinearFactor.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gtsam/nonlinear/NonlinearFactor.cpp b/gtsam/nonlinear/NonlinearFactor.cpp index 7d229a1ea..08b131152 100644 --- a/gtsam/nonlinear/NonlinearFactor.cpp +++ b/gtsam/nonlinear/NonlinearFactor.cpp @@ -72,24 +72,24 @@ bool NoiseModelFactor::equals(const NonlinearFactor& f, double tol) const { && noiseModel_->equals(*e->noiseModel_, tol))); } -static void check(const SharedNoiseModel& noiseModel, const Vector& b) { +static void check(const SharedNoiseModel& noiseModel, size_t m) { if (!noiseModel) throw std::invalid_argument("NoiseModelFactor: no NoiseModel."); - if ((size_t) b.size() != noiseModel->dim()) + if (m != noiseModel->dim()) throw std::invalid_argument( "NoiseModelFactor was created with a NoiseModel of incorrect dimension."); } Vector NoiseModelFactor::whitenedError(const Values& c) const { const Vector b = unwhitenedError(c); - check(noiseModel_, b); + check(noiseModel_, b.size()); return noiseModel_->whiten(b); } double NoiseModelFactor::error(const Values& c) const { if (this->active(c)) { const Vector b = unwhitenedError(c); - check(noiseModel_, b); + check(noiseModel_, b.size()); return 0.5 * noiseModel_->distance(b); } else { return 0.0; @@ -106,7 +106,7 @@ boost::shared_ptr NoiseModelFactor::linearize( // Call evaluate error to get Jacobians and RHS vector b std::vector A(this->size()); Vector b = -unwhitenedError(x, A); - check(noiseModel_, b); + check(noiseModel_, b.size()); // Whiten the corresponding system now this->noiseModel_->WhitenSystem(A, b); From f3e1561105be29e2d9f22013f710a7c736977f93 Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 14 Oct 2014 11:13:49 +0200 Subject: [PATCH 32/35] Prepare VerticalBlockMatrix for filling --- gtsam_unstable/nonlinear/ExpressionFactor.h | 41 +++++++++++---------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/gtsam_unstable/nonlinear/ExpressionFactor.h b/gtsam_unstable/nonlinear/ExpressionFactor.h index 0e5e2da70..a26129d2c 100644 --- a/gtsam_unstable/nonlinear/ExpressionFactor.h +++ b/gtsam_unstable/nonlinear/ExpressionFactor.h @@ -42,6 +42,11 @@ public: const T& measurement, const Expression& expression) : NoiseModelFactor(noiseModel, expression.keys()), // measurement_(measurement), expression_(expression) { + if (!noiseModel) + throw std::invalid_argument("ExpressionFactor: no NoiseModel."); + if (noiseModel->dim() != T::dimension) + throw std::invalid_argument( + "ExpressionFactor was created with a NoiseModel of incorrect dimension."); } /** @@ -72,35 +77,32 @@ public: if (!this->active(x)) return boost::shared_ptr(); - // Evaluate error to get Jacobians and RHS vector b - JacobianMap terms; - T value = expression_.value(x, terms); - Vector b = -measurement_.localCoordinates(value); - // check(noiseModel_, b); // TODO: use, but defined in NonlinearFactor.cpp - - // Whiten the corresponding system now - // TODO ! this->noiseModel_->WhitenSystem(A, b); - // Get dimensions of matrices - std::map map = expression_.dimensions(); + std::map map = expression_.dimensions(); size_t n = map.size(); - // Get actual diemnsions. TODO: why can't we pass map | map_values directly? + // Get actual dimensions. TODO: why can't we pass map | map_values directly? std::vector dims(n); boost::copy(map | map_values, dims.begin()); // Construct block matrix, is of right size but un-initialized - VerticalBlockMatrix Ab(dims, b.size(), true); + VerticalBlockMatrix Ab(dims, T::dimension, true); - // Check and add terms + // Create and zero out blocks to be passed to expression_ DenseIndex i = 0; // For block index - for (JacobianMap::const_iterator it = terms.begin(); it != terms.end(); - ++it) { - const std::pair& term = *it; - const Matrix& Ai = term.second; - Ab(i++) = Ai; + typedef std::pair Pair; + BOOST_FOREACH(const Pair& keyValue, map) { + Ab(i++) = zeros(T::dimension, keyValue.second); } + // Evaluate error to get Jacobians and RHS vector b + // JacobianMap terms; + T value = expression_.value(x); + Vector b = -measurement_.localCoordinates(value); + + // Whiten the corresponding system now + // TODO ! this->noiseModel_->WhitenSystem(A, b); + // Fill in RHS Ab(n).col(0) = b; @@ -109,7 +111,8 @@ public: noiseModel::Constrained::shared_ptr constrained = // boost::dynamic_pointer_cast(this->noiseModel_); if (constrained) { - return boost::make_shared(map | map_keys, Ab, constrained->unit()); + return boost::make_shared(map | map_keys, Ab, + constrained->unit()); } else return boost::make_shared(map | map_keys, Ab); } From 1c3f328fb28f13a90c041329210e3ecbf16939f2 Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 14 Oct 2014 15:43:41 +0200 Subject: [PATCH 33/35] Successful switch to Blocks ! --- gtsam_unstable/nonlinear/Expression-inl.h | 238 +++--------------- gtsam_unstable/nonlinear/Expression.h | 5 - gtsam_unstable/nonlinear/ExpressionFactor.h | 36 ++- .../nonlinear/tests/testExpression.cpp | 8 +- .../nonlinear/tests/testExpressionFactor.cpp | 81 +++--- 5 files changed, 99 insertions(+), 269 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index f9a0c91bf..a16ad8a79 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -48,125 +48,7 @@ namespace gtsam { template class Expression; -typedef std::map JacobianMap; - -/// Move terms to array, destroys content -void move(JacobianMap& jacobians, std::vector& H) { - assert(H.size()==jacobians.size()); - size_t j = 0; - JacobianMap::iterator it = jacobians.begin(); - for (; it != jacobians.end(); ++it) - it->second.swap(H[j++]); -} - -//----------------------------------------------------------------------------- -/** - * Value and Jacobians - */ -template -class Augmented { - -private: - - T value_; - JacobianMap jacobians_; - - typedef std::pair Pair; - - /// Insert terms into jacobians_, adding if already exists - void add(const JacobianMap& terms) { - BOOST_FOREACH(const Pair& term, terms) { - JacobianMap::iterator it = jacobians_.find(term.first); - if (it != jacobians_.end()) - it->second += term.second; - else - jacobians_[term.first] = term.second; - } - } - - /// Insert terms into jacobians_, premultiplying by H, adding if already exists - void add(const Matrix& H, const JacobianMap& terms) { - BOOST_FOREACH(const Pair& term, terms) { - JacobianMap::iterator it = jacobians_.find(term.first); - if (it != jacobians_.end()) - it->second += H * term.second; - else - jacobians_[term.first] = H * term.second; - } - } - -public: - - /// Construct value that does not depend on anything - Augmented(const T& t) : - value_(t) { - } - - /// Construct value dependent on a single key - Augmented(const T& t, Key key) : - value_(t) { - size_t n = t.dim(); - jacobians_[key] = Eigen::MatrixXd::Identity(n, n); - } - - /// Construct value, pre-multiply jacobians by dTdA - Augmented(const T& t, const Matrix& dTdA, const JacobianMap& jacobians) : - value_(t) { - add(dTdA, jacobians); - } - - /// Construct value, pre-multiply jacobians - Augmented(const T& t, const Matrix& dTdA1, const JacobianMap& jacobians1, - const Matrix& dTdA2, const JacobianMap& jacobians2) : - value_(t) { - add(dTdA1, jacobians1); - add(dTdA2, jacobians2); - } - - /// Construct value, pre-multiply jacobians - Augmented(const T& t, const Matrix& dTdA1, const JacobianMap& jacobians1, - const Matrix& dTdA2, const JacobianMap& jacobians2, const Matrix& dTdA3, - const JacobianMap& jacobians3) : - value_(t) { - add(dTdA1, jacobians1); - add(dTdA2, jacobians2); - add(dTdA3, jacobians3); - } - - /// Return value - const T& value() const { - return value_; - } - - /// Return jacobians - const JacobianMap& jacobians() const { - return jacobians_; - } - - /// Return jacobians - JacobianMap& jacobians() { - return jacobians_; - } - - /// Not dependent on any key - bool constant() const { - return jacobians_.empty(); - } - - /// debugging - void print(const KeyFormatter& keyFormatter = DefaultKeyFormatter) { - BOOST_FOREACH(const Pair& term, jacobians_) - std::cout << "(" << keyFormatter(term.first) << ", " << term.second.rows() - << "x" << term.second.cols() << ") "; - std::cout << std::endl; - } - - /// Move terms to array, destroys content - void move(std::vector& H) { - move(jacobians_, H); - } - -}; +typedef std::map > JacobianMap; //----------------------------------------------------------------------------- /** @@ -244,39 +126,46 @@ public: return p ? boost::optional(p) : boost::none; } } - // *** This is the main entry point for reverseAD, called from Expression::augmented *** - // Called only once, either inserts identity into Jacobians (Leaf) or starts AD (Function) + /// reverseAD in case of Leaf + template + static void handleLeafCase(const Eigen::MatrixBase& dTdA, + JacobianMap& jacobians, Key key) { + JacobianMap::iterator it = jacobians.find(key); + if (it == jacobians.end()) { + std::cout << "No block for key " << key << std::endl; + throw std::runtime_error("Reverse AD internal error"); + } + // we have pre-loaded them with zeros + Eigen::Block& block = it->second; + block += dTdA; + } + /** + * *** This is the main entry point for reverseAD, called from Expression *** + * Called only once, either inserts I into Jacobians (Leaf) or starts AD (Function) + */ void startReverseAD(JacobianMap& jacobians) const { if (kind == Leaf) { // This branch will only be called on trivial Leaf expressions, i.e. Priors size_t n = T::Dim(); - jacobians[content.key] = Eigen::MatrixXd::Identity(n, n); + handleLeafCase(Eigen::MatrixXd::Identity(n, n), jacobians, content.key); } else if (kind == Function) // This is the more typical entry point, starting the AD pipeline - // It is inside the startReverseAD that the correctly dimensioned pipeline is chosen. + // Inside the startReverseAD that the correctly dimensioned pipeline is chosen. content.ptr->startReverseAD(jacobians); } // Either add to Jacobians (Leaf) or propagate (Function) void reverseAD(const Matrix& dTdA, JacobianMap& jacobians) const { - if (kind == Leaf) { - JacobianMap::iterator it = jacobians.find(content.key); - if (it != jacobians.end()) - it->second += dTdA; - else - jacobians[content.key] = dTdA; - } else if (kind == Function) + if (kind == Leaf) + handleLeafCase(dTdA, jacobians, content.key); + else if (kind == Function) content.ptr->reverseAD(dTdA, jacobians); } // Either add to Jacobians (Leaf) or propagate (Function) typedef Eigen::Matrix Jacobian2T; void reverseAD2(const Jacobian2T& dTdA, JacobianMap& jacobians) const { - if (kind == Leaf) { - JacobianMap::iterator it = jacobians.find(content.key); - if (it != jacobians.end()) - it->second += dTdA; - else - jacobians[content.key] = dTdA; - } else if (kind == Function) + if (kind == Leaf) + handleLeafCase(dTdA, jacobians, content.key); + else if (kind == Function) content.ptr->reverseAD2(dTdA, jacobians); } @@ -337,8 +226,8 @@ public: } /// Return dimensions for each argument - virtual std::map dimensions() const { - std::map map; + virtual std::map dimensions() const { + std::map map; return map; } @@ -350,9 +239,6 @@ public: /// Return value virtual T value(const Values& values) const = 0; - /// Return value and derivatives - virtual Augmented forward(const Values& values) const = 0; - /// Construct an execution trace for reverse AD virtual T traceExecution(const Values& values, ExecutionTrace& trace, char* raw) const = 0; @@ -380,11 +266,6 @@ public: return constant_; } - /// Return value and derivatives - virtual Augmented forward(const Values& values) const { - return Augmented(constant_); - } - /// Construct an execution trace for reverse AD virtual T traceExecution(const Values& values, ExecutionTrace& trace, char* raw) const { @@ -417,8 +298,8 @@ public: } /// Return dimensions for each argument - virtual std::map dimensions() const { - std::map map; + virtual std::map dimensions() const { + std::map map; map[key_] = T::dimension; return map; } @@ -428,11 +309,6 @@ public: return values.at(key_); } - /// Return value and derivatives - virtual Augmented forward(const Values& values) const { - return Augmented(values.at(key_), key_); - } - /// Construct an execution trace for reverse AD virtual T traceExecution(const Values& values, ExecutionTrace& trace, char* raw) const { @@ -540,9 +416,9 @@ struct GenerateFunctionalNode: Argument, Base { } /// Return dimensions for each argument - virtual std::map dimensions() const { - std::map map = Base::dimensions(); - std::map myMap = This::expression->dimensions(); + virtual std::map dimensions() const { + std::map map = Base::dimensions(); + std::map myMap = This::expression->dimensions(); map.insert(myMap.begin(), myMap.end()); return map; } @@ -690,19 +566,6 @@ public: return function_(this->template expression()->value(values), boost::none); } - /// Return value and derivatives - virtual Augmented forward(const Values& values) const { - using boost::none; - Augmented a1 = this->template expression()->forward(values); - - // Declare Jacobians - using boost::mpl::at_c; - typename at_c::type H1; - - T t = function_(a1.value(), H1); - return Augmented(t, H1, a1.jacobians()); - } - /// Construct an execution trace for reverse AD virtual T traceExecution(const Values& values, ExecutionTrace& trace, char* raw) const { @@ -756,21 +619,6 @@ public: none, none); } - /// Return value and derivatives - virtual Augmented forward(const Values& values) const { - using boost::none; - Augmented a1 = this->template expression()->forward(values); - Augmented a2 = this->template expression()->forward(values); - - // Declare Jacobians - using boost::mpl::at_c; - typename at_c::type H1; - typename at_c::type H2; - - T t = function_(a1.value(), a2.value(),H1,H2); - return Augmented(t, H1, a1.jacobians(), H2, a2.jacobians()); - } - /// Construct an execution trace for reverse AD virtual T traceExecution(const Values& values, ExecutionTrace& trace, char* raw) const { @@ -826,24 +674,6 @@ public: none, none, none); } - /// Return value and derivatives - virtual Augmented forward(const Values& values) const { - using boost::none; - Augmented a1 = this->template expression()->forward(values); - Augmented a2 = this->template expression()->forward(values); - Augmented a3 = this->template expression()->forward(values); - - // Declare Jacobians - using boost::mpl::at_c; - typename at_c::type H1; - typename at_c::type H2; - typename at_c::type H3; - - T t = function_(a1.value(), a2.value(), a3.value(),H1,H2,H3); - return Augmented(t, H1, a1.jacobians(), H2, a2.jacobians(), - H3, a3.jacobians()); - } - /// Construct an execution trace for reverse AD virtual T traceExecution(const Values& values, ExecutionTrace& trace, char* raw) const { @@ -859,5 +689,5 @@ public: }; //----------------------------------------------------------------------------- - } +} diff --git a/gtsam_unstable/nonlinear/Expression.h b/gtsam_unstable/nonlinear/Expression.h index 2f6367734..56b418ea3 100644 --- a/gtsam_unstable/nonlinear/Expression.h +++ b/gtsam_unstable/nonlinear/Expression.h @@ -112,11 +112,6 @@ public: return root_->dimensions(); } - /// Return value and derivatives, forward AD version - Augmented forward(const Values& values) const { - return root_->forward(values); - } - // Return size needed for memory buffer in traceExecution size_t traceSize() const { return root_->traceSize(); diff --git a/gtsam_unstable/nonlinear/ExpressionFactor.h b/gtsam_unstable/nonlinear/ExpressionFactor.h index a26129d2c..3c310b789 100644 --- a/gtsam_unstable/nonlinear/ExpressionFactor.h +++ b/gtsam_unstable/nonlinear/ExpressionFactor.h @@ -57,11 +57,26 @@ public: virtual Vector unwhitenedError(const Values& x, boost::optional&> H = boost::none) const { if (H) { + // H should be pre-allocated assert(H->size()==size()); - JacobianMap jacobians; - T value = expression_.value(x, jacobians); - // move terms to H, which is pre-allocated to correct size - move(jacobians, *H); + + // Get dimensions of Jacobian matrices + std::map map = expression_.dimensions(); + + // Create and zero out blocks to be passed to expression_ + DenseIndex i = 0; // For block index + typedef std::pair Pair; + std::map blocks; + BOOST_FOREACH(const Pair& pair, map) { + Matrix& Hi = H->at(i++); + size_t mi = pair.second; // width of i'th Jacobian + Hi.resize(T::dimension, mi); + Hi.setZero(); // zero out + Eigen::Block block = Hi.block(0,0,T::dimension, mi); + blocks.insert(std::make_pair(pair.first, block)); + } + + T value = expression_.value(x, blocks); return measurement_.localCoordinates(value); } else { const T& value = expression_.value(x); @@ -77,7 +92,7 @@ public: if (!this->active(x)) return boost::shared_ptr(); - // Get dimensions of matrices + // Get dimensions of Jacobian matrices std::map map = expression_.dimensions(); size_t n = map.size(); @@ -87,17 +102,18 @@ public: // Construct block matrix, is of right size but un-initialized VerticalBlockMatrix Ab(dims, T::dimension, true); + Ab.matrix().setZero(); // zero out - // Create and zero out blocks to be passed to expression_ + // Create blocks to be passed to expression_ DenseIndex i = 0; // For block index typedef std::pair Pair; - BOOST_FOREACH(const Pair& keyValue, map) { - Ab(i++) = zeros(T::dimension, keyValue.second); + std::map blocks; + BOOST_FOREACH(const Pair& pair, map) { + blocks.insert(std::make_pair(pair.first, Ab(i++))); } // Evaluate error to get Jacobians and RHS vector b - // JacobianMap terms; - T value = expression_.value(x); + T value = expression_.value(x, blocks); Vector b = -measurement_.localCoordinates(value); // Whiten the corresponding system now diff --git a/gtsam_unstable/nonlinear/tests/testExpression.cpp b/gtsam_unstable/nonlinear/tests/testExpression.cpp index e6fd12ab4..a2aa12868 100644 --- a/gtsam_unstable/nonlinear/tests/testExpression.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpression.cpp @@ -65,13 +65,11 @@ TEST(Expression, leaf) { values.insert(100, someR); JacobianMap expected; - expected[100] = eye(3); - - Augmented actual1 = R.forward(values); - EXPECT(assert_equal(someR, actual1.value())); - EXPECT(actual1.jacobians() == expected); + Matrix H = eye(3); + expected.insert(make_pair(100,H.block(0,0,3,3))); JacobianMap actualMap2; + actualMap2.insert(make_pair(100,H.block(0,0,3,3))); Rot3 actual2 = R.reverse(values, actualMap2); EXPECT(assert_equal(someR, actual2)); EXPECT(actualMap2 == expected); diff --git a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp index 5867f9dcf..93c2ecac1 100644 --- a/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpressionFactor.cpp @@ -62,41 +62,41 @@ TEST(ExpressionFactor, Leaf) { EXPECT( assert_equal(*old.linearize(values), *gf2, 1e-9)); } -/* ************************************************************************* */ -// non-zero noise model -TEST(ExpressionFactor, Model) { - using namespace leaf; - - SharedNoiseModel model = noiseModel::Diagonal::Sigmas(Vector2(0.1, 0.01)); - - // Create old-style factor to create expected value and derivatives - PriorFactor old(2, Point2(0, 0), model); - - // Concise version - ExpressionFactor f(model, Point2(0, 0), p); - EXPECT_DOUBLES_EQUAL(old.error(values), f.error(values), 1e-9); - EXPECT_LONGS_EQUAL(2, f.dim()); - boost::shared_ptr gf2 = f.linearize(values); - EXPECT( assert_equal(*old.linearize(values), *gf2, 1e-9)); -} - -/* ************************************************************************* */ -// Constrained noise model -TEST(ExpressionFactor, Constrained) { - using namespace leaf; - - SharedDiagonal model = noiseModel::Constrained::MixedSigmas(Vector2(0.2, 0)); - - // Create old-style factor to create expected value and derivatives - PriorFactor old(2, Point2(0, 0), model); - - // Concise version - ExpressionFactor f(model, Point2(0, 0), p); - EXPECT_DOUBLES_EQUAL(old.error(values), f.error(values), 1e-9); - EXPECT_LONGS_EQUAL(2, f.dim()); - boost::shared_ptr gf2 = f.linearize(values); - EXPECT( assert_equal(*old.linearize(values), *gf2, 1e-9)); -} +///* ************************************************************************* */ +//// non-zero noise model +//TEST(ExpressionFactor, Model) { +// using namespace leaf; +// +// SharedNoiseModel model = noiseModel::Diagonal::Sigmas(Vector2(0.1, 0.01)); +// +// // Create old-style factor to create expected value and derivatives +// PriorFactor old(2, Point2(0, 0), model); +// +// // Concise version +// ExpressionFactor f(model, Point2(0, 0), p); +// EXPECT_DOUBLES_EQUAL(old.error(values), f.error(values), 1e-9); +// EXPECT_LONGS_EQUAL(2, f.dim()); +// boost::shared_ptr gf2 = f.linearize(values); +// EXPECT( assert_equal(*old.linearize(values), *gf2, 1e-9)); +//} +// +///* ************************************************************************* */ +//// Constrained noise model +//TEST(ExpressionFactor, Constrained) { +// using namespace leaf; +// +// SharedDiagonal model = noiseModel::Constrained::MixedSigmas(Vector2(0.2, 0)); +// +// // Create old-style factor to create expected value and derivatives +// PriorFactor old(2, Point2(0, 0), model); +// +// // Concise version +// ExpressionFactor f(model, Point2(0, 0), p); +// EXPECT_DOUBLES_EQUAL(old.error(values), f.error(values), 1e-9); +// EXPECT_LONGS_EQUAL(2, f.dim()); +// boost::shared_ptr gf2 = f.linearize(values); +// EXPECT( assert_equal(*old.linearize(values), *gf2, 1e-9)); +//} /* ************************************************************************* */ // Unary(Leaf)) @@ -256,15 +256,6 @@ TEST(ExpressionFactor, tree) { Point2_ xy_hat(PinholeCamera::project_to_camera, p_cam); Point2_ uv_hat(K, &Cal3_S2::uncalibrate, xy_hat); - // Compare reverse and forward - { - JacobianMap expectedMap; // via reverse - Point2 expectedValue = uv_hat.reverse(values, expectedMap); - Augmented actual = uv_hat.forward(values); - EXPECT(assert_equal(expectedValue, actual.value())); - EXPECT(actual.jacobians() == expectedMap); - } - // Create factor and check value, dimension, linearization ExpressionFactor f(model, measured, uv_hat); EXPECT_DOUBLES_EQUAL(expected_error, f.error(values), 1e-9); @@ -292,7 +283,7 @@ TEST(ExpressionFactor, tree) { /* ************************************************************************* */ -TEST(ExpressionFactor, compose1) { +TEST(ExpressionFactor, Compose1) { // Create expression Rot3_ R1(1), R2(2); From c971207abfcb0f58ee0cdcbb76aa7f46e8c18eed Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 14 Oct 2014 17:16:31 +0200 Subject: [PATCH 34/35] Switched to vector for dimensions --- gtsam_unstable/nonlinear/Expression.h | 11 +++- gtsam_unstable/nonlinear/ExpressionFactor.h | 65 ++++++++----------- .../nonlinear/tests/testExpression.cpp | 18 ++--- 3 files changed, 40 insertions(+), 54 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression.h b/gtsam_unstable/nonlinear/Expression.h index 56b418ea3..8ef72f914 100644 --- a/gtsam_unstable/nonlinear/Expression.h +++ b/gtsam_unstable/nonlinear/Expression.h @@ -22,6 +22,8 @@ #include "Expression-inl.h" #include #include +#include +#include namespace gtsam { @@ -107,9 +109,12 @@ public: return root_->keys(); } - /// Return dimensions for each argument, as a map (allows order to change later) - std::map dimensions() const { - return root_->dimensions(); + /// Return dimensions for each argument, must be same order as keys + std::vector dimensions() const { + std::map map = root_->dimensions(); + std::vector dims(map.size()); + boost::copy(map | boost::adaptors::map_values, dims.begin()); + return dims; } // Return size needed for memory buffer in traceExecution diff --git a/gtsam_unstable/nonlinear/ExpressionFactor.h b/gtsam_unstable/nonlinear/ExpressionFactor.h index 3c310b789..a2e9fb273 100644 --- a/gtsam_unstable/nonlinear/ExpressionFactor.h +++ b/gtsam_unstable/nonlinear/ExpressionFactor.h @@ -21,9 +21,6 @@ #include #include -#include -#include - namespace gtsam { /** @@ -61,19 +58,16 @@ public: assert(H->size()==size()); // Get dimensions of Jacobian matrices - std::map map = expression_.dimensions(); + std::vector dims = expression_.dimensions(); // Create and zero out blocks to be passed to expression_ - DenseIndex i = 0; // For block index - typedef std::pair Pair; - std::map blocks; - BOOST_FOREACH(const Pair& pair, map) { - Matrix& Hi = H->at(i++); - size_t mi = pair.second; // width of i'th Jacobian - Hi.resize(T::dimension, mi); + JacobianMap blocks; + for(DenseIndex i=0;iat(i); + Hi.resize(T::dimension, dims[i]); Hi.setZero(); // zero out - Eigen::Block block = Hi.block(0,0,T::dimension, mi); - blocks.insert(std::make_pair(pair.first, block)); + Eigen::Block block = Hi.block(0,0,T::dimension, dims[i]); + blocks.insert(std::make_pair(keys_[i], block)); } T value = expression_.value(x, blocks); @@ -84,53 +78,46 @@ public: } } - virtual boost::shared_ptr linearize(const Values& x) const { - - using namespace boost::adaptors; - - // Only linearize if the factor is active - if (!this->active(x)) - return boost::shared_ptr(); + // Internal function to allocate a VerticalBlockMatrix and + // create Eigen::Block views into it + VerticalBlockMatrix prepareBlocks(JacobianMap& blocks) const { // Get dimensions of Jacobian matrices - std::map map = expression_.dimensions(); - size_t n = map.size(); - - // Get actual dimensions. TODO: why can't we pass map | map_values directly? - std::vector dims(n); - boost::copy(map | map_values, dims.begin()); + std::vector dims = expression_.dimensions(); // Construct block matrix, is of right size but un-initialized VerticalBlockMatrix Ab(dims, T::dimension, true); Ab.matrix().setZero(); // zero out // Create blocks to be passed to expression_ - DenseIndex i = 0; // For block index - typedef std::pair Pair; - std::map blocks; - BOOST_FOREACH(const Pair& pair, map) { - blocks.insert(std::make_pair(pair.first, Ab(i++))); - } + for(DenseIndex i=0;i linearize(const Values& x) const { + + // Construct VerticalBlockMatrix and views into it + JacobianMap blocks; + VerticalBlockMatrix Ab = prepareBlocks(blocks); // Evaluate error to get Jacobians and RHS vector b T value = expression_.value(x, blocks); - Vector b = -measurement_.localCoordinates(value); + Ab(size()).col(0) = -measurement_.localCoordinates(value); // Whiten the corresponding system now - // TODO ! this->noiseModel_->WhitenSystem(A, b); - - // Fill in RHS - Ab(n).col(0) = b; + // TODO ! this->noiseModel_->WhitenSystem(Ab); // TODO pass unwhitened + noise model to Gaussian factor // For now, only linearized constrained factors have noise model at linear level!!! noiseModel::Constrained::shared_ptr constrained = // boost::dynamic_pointer_cast(this->noiseModel_); if (constrained) { - return boost::make_shared(map | map_keys, Ab, + return boost::make_shared(this->keys(), Ab, constrained->unit()); } else - return boost::make_shared(map | map_keys, Ab); + return boost::make_shared(this->keys(), Ab); } }; // ExpressionFactor diff --git a/gtsam_unstable/nonlinear/tests/testExpression.cpp b/gtsam_unstable/nonlinear/tests/testExpression.cpp index a2aa12868..ad738d50c 100644 --- a/gtsam_unstable/nonlinear/tests/testExpression.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpression.cpp @@ -33,8 +33,6 @@ using boost::assign::map_list_of; using namespace std; using namespace gtsam; -typedef pair Pair; - /* ************************************************************************* */ template @@ -66,10 +64,10 @@ TEST(Expression, leaf) { JacobianMap expected; Matrix H = eye(3); - expected.insert(make_pair(100,H.block(0,0,3,3))); + expected.insert(make_pair(100, H.block(0, 0, 3, 3))); JacobianMap actualMap2; - actualMap2.insert(make_pair(100,H.block(0,0,3,3))); + actualMap2.insert(make_pair(100, H.block(0, 0, 3, 3))); Rot3 actual2 = R.reverse(values, actualMap2); EXPECT(assert_equal(someR, actual2)); EXPECT(actualMap2 == expected); @@ -105,11 +103,9 @@ TEST(Expression, BinaryKeys) { /* ************************************************************************* */ // dimensions TEST(Expression, BinaryDimensions) { - map expected = map_list_of(1, 6)(2, 3), // + vector expected = list_of(6)(3), // actual = binary::p_cam.dimensions(); - EXPECT_LONGS_EQUAL(expected.size(),actual.size()); - BOOST_FOREACH(Pair pair, actual) - EXPECT_LONGS_EQUAL(expected[pair.first],pair.second); + EXPECT(expected==actual); } /* ************************************************************************* */ // Binary(Leaf,Unary(Binary(Leaf,Leaf))) @@ -131,11 +127,9 @@ TEST(Expression, TreeKeys) { /* ************************************************************************* */ // dimensions TEST(Expression, TreeDimensions) { - map expected = map_list_of(1, 6)(2, 3)(3, 5), // + vector expected = list_of(6)(3)(5), // actual = tree::uv_hat.dimensions(); - EXPECT_LONGS_EQUAL(expected.size(),actual.size()); - BOOST_FOREACH(Pair pair, actual) - EXPECT_LONGS_EQUAL(expected[pair.first],pair.second); + EXPECT(expected==actual); } /* ************************************************************************* */ From baaeaacabe952a38f145b5e009181563ef2118ba Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 14 Oct 2014 17:46:57 +0200 Subject: [PATCH 35/35] Made dimensions constant property. Now performance is ***blazing***, way past custom factors. --- gtsam_unstable/nonlinear/Expression-inl.h | 27 ++++++---- gtsam_unstable/nonlinear/Expression.h | 62 +++++++++++++---------- 2 files changed, 52 insertions(+), 37 deletions(-) diff --git a/gtsam_unstable/nonlinear/Expression-inl.h b/gtsam_unstable/nonlinear/Expression-inl.h index a16ad8a79..3d619b5b4 100644 --- a/gtsam_unstable/nonlinear/Expression-inl.h +++ b/gtsam_unstable/nonlinear/Expression-inl.h @@ -24,8 +24,8 @@ #include #include #include -#include // for placement new -struct TestBinaryExpression; +#include +#include // template meta-programming headers #include @@ -37,9 +37,10 @@ struct TestBinaryExpression; #include #include #include - namespace MPL = boost::mpl::placeholders; +#include // for placement new + class ExpressionFactorBinaryTest; // Forward declare for testing @@ -225,12 +226,20 @@ public: return keys; } - /// Return dimensions for each argument - virtual std::map dimensions() const { + /// Return dimensions for each argument, as a map + virtual std::map dims() const { std::map map; return map; } + /// Return dimensions as vector, ordered as keys + std::vector dimensions() const { + std::map map = dims(); + std::vector dims(map.size()); + boost::copy(map | boost::adaptors::map_values, dims.begin()); + return dims; + } + // Return size needed for memory buffer in traceExecution size_t traceSize() const { return traceSize_; @@ -298,7 +307,7 @@ public: } /// Return dimensions for each argument - virtual std::map dimensions() const { + virtual std::map dims() const { std::map map; map[key_] = T::dimension; return map; @@ -416,9 +425,9 @@ struct GenerateFunctionalNode: Argument, Base { } /// Return dimensions for each argument - virtual std::map dimensions() const { - std::map map = Base::dimensions(); - std::map myMap = This::expression->dimensions(); + virtual std::map dims() const { + std::map map = Base::dims(); + std::map myMap = This::expression->dims(); map.insert(myMap.begin(), myMap.end()); return map; } diff --git a/gtsam_unstable/nonlinear/Expression.h b/gtsam_unstable/nonlinear/Expression.h index 8ef72f914..7556ea629 100644 --- a/gtsam_unstable/nonlinear/Expression.h +++ b/gtsam_unstable/nonlinear/Expression.h @@ -22,8 +22,6 @@ #include "Expression-inl.h" #include #include -#include -#include namespace gtsam { @@ -36,43 +34,51 @@ class Expression { private: // Paul's trick shared pointer, polymorphic root of entire expression tree - boost::shared_ptr > root_; + const boost::shared_ptr > root_; + + // Fixed dimensions: an Expression is assumed unmutable + const std::vector dimensions_; public: // Construct a constant expression Expression(const T& value) : - root_(new ConstantExpression(value)) { + root_(new ConstantExpression(value)), // + dimensions_(root_->dimensions()) { } // Construct a leaf expression, with Key Expression(const Key& key) : - root_(new LeafExpression(key)) { + root_(new LeafExpression(key)), // + dimensions_(root_->dimensions()) { } // Construct a leaf expression, with Symbol Expression(const Symbol& symbol) : - root_(new LeafExpression(symbol)) { + root_(new LeafExpression(symbol)), // + dimensions_(root_->dimensions()) { } // Construct a leaf expression, creating Symbol Expression(unsigned char c, size_t j) : - root_(new LeafExpression(Symbol(c, j))) { + root_(new LeafExpression(Symbol(c, j))), // + dimensions_(root_->dimensions()) { } /// Construct a nullary method expression template Expression(const Expression& expression, - T (A::*method)(typename Optional::type) const) { - root_.reset( - new UnaryExpression(boost::bind(method, _1, _2), expression)); + T (A::*method)(typename Optional::type) const) : + root_(new UnaryExpression(boost::bind(method, _1, _2), expression)), // + dimensions_(root_->dimensions()) { } /// Construct a unary function expression template Expression(typename UnaryExpression::Function function, - const Expression& expression) { - root_.reset(new UnaryExpression(function, expression)); + const Expression& expression) : + root_(new UnaryExpression(function, expression)), // + dimensions_(root_->dimensions()) { } /// Construct a unary method expression @@ -80,28 +86,31 @@ public: Expression(const Expression& expression1, T (A1::*method)(const A2&, typename Optional::type, typename Optional::type) const, - const Expression& expression2) { - root_.reset( - new BinaryExpression(boost::bind(method, _1, _2, _3, _4), - expression1, expression2)); + const Expression& expression2) : + root_( + new BinaryExpression(boost::bind(method, _1, _2, _3, _4), + expression1, expression2)), // + dimensions_(root_->dimensions()) { } /// Construct a binary function expression template Expression(typename BinaryExpression::Function function, - const Expression& expression1, const Expression& expression2) { - root_.reset( - new BinaryExpression(function, expression1, expression2)); + const Expression& expression1, const Expression& expression2) : + root_( + new BinaryExpression(function, expression1, expression2)), // + dimensions_(root_->dimensions()) { } /// Construct a ternary function expression template Expression(typename TernaryExpression::Function function, const Expression& expression1, const Expression& expression2, - const Expression& expression3) { - root_.reset( - new TernaryExpression(function, expression1, expression2, - expression3)); + const Expression& expression3) : + root_( + new TernaryExpression(function, expression1, + expression2, expression3)), // + dimensions_(root_->dimensions()) { } /// Return keys that play in this expression @@ -110,11 +119,8 @@ public: } /// Return dimensions for each argument, must be same order as keys - std::vector dimensions() const { - std::map map = root_->dimensions(); - std::vector dims(map.size()); - boost::copy(map | boost::adaptors::map_values, dims.begin()); - return dims; + const std::vector& dimensions() const { + return dimensions_; } // Return size needed for memory buffer in traceExecution