Updated unit tests for changes in the Concurrent Filtering and Smoothing classes. Currently the synchronization tests are disabled.

release/4.3a0
Stephen Williams 2013-04-09 21:48:48 +00:00
parent 0b5c07e543
commit ed560aa13a
2 changed files with 699 additions and 637 deletions

View File

@ -17,24 +17,20 @@
*/
#include <gtsam_unstable/nonlinear/ConcurrentBatchFilter.h>
#include <gtsam_unstable/nonlinear/LinearizedFactor.h>
#include <gtsam/slam/PriorFactor.h>
#include <gtsam/slam/BetweenFactor.h>
#include <gtsam/nonlinear/ISAM2.h>
#include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
#include <gtsam/nonlinear/Ordering.h>
#include <gtsam/nonlinear/Values.h>
#include <gtsam/nonlinear/Symbol.h>
#include <gtsam/nonlinear/Key.h>
#include <gtsam/inference/JunctionTree.h>
#include <gtsam/geometry/Pose3.h>
#include <CppUnitLite/TestHarness.h>
using namespace std;
using namespace gtsam;
namespace {
// Set up initial pose, odometry difference, loop closure difference, and initialization errors
const Pose3 poseInitial;
@ -49,7 +45,7 @@ const SharedDiagonal noiseLoop = noiseModel::Diagonal::Sigmas(Vector_(6, 0.25, 0
// Create a derived class to allow testing protected member functions
class ConcurrentBatchFilterTester : public ConcurrentBatchFilter {
public:
ConcurrentBatchFilterTester(const LevenbergMarquardtParams& parameters, double lag) : ConcurrentBatchFilter(parameters, lag) { };
ConcurrentBatchFilterTester(const LevenbergMarquardtParams& parameters) : ConcurrentBatchFilter(parameters) { };
virtual ~ConcurrentBatchFilterTester() { };
// Add accessors to the protected members
@ -103,7 +99,7 @@ bool hessian_equal(const NonlinearFactorGraph& expected, const NonlinearFactorGr
}
///* ************************************************************************* */
void CreateFactors(NonlinearFactorGraph& graph, Values& theta, ConcurrentBatchFilter::KeyTimestampMap& timestamps, size_t index1 = 0, size_t index2 = 1) {
void CreateFactors(NonlinearFactorGraph& graph, Values& theta, size_t index1 = 0, size_t index2 = 1) {
// Calculate all poses
Pose3 poses[20];
@ -128,7 +124,6 @@ void CreateFactors(NonlinearFactorGraph& graph, Values& theta, ConcurrentBatchFi
graph.add(PriorFactor<Pose3>(keys[0], poses[0], noisePrior));
// Add new variables
theta.insert(keys[0], poses[0].compose(poseError));
timestamps[keys[0]] = double(0);
break;
}
case 1:
@ -138,7 +133,6 @@ void CreateFactors(NonlinearFactorGraph& graph, Values& theta, ConcurrentBatchFi
graph.add(BetweenFactor<Pose3>(keys[0], keys[1], poseDelta, noiseOdometery));
// Add new variables
theta.insert(keys[1], poses[1].compose(poseError));
timestamps[keys[1]] = double(1);
break;
}
case 2:
@ -155,9 +149,7 @@ void CreateFactors(NonlinearFactorGraph& graph, Values& theta, ConcurrentBatchFi
graph.add(BetweenFactor<Pose3>(keys[2], keys[3], poseDelta, noiseOdometery));
// Add new variables
theta.insert(keys[2], poses[2].compose(poseError));
timestamps[keys[2]] = double(2);
theta.insert(keys[3], poses[3].compose(poseError));
timestamps[keys[3]] = double(3);
break;
}
case 4:
@ -171,7 +163,6 @@ void CreateFactors(NonlinearFactorGraph& graph, Values& theta, ConcurrentBatchFi
graph.add(BetweenFactor<Pose3>(keys[3], keys[5], poseDelta, noiseOdometery));
// Add new variables
theta.insert(keys[5], poses[5].compose(poseError));
timestamps[keys[5]] = double(5);
break;
}
case 6:
@ -184,7 +175,6 @@ void CreateFactors(NonlinearFactorGraph& graph, Values& theta, ConcurrentBatchFi
graph.add(BetweenFactor<Pose3>(keys[5], keys[6], poseDelta, noiseOdometery));
// Add new variables
theta.insert(keys[6], poses[6].compose(poseError));
timestamps[keys[6]] = double(6);
break;
}
case 7:
@ -197,9 +187,7 @@ void CreateFactors(NonlinearFactorGraph& graph, Values& theta, ConcurrentBatchFi
graph.add(BetweenFactor<Pose3>(keys[6], keys[7], poseDelta, noiseOdometery));
// Add new variables
theta.insert(keys[4], poses[4].compose(poseError));
timestamps[keys[4]] = double(4);
theta.insert(keys[7], poses[7].compose(poseError));
timestamps[keys[7]] = double(7);
break;
}
case 8:
@ -218,9 +206,7 @@ void CreateFactors(NonlinearFactorGraph& graph, Values& theta, ConcurrentBatchFi
graph.add(BetweenFactor<Pose3>(keys[8], keys[9], poseDelta, noiseOdometery));
// Add new variables
theta.insert(keys[8], poses[8].compose(poseError));
timestamps[keys[8]] = double(8);
theta.insert(keys[9], poses[9].compose(poseError));
timestamps[keys[9]] = double(9);
break;
}
case 10:
@ -230,7 +216,6 @@ void CreateFactors(NonlinearFactorGraph& graph, Values& theta, ConcurrentBatchFi
graph.add(BetweenFactor<Pose3>(keys[9], keys[10], poseDelta, noiseOdometery));
// Add new variables
theta.insert(keys[10], poses[10].compose(poseError));
timestamps[keys[10]] = double(10);
break;
}
case 11:
@ -240,7 +225,6 @@ void CreateFactors(NonlinearFactorGraph& graph, Values& theta, ConcurrentBatchFi
graph.add(BetweenFactor<Pose3>(keys[10], keys[11], poseDelta, noiseOdometery));
// Add new variables
theta.insert(keys[11], poses[11].compose(poseError));
timestamps[keys[11]] = double(11);
break;
}
case 12:
@ -253,7 +237,6 @@ void CreateFactors(NonlinearFactorGraph& graph, Values& theta, ConcurrentBatchFi
graph.add(BetweenFactor<Pose3>(keys[9], keys[12], poseDelta, noiseOdometery));
// Add new variables
theta.insert(keys[12], poses[12].compose(poseError));
timestamps[keys[12]] = double(12);
break;
}
@ -271,7 +254,6 @@ void CreateFactors(NonlinearFactorGraph& graph, Values& theta, ConcurrentBatchFi
graph.add(BetweenFactor<Pose3>(keys[12], keys[13], poseDelta, noiseOdometery));
// Add new variables
theta.insert(keys[13], poses[13].compose(poseError));
timestamps[keys[13]] = double(13);
break;
}
case 14:
@ -284,7 +266,6 @@ void CreateFactors(NonlinearFactorGraph& graph, Values& theta, ConcurrentBatchFi
graph.add(BetweenFactor<Pose3>(keys[13], keys[14], poseDelta, noiseOdometery));
// Add new variables
theta.insert(keys[14], poses[14].compose(poseError));
timestamps[keys[14]] = double(14);
break;
}
case 15:
@ -303,9 +284,7 @@ void CreateFactors(NonlinearFactorGraph& graph, Values& theta, ConcurrentBatchFi
graph.add(BetweenFactor<Pose3>(keys[15], keys[16], poseDelta, noiseOdometery));
// Add new variables
theta.insert(keys[15], poses[15].compose(poseError));
timestamps[keys[15]] = double(15);
theta.insert(keys[16], poses[16].compose(poseError));
timestamps[keys[16]] = double(16);
break;
}
case 17:
@ -315,7 +294,6 @@ void CreateFactors(NonlinearFactorGraph& graph, Values& theta, ConcurrentBatchFi
graph.add(BetweenFactor<Pose3>(keys[16], keys[17], poseDelta, noiseOdometery));
// Add new variables
theta.insert(keys[17], poses[17].compose(poseError));
timestamps[keys[17]] = double(17);
break;
}
case 18:
@ -325,7 +303,6 @@ void CreateFactors(NonlinearFactorGraph& graph, Values& theta, ConcurrentBatchFi
graph.add(BetweenFactor<Pose3>(keys[17], keys[18], poseDelta, noiseOdometery));
// Add new variables
theta.insert(keys[18], poses[18].compose(poseError));
timestamps[keys[18]] = double(18);
break;
}
case 19:
@ -338,7 +315,6 @@ void CreateFactors(NonlinearFactorGraph& graph, Values& theta, ConcurrentBatchFi
graph.add(BetweenFactor<Pose3>(keys[16], keys[19], poseDelta, noiseOdometery));
// Add new variables
theta.insert(keys[19], poses[19].compose(poseError));
timestamps[keys[19]] = double(19);
break;
}
@ -349,7 +325,7 @@ void CreateFactors(NonlinearFactorGraph& graph, Values& theta, ConcurrentBatchFi
}
/* ************************************************************************* */
Values BatchOptimize(const NonlinearFactorGraph& graph, const Values& theta, const Values& rootValues = Values()) {
Values BatchOptimize(const NonlinearFactorGraph& graph, const Values& theta, const Values& separatorValues = Values()) {
// Create an L-M optimizer
LevenbergMarquardtParams parameters;
@ -361,9 +337,9 @@ Values BatchOptimize(const NonlinearFactorGraph& graph, const Values& theta, con
double currentError;
do {
// Force variables associated with root keys to keep the same linearization point
if(rootValues.size() > 0) {
if(separatorValues.size() > 0) {
// Put the old values of the root keys back into the optimizer state
optimizer.state().values.update(rootValues);
optimizer.state().values.update(separatorValues);
// Update the error value with the new theta
optimizer.state().error = graph.error(optimizer.state().values);
}
@ -380,60 +356,60 @@ Values BatchOptimize(const NonlinearFactorGraph& graph, const Values& theta, con
return optimizer.values();
}
/* ************************************************************************* */
void FindFactorsWithAny(const std::set<Key>& keys, const NonlinearFactorGraph& sourceFactors, NonlinearFactorGraph& destinationFactors) {
BOOST_FOREACH(const NonlinearFactor::shared_ptr& factor, sourceFactors) {
NonlinearFactor::const_iterator key = factor->begin();
while((key != factor->end()) && (!std::binary_search(keys.begin(), keys.end(), *key))) {
++key;
}
if(key != factor->end()) {
destinationFactors.push_back(factor);
}
}
}
///* ************************************************************************* */
//void FindFactorsWithAny(const std::set<Key>& keys, const NonlinearFactorGraph& sourceFactors, NonlinearFactorGraph& destinationFactors) {
//
// BOOST_FOREACH(const NonlinearFactor::shared_ptr& factor, sourceFactors) {
// NonlinearFactor::const_iterator key = factor->begin();
// while((key != factor->end()) && (!std::binary_search(keys.begin(), keys.end(), *key))) {
// ++key;
// }
// if(key != factor->end()) {
// destinationFactors.push_back(factor);
// }
// }
//
//}
//
///* ************************************************************************* */
//void FindFactorsWithOnly(const std::set<Key>& keys, const NonlinearFactorGraph& sourceFactors, NonlinearFactorGraph& destinationFactors) {
//
// BOOST_FOREACH(const NonlinearFactor::shared_ptr& factor, sourceFactors) {
// NonlinearFactor::const_iterator key = factor->begin();
// while((key != factor->end()) && (std::binary_search(keys.begin(), keys.end(), *key))) {
// ++key;
// }
// if(key == factor->end()) {
// destinationFactors.push_back(factor);
// }
// }
//
//}
//
///* ************************************************************************* */
//typedef BayesTree<GaussianConditional,ISAM2Clique>::sharedClique Clique;
//void SymbolicPrintTree(const Clique& clique, const Ordering& ordering, const std::string indent = "") {
// std::cout << indent << "P( ";
// BOOST_FOREACH(Index index, clique->conditional()->frontals()){
// std::cout << DefaultKeyFormatter(ordering.key(index)) << " ";
// }
// if(clique->conditional()->nrParents() > 0) {
// std::cout << "| ";
// }
// BOOST_FOREACH(Index index, clique->conditional()->parents()){
// std::cout << DefaultKeyFormatter(ordering.key(index)) << " ";
// }
// std::cout << ")" << std::endl;
//
// BOOST_FOREACH(const Clique& child, clique->children()) {
// SymbolicPrintTree(child, ordering, indent+" ");
// }
//}
//
//}
/* ************************************************************************* */
void FindFactorsWithOnly(const std::set<Key>& keys, const NonlinearFactorGraph& sourceFactors, NonlinearFactorGraph& destinationFactors) {
BOOST_FOREACH(const NonlinearFactor::shared_ptr& factor, sourceFactors) {
NonlinearFactor::const_iterator key = factor->begin();
while((key != factor->end()) && (std::binary_search(keys.begin(), keys.end(), *key))) {
++key;
}
if(key == factor->end()) {
destinationFactors.push_back(factor);
}
}
}
/* ************************************************************************* */
typedef BayesTree<GaussianConditional,ISAM2Clique>::sharedClique Clique;
void SymbolicPrintTree(const Clique& clique, const Ordering& ordering, const std::string indent = "") {
std::cout << indent << "P( ";
BOOST_FOREACH(Index index, clique->conditional()->frontals()){
std::cout << DefaultKeyFormatter(ordering.key(index)) << " ";
}
if(clique->conditional()->nrParents() > 0) {
std::cout << "| ";
}
BOOST_FOREACH(Index index, clique->conditional()->parents()){
std::cout << DefaultKeyFormatter(ordering.key(index)) << " ";
}
std::cout << ")" << std::endl;
BOOST_FOREACH(const Clique& child, clique->children()) {
SymbolicPrintTree(child, ordering, indent+" ");
}
}
}
/* ************************************************************************* */
TEST_UNSAFE( ConcurrentBatchFilter, update_Batch )
TEST_UNSAFE( ConcurrentBatchFilter, update_batch )
{
// Test the 'update' function of the ConcurrentBatchFilter in a nonlinear environment.
// Thus, a full L-M optimization and the ConcurrentBatchFilter results should be identical
@ -441,23 +417,22 @@ TEST_UNSAFE( ConcurrentBatchFilter, update_Batch )
// Create a set of optimizer parameters
LevenbergMarquardtParams parameters;
double lag = 4.0;
// Create a Concurrent Batch Filter
ConcurrentBatchFilter filter(parameters, lag);
ConcurrentBatchFilter filter(parameters);
// Create containers to keep the full graph
Values fullTheta;
NonlinearFactorGraph fullGraph;
ConcurrentBatchFilter::KeyTimestampMap fullTimestamps;
// Create all factors
CreateFactors(fullGraph, fullTheta, fullTimestamps, 0, 20);
CreateFactors(fullGraph, fullTheta, 0, 20);
// Optimize with Concurrent Batch Filter
filter.update(fullGraph, fullTheta, fullTimestamps);
filter.update(fullGraph, fullTheta, boost::none);
Values actual = filter.calculateEstimate();
// Optimize with L-M
Values expected = BatchOptimize(fullGraph, fullTheta);
@ -465,8 +440,50 @@ TEST_UNSAFE( ConcurrentBatchFilter, update_Batch )
CHECK(assert_equal(expected, actual, 1e-4));
}
///* ************************************************************************* */
//TEST_UNSAFE( ConcurrentBatchFilter, update_batch_with_marginalization )
//{
// // Test the 'update' function of the ConcurrentBatchFilter in a nonlinear environment.
// // Thus, a full L-M optimization and the ConcurrentBatchFilter results should be identical
// // This tests adds all of the factors to the filter at once (i.e. batch)
//
// // Create a set of optimizer parameters
// LevenbergMarquardtParams parameters;
//
// // Create a Concurrent Batch Filter
// ConcurrentBatchFilter filter(parameters);
//
// // Create containers to keep the full graph
// Values fullTheta;
// NonlinearFactorGraph fullGraph;
//
// // Create all factors
// CreateFactors(fullGraph, fullTheta, 0, 20);
//
// // Create the set of key to marginalize out
// FastList<Key> marginalizeKeys;
// for(size_t j = 0; j < 15; ++j) {
// marginalizeKeys.push_back(Symbol('X', j));
// }
//
// // Optimize with Concurrent Batch Filter
// filter.update(fullGraph, fullTheta, marginalizeKeys);
// Values actual = filter.calculateEstimate();
//
//
// // Optimize with L-M
// Values expected = BatchOptimize(fullGraph, fullTheta);
// // Remove the marginalized keys
// for(size_t j = 0; j < 15; ++j) {
// expected.erase(Symbol('X', j));
// }
//
// // Check smoother versus batch
// CHECK(assert_equal(expected, actual, 1e-4));
//}
/* ************************************************************************* */
TEST_UNSAFE( ConcurrentBatchFilter, update_Incremental )
TEST_UNSAFE( ConcurrentBatchFilter, update_incremental )
{
// Test the 'update' function of the ConcurrentBatchFilter in a nonlinear environment.
// Thus, a full L-M optimization and the ConcurrentBatchFilter results should be identical
@ -474,10 +491,9 @@ TEST_UNSAFE( ConcurrentBatchFilter, update_Incremental )
// Create a set of optimizer parameters
LevenbergMarquardtParams parameters;
double lag = 4.0;
// Create a Concurrent Batch Filter
ConcurrentBatchFilter filter(parameters, lag);
ConcurrentBatchFilter filter(parameters);
// Create containers to keep the full graph
Values fullTheta;
@ -488,13 +504,12 @@ TEST_UNSAFE( ConcurrentBatchFilter, update_Incremental )
// Create containers to keep the new factors
Values newTheta;
NonlinearFactorGraph newGraph;
ConcurrentBatchFilter::KeyTimestampMap newTimestamps;
// Create factors
CreateFactors(newGraph, newTheta, newTimestamps, i, i+1);
CreateFactors(newGraph, newTheta, i, i+1);
// Add these entries to the filter
filter.update(newGraph, newTheta, newTimestamps);
filter.update(newGraph, newTheta, boost::none);
Values actual = filter.calculateEstimate();
// Add these entries to the full batch version
@ -509,278 +524,330 @@ TEST_UNSAFE( ConcurrentBatchFilter, update_Incremental )
}
/* ************************************************************************* */
TEST_UNSAFE( ConcurrentBatchFilter, synchronize )
{
// Test the 'synchronize' function of the ConcurrentBatchFilter in a nonlinear environment.
// The filter is operating on a known tree structure, so the factors and summarization can
// be predicted for testing purposes
///* ************************************************************************* */
//TEST_UNSAFE( ConcurrentBatchFilter, update_incremental_with_marginalization )
//{
// // Test the 'update' function of the ConcurrentBatchFilter in a nonlinear environment.
// // Thus, a full L-M optimization and the ConcurrentBatchFilter results should be identical
// // This tests adds the factors to the filter as they are created (i.e. incrementally)
//
// // Create a set of optimizer parameters
// LevenbergMarquardtParams parameters;
//
// // Create a Concurrent Batch Filter
// ConcurrentBatchFilter filter(parameters);
//
// // Create containers to keep the full graph
// Values fullTheta;
// NonlinearFactorGraph fullGraph;
//
// // Add odometry from time 0 to time 10
// for(size_t i = 0; i < 20; ++i) {
// // Create containers to keep the new factors
// Values newTheta;
// NonlinearFactorGraph newGraph;
//
// // Create factors
// CreateFactors(newGraph, newTheta, i, i+1);
//
// // Create the set of factors to marginalize
// FastList<Key> marginalizeKeys;
// if(i >= 4) {
// marginalizeKeys.push_back(Symbol('X', i-4));
// }
//
// // Add these entries to the filter
// filter.update(newGraph, newTheta, marginalizeKeys);
// Values actual = filter.calculateEstimate();
//
// // Add these entries to the full batch version
// fullGraph.push_back(newGraph);
// fullTheta.insert(newTheta);
// Values expected = BatchOptimize(fullGraph, fullTheta);
// fullTheta = expected;
// // Remove marginalized keys
// for(int j = 0; j < (int)i - 4; ++j) {
// expected.erase(Symbol('X', j));
// }
//
// // Compare filter solution with full batch
// CHECK(assert_equal(expected, actual, 1e-4));
// }
//
//}
// Create a set of optimizer parameters
LevenbergMarquardtParams parameters;
double lag = 4.0;
// Create a Concurrent Batch Filter
ConcurrentBatchFilterTester filter(parameters, lag);
// Create containers to keep the full graph
Values newTheta, fullTheta;
NonlinearFactorGraph newGraph, fullGraph;
ConcurrentBatchFilter::KeyTimestampMap newTimestamps;
// Create all factors
CreateFactors(newGraph, newTheta, newTimestamps, 0, 13);
fullTheta.insert(newTheta);
fullGraph.push_back(newGraph);
// Optimize with Concurrent Batch Filter
filter.update(newGraph, newTheta, newTimestamps);
Values updatedTheta = filter.calculateEstimate();
// Eliminate the factor graph into a Bayes Tree to create the summarized factors
// Create Ordering
std::map<Key, int> constraints;
constraints[Symbol('X', 8)] = 1;
constraints[Symbol('X', 9)] = 1;
constraints[Symbol('X', 10)] = 1;
constraints[Symbol('X', 11)] = 1;
constraints[Symbol('X', 12)] = 1;
Ordering ordering = *fullGraph.orderingCOLAMDConstrained(fullTheta, constraints);
// Linearize into a Gaussian Factor Graph
GaussianFactorGraph linearGraph = *fullGraph.linearize(fullTheta, ordering);
// Eliminate into a Bayes Net with iSAM2-type cliques
JunctionTree<GaussianFactorGraph, ISAM2Clique> jt(linearGraph);
ISAM2Clique::shared_ptr root = jt.eliminate(EliminateQR);
BayesTree<GaussianConditional, ISAM2Clique> bayesTree;
bayesTree.insert(root);
// std::cout << "BAYES TREE:" << std::endl;
// SymbolicPrintTree(root, ordering.invert(), " ");
// BAYES TREE:
// P( X7 X9 X12 )
// P( X10 | X9 )
// P( X11 | X10 )
// P( X8 | X9 )
// P( X6 | X7 X9 )
// P( X3 X5 | X6 )
// P( X2 | X3 )
// P( X1 | X3 )
// P( X0 | X1 )
// P( X4 | X7 )
// Extract the nonlinear factors that should be sent to the smoother
std::vector<Key> smootherKeys;
smootherKeys.push_back(Symbol('X', 0));
smootherKeys.push_back(Symbol('X', 1));
smootherKeys.push_back(Symbol('X', 2));
smootherKeys.push_back(Symbol('X', 3));
smootherKeys.push_back(Symbol('X', 4));
smootherKeys.push_back(Symbol('X', 5));
smootherKeys.push_back(Symbol('X', 6));
NonlinearFactorGraph expectedSmootherFactors;
BOOST_FOREACH(const NonlinearFactor::shared_ptr& factor, fullGraph) {
if(std::find_first_of(factor->begin(), factor->end(), smootherKeys.begin(), smootherKeys.end()) != factor->end()) {
expectedSmootherFactors.push_back(factor);
}
}
// Extract smoother values
Values expectedSmootherValues;
expectedSmootherValues.insert(Symbol('X', 0), updatedTheta.at(Symbol('X', 0)));
expectedSmootherValues.insert(Symbol('X', 1), updatedTheta.at(Symbol('X', 1)));
expectedSmootherValues.insert(Symbol('X', 2), updatedTheta.at(Symbol('X', 2)));
expectedSmootherValues.insert(Symbol('X', 3), updatedTheta.at(Symbol('X', 3)));
expectedSmootherValues.insert(Symbol('X', 4), updatedTheta.at(Symbol('X', 4)));
expectedSmootherValues.insert(Symbol('X', 5), updatedTheta.at(Symbol('X', 5)));
expectedSmootherValues.insert(Symbol('X', 6), updatedTheta.at(Symbol('X', 6)));
// Extract the filter summarized factors
// Cached factors that represent the filter side (i.e. the X8 and X10 clique)
NonlinearFactorGraph expectedFilterSumarization;
expectedFilterSumarization.add(LinearizedJacobianFactor(boost::static_pointer_cast<JacobianFactor>(bayesTree.nodes().at(ordering.at(Symbol('X', 8)))->cachedFactor()), ordering, fullTheta));
expectedFilterSumarization.add(LinearizedJacobianFactor(boost::static_pointer_cast<JacobianFactor>(bayesTree.nodes().at(ordering.at(Symbol('X', 10)))->cachedFactor()), ordering, fullTheta));
// And any factors that involve only the root (X7, X9, X12)
std::vector<Key> rootKeys;
rootKeys.push_back(Symbol('X', 7));
rootKeys.push_back(Symbol('X', 9));
rootKeys.push_back(Symbol('X', 12));
BOOST_FOREACH(const NonlinearFactor::shared_ptr& factor, fullGraph) {
size_t count = 0;
BOOST_FOREACH(Key key, *factor) {
if(std::binary_search(rootKeys.begin(), rootKeys.end(), key)) ++count;
}
if(count == factor->size()) expectedFilterSumarization.push_back(factor);
}
// Extract the new root values
Values expectedRootValues;
expectedRootValues.insert(Symbol('X', 7), updatedTheta.at(Symbol('X', 7)));
expectedRootValues.insert(Symbol('X', 9), updatedTheta.at(Symbol('X', 9)));
expectedRootValues.insert(Symbol('X', 12), updatedTheta.at(Symbol('X', 12)));
// Start the synchronization process
NonlinearFactorGraph actualSmootherFactors, actualFilterSumarization, smootherSummarization;
Values actualSmootherValues, actualRootValues;
filter.presync();
filter.synchronize(smootherSummarization); // Supplying an empty factor graph here
filter.getSmootherFactors(actualSmootherFactors, actualSmootherValues);
filter.getSummarizedFactors(actualFilterSumarization, actualRootValues);
filter.postsync();
// Compare filter sync variables versus the expected
CHECK(hessian_equal(expectedSmootherFactors, actualSmootherFactors, updatedTheta, 1e-8));
CHECK(assert_equal(expectedSmootherValues, actualSmootherValues, 1e-4));
CHECK(hessian_equal(expectedFilterSumarization, actualFilterSumarization, updatedTheta, 1e-9));
CHECK(assert_equal(expectedRootValues, actualRootValues, 1e-4));
// Now add additional factors to the filter and re-sync
newGraph.resize(0); newTheta.clear(); newTimestamps.clear();
CreateFactors(newGraph, newTheta, newTimestamps, 13, 20);
fullTheta.insert(newTheta);
fullGraph.push_back(newGraph);
// Optimize with Concurrent Batch Filter
filter.update(newGraph, newTheta, newTimestamps);
updatedTheta = filter.calculateEstimate();
// Eliminate the factor graph into a Bayes Tree to create the summarized factors
// Create Ordering
constraints.clear();
constraints[Symbol('X', 15)] = 1;
constraints[Symbol('X', 16)] = 1;
constraints[Symbol('X', 17)] = 1;
constraints[Symbol('X', 18)] = 1;
constraints[Symbol('X', 19)] = 1;
ordering = *fullGraph.orderingCOLAMDConstrained(fullTheta, constraints);
// Linearize into a Gaussian Factor Graph
linearGraph = *fullGraph.linearize(fullTheta, ordering);
// Eliminate into a Bayes Net with iSAM2-type cliques
jt = JunctionTree<GaussianFactorGraph, ISAM2Clique>(linearGraph);
root = jt.eliminate(EliminateQR);
bayesTree = BayesTree<GaussianConditional, ISAM2Clique>();
bayesTree.insert(root);
// std::cout << "BAYES TREE:" << std::endl;
// SymbolicPrintTree(root, ordering.invert(), " ");
// BAYES TREE:
// P( X14 X16 X19 )
// P( X17 | X16 )
// P( X18 | X17 )
// P( X15 | X16 )
// P( X13 | X14 X16 )
// P( X11 | X13 X14 )
// P( X10 | X11 X13 )
// P( X12 | X10 X13 )
// P( X9 | X12 X10 )
// P( X7 | X9 X12 )
// P( X6 | X7 X9 )
// P( X3 X5 | X6 )
// P( X2 | X3 )
// P( X1 | X3 )
// P( X0 | X1 )
// P( X4 | X7 )
// P( X8 | X9 )
// Extract the cached factors for X4 and X6. These are the new smoother summarized factors
// TODO: I'm concerned about the linearization point used to create these factors. It may need to be the updated lin points?
smootherSummarization.resize(0);
smootherSummarization.add(LinearizedJacobianFactor(boost::static_pointer_cast<JacobianFactor>(bayesTree.nodes().at(ordering.at(Symbol('X', 4)))->cachedFactor()), ordering, fullTheta));
smootherSummarization.add(LinearizedJacobianFactor(boost::static_pointer_cast<JacobianFactor>(bayesTree.nodes().at(ordering.at(Symbol('X', 6)))->cachedFactor()), ordering, fullTheta));
// Extract the nonlinear factors that should be sent to the smoother
smootherKeys.clear();
smootherKeys.push_back(Symbol('X', 8));
smootherKeys.push_back(Symbol('X', 10));
smootherKeys.push_back(Symbol('X', 11));
smootherKeys.push_back(Symbol('X', 13));
expectedSmootherFactors.resize(0);
BOOST_FOREACH(const NonlinearFactor::shared_ptr& factor, fullGraph) {
if(std::find_first_of(factor->begin(), factor->end(), smootherKeys.begin(), smootherKeys.end()) != factor->end()) {
expectedSmootherFactors.push_back(factor);
}
}
// And any factors that involve only the old root (X7, X9, X12)
rootKeys.clear();
rootKeys.push_back(Symbol('X', 7));
rootKeys.push_back(Symbol('X', 9));
rootKeys.push_back(Symbol('X', 12));
BOOST_FOREACH(const NonlinearFactor::shared_ptr& factor, fullGraph) {
size_t count = 0;
BOOST_FOREACH(Key key, *factor) {
if(std::binary_search(rootKeys.begin(), rootKeys.end(), key)) ++count;
}
if(count == factor->size()) expectedSmootherFactors.push_back(factor);
}
// Extract smoother Values
expectedSmootherValues.clear();
expectedSmootherValues.insert(Symbol('X', 7), updatedTheta.at(Symbol('X', 7)));
expectedSmootherValues.insert(Symbol('X', 8), updatedTheta.at(Symbol('X', 8)));
expectedSmootherValues.insert(Symbol('X', 9), updatedTheta.at(Symbol('X', 9)));
expectedSmootherValues.insert(Symbol('X', 10), updatedTheta.at(Symbol('X', 10)));
expectedSmootherValues.insert(Symbol('X', 11), updatedTheta.at(Symbol('X', 11)));
expectedSmootherValues.insert(Symbol('X', 12), updatedTheta.at(Symbol('X', 12)));
expectedSmootherValues.insert(Symbol('X', 13), updatedTheta.at(Symbol('X', 13)));
// Extract the filter summarized factors
// Cached factors that represent the filter side (i.e. the X15 and X17 clique)
expectedFilterSumarization.resize(0);
expectedFilterSumarization.add(LinearizedJacobianFactor(boost::static_pointer_cast<JacobianFactor>(bayesTree.nodes().at(ordering.at(Symbol('X', 15)))->cachedFactor()), ordering, fullTheta));
expectedFilterSumarization.add(LinearizedJacobianFactor(boost::static_pointer_cast<JacobianFactor>(bayesTree.nodes().at(ordering.at(Symbol('X', 17)))->cachedFactor()), ordering, fullTheta));
// And any factors that involve only the root (X14, X16, X17)
rootKeys.clear();
rootKeys.push_back(Symbol('X', 14));
rootKeys.push_back(Symbol('X', 16));
rootKeys.push_back(Symbol('X', 19));
BOOST_FOREACH(const NonlinearFactor::shared_ptr& factor, fullGraph) {
size_t count = 0;
BOOST_FOREACH(Key key, *factor) {
if(std::binary_search(rootKeys.begin(), rootKeys.end(), key)) ++count;
}
if(count == factor->size()) expectedFilterSumarization.push_back(factor);
}
// Extract the new root keys
expectedRootValues.clear();
expectedRootValues.insert(Symbol('X', 14), updatedTheta.at(Symbol('X', 14)));
expectedRootValues.insert(Symbol('X', 16), updatedTheta.at(Symbol('X', 16)));
expectedRootValues.insert(Symbol('X', 19), updatedTheta.at(Symbol('X', 19)));
// Start the synchronization process
actualSmootherFactors.resize(0); actualFilterSumarization.resize(0);
actualSmootherValues.clear(); actualRootValues.clear();
filter.presync();
filter.synchronize(smootherSummarization);
filter.getSmootherFactors(actualSmootherFactors, actualSmootherValues);
filter.getSummarizedFactors(actualFilterSumarization, actualRootValues);
filter.postsync();
// Compare filter sync variables versus the expected
CHECK(hessian_equal(expectedSmootherFactors, actualSmootherFactors, updatedTheta, 1e-8));
CHECK(assert_equal(expectedSmootherValues, actualSmootherValues, 1e-3));
CHECK(hessian_equal(expectedFilterSumarization, actualFilterSumarization, updatedTheta, 1e-8));
CHECK(assert_equal(expectedRootValues, actualRootValues, 1e-4));
}
///* ************************************************************************* */
//TEST_UNSAFE( ConcurrentBatchFilter, synchronize )
//{
// // Test the 'synchronize' function of the ConcurrentBatchFilter in a nonlinear environment.
// // The filter is operating on a known tree structure, so the factors and summarization can
// // be predicted for testing purposes
//
// // Create a set of optimizer parameters
// LevenbergMarquardtParams parameters;
// double lag = 4.0;
//
// // Create a Concurrent Batch Filter
// ConcurrentBatchFilterTester filter(parameters, lag);
//
// // Create containers to keep the full graph
// Values newTheta, fullTheta;
// NonlinearFactorGraph newGraph, fullGraph;
// ConcurrentBatchFilter::KeyTimestampMap newTimestamps;
//
// // Create all factors
// CreateFactors(newGraph, newTheta, newTimestamps, 0, 13);
// fullTheta.insert(newTheta);
// fullGraph.push_back(newGraph);
//
// // Optimize with Concurrent Batch Filter
// filter.update(newGraph, newTheta, newTimestamps);
// Values updatedTheta = filter.calculateEstimate();
//
// // Eliminate the factor graph into a Bayes Tree to create the summarized factors
// // Create Ordering
// std::map<Key, int> constraints;
// constraints[Symbol('X', 8)] = 1;
// constraints[Symbol('X', 9)] = 1;
// constraints[Symbol('X', 10)] = 1;
// constraints[Symbol('X', 11)] = 1;
// constraints[Symbol('X', 12)] = 1;
// Ordering ordering = *fullGraph.orderingCOLAMDConstrained(fullTheta, constraints);
// // Linearize into a Gaussian Factor Graph
// GaussianFactorGraph linearGraph = *fullGraph.linearize(fullTheta, ordering);
// // Eliminate into a Bayes Net with iSAM2-type cliques
// JunctionTree<GaussianFactorGraph, ISAM2Clique> jt(linearGraph);
// ISAM2Clique::shared_ptr root = jt.eliminate(EliminateQR);
// BayesTree<GaussianConditional, ISAM2Clique> bayesTree;
// bayesTree.insert(root);
//
// // std::cout << "BAYES TREE:" << std::endl;
// // SymbolicPrintTree(root, ordering.invert(), " ");
//
// // BAYES TREE:
// // P( X7 X9 X12 )
// // P( X10 | X9 )
// // P( X11 | X10 )
// // P( X8 | X9 )
// // P( X6 | X7 X9 )
// // P( X3 X5 | X6 )
// // P( X2 | X3 )
// // P( X1 | X3 )
// // P( X0 | X1 )
// // P( X4 | X7 )
//
// // Extract the nonlinear factors that should be sent to the smoother
// std::vector<Key> smootherKeys;
// smootherKeys.push_back(Symbol('X', 0));
// smootherKeys.push_back(Symbol('X', 1));
// smootherKeys.push_back(Symbol('X', 2));
// smootherKeys.push_back(Symbol('X', 3));
// smootherKeys.push_back(Symbol('X', 4));
// smootherKeys.push_back(Symbol('X', 5));
// smootherKeys.push_back(Symbol('X', 6));
// NonlinearFactorGraph expectedSmootherFactors;
// BOOST_FOREACH(const NonlinearFactor::shared_ptr& factor, fullGraph) {
// if(std::find_first_of(factor->begin(), factor->end(), smootherKeys.begin(), smootherKeys.end()) != factor->end()) {
// expectedSmootherFactors.push_back(factor);
// }
// }
//
// // Extract smoother values
// Values expectedSmootherValues;
// expectedSmootherValues.insert(Symbol('X', 0), updatedTheta.at(Symbol('X', 0)));
// expectedSmootherValues.insert(Symbol('X', 1), updatedTheta.at(Symbol('X', 1)));
// expectedSmootherValues.insert(Symbol('X', 2), updatedTheta.at(Symbol('X', 2)));
// expectedSmootherValues.insert(Symbol('X', 3), updatedTheta.at(Symbol('X', 3)));
// expectedSmootherValues.insert(Symbol('X', 4), updatedTheta.at(Symbol('X', 4)));
// expectedSmootherValues.insert(Symbol('X', 5), updatedTheta.at(Symbol('X', 5)));
// expectedSmootherValues.insert(Symbol('X', 6), updatedTheta.at(Symbol('X', 6)));
//
// // Extract the filter summarized factors
// // Cached factors that represent the filter side (i.e. the X8 and X10 clique)
// NonlinearFactorGraph expectedFilterSumarization;
// expectedFilterSumarization.add(LinearizedJacobianFactor(boost::static_pointer_cast<JacobianFactor>(bayesTree.nodes().at(ordering.at(Symbol('X', 8)))->cachedFactor()), ordering, fullTheta));
// expectedFilterSumarization.add(LinearizedJacobianFactor(boost::static_pointer_cast<JacobianFactor>(bayesTree.nodes().at(ordering.at(Symbol('X', 10)))->cachedFactor()), ordering, fullTheta));
// // And any factors that involve only the root (X7, X9, X12)
// std::vector<Key> rootKeys;
// rootKeys.push_back(Symbol('X', 7));
// rootKeys.push_back(Symbol('X', 9));
// rootKeys.push_back(Symbol('X', 12));
// BOOST_FOREACH(const NonlinearFactor::shared_ptr& factor, fullGraph) {
// size_t count = 0;
// BOOST_FOREACH(Key key, *factor) {
// if(std::binary_search(rootKeys.begin(), rootKeys.end(), key)) ++count;
// }
// if(count == factor->size()) expectedFilterSumarization.push_back(factor);
// }
//
// // Extract the new root values
// Values expectedRootValues;
// expectedRootValues.insert(Symbol('X', 7), updatedTheta.at(Symbol('X', 7)));
// expectedRootValues.insert(Symbol('X', 9), updatedTheta.at(Symbol('X', 9)));
// expectedRootValues.insert(Symbol('X', 12), updatedTheta.at(Symbol('X', 12)));
//
//
//
// // Start the synchronization process
// NonlinearFactorGraph actualSmootherFactors, actualFilterSumarization, smootherSummarization;
// Values actualSmootherValues, actualRootValues;
// filter.presync();
// filter.synchronize(smootherSummarization); // Supplying an empty factor graph here
// filter.getSmootherFactors(actualSmootherFactors, actualSmootherValues);
// filter.getSummarizedFactors(actualFilterSumarization, actualRootValues);
// filter.postsync();
//
//
//
// // Compare filter sync variables versus the expected
// CHECK(hessian_equal(expectedSmootherFactors, actualSmootherFactors, updatedTheta, 1e-8));
// CHECK(assert_equal(expectedSmootherValues, actualSmootherValues, 1e-4));
// CHECK(hessian_equal(expectedFilterSumarization, actualFilterSumarization, updatedTheta, 1e-9));
// CHECK(assert_equal(expectedRootValues, actualRootValues, 1e-4));
//
//
//
//
//
//
// // Now add additional factors to the filter and re-sync
// newGraph.resize(0); newTheta.clear(); newTimestamps.clear();
// CreateFactors(newGraph, newTheta, newTimestamps, 13, 20);
// fullTheta.insert(newTheta);
// fullGraph.push_back(newGraph);
//
// // Optimize with Concurrent Batch Filter
// filter.update(newGraph, newTheta, newTimestamps);
// updatedTheta = filter.calculateEstimate();
//
// // Eliminate the factor graph into a Bayes Tree to create the summarized factors
// // Create Ordering
// constraints.clear();
// constraints[Symbol('X', 15)] = 1;
// constraints[Symbol('X', 16)] = 1;
// constraints[Symbol('X', 17)] = 1;
// constraints[Symbol('X', 18)] = 1;
// constraints[Symbol('X', 19)] = 1;
// ordering = *fullGraph.orderingCOLAMDConstrained(fullTheta, constraints);
// // Linearize into a Gaussian Factor Graph
// linearGraph = *fullGraph.linearize(fullTheta, ordering);
// // Eliminate into a Bayes Net with iSAM2-type cliques
// jt = JunctionTree<GaussianFactorGraph, ISAM2Clique>(linearGraph);
// root = jt.eliminate(EliminateQR);
// bayesTree = BayesTree<GaussianConditional, ISAM2Clique>();
// bayesTree.insert(root);
//
// // std::cout << "BAYES TREE:" << std::endl;
// // SymbolicPrintTree(root, ordering.invert(), " ");
//
// // BAYES TREE:
// // P( X14 X16 X19 )
// // P( X17 | X16 )
// // P( X18 | X17 )
// // P( X15 | X16 )
// // P( X13 | X14 X16 )
// // P( X11 | X13 X14 )
// // P( X10 | X11 X13 )
// // P( X12 | X10 X13 )
// // P( X9 | X12 X10 )
// // P( X7 | X9 X12 )
// // P( X6 | X7 X9 )
// // P( X3 X5 | X6 )
// // P( X2 | X3 )
// // P( X1 | X3 )
// // P( X0 | X1 )
// // P( X4 | X7 )
// // P( X8 | X9 )
//
// // Extract the cached factors for X4 and X6. These are the new smoother summarized factors
// // TODO: I'm concerned about the linearization point used to create these factors. It may need to be the updated lin points?
// smootherSummarization.resize(0);
// smootherSummarization.add(LinearizedJacobianFactor(boost::static_pointer_cast<JacobianFactor>(bayesTree.nodes().at(ordering.at(Symbol('X', 4)))->cachedFactor()), ordering, fullTheta));
// smootherSummarization.add(LinearizedJacobianFactor(boost::static_pointer_cast<JacobianFactor>(bayesTree.nodes().at(ordering.at(Symbol('X', 6)))->cachedFactor()), ordering, fullTheta));
//
//
//
//
//
// // Extract the nonlinear factors that should be sent to the smoother
// smootherKeys.clear();
// smootherKeys.push_back(Symbol('X', 8));
// smootherKeys.push_back(Symbol('X', 10));
// smootherKeys.push_back(Symbol('X', 11));
// smootherKeys.push_back(Symbol('X', 13));
// expectedSmootherFactors.resize(0);
// BOOST_FOREACH(const NonlinearFactor::shared_ptr& factor, fullGraph) {
// if(std::find_first_of(factor->begin(), factor->end(), smootherKeys.begin(), smootherKeys.end()) != factor->end()) {
// expectedSmootherFactors.push_back(factor);
// }
// }
// // And any factors that involve only the old root (X7, X9, X12)
// rootKeys.clear();
// rootKeys.push_back(Symbol('X', 7));
// rootKeys.push_back(Symbol('X', 9));
// rootKeys.push_back(Symbol('X', 12));
// BOOST_FOREACH(const NonlinearFactor::shared_ptr& factor, fullGraph) {
// size_t count = 0;
// BOOST_FOREACH(Key key, *factor) {
// if(std::binary_search(rootKeys.begin(), rootKeys.end(), key)) ++count;
// }
// if(count == factor->size()) expectedSmootherFactors.push_back(factor);
// }
//
//
// // Extract smoother Values
// expectedSmootherValues.clear();
// expectedSmootherValues.insert(Symbol('X', 7), updatedTheta.at(Symbol('X', 7)));
// expectedSmootherValues.insert(Symbol('X', 8), updatedTheta.at(Symbol('X', 8)));
// expectedSmootherValues.insert(Symbol('X', 9), updatedTheta.at(Symbol('X', 9)));
// expectedSmootherValues.insert(Symbol('X', 10), updatedTheta.at(Symbol('X', 10)));
// expectedSmootherValues.insert(Symbol('X', 11), updatedTheta.at(Symbol('X', 11)));
// expectedSmootherValues.insert(Symbol('X', 12), updatedTheta.at(Symbol('X', 12)));
// expectedSmootherValues.insert(Symbol('X', 13), updatedTheta.at(Symbol('X', 13)));
//
// // Extract the filter summarized factors
// // Cached factors that represent the filter side (i.e. the X15 and X17 clique)
// expectedFilterSumarization.resize(0);
// expectedFilterSumarization.add(LinearizedJacobianFactor(boost::static_pointer_cast<JacobianFactor>(bayesTree.nodes().at(ordering.at(Symbol('X', 15)))->cachedFactor()), ordering, fullTheta));
// expectedFilterSumarization.add(LinearizedJacobianFactor(boost::static_pointer_cast<JacobianFactor>(bayesTree.nodes().at(ordering.at(Symbol('X', 17)))->cachedFactor()), ordering, fullTheta));
// // And any factors that involve only the root (X14, X16, X17)
// rootKeys.clear();
// rootKeys.push_back(Symbol('X', 14));
// rootKeys.push_back(Symbol('X', 16));
// rootKeys.push_back(Symbol('X', 19));
// BOOST_FOREACH(const NonlinearFactor::shared_ptr& factor, fullGraph) {
// size_t count = 0;
// BOOST_FOREACH(Key key, *factor) {
// if(std::binary_search(rootKeys.begin(), rootKeys.end(), key)) ++count;
// }
// if(count == factor->size()) expectedFilterSumarization.push_back(factor);
// }
//
// // Extract the new root keys
// expectedRootValues.clear();
// expectedRootValues.insert(Symbol('X', 14), updatedTheta.at(Symbol('X', 14)));
// expectedRootValues.insert(Symbol('X', 16), updatedTheta.at(Symbol('X', 16)));
// expectedRootValues.insert(Symbol('X', 19), updatedTheta.at(Symbol('X', 19)));
//
//
//
// // Start the synchronization process
// actualSmootherFactors.resize(0); actualFilterSumarization.resize(0);
// actualSmootherValues.clear(); actualRootValues.clear();
// filter.presync();
// filter.synchronize(smootherSummarization);
// filter.getSmootherFactors(actualSmootherFactors, actualSmootherValues);
// filter.getSummarizedFactors(actualFilterSumarization, actualRootValues);
// filter.postsync();
//
//
//
// // Compare filter sync variables versus the expected
// CHECK(hessian_equal(expectedSmootherFactors, actualSmootherFactors, updatedTheta, 1e-8));
// CHECK(assert_equal(expectedSmootherValues, actualSmootherValues, 1e-3));
// CHECK(hessian_equal(expectedFilterSumarization, actualFilterSumarization, updatedTheta, 1e-8));
// CHECK(assert_equal(expectedRootValues, actualRootValues, 1e-4));
//}
/* ************************************************************************* */
int main() { TestResult tr; return TestRegistry::runAllTests(tr);}

View File

@ -17,25 +17,20 @@
*/
#include <gtsam_unstable/nonlinear/ConcurrentBatchSmoother.h>
#include <gtsam_unstable/nonlinear/LinearizedFactor.h>
#include <gtsam/slam/PriorFactor.h>
#include <gtsam/slam/BetweenFactor.h>
#include <gtsam/nonlinear/ISAM2.h>
#include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
#include <gtsam/nonlinear/Ordering.h>
#include <gtsam/nonlinear/Values.h>
#include <gtsam/nonlinear/Symbol.h>
#include <gtsam/nonlinear/Key.h>
#include <gtsam/inference/JunctionTree.h>
#include <gtsam/geometry/Pose3.h>
#include <CppUnitLite/TestHarness.h>
using namespace std;
using namespace gtsam;
namespace {
// Set up initial pose, odometry difference, loop closure difference, and initialization errors
const Pose3 poseInitial;
const Pose3 poseOdometry( Rot3::RzRyRx(Vector_(3, 0.05, 0.10, -0.75)), Point3(1.0, -0.25, 0.10) );
@ -336,7 +331,7 @@ void CreateFactors(NonlinearFactorGraph& graph, Values& theta, size_t index1 = 0
}
/* ************************************************************************* */
Values BatchOptimize(const NonlinearFactorGraph& graph, const Values& theta, const Values& rootValues = Values()) {
Values BatchOptimize(const NonlinearFactorGraph& graph, const Values& theta, const Values& separatorValues = Values()) {
// Create an L-M optimizer
LevenbergMarquardtParams parameters;
@ -348,9 +343,9 @@ Values BatchOptimize(const NonlinearFactorGraph& graph, const Values& theta, con
double currentError;
do {
// Force variables associated with root keys to keep the same linearization point
if(rootValues.size() > 0) {
if(separatorValues.size() > 0) {
// Put the old values of the root keys back into the optimizer state
optimizer.state().values.update(rootValues);
optimizer.state().values.update(separatorValues);
// Update the error value with the new theta
optimizer.state().error = graph.error(optimizer.state().values);
}
@ -367,60 +362,60 @@ Values BatchOptimize(const NonlinearFactorGraph& graph, const Values& theta, con
return optimizer.values();
}
/* ************************************************************************* */
void FindFactorsWithAny(const std::set<Key>& keys, const NonlinearFactorGraph& sourceFactors, NonlinearFactorGraph& destinationFactors) {
BOOST_FOREACH(const NonlinearFactor::shared_ptr& factor, sourceFactors) {
NonlinearFactor::const_iterator key = factor->begin();
while((key != factor->end()) && (!std::binary_search(keys.begin(), keys.end(), *key))) {
++key;
}
if(key != factor->end()) {
destinationFactors.push_back(factor);
}
}
}
///* ************************************************************************* */
//void FindFactorsWithAny(const std::set<Key>& keys, const NonlinearFactorGraph& sourceFactors, NonlinearFactorGraph& destinationFactors) {
//
// BOOST_FOREACH(const NonlinearFactor::shared_ptr& factor, sourceFactors) {
// NonlinearFactor::const_iterator key = factor->begin();
// while((key != factor->end()) && (!std::binary_search(keys.begin(), keys.end(), *key))) {
// ++key;
// }
// if(key != factor->end()) {
// destinationFactors.push_back(factor);
// }
// }
//
//}
//
///* ************************************************************************* */
//void FindFactorsWithOnly(const std::set<Key>& keys, const NonlinearFactorGraph& sourceFactors, NonlinearFactorGraph& destinationFactors) {
//
// BOOST_FOREACH(const NonlinearFactor::shared_ptr& factor, sourceFactors) {
// NonlinearFactor::const_iterator key = factor->begin();
// while((key != factor->end()) && (std::binary_search(keys.begin(), keys.end(), *key))) {
// ++key;
// }
// if(key == factor->end()) {
// destinationFactors.push_back(factor);
// }
// }
//
//}
//
///* ************************************************************************* */
//typedef BayesTree<GaussianConditional,ISAM2Clique>::sharedClique Clique;
//void SymbolicPrintTree(const Clique& clique, const Ordering& ordering, const std::string indent = "") {
// std::cout << indent << "P( ";
// BOOST_FOREACH(Index index, clique->conditional()->frontals()){
// std::cout << DefaultKeyFormatter(ordering.key(index)) << " ";
// }
// if(clique->conditional()->nrParents() > 0) {
// std::cout << "| ";
// }
// BOOST_FOREACH(Index index, clique->conditional()->parents()){
// std::cout << DefaultKeyFormatter(ordering.key(index)) << " ";
// }
// std::cout << ")" << std::endl;
//
// BOOST_FOREACH(const Clique& child, clique->children()) {
// SymbolicPrintTree(child, ordering, indent+" ");
// }
//}
//
//}
/* ************************************************************************* */
void FindFactorsWithOnly(const std::set<Key>& keys, const NonlinearFactorGraph& sourceFactors, NonlinearFactorGraph& destinationFactors) {
BOOST_FOREACH(const NonlinearFactor::shared_ptr& factor, sourceFactors) {
NonlinearFactor::const_iterator key = factor->begin();
while((key != factor->end()) && (std::binary_search(keys.begin(), keys.end(), *key))) {
++key;
}
if(key == factor->end()) {
destinationFactors.push_back(factor);
}
}
}
/* ************************************************************************* */
typedef BayesTree<GaussianConditional,ISAM2Clique>::sharedClique Clique;
void SymbolicPrintTree(const Clique& clique, const Ordering& ordering, const std::string indent = "") {
std::cout << indent << "P( ";
BOOST_FOREACH(Index index, clique->conditional()->frontals()){
std::cout << DefaultKeyFormatter(ordering.key(index)) << " ";
}
if(clique->conditional()->nrParents() > 0) {
std::cout << "| ";
}
BOOST_FOREACH(Index index, clique->conditional()->parents()){
std::cout << DefaultKeyFormatter(ordering.key(index)) << " ";
}
std::cout << ")" << std::endl;
BOOST_FOREACH(const Clique& child, clique->children()) {
SymbolicPrintTree(child, ordering, indent+" ");
}
}
}
/* ************************************************************************* */
TEST_UNSAFE( ConcurrentBatchSmoother, update_Batch )
TEST_UNSAFE( ConcurrentBatchSmoother, update_batch )
{
// Test the 'update' function of the ConcurrentBatchSmoother in a nonlinear environment.
// Thus, a full L-M optimization and the ConcurrentBatchSmoother results should be identical
@ -451,7 +446,7 @@ TEST_UNSAFE( ConcurrentBatchSmoother, update_Batch )
}
/* ************************************************************************* */
TEST_UNSAFE( ConcurrentBatchSmoother, update_Incremental )
TEST_UNSAFE( ConcurrentBatchSmoother, update_incremental )
{
// Test the 'update' function of the ConcurrentBatchSmoother in a nonlinear environment.
// Thus, a full L-M optimization and the ConcurrentBatchSmoother results should be identical
@ -492,220 +487,220 @@ TEST_UNSAFE( ConcurrentBatchSmoother, update_Incremental )
}
/* ************************************************************************* */
TEST_UNSAFE( ConcurrentBatchSmoother, synchronize )
{
// Test the 'synchronize' function of the ConcurrentBatchSmoother in a nonlinear environment.
// The smoother is operating on a known tree structure, so the factors and summarization can
// be predicted for testing purposes
// Create a set of optimizer parameters
LevenbergMarquardtParams parameters;
// Create a Concurrent Batch Smoother
ConcurrentBatchSmootherTester smoother(parameters);
// Create containers to keep the full graph
Values fullTheta;
NonlinearFactorGraph fullGraph;
// Create factors for times 0 - 12
// When eliminated with ordering (X2 X0 X1 X4 X5 X3 X6 X8 X11 X10 X7 X9 X12)augmentedHessian
// ... this Bayes Tree is produced:
// Bayes Tree:
// P( X7 X9 X12 )
// P( X10 | X9 )
// P( X11 | X10 )
// P( X8 | X9 )
// P( X6 | X7 X9 )
// P( X5 X3 | X6 )
// P( X1 | X3 )
// P( X0 | X1 )
// P( X2 | X3 )
// P( X4 | X7 )
// We then produce the inputs necessary for the 'synchronize' function.
// The smoother is branches X4 and X6, the filter is branches X8 and X10, and the root is (X7 X9 X12)
CreateFactors(fullGraph, fullTheta, 0, 13);
// Optimize the full graph
Values optimalTheta = BatchOptimize(fullGraph, fullTheta);
// Re-eliminate to create the Bayes Tree
Ordering ordering;
ordering.push_back(Symbol('X', 2));
ordering.push_back(Symbol('X', 0));
ordering.push_back(Symbol('X', 1));
ordering.push_back(Symbol('X', 4));
ordering.push_back(Symbol('X', 5));
ordering.push_back(Symbol('X', 3));
ordering.push_back(Symbol('X', 6));
ordering.push_back(Symbol('X', 8));
ordering.push_back(Symbol('X', 11));
ordering.push_back(Symbol('X', 10));
ordering.push_back(Symbol('X', 7));
ordering.push_back(Symbol('X', 9));
ordering.push_back(Symbol('X', 12));
Values linpoint;
linpoint.insert(optimalTheta);
GaussianFactorGraph linearGraph = *fullGraph.linearize(linpoint, ordering);
JunctionTree<GaussianFactorGraph, ISAM2Clique> jt(linearGraph);
ISAM2Clique::shared_ptr root = jt.eliminate(EliminateQR);
BayesTree<GaussianConditional, ISAM2Clique> bayesTree;
bayesTree.insert(root);
// Extract the values for the smoother keys. This consists of the branches: X4 and X6
// Extract the non-root values from the initial values to test the smoother optimization
Values smootherValues;
smootherValues.insert(Symbol('X', 0), fullTheta.at(Symbol('X', 0)));
smootherValues.insert(Symbol('X', 1), fullTheta.at(Symbol('X', 1)));
smootherValues.insert(Symbol('X', 2), fullTheta.at(Symbol('X', 2)));
smootherValues.insert(Symbol('X', 3), fullTheta.at(Symbol('X', 3)));
smootherValues.insert(Symbol('X', 4), fullTheta.at(Symbol('X', 4)));
smootherValues.insert(Symbol('X', 5), fullTheta.at(Symbol('X', 5)));
smootherValues.insert(Symbol('X', 6), fullTheta.at(Symbol('X', 6)));
// Extract the optimal root values
Values rootValues;
rootValues.insert(Symbol('X', 7), optimalTheta.at(Symbol('X', 7)));
rootValues.insert(Symbol('X', 9), optimalTheta.at(Symbol('X', 9)));
rootValues.insert(Symbol('X', 12), optimalTheta.at(Symbol('X', 12)));
// Extract the nonlinear smoother factors as any factor with a non-root smoother key
std::set<Key> smootherKeys;
smootherKeys.insert(Symbol('X', 0));
smootherKeys.insert(Symbol('X', 1));
smootherKeys.insert(Symbol('X', 2));
smootherKeys.insert(Symbol('X', 3));
smootherKeys.insert(Symbol('X', 4));
smootherKeys.insert(Symbol('X', 5));
smootherKeys.insert(Symbol('X', 6));
NonlinearFactorGraph smootherFactors;
FindFactorsWithAny(smootherKeys, fullGraph, smootherFactors);
// Extract the filter summarized factors. This consists of the linear cached factors from
// the filter branches X8 and X10, as well as any nonlinear factor that involves only root keys
NonlinearFactorGraph filterSummarization;
filterSummarization.add(LinearizedJacobianFactor(boost::static_pointer_cast<JacobianFactor>(bayesTree.nodes().at(ordering.at(Symbol('X', 8)))->cachedFactor()), ordering, linpoint));
filterSummarization.add(LinearizedJacobianFactor(boost::static_pointer_cast<JacobianFactor>(bayesTree.nodes().at(ordering.at(Symbol('X', 10)))->cachedFactor()), ordering, linpoint));
std::set<Key> rootKeys;
rootKeys.insert(Symbol('X', 7));
rootKeys.insert(Symbol('X', 9));
rootKeys.insert(Symbol('X', 12));
FindFactorsWithOnly(rootKeys, fullGraph, filterSummarization);
// Perform the synchronization procedure
NonlinearFactorGraph actualSmootherSummarization;
smoother.presync();
smoother.getSummarizedFactors(actualSmootherSummarization);
smoother.synchronize(smootherFactors, smootherValues, filterSummarization, rootValues);
smoother.postsync();
// Verify the returned smoother values is empty in the first iteration
NonlinearFactorGraph expectedSmootherSummarization;
CHECK(assert_equal(expectedSmootherSummarization, actualSmootherSummarization, 1e-4));
// Perform a full update of the smoother. Since the root values/summarized filter factors were
// created at the optimal values, the smoother should be identical to the batch optimization
smoother.update();
Values actualSmootherTheta = smoother.calculateEstimate();
// Create the expected values as the optimal set
Values expectedSmootherTheta;
expectedSmootherTheta.insert(Symbol('X', 0), optimalTheta.at(Symbol('X', 0)));
expectedSmootherTheta.insert(Symbol('X', 1), optimalTheta.at(Symbol('X', 1)));
expectedSmootherTheta.insert(Symbol('X', 2), optimalTheta.at(Symbol('X', 2)));
expectedSmootherTheta.insert(Symbol('X', 3), optimalTheta.at(Symbol('X', 3)));
expectedSmootherTheta.insert(Symbol('X', 4), optimalTheta.at(Symbol('X', 4)));
expectedSmootherTheta.insert(Symbol('X', 5), optimalTheta.at(Symbol('X', 5)));
expectedSmootherTheta.insert(Symbol('X', 6), optimalTheta.at(Symbol('X', 6)));
// Compare filter solution with full batch
CHECK(assert_equal(expectedSmootherTheta, actualSmootherTheta, 1e-4));
// Add a loop closure factor to the smoother and re-check. Since the filter
// factors were created at the optimal linpoint, and since the new loop closure
// does not involve filter keys, the smoother should still yeild the optimal solution
// The new Bayes Tree is:
// Bayes Tree:
// P( X7 X9 X12 )
// P( X10 | X9 )
// P( X11 | X10 )
// P( X8 | X9 )
// P( X6 | X7 X9 )
// P( X4 | X6 X7 )
// P( X3 X5 | X4 X6 )
// P( X2 | X3 )
// P( X1 | X3 X4 )
// P( X0 | X1 )
Pose3 poseDelta = fullTheta.at<Pose3>(Symbol('X', 1)).between(fullTheta.at<Pose3>(Symbol('X', 4)));
NonlinearFactor::shared_ptr loopClosure = NonlinearFactor::shared_ptr(new BetweenFactor<Pose3>(Symbol('X', 1), Symbol('X', 4), poseDelta, noiseOdometery));
fullGraph.push_back(loopClosure);
optimalTheta = BatchOptimize(fullGraph, fullTheta, rootValues);
// Recreate the Bayes Tree
linpoint.clear();
linpoint.insert(optimalTheta);
linpoint.update(rootValues);
linearGraph = *fullGraph.linearize(linpoint, ordering);
jt = JunctionTree<GaussianFactorGraph, ISAM2Clique>(linearGraph);
root = jt.eliminate(EliminateQR);
bayesTree = BayesTree<GaussianConditional, ISAM2Clique>();
bayesTree.insert(root);
// Add the loop closure to the smoother
NonlinearFactorGraph newFactors;
newFactors.push_back(loopClosure);
smoother.update(newFactors);
actualSmootherTheta = smoother.calculateEstimate();
// Create the expected values as the optimal set
expectedSmootherTheta.clear();
expectedSmootherTheta.insert(Symbol('X', 0), optimalTheta.at(Symbol('X', 0)));
expectedSmootherTheta.insert(Symbol('X', 1), optimalTheta.at(Symbol('X', 1)));
expectedSmootherTheta.insert(Symbol('X', 2), optimalTheta.at(Symbol('X', 2)));
expectedSmootherTheta.insert(Symbol('X', 3), optimalTheta.at(Symbol('X', 3)));
expectedSmootherTheta.insert(Symbol('X', 4), optimalTheta.at(Symbol('X', 4)));
expectedSmootherTheta.insert(Symbol('X', 5), optimalTheta.at(Symbol('X', 5)));
expectedSmootherTheta.insert(Symbol('X', 6), optimalTheta.at(Symbol('X', 6)));
// Compare filter solution with full batch
// TODO: Check This
///* ************************************************************************* */
//TEST_UNSAFE( ConcurrentBatchSmoother, synchronize )
//{
// // Test the 'synchronize' function of the ConcurrentBatchSmoother in a nonlinear environment.
// // The smoother is operating on a known tree structure, so the factors and summarization can
// // be predicted for testing purposes
//
// // Create a set of optimizer parameters
// LevenbergMarquardtParams parameters;
//
// // Create a Concurrent Batch Smoother
// ConcurrentBatchSmootherTester smoother(parameters);
//
// // Create containers to keep the full graph
// Values fullTheta;
// NonlinearFactorGraph fullGraph;
//
// // Create factors for times 0 - 12
// // When eliminated with ordering (X2 X0 X1 X4 X5 X3 X6 X8 X11 X10 X7 X9 X12)augmentedHessian
// // ... this Bayes Tree is produced:
// // Bayes Tree:
// // P( X7 X9 X12 )
// // P( X10 | X9 )
// // P( X11 | X10 )
// // P( X8 | X9 )
// // P( X6 | X7 X9 )
// // P( X5 X3 | X6 )
// // P( X1 | X3 )
// // P( X0 | X1 )
// // P( X2 | X3 )
// // P( X4 | X7 )
// // We then produce the inputs necessary for the 'synchronize' function.
// // The smoother is branches X4 and X6, the filter is branches X8 and X10, and the root is (X7 X9 X12)
// CreateFactors(fullGraph, fullTheta, 0, 13);
//
// // Optimize the full graph
// Values optimalTheta = BatchOptimize(fullGraph, fullTheta);
//
// // Re-eliminate to create the Bayes Tree
// Ordering ordering;
// ordering.push_back(Symbol('X', 2));
// ordering.push_back(Symbol('X', 0));
// ordering.push_back(Symbol('X', 1));
// ordering.push_back(Symbol('X', 4));
// ordering.push_back(Symbol('X', 5));
// ordering.push_back(Symbol('X', 3));
// ordering.push_back(Symbol('X', 6));
// ordering.push_back(Symbol('X', 8));
// ordering.push_back(Symbol('X', 11));
// ordering.push_back(Symbol('X', 10));
// ordering.push_back(Symbol('X', 7));
// ordering.push_back(Symbol('X', 9));
// ordering.push_back(Symbol('X', 12));
// Values linpoint;
// linpoint.insert(optimalTheta);
// GaussianFactorGraph linearGraph = *fullGraph.linearize(linpoint, ordering);
// JunctionTree<GaussianFactorGraph, ISAM2Clique> jt(linearGraph);
// ISAM2Clique::shared_ptr root = jt.eliminate(EliminateQR);
// BayesTree<GaussianConditional, ISAM2Clique> bayesTree;
// bayesTree.insert(root);
//
// // Extract the values for the smoother keys. This consists of the branches: X4 and X6
// // Extract the non-root values from the initial values to test the smoother optimization
// Values smootherValues;
// smootherValues.insert(Symbol('X', 0), fullTheta.at(Symbol('X', 0)));
// smootherValues.insert(Symbol('X', 1), fullTheta.at(Symbol('X', 1)));
// smootherValues.insert(Symbol('X', 2), fullTheta.at(Symbol('X', 2)));
// smootherValues.insert(Symbol('X', 3), fullTheta.at(Symbol('X', 3)));
// smootherValues.insert(Symbol('X', 4), fullTheta.at(Symbol('X', 4)));
// smootherValues.insert(Symbol('X', 5), fullTheta.at(Symbol('X', 5)));
// smootherValues.insert(Symbol('X', 6), fullTheta.at(Symbol('X', 6)));
//
// // Extract the optimal root values
// Values rootValues;
// rootValues.insert(Symbol('X', 7), optimalTheta.at(Symbol('X', 7)));
// rootValues.insert(Symbol('X', 9), optimalTheta.at(Symbol('X', 9)));
// rootValues.insert(Symbol('X', 12), optimalTheta.at(Symbol('X', 12)));
//
// // Extract the nonlinear smoother factors as any factor with a non-root smoother key
// std::set<Key> smootherKeys;
// smootherKeys.insert(Symbol('X', 0));
// smootherKeys.insert(Symbol('X', 1));
// smootherKeys.insert(Symbol('X', 2));
// smootherKeys.insert(Symbol('X', 3));
// smootherKeys.insert(Symbol('X', 4));
// smootherKeys.insert(Symbol('X', 5));
// smootherKeys.insert(Symbol('X', 6));
// NonlinearFactorGraph smootherFactors;
// FindFactorsWithAny(smootherKeys, fullGraph, smootherFactors);
//
// // Extract the filter summarized factors. This consists of the linear cached factors from
// // the filter branches X8 and X10, as well as any nonlinear factor that involves only root keys
// NonlinearFactorGraph filterSummarization;
// filterSummarization.add(LinearizedJacobianFactor(boost::static_pointer_cast<JacobianFactor>(bayesTree.nodes().at(ordering.at(Symbol('X', 8)))->cachedFactor()), ordering, linpoint));
// filterSummarization.add(LinearizedJacobianFactor(boost::static_pointer_cast<JacobianFactor>(bayesTree.nodes().at(ordering.at(Symbol('X', 10)))->cachedFactor()), ordering, linpoint));
// std::set<Key> rootKeys;
// rootKeys.insert(Symbol('X', 7));
// rootKeys.insert(Symbol('X', 9));
// rootKeys.insert(Symbol('X', 12));
// FindFactorsWithOnly(rootKeys, fullGraph, filterSummarization);
//
//
//
// // Perform the synchronization procedure
// NonlinearFactorGraph actualSmootherSummarization;
// smoother.presync();
// smoother.getSummarizedFactors(actualSmootherSummarization);
// smoother.synchronize(smootherFactors, smootherValues, filterSummarization, rootValues);
// smoother.postsync();
//
// // Verify the returned smoother values is empty in the first iteration
// NonlinearFactorGraph expectedSmootherSummarization;
// CHECK(assert_equal(expectedSmootherSummarization, actualSmootherSummarization, 1e-4));
//
//
//
// // Perform a full update of the smoother. Since the root values/summarized filter factors were
// // created at the optimal values, the smoother should be identical to the batch optimization
// smoother.update();
// Values actualSmootherTheta = smoother.calculateEstimate();
//
// // Create the expected values as the optimal set
// Values expectedSmootherTheta;
// expectedSmootherTheta.insert(Symbol('X', 0), optimalTheta.at(Symbol('X', 0)));
// expectedSmootherTheta.insert(Symbol('X', 1), optimalTheta.at(Symbol('X', 1)));
// expectedSmootherTheta.insert(Symbol('X', 2), optimalTheta.at(Symbol('X', 2)));
// expectedSmootherTheta.insert(Symbol('X', 3), optimalTheta.at(Symbol('X', 3)));
// expectedSmootherTheta.insert(Symbol('X', 4), optimalTheta.at(Symbol('X', 4)));
// expectedSmootherTheta.insert(Symbol('X', 5), optimalTheta.at(Symbol('X', 5)));
// expectedSmootherTheta.insert(Symbol('X', 6), optimalTheta.at(Symbol('X', 6)));
//
// // Compare filter solution with full batch
// CHECK(assert_equal(expectedSmootherTheta, actualSmootherTheta, 1e-4));
// Now perform a second synchronization to test the smoother-calculated summarization
actualSmootherSummarization.resize(0);
smootherFactors.resize(0);
smootherValues.clear();
smoother.presync();
smoother.getSummarizedFactors(actualSmootherSummarization);
smoother.synchronize(smootherFactors, smootherValues, filterSummarization, rootValues);
smoother.postsync();
// Extract the expected smoother summarization from the Bayes Tree
// The smoother branches after the addition of the loop closure is only X6
expectedSmootherSummarization.resize(0);
JacobianFactor::shared_ptr jf = boost::dynamic_pointer_cast<JacobianFactor>(bayesTree.nodes().at(ordering.at(Symbol('X', 6)))->cachedFactor());
LinearizedJacobianFactor::shared_ptr ljf(new LinearizedJacobianFactor(jf, ordering, linpoint));
expectedSmootherSummarization.push_back(ljf);
// Compare smoother factors with the expected factors by computing the hessian information matrix
// TODO: Check This
// CHECK(hessian_equal(expectedSmootherSummarization, actualSmootherSummarization, linpoint, 1e-4));
// TODO: Modify the second synchronization so that the filter sends an additional set of factors.
// I'm not sure what additional code this will exercise, but just for good measure.
}
//
//
//
// // Add a loop closure factor to the smoother and re-check. Since the filter
// // factors were created at the optimal linpoint, and since the new loop closure
// // does not involve filter keys, the smoother should still yeild the optimal solution
// // The new Bayes Tree is:
// // Bayes Tree:
// // P( X7 X9 X12 )
// // P( X10 | X9 )
// // P( X11 | X10 )
// // P( X8 | X9 )
// // P( X6 | X7 X9 )
// // P( X4 | X6 X7 )
// // P( X3 X5 | X4 X6 )
// // P( X2 | X3 )
// // P( X1 | X3 X4 )
// // P( X0 | X1 )
// Pose3 poseDelta = fullTheta.at<Pose3>(Symbol('X', 1)).between(fullTheta.at<Pose3>(Symbol('X', 4)));
// NonlinearFactor::shared_ptr loopClosure = NonlinearFactor::shared_ptr(new BetweenFactor<Pose3>(Symbol('X', 1), Symbol('X', 4), poseDelta, noiseOdometery));
// fullGraph.push_back(loopClosure);
// optimalTheta = BatchOptimize(fullGraph, fullTheta, rootValues);
//
// // Recreate the Bayes Tree
// linpoint.clear();
// linpoint.insert(optimalTheta);
// linpoint.update(rootValues);
// linearGraph = *fullGraph.linearize(linpoint, ordering);
// jt = JunctionTree<GaussianFactorGraph, ISAM2Clique>(linearGraph);
// root = jt.eliminate(EliminateQR);
// bayesTree = BayesTree<GaussianConditional, ISAM2Clique>();
// bayesTree.insert(root);
//
// // Add the loop closure to the smoother
// NonlinearFactorGraph newFactors;
// newFactors.push_back(loopClosure);
// smoother.update(newFactors);
// actualSmootherTheta = smoother.calculateEstimate();
//
// // Create the expected values as the optimal set
// expectedSmootherTheta.clear();
// expectedSmootherTheta.insert(Symbol('X', 0), optimalTheta.at(Symbol('X', 0)));
// expectedSmootherTheta.insert(Symbol('X', 1), optimalTheta.at(Symbol('X', 1)));
// expectedSmootherTheta.insert(Symbol('X', 2), optimalTheta.at(Symbol('X', 2)));
// expectedSmootherTheta.insert(Symbol('X', 3), optimalTheta.at(Symbol('X', 3)));
// expectedSmootherTheta.insert(Symbol('X', 4), optimalTheta.at(Symbol('X', 4)));
// expectedSmootherTheta.insert(Symbol('X', 5), optimalTheta.at(Symbol('X', 5)));
// expectedSmootherTheta.insert(Symbol('X', 6), optimalTheta.at(Symbol('X', 6)));
//
// // Compare filter solution with full batch
// // TODO: Check This
//// CHECK(assert_equal(expectedSmootherTheta, actualSmootherTheta, 1e-4));
//
//
//
// // Now perform a second synchronization to test the smoother-calculated summarization
// actualSmootherSummarization.resize(0);
// smootherFactors.resize(0);
// smootherValues.clear();
// smoother.presync();
// smoother.getSummarizedFactors(actualSmootherSummarization);
// smoother.synchronize(smootherFactors, smootherValues, filterSummarization, rootValues);
// smoother.postsync();
//
// // Extract the expected smoother summarization from the Bayes Tree
// // The smoother branches after the addition of the loop closure is only X6
// expectedSmootherSummarization.resize(0);
// JacobianFactor::shared_ptr jf = boost::dynamic_pointer_cast<JacobianFactor>(bayesTree.nodes().at(ordering.at(Symbol('X', 6)))->cachedFactor());
// LinearizedJacobianFactor::shared_ptr ljf(new LinearizedJacobianFactor(jf, ordering, linpoint));
// expectedSmootherSummarization.push_back(ljf);
//
// // Compare smoother factors with the expected factors by computing the hessian information matrix
// // TODO: Check This
//// CHECK(hessian_equal(expectedSmootherSummarization, actualSmootherSummarization, linpoint, 1e-4));
//
//
//
// // TODO: Modify the second synchronization so that the filter sends an additional set of factors.
// // I'm not sure what additional code this will exercise, but just for good measure.
//
//}
/* ************************************************************************* */
int main() { TestResult tr; return TestRegistry::runAllTests(tr);}