102 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			C
		
	
	
		
		
			
		
	
	
			102 lines
		
	
	
		
			4.1 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 std::vector<Key>& 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 boost::shared_ptr<Cluster>& cluster) {
							 | 
						||
| 
								 | 
							
								      auto nonlinearCluster = boost::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, boost::optional<Ordering> ordering = boost::none,
							 | 
						||
| 
								 | 
							
								        const NonlinearFactorGraph::Dampen& dampen = nullptr) const {
							 | 
						||
| 
								 | 
							
								      if (!ordering)
							 | 
						||
| 
								 | 
							
								        ordering.reset(Ordering::ColamdConstrainedFirst(factors, orderedFrontalKeys, true));
							 | 
						||
| 
								 | 
							
								      return factors.linearizeToHessianFactor(values, *ordering, dampen);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // 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, boost::optional<Ordering> ordering = boost::none,
							 | 
						||
| 
								 | 
							
								        const NonlinearFactorGraph::Dampen& dampen = nullptr) const {
							 | 
						||
| 
								 | 
							
								      // Linearize and create HessianFactor f(front,separator)
							 | 
						||
| 
								 | 
							
								      HessianFactor::shared_ptr localFactor = linearizeToHessianFactor(values, ordering, dampen);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // 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
							 | 
						||
| 
								 | 
							
								    // 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,
							 | 
						||
| 
								 | 
							
								        boost::optional<Ordering> ordering = boost::none,
							 | 
						||
| 
								 | 
							
								        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
							 |