diff --git a/cpp/BayesTree-inl.h b/cpp/BayesTree-inl.h index 819a5b0fa..516bf93c2 100644 --- a/cpp/BayesTree-inl.h +++ b/cpp/BayesTree-inl.h @@ -235,12 +235,12 @@ namespace gtsam { /* ************************************************************************* */ template void BayesTree::print(const string& s) const { - cout << s << ": size == " << size() << endl; - if (nodes_.empty()) return; if (root_.use_count() == 0) { - printf("WARNING: Forest...\n"); + printf("WARNING: BayesTree.print encountered a forest...\n"); return; } + cout << s << ": size == " << size() << endl; + if (nodes_.empty()) return; root_->printTree(""); } diff --git a/cpp/ISAM2-inl.h b/cpp/ISAM2-inl.h index 2799d98b5..896329beb 100644 --- a/cpp/ISAM2-inl.h +++ b/cpp/ISAM2-inl.h @@ -22,6 +22,44 @@ namespace gtsam { using namespace std; + // from inference-inl.h - need to additionally return the newly created factor for caching + boost::shared_ptr _eliminateOne(FactorGraph& graph, cachedFactors& cached, const string& key) { + + // combine the factors of all nodes connected to the variable to be eliminated + // if no factors are connected to key, returns an empty factor + boost::shared_ptr joint_factor = removeAndCombineFactors(graph,key); + + // eliminate that joint factor + boost::shared_ptr factor; + boost::shared_ptr conditional; + boost::tie(conditional, factor) = joint_factor->eliminate(key); + + // remember the intermediate result to be able to later restart computation in the middle + cached[key] = factor; + + // add new factor on separator back into the graph + if (!factor->empty()) graph.push_back(factor); + + // return the conditional Gaussian + return conditional; + } + + // from GaussianFactorGraph.cpp, see _eliminateOne above + GaussianBayesNet _eliminate(FactorGraph& graph, cachedFactors& cached, const Ordering& ordering) { + GaussianBayesNet chordalBayesNet; // empty + BOOST_FOREACH(string key, ordering) { + GaussianConditional::shared_ptr cg = _eliminateOne(graph, cached, key); + chordalBayesNet.push_back(cg); + } + return chordalBayesNet; + } + + GaussianBayesNet _eliminate_const(const FactorGraph& graph, cachedFactors& cached, const Ordering& ordering) { + // make a copy that can be modified locally + FactorGraph graph_ignored = graph; + return _eliminate(graph_ignored, cached, ordering); + } + /** Create an empty Bayes Tree */ template ISAM2::ISAM2() : BayesTree() {} @@ -29,7 +67,10 @@ namespace gtsam { /** Create a Bayes Tree from a nonlinear factor graph */ template ISAM2::ISAM2(const NonlinearFactorGraph& nlfg, const Ordering& ordering, const Config& config) - : BayesTree(nlfg.linearize(config).eliminate(ordering)), nonlinearFactors_(nlfg), config_(config) {} + : BayesTree(nlfg.linearize(config).eliminate(ordering)), nonlinearFactors_(nlfg), config_(config) { + // todo: repeats calculation above, just to set "cached" + _eliminate_const(nlfg.linearize(config), cached, ordering); + } /* ************************************************************************* */ template @@ -42,7 +83,6 @@ namespace gtsam { config_.insert(it->first, it->second); } } - nonlinearFactors_.push_back(newFactors); FactorGraph newFactorsLinearized = newFactors.linearize(config_); @@ -50,8 +90,8 @@ namespace gtsam { FactorGraph affectedFactors; boost::tie(affectedFactors, orphans) = this->removeTop(newFactorsLinearized); - #if 1 +#if 0 // find the corresponding original nonlinear factors, and relinearize them NonlinearFactorGraph nonlinearAffectedFactors; set idxs; // avoid duplicates by putting index into set @@ -75,29 +115,92 @@ namespace gtsam { BOOST_FOREACH(int idx, idxs) { nonlinearAffectedFactors.push_back(nonlinearFactors_[idx]); } + FactorGraph factors = nonlinearAffectedFactors.linearize(config_); +#else + + NonlinearFactorGraph nonlinearAffectedFactors; + + // retrieve all factors that ONLY contain the affected variables + // (note that the remaining stuff is summarized in the cached factors) + list affectedKeys = affectedFactors.keys(); + typename FactorGraph >::iterator it; + for(it = nonlinearFactors_.begin(); it != nonlinearFactors_.end(); it++) { + bool inside = true; + BOOST_FOREACH(string key, (*it)->keys()) { + if (find(affectedKeys.begin(), affectedKeys.end(), key) == affectedKeys.end()) + inside = false; + } + if (inside) + nonlinearAffectedFactors.push_back(*it); + } + + FactorGraph factors = nonlinearAffectedFactors.linearize(config_); + + // recover intermediate factors from cache that are passed into the affected area + FactorGraph cachedBoundary; + BOOST_FOREACH(sharedClique orphan, orphans) { + // find the last variable that is not part of the separator + string oneTooFar = orphan->separator_.front(); + list keys = orphan->keys(); + list::iterator it = find(keys.begin(), keys.end(), oneTooFar); + it--; + string key = *it; + // retrieve the cached factor and add to boundary + cachedBoundary.push_back(cached[key]); + } + factors.push_back(cachedBoundary); + +#endif + + +#if 0 + printf("**************\n"); + nonlinearFactors_.linearize(config).print("all factors"); + printf("--------------\n"); + newFactorsLinearized.print("newFactorsLinearized"); + printf("--------------\n"); + factors.print("factors"); + printf("--------------\n"); + affectedFactors.print("affectedFactors"); + printf("--------------\n"); +#endif + // add the new factors themselves factors.push_back(newFactorsLinearized); #endif - affectedFactors.push_back(newFactorsLinearized); - // create an ordering for the new and contaminated factors Ordering ordering; if (true) { - ordering = /*affectedF*/factors.getOrdering(); + ordering = factors.getOrdering(); } else { - list keys = /*affectedF*/factors.keys(); + list keys = factors.keys(); keys.sort(); // todo: correct sorting order? ordering = keys; } +#if 0 + ordering.print(); + + factors.print("factors BEFORE"); + printf("--------------\n"); +#endif + // eliminate into a Bayes net - BayesNet bayesNet = eliminate(affectedFactors,ordering); + BayesNet bayesNet = _eliminate(factors, cached, ordering); #if 1 - BayesNet bayesNetTest = eliminate(factors,ordering); // todo - debug only + // check if relinearized agrees with correct solution + affectedFactors.push_back(newFactorsLinearized); + +#if 0 + affectedFactors.print("affectedFactors BEFORE"); + printf("--------------\n"); +#endif + + BayesNet bayesNetTest = eliminate(affectedFactors, ordering); if (!bayesNet.equals(bayesNetTest)) { printf("differ\n"); bayesNet.print(); @@ -106,6 +209,8 @@ namespace gtsam { } #endif + nonlinearFactors_.push_back(newFactors); + // insert conditionals back in, straight into the topless bayesTree typename BayesNet::const_reverse_iterator rit; for ( rit=bayesNet.rbegin(); rit != bayesNet.rend(); ++rit ) @@ -125,6 +230,15 @@ namespace gtsam { template void ISAM2::update(const NonlinearFactorGraph& newFactors, const Config& config) { + +#if 0 + printf("8888888888888888\n"); + try { + this->print("BayesTree"); + } catch (char * c) {}; + printf("8888888888888888\n"); +#endif + Cliques orphans; this->update_internal(newFactors, config, orphans); } diff --git a/cpp/ISAM2.h b/cpp/ISAM2.h index e0a5034a3..22da93f78 100644 --- a/cpp/ISAM2.h +++ b/cpp/ISAM2.h @@ -23,6 +23,8 @@ namespace gtsam { + typedef std::map cachedFactors; + template class ISAM2: public BayesTree { @@ -32,6 +34,9 @@ namespace gtsam { Config config_; NonlinearFactorGraph nonlinearFactors_; + // cached intermediate results for restarting computation in the middle + cachedFactors cached; + public: /** Create an empty Bayes Tree */ diff --git a/cpp/testGaussianISAM2.cpp b/cpp/testGaussianISAM2.cpp index fbdb9dd86..0ddf6c14c 100644 --- a/cpp/testGaussianISAM2.cpp +++ b/cpp/testGaussianISAM2.cpp @@ -19,7 +19,7 @@ using namespace boost::assign; using namespace std; using namespace gtsam; -/* ************************************************************************* * +/* ************************************************************************* */ TEST( ISAM2, ISAM2_smoother ) { // Create smoother with 7 nodes