The great collapse: instead of two recursively defined classes, there is now only one. The Record class is now a (recursive) inner class.

release/4.3a0
dellaert 2014-10-13 10:50:05 +02:00
parent bc9e11f43c
commit da0e5fe52f
2 changed files with 76 additions and 149 deletions

View File

@ -38,6 +38,9 @@ struct TestBinaryExpression;
namespace MPL = boost::mpl::placeholders; namespace MPL = boost::mpl::placeholders;
class ExpressionFactorBinaryTest;
// Forward declare for testing
namespace gtsam { namespace gtsam {
template<typename T> template<typename T>
@ -421,84 +424,6 @@ public:
}; };
//-----------------------------------------------------------------------------
/// meta-function to generate fixed-size JacobianTA type
template<class T, class A>
struct Jacobian {
typedef Eigen::Matrix<double, T::dimension, A::dimension> type;
typedef boost::optional<type&> 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<class T, class A, size_t N>
struct JacobianTrace {
ExecutionTrace<A> trace;
typename Jacobian<T, A>::type dTdA;
};
/**
* Recursive Record Class for Functional Expressions
*/
template<class T, class A, class Base>
struct GenerateRecord: JacobianTrace<T, A, Base::N + 1>, Base {
typedef T return_type;
static size_t const N = Base::N + 1;
typedef JacobianTrace<T, A, N> 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<T::dimension, A>::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<double, 2, T::dimension> 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<class T, class TYPES>
struct Record: public boost::mpl::fold<TYPES, CallRecord<T::dimension>,
GenerateRecord<T, MPL::_2, MPL::_1> >::type {
/// Access Trace
template<class A, size_t N>
ExecutionTrace<A>& trace() {
return static_cast<JacobianTrace<T, A, N>&>(*this).trace;
}
/// Access Jacobian
template<class A, size_t N>
typename Jacobian<T, A>::type& jacobian() {
return static_cast<JacobianTrace<T, A, N>&>(*this).dTdA;
}
};
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Below we use the "Class Composition" technique described in the book // Below we use the "Class Composition" technique described in the book
// C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost // C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost
@ -530,6 +455,13 @@ struct Record: public boost::mpl::fold<TYPES, CallRecord<T::dimension>,
// by invoking boost::mpl::fold over the meta-function GenerateFunctionalNode // by invoking boost::mpl::fold over the meta-function GenerateFunctionalNode
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/// meta-function to generate fixed-size JacobianTA type
template<class T, class A>
struct Jacobian {
typedef Eigen::Matrix<double, T::dimension, A::dimension> type;
typedef boost::optional<type&> optional;
};
/** /**
* Base case for recursive FunctionalNode class * Base case for recursive FunctionalNode class
*/ */
@ -537,10 +469,10 @@ template<class T>
struct FunctionalBase: ExpressionNode<T> { struct FunctionalBase: ExpressionNode<T> {
static size_t const N = 0; // number of arguments static size_t const N = 0; // number of arguments
typedef CallRecord<T::dimension> Record2; typedef CallRecord<T::dimension> Record;
/// Construct an execution trace for reverse AD /// 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<ExpressionNode<A> > expression; boost::shared_ptr<ExpressionNode<A> > expression;
}; };
/**
* Building block for Recursive Record Class
* Records the evaluation of a single argument in a functional expression
*/
template<class T, class A, size_t N>
struct JacobianTrace {
ExecutionTrace<A> trace;
typename Jacobian<T, A>::type dTdA;
};
/** /**
* Recursive Definition of Functional ExpressionNode * Recursive Definition of Functional ExpressionNode
*/ */
@ -572,17 +514,15 @@ struct GenerateFunctionalNode: Argument<T, A, Base::N + 1>, Base {
return keys; return keys;
} }
/** /// Recursive Record Class for Functional Expressions
* Recursive Record Class for Functional Expressions struct Record: JacobianTrace<T, A, N>, Base::Record {
*/
struct Record2: JacobianTrace<T, A, N>, Base::Record2 {
typedef T return_type; typedef T return_type;
typedef JacobianTrace<T, A, N> This; typedef JacobianTrace<T, A, N> This;
/// Print to std::cout /// Print to std::cout
virtual void print(const std::string& indent) const { virtual void print(const std::string& indent) const {
Base::Record2::print(indent); Base::Record::print(indent);
static const Eigen::IOFormat matlab(0, 1, " ", "; ", "", "", "[", "]"); static const Eigen::IOFormat matlab(0, 1, " ", "; ", "", "", "[", "]");
std::cout << This::dTdA.format(matlab) << std::endl; std::cout << This::dTdA.format(matlab) << std::endl;
This::trace.print(indent); This::trace.print(indent);
@ -590,13 +530,13 @@ struct GenerateFunctionalNode: Argument<T, A, Base::N + 1>, Base {
/// Start the reverse AD process /// Start the reverse AD process
virtual void startReverseAD(JacobianMap& jacobians) const { virtual void startReverseAD(JacobianMap& jacobians) const {
Base::Record2::startReverseAD(jacobians); Base::Record::startReverseAD(jacobians);
Select<T::dimension, A>::reverseAD(This::trace, This::dTdA, jacobians); Select<T::dimension, A>::reverseAD(This::trace, This::dTdA, jacobians);
} }
/// Given df/dT, multiply in dT/dA and continue reverse AD process /// Given df/dT, multiply in dT/dA and continue reverse AD process
virtual void reverseAD(const Matrix& dFdT, JacobianMap& jacobians) const { 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); This::trace.reverseAD(dFdT * This::dTdA, jacobians);
} }
@ -604,15 +544,16 @@ struct GenerateFunctionalNode: Argument<T, A, Base::N + 1>, Base {
typedef Eigen::Matrix<double, 2, T::dimension> Jacobian2T; typedef Eigen::Matrix<double, 2, T::dimension> Jacobian2T;
virtual void reverseAD2(const Jacobian2T& dFdT, virtual void reverseAD2(const Jacobian2T& dFdT,
JacobianMap& jacobians) const { JacobianMap& jacobians) const {
Base::Record2::reverseAD2(dFdT, jacobians); Base::Record::reverseAD2(dFdT, jacobians);
This::trace.reverseAD2(dFdT * This::dTdA, jacobians); This::trace.reverseAD2(dFdT * This::dTdA, jacobians);
} }
}; };
/// Construct an execution trace for reverse AD /// 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); 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(); raw = raw + This::expression->traceSize();
} }
}; };
@ -639,10 +580,27 @@ struct FunctionalNode {
return static_cast<Argument<T, A, N> const &>(*this).expression; return static_cast<Argument<T, A, N> const &>(*this).expression;
} }
/// Provide convenience access to Record storage
struct Record: public Base::Record {
/// Access Trace
template<class A, size_t N>
ExecutionTrace<A>& trace() {
return static_cast<JacobianTrace<T, A, N>&>(*this).trace;
}
/// Access Jacobian
template<class A, size_t N>
typename Jacobian<T, A>::type& jacobian() {
return static_cast<JacobianTrace<T, A, N>&>(*this).dTdA;
}
};
/// Construct an execution trace for reverse AD /// Construct an execution trace for reverse AD
virtual T traceExecution(const Values& values, ExecutionTrace<T>& trace, virtual T traceExecution(const Values& values, ExecutionTrace<T>& trace,
char* raw) const { char* raw) const {
typename Base::Record2* record = new (raw) typename Base::Record2(); Record* record = new (raw) Record();
trace.setFunction(record); trace.setFunction(record);
raw = (char*) (record + 1); raw = (char*) (record + 1);
@ -658,12 +616,11 @@ struct FunctionalNode {
template<class T, class A1> template<class T, class A1>
class UnaryExpression: public FunctionalNode<T, boost::mpl::vector<A1> >::type { class UnaryExpression: public FunctionalNode<T, boost::mpl::vector<A1> >::type {
/// The automatically generated Base class
typedef FunctionalNode<T, boost::mpl::vector<A1> > Base;
public: public:
typedef boost::function<T(const A1&, typename Jacobian<T, A1>::optional)> Function; typedef boost::function<T(const A1&, typename Jacobian<T, A1>::optional)> Function;
typedef typename FunctionalNode<T, boost::mpl::vector<A1> >::type Base;
typedef typename Base::Record Record;
private: private:
@ -694,10 +651,6 @@ public:
return Augmented<T>(t, dTdA1, a1.jacobians()); return Augmented<T>(t, dTdA1, a1.jacobians());
} }
/// CallRecord structure for reverse AD
typedef boost::mpl::vector<A1> Arguments;
typedef Record<T, Arguments> Record;
/// Construct an execution trace for reverse AD /// Construct an execution trace for reverse AD
virtual T traceExecution(const Values& values, ExecutionTrace<T>& trace, virtual T traceExecution(const Values& values, ExecutionTrace<T>& trace,
char* raw) const { char* raw) const {
@ -724,6 +677,8 @@ public:
typedef boost::function< typedef boost::function<
T(const A1&, const A2&, typename Jacobian<T, A1>::optional, T(const A1&, const A2&, typename Jacobian<T, A1>::optional,
typename Jacobian<T, A2>::optional)> Function; typename Jacobian<T, A2>::optional)> Function;
typedef typename FunctionalNode<T, boost::mpl::vector<A1, A2> >::type Base;
typedef typename Base::Record Record;
private: private:
@ -740,7 +695,7 @@ private:
} }
friend class Expression<T> ; friend class Expression<T> ;
friend struct ::TestBinaryExpression; friend class ::ExpressionFactorBinaryTest;
public: public:
@ -765,10 +720,6 @@ public:
return Augmented<T>(t, dTdA1, a1.jacobians(), dTdA2, a2.jacobians()); return Augmented<T>(t, dTdA1, a1.jacobians(), dTdA2, a2.jacobians());
} }
/// CallRecord structure for reverse AD
typedef boost::mpl::vector<A1, A2> Arguments;
typedef Record<T, Arguments> Record;
/// Construct an execution trace for reverse AD /// Construct an execution trace for reverse AD
virtual T traceExecution(const Values& values, ExecutionTrace<T>& trace, virtual T traceExecution(const Values& values, ExecutionTrace<T>& trace,
char* raw) const { char* raw) const {
@ -801,6 +752,8 @@ public:
T(const A1&, const A2&, const A3&, typename Jacobian<T, A1>::optional, T(const A1&, const A2&, const A3&, typename Jacobian<T, A1>::optional,
typename Jacobian<T, A2>::optional, typename Jacobian<T, A2>::optional,
typename Jacobian<T, A3>::optional)> Function; typename Jacobian<T, A3>::optional)> Function;
typedef typename FunctionalNode<T, boost::mpl::vector<A1, A2, A3> >::type Base;
typedef typename Base::Record Record;
private: private:
@ -847,10 +800,6 @@ public:
a3.jacobians()); a3.jacobians());
} }
/// CallRecord structure for reverse AD
typedef boost::mpl::vector<A1, A2, A3> Arguments;
typedef Record<T, Arguments> Record;
/// Construct an execution trace for reverse AD /// Construct an execution trace for reverse AD
virtual T traceExecution(const Values& values, ExecutionTrace<T>& trace, virtual T traceExecution(const Values& values, ExecutionTrace<T>& trace,
char* raw) const { char* raw) const {

View File

@ -48,7 +48,7 @@ Point2_ p(2);
/* ************************************************************************* */ /* ************************************************************************* */
// Leaf // Leaf
TEST(ExpressionFactor, leaf) { TEST(ExpressionFactor, Leaf) {
using namespace leaf; using namespace leaf;
// Create old-style factor to create expected value and derivatives // Create old-style factor to create expected value and derivatives
@ -64,7 +64,7 @@ TEST(ExpressionFactor, leaf) {
/* ************************************************************************* */ /* ************************************************************************* */
// non-zero noise model // non-zero noise model
TEST(ExpressionFactor, model) { TEST(ExpressionFactor, Model) {
using namespace leaf; using namespace leaf;
SharedNoiseModel model = noiseModel::Diagonal::Sigmas(Vector2(0.1, 0.01)); SharedNoiseModel model = noiseModel::Diagonal::Sigmas(Vector2(0.1, 0.01));
@ -82,7 +82,7 @@ TEST(ExpressionFactor, model) {
/* ************************************************************************* */ /* ************************************************************************* */
// Constrained noise model // Constrained noise model
TEST(ExpressionFactor, constrained) { TEST(ExpressionFactor, Constrained) {
using namespace leaf; using namespace leaf;
SharedDiagonal model = noiseModel::Constrained::MixedSigmas(Vector2(0.2, 0)); SharedDiagonal model = noiseModel::Constrained::MixedSigmas(Vector2(0.2, 0));
@ -100,7 +100,7 @@ TEST(ExpressionFactor, constrained) {
/* ************************************************************************* */ /* ************************************************************************* */
// Unary(Leaf)) // Unary(Leaf))
TEST(ExpressionFactor, unary) { TEST(ExpressionFactor, Unary) {
// Create some values // Create some values
Values values; Values values;
@ -121,25 +121,21 @@ TEST(ExpressionFactor, unary) {
boost::dynamic_pointer_cast<JacobianFactor>(gf); boost::dynamic_pointer_cast<JacobianFactor>(gf);
EXPECT( assert_equal(expected, *jf, 1e-9)); EXPECT( assert_equal(expected, *jf, 1e-9));
} }
/* ************************************************************************* */ /* ************************************************************************* */
struct TestBinaryExpression { static Point2 myUncal(const Cal3_S2& K, const Point2& p,
static Point2 myUncal(const Cal3_S2& K, const Point2& p,
boost::optional<Matrix25&> Dcal, boost::optional<Matrix2&> Dp) { boost::optional<Matrix25&> Dcal, boost::optional<Matrix2&> Dp) {
return K.uncalibrate(p, Dcal, Dp); return K.uncalibrate(p, Dcal, Dp);
} }
Cal3_S2_ K_;
Point2_ p_;
BinaryExpression<Point2, Cal3_S2, Point2> binary_;
TestBinaryExpression() :
K_(1), p_(2), binary_(myUncal, K_, p_) {
}
};
/* ************************************************************************* */
// Binary(Leaf,Leaf) // Binary(Leaf,Leaf)
TEST(ExpressionFactor, binary) { TEST(ExpressionFactor, Binary) {
typedef BinaryExpression<Point2, Cal3_S2, Point2> Binary; typedef BinaryExpression<Point2, Cal3_S2, Point2> Binary;
TestBinaryExpression tester;
Cal3_S2_ K_(1);
Point2_ p_(2);
Binary binary(myUncal, K_, p_);
// Create some values // Create some values
Values values; Values values;
@ -156,14 +152,14 @@ TEST(ExpressionFactor, binary) {
EXPECT_LONGS_EQUAL(expectedRecordSize, sizeof(Binary::Record)); EXPECT_LONGS_EQUAL(expectedRecordSize, sizeof(Binary::Record));
// Check size // Check size
size_t size = tester.binary_.traceSize(); size_t size = binary.traceSize();
CHECK(size); CHECK(size);
EXPECT_LONGS_EQUAL(expectedRecordSize, size); EXPECT_LONGS_EQUAL(expectedRecordSize, size);
// Use Variable Length Array, allocated on stack by gcc // Use Variable Length Array, allocated on stack by gcc
// Note unclear for Clang: http://clang.llvm.org/compatibility.html#vla // Note unclear for Clang: http://clang.llvm.org/compatibility.html#vla
char raw[size]; char raw[size];
ExecutionTrace<Point2> trace; ExecutionTrace<Point2> trace;
Point2 value = tester.binary_.traceExecution(values, trace, raw); Point2 value = binary.traceExecution(values, trace, raw);
// trace.print(); // trace.print();
// Expected Jacobians // Expected Jacobians
@ -181,7 +177,7 @@ TEST(ExpressionFactor, binary) {
} }
/* ************************************************************************* */ /* ************************************************************************* */
// Unary(Binary(Leaf,Leaf)) // Unary(Binary(Leaf,Leaf))
TEST(ExpressionFactor, shallow) { TEST(ExpressionFactor, Shallow) {
// Create some values // Create some values
Values values; Values values;
@ -434,27 +430,9 @@ namespace mpl = boost::mpl;
template<class T> struct Incomplete; template<class T> struct Incomplete;
typedef mpl::vector<Pose3, Point3, Cal3_S2> MyTypes; typedef mpl::vector<Pose3, Point3, Cal3_S2> MyTypes;
typedef Record<Point2, MyTypes> Generated; typedef FunctionalNode<Point2, MyTypes>::type Generated;
//Incomplete<Generated> incomplete; //Incomplete<Generated> incomplete;
//BOOST_MPL_ASSERT((boost::is_same< Matrix25, Generated::JacobianTA >)); BOOST_MPL_ASSERT((boost::is_same< Matrix2, Generated::Record::Jacobian2T >));
BOOST_MPL_ASSERT((boost::is_same< Matrix2, Generated::Jacobian2T >));
Generated generated;
typedef mpl::vector1<Point3> OneType;
typedef mpl::pop_front<OneType>::type Empty;
typedef mpl::pop_front<Empty>::type Bad;
//typedef ProtoTrace<OneType> UnaryTrace;
//BOOST_MPL_ASSERT((boost::is_same< UnaryTrace::A, Point3 >));
#include <boost/static_assert.hpp>
#include <boost/mpl/plus.hpp>
#include <boost/mpl/int.hpp>
//#include <boost/mpl/print.hpp>
typedef struct {
} Expected0;
BOOST_MPL_ASSERT((boost::is_same< Expected0, Expected0 >));
/* ************************************************************************* */ /* ************************************************************************* */
int main() { int main() {