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
 |