Merged in feature/BAD_VerticalBlockMatrix (pull request #29)
Straight copy into VerticalBlockMatrixrelease/4.3a0
commit
a1c6c3cbe5
|
|
@ -23,6 +23,7 @@
|
||||||
#include <gtsam/base/Matrix.h>
|
#include <gtsam/base/Matrix.h>
|
||||||
#include <gtsam/base/Testable.h>
|
#include <gtsam/base/Testable.h>
|
||||||
#include <gtsam/base/Manifold.h>
|
#include <gtsam/base/Manifold.h>
|
||||||
|
#include <gtsam/base/VerticalBlockMatrix.h>
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
#include <boost/tuple/tuple.hpp>
|
#include <boost/tuple/tuple.hpp>
|
||||||
|
|
@ -49,8 +50,25 @@ namespace gtsam {
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class Expression;
|
class Expression;
|
||||||
|
|
||||||
typedef std::pair<Key, Eigen::Block<Matrix> > JacobianPair;
|
/**
|
||||||
typedef std::vector<JacobianPair> JacobianMap;
|
* Expressions are designed to write their derivatives into an already allocated
|
||||||
|
* Jacobian of the correct size, of type VerticalBlockMatrix.
|
||||||
|
* The JacobianMap provides a mapping from keys to the underlying blocks.
|
||||||
|
*/
|
||||||
|
class JacobianMap {
|
||||||
|
const FastVector<Key>& keys_;
|
||||||
|
VerticalBlockMatrix& Ab_;
|
||||||
|
public:
|
||||||
|
JacobianMap(const FastVector<Key>& keys, VerticalBlockMatrix& Ab) :
|
||||||
|
keys_(keys), Ab_(Ab) {
|
||||||
|
}
|
||||||
|
/// Access via key
|
||||||
|
VerticalBlockMatrix::Block operator()(Key key) {
|
||||||
|
FastVector<Key>::const_iterator it = std::find(keys_.begin(),keys_.end(),key);
|
||||||
|
DenseIndex block = it - keys_.begin();
|
||||||
|
return Ab_(block);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
|
|
@ -80,20 +98,14 @@ struct CallRecord {
|
||||||
template<int ROWS, int COLS>
|
template<int ROWS, int COLS>
|
||||||
void handleLeafCase(const Eigen::Matrix<double, ROWS, COLS>& dTdA,
|
void handleLeafCase(const Eigen::Matrix<double, ROWS, COLS>& dTdA,
|
||||||
JacobianMap& jacobians, Key key) {
|
JacobianMap& jacobians, Key key) {
|
||||||
JacobianMap::iterator it = std::find_if(jacobians.begin(), jacobians.end(),
|
jacobians(key).block < ROWS, COLS > (0, 0) += dTdA; // block makes HUGE difference
|
||||||
boost::bind(&JacobianPair::first, _1) == key);
|
|
||||||
assert(it!=jacobians.end());
|
|
||||||
it->second.block < ROWS, COLS > (0, 0) += dTdA; // block makes HUGE difference
|
|
||||||
}
|
}
|
||||||
/// Handle Leaf Case for Dynamic Matrix type (slower)
|
/// Handle Leaf Case for Dynamic Matrix type (slower)
|
||||||
template<>
|
template<>
|
||||||
void handleLeafCase(
|
void handleLeafCase(
|
||||||
const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>& dTdA,
|
const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>& dTdA,
|
||||||
JacobianMap& jacobians, Key key) {
|
JacobianMap& jacobians, Key key) {
|
||||||
JacobianMap::iterator it = std::find_if(jacobians.begin(), jacobians.end(),
|
jacobians(key) += dTdA;
|
||||||
boost::bind(&JacobianPair::first, _1) == key);
|
|
||||||
assert(it!=jacobians.end());
|
|
||||||
it->second += dTdA;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return value and derivatives, reverse AD version
|
/// Return value and derivatives, reverse AD version
|
||||||
T reverse(const Values& values, JacobianMap& jacobians) const {
|
T value(const Values& values, JacobianMap& jacobians) const {
|
||||||
// The following piece of code is absolutely crucial for performance.
|
// The following piece of code is absolutely crucial for performance.
|
||||||
// We allocate a block of memory on the stack, which can be done at runtime
|
// We allocate a block of memory on the stack, which can be done at runtime
|
||||||
// with modern C++ compilers. The traceExecution then fills this memory
|
// with modern C++ compilers. The traceExecution then fills this memory
|
||||||
|
|
@ -142,11 +142,6 @@ public:
|
||||||
return root_->value(values);
|
return root_->value(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return value and derivatives
|
|
||||||
T value(const Values& values, JacobianMap& jacobians) const {
|
|
||||||
return reverse(values, jacobians);
|
|
||||||
}
|
|
||||||
|
|
||||||
const boost::shared_ptr<ExpressionNode<T> >& root() const {
|
const boost::shared_ptr<ExpressionNode<T> >& root() const {
|
||||||
return root_;
|
return root_;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error function *without* the NoiseModel, \f$ z-h(x) \f$.
|
* Error function *without* the NoiseModel, \f$ h(x)-z \f$.
|
||||||
* We override this method to provide
|
* We override this method to provide
|
||||||
* both the function evaluation and its derivative(s) in H.
|
* both the function evaluation and its derivative(s) in H.
|
||||||
*/
|
*/
|
||||||
|
|
@ -85,18 +85,19 @@ public:
|
||||||
// H should be pre-allocated
|
// H should be pre-allocated
|
||||||
assert(H->size()==size());
|
assert(H->size()==size());
|
||||||
|
|
||||||
// Create and zero out blocks to be passed to expression_
|
VerticalBlockMatrix Ab(dimensions_, Dim);
|
||||||
JacobianMap blocks;
|
|
||||||
blocks.reserve(size());
|
// Wrap keys and VerticalBlockMatrix into structure passed to expression_
|
||||||
for (DenseIndex i = 0; i < size(); i++) {
|
JacobianMap map(keys_, Ab);
|
||||||
Matrix& Hi = H->at(i);
|
Ab.matrix().setZero();
|
||||||
Hi.resize(Dim, dimensions_[i]);
|
|
||||||
Hi.setZero(); // zero out
|
// Evaluate error to get Jacobians and RHS vector b
|
||||||
Eigen::Block<Matrix> block = Hi.block(0, 0, Dim, dimensions_[i]);
|
T value = expression_.value(x, map); // <<< Reverse AD happens here !
|
||||||
blocks.push_back(std::make_pair(keys_[i], block));
|
|
||||||
}
|
// Copy blocks into the vector of jacobians passed in
|
||||||
|
for (DenseIndex i = 0; i < size(); i++)
|
||||||
|
H->at(i) = Ab(i);
|
||||||
|
|
||||||
T value = expression_.value(x, blocks);
|
|
||||||
return measurement_.localCoordinates(value);
|
return measurement_.localCoordinates(value);
|
||||||
} else {
|
} else {
|
||||||
const T& value = expression_.value(x);
|
const T& value = expression_.value(x);
|
||||||
|
|
@ -108,8 +109,8 @@ public:
|
||||||
|
|
||||||
// This method has been heavily optimized for maximum performance.
|
// This method has been heavily optimized for maximum performance.
|
||||||
// We allocate a VerticalBlockMatrix on the stack first, and then create
|
// We allocate a VerticalBlockMatrix on the stack first, and then create
|
||||||
// Eigen::Block<Matrix> views on this piece of memory which is then passed
|
// a JacobianMap view onto it, which is then passed
|
||||||
// to [expression_.value] below, which writes directly into Ab_.
|
// to [expression_.value] to allow it to write directly into Ab_.
|
||||||
|
|
||||||
// Another malloc saved by creating a Matrix on the stack
|
// Another malloc saved by creating a Matrix on the stack
|
||||||
double memory[Dim * augmentedCols_];
|
double memory[Dim * augmentedCols_];
|
||||||
|
|
@ -120,14 +121,11 @@ public:
|
||||||
// Construct block matrix, is of right size but un-initialized
|
// Construct block matrix, is of right size but un-initialized
|
||||||
VerticalBlockMatrix Ab(dimensions_, matrix, true);
|
VerticalBlockMatrix Ab(dimensions_, matrix, true);
|
||||||
|
|
||||||
// Create blocks into Ab_ to be passed to expression_
|
// Wrap keys and VerticalBlockMatrix into structure passed to expression_
|
||||||
JacobianMap blocks;
|
JacobianMap map(keys_, Ab);
|
||||||
blocks.reserve(size());
|
|
||||||
for (DenseIndex i = 0; i < size(); i++)
|
|
||||||
blocks.push_back(std::make_pair(keys_[i], Ab(i)));
|
|
||||||
|
|
||||||
// Evaluate error to get Jacobians and RHS vector b
|
// Evaluate error to get Jacobians and RHS vector b
|
||||||
T value = expression_.value(x, blocks); // <<< Reverse AD happens here !
|
T value = expression_.value(x, map); // <<< Reverse AD happens here !
|
||||||
Ab(size()).col(0) = -measurement_.localCoordinates(value);
|
Ab(size()).col(0) = -measurement_.localCoordinates(value);
|
||||||
|
|
||||||
// Whiten the corresponding system now
|
// Whiten the corresponding system now
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,8 @@ static const Rot3 someR = Rot3::RzRyRx(1, 2, 3);
|
||||||
TEST(Expression, constant) {
|
TEST(Expression, constant) {
|
||||||
Expression<Rot3> R(someR);
|
Expression<Rot3> R(someR);
|
||||||
Values values;
|
Values values;
|
||||||
JacobianMap actualMap;
|
Rot3 actual = R.value(values);
|
||||||
Rot3 actual = R.value(values, actualMap);
|
|
||||||
EXPECT(assert_equal(someR, actual));
|
EXPECT(assert_equal(someR, actual));
|
||||||
JacobianMap expected;
|
|
||||||
EXPECT(actualMap == expected);
|
|
||||||
EXPECT_LONGS_EQUAL(0, R.traceSize())
|
EXPECT_LONGS_EQUAL(0, R.traceSize())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,15 +58,8 @@ TEST(Expression, Leaf) {
|
||||||
Values values;
|
Values values;
|
||||||
values.insert(100, someR);
|
values.insert(100, someR);
|
||||||
|
|
||||||
JacobianMap expected;
|
Rot3 actual2 = R.value(values);
|
||||||
Matrix H = eye(3);
|
|
||||||
expected.push_back(make_pair(100, H.block(0, 0, 3, 3)));
|
|
||||||
|
|
||||||
JacobianMap actualMap2;
|
|
||||||
actualMap2.push_back(make_pair(100, H.block(0, 0, 3, 3)));
|
|
||||||
Rot3 actual2 = R.reverse(values, actualMap2);
|
|
||||||
EXPECT(assert_equal(someR, actual2));
|
EXPECT(assert_equal(someR, actual2));
|
||||||
EXPECT(actualMap2 == expected);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue