No more boost::mpl needed for Expressions
parent
4a8dbd689a
commit
4f846ff75f
|
@ -2038,14 +2038,6 @@
|
|||
<useDefaultCommand>true</useDefaultCommand>
|
||||
<runAllBuilders>true</runAllBuilders>
|
||||
</target>
|
||||
<target name="testExpressionMeta.run" path="build/gtsam_unstable/nonlinear/tests" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||
<buildCommand>make</buildCommand>
|
||||
<buildArguments>-j5</buildArguments>
|
||||
<buildTarget>testExpressionMeta.run</buildTarget>
|
||||
<stopOnError>true</stopOnError>
|
||||
<useDefaultCommand>true</useDefaultCommand>
|
||||
<runAllBuilders>true</runAllBuilders>
|
||||
</target>
|
||||
<target name="schedulingExample.run" path="build/gtsam_unstable/discrete/tests" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||
<buildCommand>make</buildCommand>
|
||||
<buildArguments>-j5</buildArguments>
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <gtsam/nonlinear/internal/JacobianMap.h>
|
||||
#include <boost/mpl/transform.hpp>
|
||||
|
||||
namespace gtsam {
|
||||
namespace internal {
|
||||
|
|
|
@ -23,10 +23,6 @@
|
|||
#include <gtsam/nonlinear/internal/CallRecord.h>
|
||||
#include <gtsam/nonlinear/Values.h>
|
||||
|
||||
// template meta-programming headers
|
||||
#include <boost/mpl/fold.hpp>
|
||||
namespace MPL = boost::mpl::placeholders;
|
||||
|
||||
#include <typeinfo> // operator typeid
|
||||
#include <ostream>
|
||||
#include <map>
|
||||
|
@ -191,272 +187,16 @@ public:
|
|||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 three arguments A1, A2, and A3 will be
|
||||
//
|
||||
// struct Base1 : Argument<T,A1,1>, FunctionalBase<T> {
|
||||
// ... storage related to A1 ...
|
||||
// ... methods that work on A1 ...
|
||||
// };
|
||||
//
|
||||
// struct Base2 : Argument<T,A2,2>, Base1 {
|
||||
// ... storage related to A2 ...
|
||||
// ... methods that work on A2 and (recursively) on A1 ...
|
||||
// };
|
||||
//
|
||||
// struct Base3 : Argument<T,A3,3>, Base2 {
|
||||
// ... storage related to A3 ...
|
||||
// ... methods that work on A3 and (recursively) on A2 and A1 ...
|
||||
// };
|
||||
//
|
||||
// struct FunctionalNode : Base3 {
|
||||
// Provides convenience access to storage in hierarchy by using
|
||||
// static_cast<Argument<T, A, N> &>(*this)
|
||||
// }
|
||||
//
|
||||
// All this magic happens when we generate the Base3 base class of FunctionalNode
|
||||
// by invoking boost::mpl::fold over the meta-function GenerateFunctionalNode
|
||||
//
|
||||
// Similarly, the inner Record struct will be
|
||||
//
|
||||
// struct Record1 : JacobianTrace<T,A1,1>, CallRecord<traits::dimension<T>::value> {
|
||||
// ... storage related to A1 ...
|
||||
// ... methods that work on A1 ...
|
||||
// };
|
||||
//
|
||||
// struct Record2 : JacobianTrace<T,A2,2>, Record1 {
|
||||
// ... storage related to A2 ...
|
||||
// ... methods that work on A2 and (recursively) on A1 ...
|
||||
// };
|
||||
//
|
||||
// struct Record3 : JacobianTrace<T,A3,3>, Record2 {
|
||||
// ... storage related to A3 ...
|
||||
// ... methods that work on A3 and (recursively) on A2 and A1 ...
|
||||
// };
|
||||
//
|
||||
// struct Record : Record3 {
|
||||
// Provides convenience access to storage in hierarchy by using
|
||||
// static_cast<JacobianTrace<T, A, N> &>(*this)
|
||||
// }
|
||||
//
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// meta-function to generate fixed-size JacobianTA type
|
||||
template<class T, class A>
|
||||
struct Jacobian {
|
||||
typedef Eigen::Matrix<double, traits<T>::dimension, traits<A>::dimension> type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Base case for recursive FunctionalNode class
|
||||
*/
|
||||
template<class T>
|
||||
struct FunctionalBase: ExpressionNode<T> {
|
||||
static size_t const N = 0; // number of arguments
|
||||
|
||||
struct Record {
|
||||
void print(const std::string& indent) const {
|
||||
}
|
||||
void startReverseAD4(JacobianMap& jacobians) const {
|
||||
}
|
||||
template<typename SomeMatrix>
|
||||
void reverseAD4(const SomeMatrix & dFdT, JacobianMap& jacobians) const {
|
||||
}
|
||||
};
|
||||
/// Construct an execution trace for reverse AD
|
||||
void trace(const Values& values, Record* record,
|
||||
ExecutionTraceStorage*& traceStorage) const {
|
||||
// base case: does not do anything
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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<class T, class A, size_t N>
|
||||
struct Argument {
|
||||
/// Expression that will generate value/derivatives for argument
|
||||
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 {
|
||||
A value;
|
||||
ExecutionTrace<A> trace;
|
||||
typename Jacobian<T, A>::type dTdA;
|
||||
};
|
||||
|
||||
// Recursive Definition of Functional ExpressionNode
|
||||
// The reason we inherit from Argument<T, A, N> is because we can then
|
||||
// case to this unique signature to retrieve the expression at any level
|
||||
template<class T, class A, class Base>
|
||||
struct GenerateFunctionalNode: Argument<T, A, Base::N + 1>, Base {
|
||||
|
||||
static size_t const N = Base::N + 1; ///< Number of arguments in hierarchy
|
||||
typedef Argument<T, A, N> This; ///< The storage we have direct access to
|
||||
|
||||
/// Return keys that play in this expression
|
||||
virtual std::set<Key> keys() const {
|
||||
std::set<Key> keys = Base::keys();
|
||||
std::set<Key> myKeys = This::expression->keys();
|
||||
keys.insert(myKeys.begin(), myKeys.end());
|
||||
return keys;
|
||||
}
|
||||
|
||||
/// Return dimensions for each argument
|
||||
virtual void dims(std::map<Key, int>& map) const {
|
||||
Base::dims(map);
|
||||
This::expression->dims(map);
|
||||
}
|
||||
|
||||
// Recursive Record Class for Functional Expressions
|
||||
// The reason we inherit from JacobianTrace<T, A, N> is because we can then
|
||||
// case to this unique signature to retrieve the value/trace at any level
|
||||
struct Record: JacobianTrace<T, A, N>, Base::Record {
|
||||
|
||||
typedef JacobianTrace<T, A, N> This;
|
||||
|
||||
/// Print to std::cout
|
||||
void print(const std::string& indent) const {
|
||||
Base::Record::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
|
||||
void startReverseAD4(JacobianMap& jacobians) const {
|
||||
Base::Record::startReverseAD4(jacobians);
|
||||
// This is the crucial point where the size of the AD pipeline is selected.
|
||||
// One pipeline is started for each argument, but the number of rows in each
|
||||
// pipeline is the same, namely the dimension of the output argument T.
|
||||
// For example, if the entire expression is rooted by a binary function
|
||||
// yielding a 2D result, then the matrix dTdA will have 2 rows.
|
||||
// ExecutionTrace::reverseAD1 just passes this on to CallRecord::reverseAD2
|
||||
// which calls the correctly sized CallRecord::reverseAD3, which in turn
|
||||
// calls reverseAD4 below.
|
||||
This::trace.reverseAD1(This::dTdA, jacobians);
|
||||
}
|
||||
|
||||
/// Given df/dT, multiply in dT/dA and continue reverse AD process
|
||||
// Cols is always known at compile time
|
||||
template<typename SomeMatrix>
|
||||
void reverseAD4(const SomeMatrix & dFdT, JacobianMap& jacobians) const {
|
||||
Base::Record::reverseAD4(dFdT, jacobians);
|
||||
This::trace.reverseAD1(dFdT * This::dTdA, jacobians);
|
||||
}
|
||||
};
|
||||
|
||||
/// Construct an execution trace for reverse AD
|
||||
void trace(const Values& values, Record* record,
|
||||
ExecutionTraceStorage*& traceStorage) const {
|
||||
Base::trace(values, record, traceStorage); // recurse
|
||||
// Write an Expression<A> execution trace in record->trace
|
||||
// Iff Constant or Leaf, this will not write to traceStorage, only to trace.
|
||||
// Iff the expression is functional, write all Records in traceStorage buffer
|
||||
// Return value of type T is recorded in record->value
|
||||
record->Record::This::value = This::expression->traceExecution(values,
|
||||
record->Record::This::trace, traceStorage);
|
||||
// traceStorage is never modified by traceExecution, but if traceExecution has
|
||||
// written in the buffer, the next caller expects we advance the pointer
|
||||
traceStorage += This::expression->traceSize();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Recursive GenerateFunctionalNode class Generator
|
||||
*/
|
||||
template<class T, class TYPES>
|
||||
struct FunctionalNode {
|
||||
|
||||
/// The following typedef generates the recursively defined Base class
|
||||
typedef typename boost::mpl::fold<TYPES, FunctionalBase<T>,
|
||||
GenerateFunctionalNode<T, MPL::_2, MPL::_1> >::type Base;
|
||||
|
||||
/**
|
||||
* The type generated by this meta-function derives from Base
|
||||
* and adds access functions as well as the crucial [trace] function
|
||||
*/
|
||||
struct type: public Base {
|
||||
|
||||
// Argument types and derived, note these are base 0 !
|
||||
// These are currently not used - useful for Phoenix in future
|
||||
#ifdef EXPRESSIONS_PHOENIX
|
||||
typedef TYPES Arguments;
|
||||
typedef typename boost::mpl::transform<TYPES, Jacobian<T, MPL::_1> >::type Jacobians;
|
||||
typedef typename boost::mpl::transform<TYPES, OptionalJacobian<T, MPL::_1> >::type Optionals;
|
||||
#endif
|
||||
|
||||
/// Reset expression shared pointer
|
||||
template<class A, size_t N>
|
||||
void reset(const boost::shared_ptr<ExpressionNode<A> >& ptr) {
|
||||
static_cast<Argument<T, A, N> &>(*this).expression = ptr;
|
||||
}
|
||||
|
||||
/// Access Expression shared pointer
|
||||
template<class A, size_t N>
|
||||
boost::shared_ptr<ExpressionNode<A> > expression() const {
|
||||
return static_cast<Argument<T, A, N> const &>(*this).expression;
|
||||
}
|
||||
|
||||
/// Provide convenience access to Record storage and implement
|
||||
/// the virtual function based interface of CallRecord using the CallRecordImplementor
|
||||
struct Record: public CallRecordImplementor<Record, traits<T>::dimension>,
|
||||
public Base::Record {
|
||||
using Base::Record::print;
|
||||
using Base::Record::startReverseAD4;
|
||||
using Base::Record::reverseAD4;
|
||||
|
||||
virtual ~Record() {
|
||||
}
|
||||
|
||||
/// Access Value
|
||||
template<class A, size_t N>
|
||||
const A& value() const {
|
||||
return static_cast<JacobianTrace<T, A, N> const &>(*this).value;
|
||||
}
|
||||
|
||||
/// 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
|
||||
Record* trace(const Values& values,
|
||||
ExecutionTraceStorage* traceStorage) const {
|
||||
assert(reinterpret_cast<size_t>(traceStorage) % TraceAlignment == 0);
|
||||
|
||||
// Create the record and advance the pointer
|
||||
Record* record = new (traceStorage) Record();
|
||||
traceStorage += upAligned(sizeof(Record));
|
||||
|
||||
// Record the traces for all arguments
|
||||
// After this, the traceStorage pointer is set to after what was written
|
||||
Base::trace(values, record, traceStorage);
|
||||
|
||||
// Return the record for this function evaluation
|
||||
return record;
|
||||
}
|
||||
};
|
||||
};
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Eigen format for printing Jacobians
|
||||
static const Eigen::IOFormat kMatlabFormat(0, 1, " ", "; ", "", "", "[", "]");
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Unary Function Expression
|
||||
template<class T, class A1>
|
||||
class UnaryExpression: public ExpressionNode<T> {
|
||||
|
@ -552,7 +292,6 @@ public:
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Binary Expression
|
||||
|
||||
template<class T, class A1, class A2>
|
||||
class BinaryExpression: public ExpressionNode<T> {
|
||||
|
||||
|
@ -646,7 +385,6 @@ public:
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Ternary Expression
|
||||
|
||||
template<class T, class A1, class A2, class A3>
|
||||
class TernaryExpression: public ExpressionNode<T> {
|
||||
|
||||
|
|
|
@ -1,248 +0,0 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
|
||||
* 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 <gtsam/nonlinear/ExpressionFactor.h>
|
||||
#include <gtsam/geometry/Pose3.h>
|
||||
#include <gtsam/geometry/Cal3_S2.h>
|
||||
#include <gtsam/base/Testable.h>
|
||||
|
||||
#include <CppUnitLite/TestHarness.h>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
using namespace gtsam;
|
||||
using namespace gtsam::internal;
|
||||
|
||||
/* ************************************************************************* */
|
||||
namespace mpl = boost::mpl;
|
||||
|
||||
#include <boost/mpl/assert.hpp>
|
||||
#include <boost/mpl/equal.hpp>
|
||||
template<class T> struct Incomplete;
|
||||
|
||||
// Check generation of FunctionalNode
|
||||
typedef mpl::vector<Pose3, Point3> MyTypes;
|
||||
typedef FunctionalNode<Point2, MyTypes>::type Generated;
|
||||
//Incomplete<Generated> incomplete;
|
||||
|
||||
// Try generating vectors of ExecutionTrace
|
||||
typedef mpl::vector<ExecutionTrace<Pose3>, ExecutionTrace<Point3> > ExpectedTraces;
|
||||
|
||||
typedef mpl::transform<MyTypes, ExecutionTrace<MPL::_1> >::type MyTraces;
|
||||
BOOST_MPL_ASSERT((mpl::equal< ExpectedTraces, MyTraces >));
|
||||
|
||||
template<class T>
|
||||
struct MakeTrace {
|
||||
typedef ExecutionTrace<T> type;
|
||||
};
|
||||
typedef mpl::transform<MyTypes, MakeTrace<MPL::_1> >::type MyTraces1;
|
||||
BOOST_MPL_ASSERT((mpl::equal< ExpectedTraces, MyTraces1 >));
|
||||
|
||||
// Try generating vectors of Expression types
|
||||
typedef mpl::vector<Expression<Pose3>, Expression<Point3> > ExpectedExpressions;
|
||||
typedef mpl::transform<MyTypes, Expression<MPL::_1> >::type Expressions;
|
||||
BOOST_MPL_ASSERT((mpl::equal< ExpectedExpressions, Expressions >));
|
||||
|
||||
// Try generating vectors of Jacobian types
|
||||
typedef mpl::vector<Matrix26, Matrix23> ExpectedJacobians;
|
||||
typedef mpl::transform<MyTypes, Jacobian<Point2, MPL::_1> >::type Jacobians;
|
||||
BOOST_MPL_ASSERT((mpl::equal< ExpectedJacobians, Jacobians >));
|
||||
|
||||
// Try accessing a Jacobian
|
||||
typedef mpl::at_c<Jacobians, 1>::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 <boost/fusion/include/mpl.hpp>
|
||||
#include <boost/fusion/include/at_c.hpp>
|
||||
|
||||
// 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 <boost/fusion/include/make_vector.hpp>
|
||||
#include <boost/fusion/include/transform.hpp>
|
||||
#include <boost/utility/result_of.hpp>
|
||||
|
||||
struct triple {
|
||||
template<class > struct result; // says we will provide result
|
||||
|
||||
template<class F>
|
||||
struct result<F(int)> {
|
||||
typedef double type; // result for int argument
|
||||
};
|
||||
|
||||
template<class F>
|
||||
struct result<F(const int&)> {
|
||||
typedef double type; // result for int argument
|
||||
};
|
||||
|
||||
template<class F>
|
||||
struct result<F(const double &)> {
|
||||
typedef double type; // result for double argument
|
||||
};
|
||||
|
||||
template<class F>
|
||||
struct result<F(double)> {
|
||||
typedef double type; // result for double argument
|
||||
};
|
||||
|
||||
// actual function
|
||||
template<typename T>
|
||||
typename result<triple(T)>::type operator()(const T& x) const {
|
||||
return (double) x;
|
||||
}
|
||||
};
|
||||
|
||||
// Test out polymorphic transform
|
||||
TEST(ExpressionFactor, Triple) {
|
||||
typedef boost::fusion::vector<int, double> IntDouble;
|
||||
IntDouble H = boost::fusion::make_vector(1, 2.0);
|
||||
|
||||
// Only works if I use Double2
|
||||
typedef boost::fusion::result_of::transform<IntDouble, triple>::type Result;
|
||||
typedef boost::fusion::vector<double, double> 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 <boost/fusion/include/invoke.hpp>
|
||||
#include <boost/functional/value_factory.hpp>
|
||||
#include <boost/fusion/functional/adapter/fused.hpp>
|
||||
#include <boost/fusion/adapted/mpl.hpp>
|
||||
|
||||
// Test out invoke
|
||||
TEST(ExpressionFactor, Invoke) {
|
||||
EXPECT_LONGS_EQUAL(2, invoke(plus<int>(),boost::fusion::make_vector(1,1)));
|
||||
|
||||
// Creating a Pose3 (is there another way?)
|
||||
boost::fusion::vector<Rot3, Point3> pair;
|
||||
Pose3 pose = boost::fusion::invoke(boost::value_factory<Pose3>(), pair);
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
// debug const issue (how to make read/write arguments for invoke)
|
||||
struct test {
|
||||
typedef void result_type;
|
||||
void operator()(int& a, int& b) const {
|
||||
a = 6;
|
||||
b = 7;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(ExpressionFactor, ConstIssue) {
|
||||
int a, b;
|
||||
boost::fusion::invoke(test(),
|
||||
boost::fusion::make_vector(boost::ref(a), boost::ref(b)));
|
||||
LONGS_EQUAL(6, a);
|
||||
LONGS_EQUAL(7, b);
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
// Test out invoke on a given GTSAM function
|
||||
// then construct prototype for it's derivatives
|
||||
TEST(ExpressionFactor, InvokeDerivatives) {
|
||||
// This is the method in Pose3:
|
||||
// Point3 transform_to(const Point3& p) const;
|
||||
// Point3 transform_to(const Point3& p,
|
||||
// boost::optional<Matrix36&> Dpose, boost::optional<Matrix3&> Dpoint) const;
|
||||
|
||||
// Let's assign it it to a boost function object
|
||||
// cast is needed because Pose3::transform_to is overloaded
|
||||
// typedef boost::function<Point3(const Pose3&, const Point3&)> F;
|
||||
// F f = static_cast<Point3 (Pose3::*)(
|
||||
// const Point3&) const >(&Pose3::transform_to);
|
||||
//
|
||||
// // Create arguments
|
||||
// Pose3 pose;
|
||||
// Point3 point;
|
||||
// typedef boost::fusion::vector<Pose3, Point3> Arguments;
|
||||
// Arguments args = boost::fusion::make_vector(pose, point);
|
||||
//
|
||||
// // Create fused function (takes fusion vector) and call it
|
||||
// boost::fusion::fused<F> g(f);
|
||||
// Point3 actual = g(args);
|
||||
// CHECK(assert_equal(point,actual));
|
||||
//
|
||||
// // We can *immediately* do this using invoke
|
||||
// Point3 actual2 = boost::fusion::invoke(f, args);
|
||||
// CHECK(assert_equal(point,actual2));
|
||||
|
||||
// Now, let's create the optional Jacobian arguments
|
||||
typedef Point3 T;
|
||||
typedef boost::mpl::vector<Pose3, Point3> TYPES;
|
||||
typedef boost::mpl::transform<TYPES, MakeOptionalJacobian<T, MPL::_1> >::type Optionals;
|
||||
|
||||
// Unfortunately this is moot: we need a pointer to a function with the
|
||||
// optional derivatives; I don't see a way of calling a function that we
|
||||
// did not get access to by the caller passing us a pointer.
|
||||
// Let's test below whether we can have a proxy object
|
||||
}
|
||||
|
||||
struct proxy {
|
||||
typedef Point3 result_type;
|
||||
Point3 operator()(const Pose3& pose, const Point3& point) const {
|
||||
return pose.transform_to(point);
|
||||
}
|
||||
Point3 operator()(const Pose3& pose, const Point3& point,
|
||||
OptionalJacobian<3,6> Dpose,
|
||||
OptionalJacobian<3,3> Dpoint) const {
|
||||
return pose.transform_to(point, Dpose, Dpoint);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(ExpressionFactor, InvokeDerivatives2) {
|
||||
// without derivatives
|
||||
Pose3 pose;
|
||||
Point3 point;
|
||||
Point3 actual = boost::fusion::invoke(proxy(),
|
||||
boost::fusion::make_vector(pose, point));
|
||||
CHECK(assert_equal(point,actual));
|
||||
|
||||
// with derivatives, does not work, const issue again
|
||||
Matrix36 Dpose;
|
||||
Matrix3 Dpoint;
|
||||
Point3 actual2 = boost::fusion::invoke(proxy(),
|
||||
boost::fusion::make_vector(pose, point, boost::ref(Dpose),
|
||||
boost::ref(Dpoint)));
|
||||
CHECK(assert_equal(point,actual2));
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
int main() {
|
||||
TestResult tr;
|
||||
return TestRegistry::runAllTests(tr);
|
||||
}
|
||||
/* ************************************************************************* */
|
||||
|
Loading…
Reference in New Issue