gtsam/gtsam_unstable/nonlinear/NonlinearClusterTree.h

140 lines
5.9 KiB
C++

/**
* @file NonlinearClusterTree.h
* @author Frank Dellaert
* @date March, 2016
*/
#pragma once
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
#include <gtsam/linear/GaussianBayesTree.h>
#include <gtsam/inference/ClusterTree.h>
namespace gtsam {
class NonlinearClusterTree : public ClusterTree<NonlinearFactorGraph> {
public:
NonlinearClusterTree() {}
struct NonlinearCluster : Cluster {
// Given graph, index, add factors with specified keys into
// Factors are erased in the graph
// TODO(frank): fairly hacky and inefficient. Think about iterating the graph once instead
NonlinearCluster(const VariableIndex& variableIndex, const KeyVector& keys,
NonlinearFactorGraph* graph) {
for (const Key key : keys) {
std::vector<NonlinearFactor::shared_ptr> factors;
for (auto i : variableIndex[key])
if (graph->at(i)) {
factors.push_back(graph->at(i));
graph->remove(i);
}
Cluster::addFactors(key, factors);
}
}
GaussianFactorGraph::shared_ptr linearize(const Values& values) {
return factors.linearize(values);
}
static NonlinearCluster* DownCast(const std::shared_ptr<Cluster>& cluster) {
auto nonlinearCluster = std::dynamic_pointer_cast<NonlinearCluster>(cluster);
if (!nonlinearCluster)
throw std::runtime_error("Expected NonlinearCluster");
return nonlinearCluster.get();
}
// linearize local custer factors straight into hessianFactor, which is returned
// If no ordering given, uses colamd
HessianFactor::shared_ptr linearizeToHessianFactor(
const Values& values,
const NonlinearFactorGraph::Dampen& dampen = nullptr) const {
Ordering ordering;
ordering = Ordering::ColamdConstrainedFirst(factors, orderedFrontalKeys, true);
return factors.linearizeToHessianFactor(values, ordering, dampen);
}
// linearize local custer factors straight into hessianFactor, which is returned
// If no ordering given, uses colamd
HessianFactor::shared_ptr linearizeToHessianFactor(
const Values& values, const Ordering& ordering,
const NonlinearFactorGraph::Dampen& dampen = nullptr) const {
return factors.linearizeToHessianFactor(values, ordering, dampen);
}
// Helper function: recursively eliminate subtree rooted at this Cluster into a Bayes net and factor on parent
// TODO(frank): Use TBB to support deep trees and parallelism
std::pair<GaussianBayesNet, HessianFactor::shared_ptr> linearizeAndEliminate(
const Values& values,
const HessianFactor::shared_ptr& localFactor) const {
// Get contributions f(front) from children, as well as p(children|front)
GaussianBayesNet bayesNet;
for (const auto& child : children) {
auto message = DownCast(child)->linearizeAndEliminate(values, &bayesNet);
message->updateHessian(localFactor.get());
}
auto gaussianConditional = localFactor->eliminateCholesky(orderedFrontalKeys);
bayesNet.add(gaussianConditional);
return {bayesNet, localFactor};
}
// Recursively eliminate subtree rooted at this Cluster into a Bayes net and factor on parent
// TODO(frank): Use TBB to support deep trees and parallelism
std::pair<GaussianBayesNet, HessianFactor::shared_ptr> linearizeAndEliminate(
const Values& values,
const NonlinearFactorGraph::Dampen& dampen = nullptr) const {
// Linearize and create HessianFactor f(front,separator)
HessianFactor::shared_ptr localFactor = linearizeToHessianFactor(values, dampen);
return linearizeAndEliminate(values, localFactor);
}
// Recursively eliminate subtree rooted at this Cluster into a Bayes net and factor on parent
// TODO(frank): Use TBB to support deep trees and parallelism
std::pair<GaussianBayesNet, HessianFactor::shared_ptr> linearizeAndEliminate(
const Values& values, const Ordering& ordering,
const NonlinearFactorGraph::Dampen& dampen = nullptr) const {
// Linearize and create HessianFactor f(front,separator)
HessianFactor::shared_ptr localFactor = linearizeToHessianFactor(values, ordering, dampen);
return linearizeAndEliminate(values, localFactor);
}
// Recursively eliminate subtree rooted at this Cluster
// Version that updates existing Bayes net and returns a new Hessian factor on parent clique
// It is possible to pass in a nullptr for the bayesNet if only interested in the new factor
HessianFactor::shared_ptr linearizeAndEliminate(
const Values& values, GaussianBayesNet* bayesNet,
const NonlinearFactorGraph::Dampen& dampen = nullptr) const {
auto bayesNet_newFactor_pair = linearizeAndEliminate(values, dampen);
if (bayesNet) {
bayesNet->push_back(bayesNet_newFactor_pair.first);
}
return bayesNet_newFactor_pair.second;
}
// Recursively eliminate subtree rooted at this Cluster
// Version that updates existing Bayes net and returns a new Hessian factor on parent clique
// It is possible to pass in a nullptr for the bayesNet if only interested in the new factor
HessianFactor::shared_ptr linearizeAndEliminate(
const Values& values, GaussianBayesNet* bayesNet,
const Ordering& ordering,
const NonlinearFactorGraph::Dampen& dampen = nullptr) const {
auto bayesNet_newFactor_pair = linearizeAndEliminate(values, ordering, dampen);
if (bayesNet) {
bayesNet->push_back(bayesNet_newFactor_pair.first);
}
return bayesNet_newFactor_pair.second;
}
};
// Linearize and update linearization point with values
Values updateCholesky(const Values& values) {
GaussianBayesNet bayesNet;
for (const auto& root : roots_) {
auto result = NonlinearCluster::DownCast(root)->linearizeAndEliminate(values);
bayesNet.push_back(result.first);
}
VectorValues delta = bayesNet.optimize();
return values.retract(delta);
}
};
} // namespace gtsam