140 lines
5.9 KiB
C++
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
|