Successful switch to Blocks !

release/4.3a0
dellaert 2014-10-14 15:43:41 +02:00
parent f3e1561105
commit 1c3f328fb2
5 changed files with 99 additions and 269 deletions

View File

@ -48,125 +48,7 @@ namespace gtsam {
template<typename T>
class Expression;
typedef std::map<Key, Matrix> JacobianMap;
/// Move terms to array, destroys content
void move(JacobianMap& jacobians, std::vector<Matrix>& 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 T>
class Augmented {
private:
T value_;
JacobianMap jacobians_;
typedef std::pair<Key, Matrix> 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<Matrix>& H) {
move(jacobians_, H);
}
};
typedef std::map<Key, Eigen::Block<Matrix> > JacobianMap;
//-----------------------------------------------------------------------------
/**
@ -244,39 +126,46 @@ public:
return p ? boost::optional<Record*>(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<class Derived>
static void handleLeafCase(const Eigen::MatrixBase<Derived>& 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<Matrix>& 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<double, 2, T::dimension> 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<Key,size_t> dimensions() const {
std::map<Key,size_t> map;
virtual std::map<Key, size_t> dimensions() const {
std::map<Key, size_t> map;
return map;
}
@ -350,9 +239,6 @@ public:
/// Return value
virtual T value(const Values& values) const = 0;
/// Return value and derivatives
virtual Augmented<T> forward(const Values& values) const = 0;
/// Construct an execution trace for reverse AD
virtual T traceExecution(const Values& values, ExecutionTrace<T>& trace,
char* raw) const = 0;
@ -380,11 +266,6 @@ public:
return constant_;
}
/// Return value and derivatives
virtual Augmented<T> forward(const Values& values) const {
return Augmented<T>(constant_);
}
/// Construct an execution trace for reverse AD
virtual T traceExecution(const Values& values, ExecutionTrace<T>& trace,
char* raw) const {
@ -417,8 +298,8 @@ public:
}
/// Return dimensions for each argument
virtual std::map<Key,size_t> dimensions() const {
std::map<Key,size_t> map;
virtual std::map<Key, size_t> dimensions() const {
std::map<Key, size_t> map;
map[key_] = T::dimension;
return map;
}
@ -428,11 +309,6 @@ public:
return values.at<T>(key_);
}
/// Return value and derivatives
virtual Augmented<T> forward(const Values& values) const {
return Augmented<T>(values.at<T>(key_), key_);
}
/// Construct an execution trace for reverse AD
virtual T traceExecution(const Values& values, ExecutionTrace<T>& trace,
char* raw) const {
@ -540,9 +416,9 @@ struct GenerateFunctionalNode: Argument<T, A, Base::N + 1>, Base {
}
/// Return dimensions for each argument
virtual std::map<Key,size_t> dimensions() const {
std::map<Key,size_t> map = Base::dimensions();
std::map<Key,size_t> myMap = This::expression->dimensions();
virtual std::map<Key, size_t> dimensions() const {
std::map<Key, size_t> map = Base::dimensions();
std::map<Key, size_t> myMap = This::expression->dimensions();
map.insert(myMap.begin(), myMap.end());
return map;
}
@ -690,19 +566,6 @@ public:
return function_(this->template expression<A1, 1>()->value(values), boost::none);
}
/// Return value and derivatives
virtual Augmented<T> forward(const Values& values) const {
using boost::none;
Augmented<A1> a1 = this->template expression<A1, 1>()->forward(values);
// Declare Jacobians
using boost::mpl::at_c;
typename at_c<typename Base::Jacobians,0>::type H1;
T t = function_(a1.value(), H1);
return Augmented<T>(t, H1, a1.jacobians());
}
/// Construct an execution trace for reverse AD
virtual T traceExecution(const Values& values, ExecutionTrace<T>& trace,
char* raw) const {
@ -756,21 +619,6 @@ public:
none, none);
}
/// Return value and derivatives
virtual Augmented<T> forward(const Values& values) const {
using boost::none;
Augmented<A1> a1 = this->template expression<A1, 1>()->forward(values);
Augmented<A2> a2 = this->template expression<A2, 2>()->forward(values);
// Declare Jacobians
using boost::mpl::at_c;
typename at_c<typename Base::Jacobians,0>::type H1;
typename at_c<typename Base::Jacobians,1>::type H2;
T t = function_(a1.value(), a2.value(),H1,H2);
return Augmented<T>(t, H1, a1.jacobians(), H2, a2.jacobians());
}
/// Construct an execution trace for reverse AD
virtual T traceExecution(const Values& values, ExecutionTrace<T>& trace,
char* raw) const {
@ -826,24 +674,6 @@ public:
none, none, none);
}
/// Return value and derivatives
virtual Augmented<T> forward(const Values& values) const {
using boost::none;
Augmented<A1> a1 = this->template expression<A1, 1>()->forward(values);
Augmented<A2> a2 = this->template expression<A2, 2>()->forward(values);
Augmented<A3> a3 = this->template expression<A3, 3>()->forward(values);
// Declare Jacobians
using boost::mpl::at_c;
typename at_c<typename Base::Jacobians,0>::type H1;
typename at_c<typename Base::Jacobians,1>::type H2;
typename at_c<typename Base::Jacobians,2>::type H3;
T t = function_(a1.value(), a2.value(), a3.value(),H1,H2,H3);
return Augmented<T>(t, H1, a1.jacobians(), H2, a2.jacobians(),
H3, a3.jacobians());
}
/// Construct an execution trace for reverse AD
virtual T traceExecution(const Values& values, ExecutionTrace<T>& trace,
char* raw) const {
@ -859,5 +689,5 @@ public:
};
//-----------------------------------------------------------------------------
}
}

View File

@ -112,11 +112,6 @@ public:
return root_->dimensions();
}
/// Return value and derivatives, forward AD version
Augmented<T> forward(const Values& values) const {
return root_->forward(values);
}
// Return size needed for memory buffer in traceExecution
size_t traceSize() const {
return root_->traceSize();

View File

@ -57,11 +57,26 @@ public:
virtual Vector unwhitenedError(const Values& x,
boost::optional<std::vector<Matrix>&> 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<Key, size_t> map = expression_.dimensions();
// Create and zero out blocks to be passed to expression_
DenseIndex i = 0; // For block index
typedef std::pair<Key, size_t> Pair;
std::map<Key, VerticalBlockMatrix::Block> 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<Matrix> 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<JacobianFactor>();
// Get dimensions of matrices
// Get dimensions of Jacobian matrices
std::map<Key, size_t> 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<Key, size_t> Pair;
BOOST_FOREACH(const Pair& keyValue, map) {
Ab(i++) = zeros(T::dimension, keyValue.second);
std::map<Key, VerticalBlockMatrix::Block> 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

View File

@ -65,13 +65,11 @@ TEST(Expression, leaf) {
values.insert(100, someR);
JacobianMap expected;
expected[100] = eye(3);
Augmented<Rot3> 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);

View File

@ -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<Point2> old(2, Point2(0, 0), model);
// Concise version
ExpressionFactor<Point2> 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<GaussianFactor> 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<Point2> old(2, Point2(0, 0), model);
// Concise version
ExpressionFactor<Point2> 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<GaussianFactor> 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<Point2> old(2, Point2(0, 0), model);
//
// // Concise version
// ExpressionFactor<Point2> 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<GaussianFactor> 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<Point2> old(2, Point2(0, 0), model);
//
// // Concise version
// ExpressionFactor<Point2> 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<GaussianFactor> 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<Cal3_S2>::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<Point2> actual = uv_hat.forward(values);
EXPECT(assert_equal(expectedValue, actual.value()));
EXPECT(actual.jacobians() == expectedMap);
}
// Create factor and check value, dimension, linearization
ExpressionFactor<Point2> 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);