gtsam/examples/UnorderedLinear.cpp

290 lines
10 KiB
C++

#include <gtsam/slam/dataset.h>
#include <gtsam/geometry/Pose2.h>
#include <gtsam/inference/VariableIndexOrdered.h>
#include <gtsam/linear/GaussianFactorGraph.h>
#include <gtsam/linear/VectorValues.h>
#include <gtsam/linear/GaussianMultifrontalSolver.h>
#include <gtsam/linear/GaussianBayesTree.h>
#include <gtsam/linear/GaussianJunctionTree.h>
#include <gtsam/linear/GaussianEliminationTree.h>
#include <gtsam/slam/PriorFactor.h>
#include <gtsam/slam/BetweenFactor.h>
#include <gtsam/nonlinear/Symbol.h>
#include <gtsam/nonlinear/ISAM2.h>
#include <string>
#include <boost/timer/timer.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/export.hpp>
using namespace gtsam;
using namespace std;
BOOST_CLASS_EXPORT(Value);
BOOST_CLASS_EXPORT(Pose2);
BOOST_CLASS_EXPORT(NonlinearFactor);
BOOST_CLASS_EXPORT(NoiseModelFactor);
BOOST_CLASS_EXPORT(NoiseModelFactor1<Pose2>);
typedef NoiseModelFactor2<Pose2,Pose2> NMF2;
BOOST_CLASS_EXPORT(NMF2);
BOOST_CLASS_EXPORT(BetweenFactor<Pose2>);
BOOST_CLASS_EXPORT(PriorFactor<Pose2>);
BOOST_CLASS_EXPORT(noiseModel::Base);
BOOST_CLASS_EXPORT(noiseModel::Isotropic);
BOOST_CLASS_EXPORT(noiseModel::Gaussian);
BOOST_CLASS_EXPORT(noiseModel::Diagonal);
BOOST_CLASS_EXPORT(noiseModel::Unit);
double chi2_red(const gtsam::NonlinearFactorGraph& graph, const gtsam::Values& config) {
// Compute degrees of freedom (observations - variables)
// In ocaml, +1 was added to the observations to account for the prior, but
// the factor graph already includes a factor for the prior/equality constraint.
// double dof = graph.size() - config.size();
int graph_dim = 0;
BOOST_FOREACH(const boost::shared_ptr<gtsam::NonlinearFactor>& nlf, graph) {
graph_dim += (int)nlf->dim();
}
double dof = double(graph_dim - config.dim()); // kaess: changed to dim
return 2. * graph.error(config) / dof; // kaess: added factor 2, graph.error returns half of actual error
}
/* ************************************************************************* */
ISAM2 solveWithOldISAM2(const NonlinearFactorGraph& measurements)
{
ISAM2 isam2;
size_t nextMeasurement = 0;
for(size_t step=1; nextMeasurement < measurements.size() && nextMeasurement < 1000; ++step) {
Values newVariables;
NonlinearFactorGraph newFactors;
// Collect measurements and new variables for the current step
gttic_(Collect_measurements);
if(step == 1) {
// cout << "Initializing " << 0 << endl;
newVariables.insert(0, Pose2());
// Add prior
newFactors.add(PriorFactor<Pose2>(0, Pose2(), noiseModel::Unit::Create(Pose2::Dim())));
}
while(nextMeasurement < measurements.size()) {
NonlinearFactor::shared_ptr measurementf = measurements[nextMeasurement];
if(BetweenFactor<Pose2>::shared_ptr measurement =
boost::dynamic_pointer_cast<BetweenFactor<Pose2> >(measurementf))
{
// Stop collecting measurements that are for future steps
if(measurement->key1() > step || measurement->key2() > step)
break;
// Require that one of the nodes is the current one
if(measurement->key1() != step && measurement->key2() != step)
throw runtime_error("Problem in data file, out-of-sequence measurements");
// Add a new factor
newFactors.push_back(measurement);
// Initialize the new variable
if(measurement->key1() == step && measurement->key2() == step-1) {
if(step == 1)
newVariables.insert(step, measurement->measured().inverse());
else {
Pose2 prevPose = isam2.calculateEstimate<Pose2>(step-1);
newVariables.insert(step, prevPose * measurement->measured().inverse());
}
// cout << "Initializing " << step << endl;
} else if(measurement->key2() == step && measurement->key1() == step-1) {
if(step == 1)
newVariables.insert(step, measurement->measured());
else {
Pose2 prevPose = isam2.calculateEstimate<Pose2>(step-1);
newVariables.insert(step, prevPose * measurement->measured());
}
// cout << "Initializing " << step << endl;
}
} else {
throw std::runtime_error("Unknown factor type read from data file");
}
++ nextMeasurement;
}
gttoc_(Collect_measurements);
// Update iSAM2
gttic_(Update_ISAM2);
isam2.update(newFactors, newVariables);
gttoc_(Update_ISAM2);
tictoc_finishedIteration_();
if(step % 1000 == 0) {
cout << "Step " << step << endl;
tictoc_print_();
}
}
return isam2;
}
/* ************************************************************************* */
GaussianFactorGraph convertToUnordered(const GaussianFactorGraphOrdered& gfg, const OrderingOrdered& ordering)
{
GaussianFactorGraph gfgu;
BOOST_FOREACH(const GaussianFactorOrdered::shared_ptr& factor, gfg)
{
vector<std::pair<Key, Matrix> > terms;
const JacobianFactorOrdered& jacobian = dynamic_cast<const JacobianFactorOrdered&>(*factor);
for(GaussianFactorOrdered::const_iterator term = jacobian.begin(); term != jacobian.end(); ++term)
{
terms.push_back(make_pair(
ordering.key(*term),
jacobian.getA(term)));
}
gfgu.add(JacobianFactor(terms, jacobian.getb(), jacobian.get_model()));
}
return gfgu;
}
/* ************************************************************************* */
void compareSolutions(const VectorValuesOrdered& orderedSoln, const OrderingOrdered& ordering, const VectorValues& unorderedSoln)
{
if(orderedSoln.size() != unorderedSoln.size())
{
cout << "Solution sizes are not the same" << endl;
}
else
{
double maxErr = 0.0;
BOOST_FOREACH(const VectorValues::KeyValuePair& v, unorderedSoln)
{
Vector orderedV = orderedSoln[ordering[v.first]];
maxErr = std::max(maxErr, (orderedV - v.second).cwiseAbs().maxCoeff());
}
cout << "Maximum abs error: " << maxErr << endl;
}
}
/* ************************************************************************* */
int main(int argc, char *argv[])
{
//try {
//// Load graph
//gttic_(Find_datafile);
//string datasetFile = findExampleDataFile("w10000-odom");
//std::pair<NonlinearFactorGraph::shared_ptr, Values::shared_ptr> data = load2D(datasetFile);
//gttoc_(Find_datafile);
//NonlinearFactorGraph measurements = *data.first;
//Values initial = *data.second;
//// Solve with old ISAM2 to get initial estimate
//cout << "Solving with old ISAM2 to obtain initial estimate" << endl;
// ISAM2 isam = solveWithOldISAM2(measurements);
// {
// std::ofstream writerStream("incremental_init1000", ios::binary);
// boost::archive::binary_oarchive writer(writerStream);
// writer << isam.calculateEstimate();
// writerStream.close();
// }
// {
// std::ofstream writerStream("incremental_graph1000", ios::binary);
// boost::archive::binary_oarchive writer(writerStream);
// writer << isam.getFactorsUnsafe();
// writerStream.close();
// }
//} catch(std::exception& e) {
// cout << e.what() << endl;
//}
Values isamsoln;
NonlinearFactorGraph nlfg;
{
std::ifstream readerStream("incremental_init", ios::binary);
boost::archive::binary_iarchive reader(readerStream);
reader >> isamsoln;
}
{
std::ifstream readerStream("incremental_graph", ios::binary);
boost::archive::binary_iarchive reader(readerStream);
reader >> nlfg;
}
// Get linear graph
cout << "Converting to unordered linear graph" << endl;
OrderingOrdered ordering = *isamsoln.orderingArbitrary();
OrderingOrdered orderingCOLAMD = *nlfg.orderingCOLAMD(isamsoln);
GaussianFactorGraphOrdered gfg = *nlfg.linearize(isamsoln, ordering);
GaussianFactorGraph gfgu = convertToUnordered(gfg, ordering);
//Ordering orderingUnordered;
//for(Index j = 0; j < ordering.size(); ++j)
// orderingUnordered.push_back(ordering.key(j));
// Solve linear graph
cout << "Optimizing unordered graph" << endl;
VectorValues unorderedSolnFinal;
{
gttic_(Solve_unordered);
VectorValues unorderedSoln;
for(size_t i = 0; i < 1; ++i) {
gttic_(VariableIndexOrdered);
VariableIndex vi(gfgu);
gttoc_(VariableIndexOrdered);
gttic_(COLAMD);
Ordering orderingUnordered = Ordering::COLAMD(vi);
gttoc_(COLAMD);
gttic_(eliminate);
GaussianBayesTree::shared_ptr bt = gfgu.eliminateMultifrontal(orderingUnordered);
gttoc_(eliminate);
gttic_(optimize);
unorderedSoln = bt->optimize();
gttoc_(optimize);
}
gttoc_(Solve_unordered);
unorderedSolnFinal = unorderedSoln;
}
// Solve linear graph with old code
cout << "Optimizing using old ordered code" << endl;
VectorValuesOrdered orderedSolnFinal;
{
OrderingOrdered orderingToUse = ordering;
GaussianFactorGraphOrdered::shared_ptr orderedGraph = nlfg.linearize(isamsoln, *nlfg.orderingCOLAMD(isamsoln));
gttic_(Solve_ordered);
VectorValuesOrdered orderedSoln;
for(size_t i = 0; i < 1; ++i) {
gttic_(VariableIndexOrdered);
boost::shared_ptr<VariableIndexOrdered> vi = boost::make_shared<VariableIndexOrdered>(gfg);
gttoc_(VariableIndexOrdered);
gttic_(COLAMD);
boost::shared_ptr<Permutation> permutation = inference::PermutationCOLAMD(*vi);
orderingToUse.permuteInPlace(*permutation);
gttoc_(COLAMD);
gttic_(eliminate);
boost::shared_ptr<GaussianBayesTreeOrdered> bt = GaussianMultifrontalSolver(*orderedGraph, true).eliminate();
gttoc_(eliminate);
gttic_(optimize);
orderedSoln = optimize(*bt);
gttoc_(optimize);
}
gttoc_(Solve_ordered);
orderedSolnFinal = orderedSoln;
}
// Compare results
compareSolutions(orderedSolnFinal, orderingCOLAMD, unorderedSolnFinal);
//GaussianEliminationTree(gfgu, orderingUnordered).print("ETree: ");
//GaussianJunctionTree(GaussianEliminationTree(gfgu, Ordering::COLAMD(gfgu))).print("JTree: ");
//gfgu.eliminateMultifrontal(orderingUnordered)->print("BayesTree: ");
tictoc_print_();
return 0;
}