Merge branch 'jtree_mania' into feature/CeresDefaults-2
commit
786b762907
|
@ -1,19 +1,19 @@
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
|
|
||||||
* GTSAM Copyright 2010, Georgia Tech Research Corporation,
|
* GTSAM Copyright 2010, Georgia Tech Research Corporation,
|
||||||
* Atlanta, Georgia 30332-0415
|
* Atlanta, Georgia 30332-0415
|
||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
||||||
|
|
||||||
* See LICENSE for the license information
|
* See LICENSE for the license information
|
||||||
|
|
||||||
* -------------------------------------------------------------------------- */
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file treeTraversal-inst.h
|
* @file treeTraversal-inst.h
|
||||||
* @author Richard Roberts
|
* @author Richard Roberts
|
||||||
* @date April 9, 2013
|
* @date April 9, 2013
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <gtsam/base/treeTraversal/parallelTraversalTasks.h>
|
#include <gtsam/base/treeTraversal/parallelTraversalTasks.h>
|
||||||
|
@ -33,192 +33,197 @@
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
|
||||||
/** Internal functions used for traversing trees */
|
/** Internal functions used for traversing trees */
|
||||||
namespace treeTraversal {
|
namespace treeTraversal {
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
namespace {
|
namespace {
|
||||||
// Internal node used in DFS preorder stack
|
// Internal node used in DFS preorder stack
|
||||||
template<typename NODE, typename DATA>
|
template<typename NODE, typename DATA>
|
||||||
struct TraversalNode {
|
struct TraversalNode {
|
||||||
bool expanded;
|
bool expanded;
|
||||||
const boost::shared_ptr<NODE>& treeNode;
|
const boost::shared_ptr<NODE>& treeNode;
|
||||||
DATA& parentData;
|
DATA& parentData;
|
||||||
typename FastList<DATA>::iterator dataPointer;
|
typename FastList<DATA>::iterator dataPointer;
|
||||||
TraversalNode(const boost::shared_ptr<NODE>& _treeNode, DATA& _parentData) :
|
TraversalNode(const boost::shared_ptr<NODE>& _treeNode, DATA& _parentData) :
|
||||||
expanded(false), treeNode(_treeNode), parentData(_parentData) {}
|
expanded(false), treeNode(_treeNode), parentData(_parentData) {
|
||||||
};
|
|
||||||
|
|
||||||
// Do nothing - default argument for post-visitor for tree traversal
|
|
||||||
struct no_op {
|
|
||||||
template<typename NODE, typename DATA>
|
|
||||||
void operator()(const boost::shared_ptr<NODE>& node, const DATA& data) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Traverse a forest depth-first with pre-order and post-order visits.
|
|
||||||
* @param forest The forest of trees to traverse. The method \c forest.roots() should exist
|
|
||||||
* and return a collection of (shared) pointers to \c FOREST::Node.
|
|
||||||
* @param visitorPre \c visitorPre(node, parentData) will be called at every node, before
|
|
||||||
* visiting its children, and will be passed, by reference, the \c DATA object returned
|
|
||||||
* by the visit to its parent. Likewise, \c visitorPre should return the \c DATA object
|
|
||||||
* to pass to the children. The returned \c DATA object will be copy-constructed only
|
|
||||||
* upon returning to store internally, thus may be modified by visiting the children.
|
|
||||||
* Regarding efficiency, this copy-on-return is usually optimized out by the compiler.
|
|
||||||
* @param visitorPost \c visitorPost(node, data) will be called at every node, after visiting
|
|
||||||
* its children, and will be passed, by reference, the \c DATA object returned by the
|
|
||||||
* call to \c visitorPre (the \c DATA object may be modified by visiting the children).
|
|
||||||
* @param rootData The data to pass by reference to \c visitorPre when it is called on each
|
|
||||||
* root node. */
|
|
||||||
template<class FOREST, typename DATA, typename VISITOR_PRE, typename VISITOR_POST>
|
|
||||||
void DepthFirstForest(FOREST& forest, DATA& rootData, VISITOR_PRE& visitorPre, VISITOR_POST& visitorPost)
|
|
||||||
{
|
|
||||||
// Typedefs
|
|
||||||
typedef typename FOREST::Node Node;
|
|
||||||
typedef boost::shared_ptr<Node> sharedNode;
|
|
||||||
|
|
||||||
// Depth first traversal stack
|
|
||||||
typedef TraversalNode<typename FOREST::Node, DATA> TraversalNode;
|
|
||||||
typedef FastList<TraversalNode> Stack;
|
|
||||||
Stack stack;
|
|
||||||
FastList<DATA> dataList; // List to store node data as it is returned from the pre-order visitor
|
|
||||||
|
|
||||||
// Add roots to stack (insert such that they are visited and processed in order
|
|
||||||
{
|
|
||||||
typename Stack::iterator insertLocation = stack.begin();
|
|
||||||
BOOST_FOREACH(const sharedNode& root, forest.roots())
|
|
||||||
stack.insert(insertLocation, TraversalNode(root, rootData));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Traverse
|
|
||||||
while(!stack.empty())
|
|
||||||
{
|
|
||||||
// Get next node
|
|
||||||
TraversalNode& node = stack.front();
|
|
||||||
|
|
||||||
if(node.expanded) {
|
|
||||||
// If already expanded, then the data stored in the node is no longer needed, so visit
|
|
||||||
// then delete it.
|
|
||||||
(void) visitorPost(node.treeNode, *node.dataPointer);
|
|
||||||
dataList.erase(node.dataPointer);
|
|
||||||
stack.pop_front();
|
|
||||||
} else {
|
|
||||||
// If not already visited, visit the node and add its children (use reverse iterators so
|
|
||||||
// children are processed in the order they appear)
|
|
||||||
node.dataPointer = dataList.insert(dataList.end(), visitorPre(node.treeNode, node.parentData));
|
|
||||||
typename Stack::iterator insertLocation = stack.begin();
|
|
||||||
BOOST_FOREACH(const sharedNode& child, node.treeNode->children)
|
|
||||||
stack.insert(insertLocation, TraversalNode(child, *node.dataPointer));
|
|
||||||
node.expanded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(dataList.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Traverse a forest depth-first, with a pre-order visit but no post-order visit.
|
|
||||||
* @param forest The forest of trees to traverse. The method \c forest.roots() should exist
|
|
||||||
* and return a collection of (shared) pointers to \c FOREST::Node.
|
|
||||||
* @param visitorPre \c visitorPre(node, parentData) will be called at every node, before
|
|
||||||
* visiting its children, and will be passed, by reference, the \c DATA object returned
|
|
||||||
* by the visit to its parent. Likewise, \c visitorPre should return the \c DATA object
|
|
||||||
* to pass to the children. The returned \c DATA object will be copy-constructed only
|
|
||||||
* upon returning to store internally, thus may be modified by visiting the children.
|
|
||||||
* Regarding efficiency, this copy-on-return is usually optimized out by the compiler.
|
|
||||||
* @param rootData The data to pass by reference to \c visitorPre when it is called on each
|
|
||||||
* root node. */
|
|
||||||
template<class FOREST, typename DATA, typename VISITOR_PRE>
|
|
||||||
void DepthFirstForest(FOREST& forest, DATA& rootData, VISITOR_PRE& visitorPre)
|
|
||||||
{
|
|
||||||
no_op visitorPost;
|
|
||||||
DepthFirstForest(forest, rootData, visitorPre, visitorPost);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Traverse a forest depth-first with pre-order and post-order visits.
|
|
||||||
* @param forest The forest of trees to traverse. The method \c forest.roots() should exist
|
|
||||||
* and return a collection of (shared) pointers to \c FOREST::Node.
|
|
||||||
* @param visitorPre \c visitorPre(node, parentData) will be called at every node, before
|
|
||||||
* visiting its children, and will be passed, by reference, the \c DATA object returned
|
|
||||||
* by the visit to its parent. Likewise, \c visitorPre should return the \c DATA object
|
|
||||||
* to pass to the children. The returned \c DATA object will be copy-constructed only
|
|
||||||
* upon returning to store internally, thus may be modified by visiting the children.
|
|
||||||
* Regarding efficiency, this copy-on-return is usually optimized out by the compiler.
|
|
||||||
* @param visitorPost \c visitorPost(node, data) will be called at every node, after visiting
|
|
||||||
* its children, and will be passed, by reference, the \c DATA object returned by the
|
|
||||||
* call to \c visitorPre (the \c DATA object may be modified by visiting the children).
|
|
||||||
* @param rootData The data to pass by reference to \c visitorPre when it is called on each
|
|
||||||
* root node. */
|
|
||||||
template<class FOREST, typename DATA, typename VISITOR_PRE, typename VISITOR_POST>
|
|
||||||
void DepthFirstForestParallel(FOREST& forest, DATA& rootData, VISITOR_PRE& visitorPre, VISITOR_POST& visitorPost,
|
|
||||||
int problemSizeThreshold = 10)
|
|
||||||
{
|
|
||||||
#ifdef GTSAM_USE_TBB
|
|
||||||
// Typedefs
|
|
||||||
typedef typename FOREST::Node Node;
|
|
||||||
typedef boost::shared_ptr<Node> sharedNode;
|
|
||||||
|
|
||||||
tbb::task::spawn_root_and_wait(internal::CreateRootTask<Node>(
|
|
||||||
forest.roots(), rootData, visitorPre, visitorPost, problemSizeThreshold));
|
|
||||||
#else
|
|
||||||
DepthFirstForest(forest, rootData, visitorPre, visitorPost);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
|
||||||
/** Traversal function for CloneForest */
|
|
||||||
namespace {
|
|
||||||
template<typename NODE>
|
|
||||||
boost::shared_ptr<NODE>
|
|
||||||
CloneForestVisitorPre(const boost::shared_ptr<NODE>& node, const boost::shared_ptr<NODE>& parentPointer)
|
|
||||||
{
|
|
||||||
// Clone the current node and add it to its cloned parent
|
|
||||||
boost::shared_ptr<NODE> clone = boost::make_shared<NODE>(*node);
|
|
||||||
clone->children.clear();
|
|
||||||
parentPointer->children.push_back(clone);
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Clone a tree, copy-constructing new nodes (calling boost::make_shared) and setting up child
|
|
||||||
* pointers for a clone of the original tree.
|
|
||||||
* @param forest The forest of trees to clone. The method \c forest.roots() should exist and
|
|
||||||
* return a collection of shared pointers to \c FOREST::Node.
|
|
||||||
* @return The new collection of roots. */
|
|
||||||
template<class FOREST>
|
|
||||||
FastVector<boost::shared_ptr<typename FOREST::Node> > CloneForest(const FOREST& forest)
|
|
||||||
{
|
|
||||||
typedef typename FOREST::Node Node;
|
|
||||||
boost::shared_ptr<Node> rootContainer = boost::make_shared<Node>();
|
|
||||||
DepthFirstForest(forest, rootContainer, CloneForestVisitorPre<Node>);
|
|
||||||
return FastVector<boost::shared_ptr<Node> >(rootContainer->children.begin(), rootContainer->children.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
|
||||||
/** Traversal function for PrintForest */
|
|
||||||
namespace {
|
|
||||||
struct PrintForestVisitorPre
|
|
||||||
{
|
|
||||||
const KeyFormatter& formatter;
|
|
||||||
PrintForestVisitorPre(const KeyFormatter& formatter) : formatter(formatter) {}
|
|
||||||
template<typename NODE> std::string operator()(const boost::shared_ptr<NODE>& node, const std::string& parentString)
|
|
||||||
{
|
|
||||||
// Print the current node
|
|
||||||
node->print(parentString + "-", formatter);
|
|
||||||
// Increment the indentation
|
|
||||||
return parentString + "| ";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Print a tree, prefixing each line with \c str, and formatting keys using \c keyFormatter.
|
|
||||||
* To print each node, this function calls the \c print function of the tree nodes. */
|
|
||||||
template<class FOREST>
|
|
||||||
void PrintForest(const FOREST& forest, std::string str, const KeyFormatter& keyFormatter) {
|
|
||||||
PrintForestVisitorPre visitor(keyFormatter);
|
|
||||||
DepthFirstForest(forest, str, visitor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Do nothing - default argument for post-visitor for tree traversal
|
||||||
|
struct no_op {
|
||||||
|
template<typename NODE, typename DATA>
|
||||||
|
void operator()(const boost::shared_ptr<NODE>& node, const DATA& data) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Traverse a forest depth-first with pre-order and post-order visits.
|
||||||
|
* @param forest The forest of trees to traverse. The method \c forest.roots() should exist
|
||||||
|
* and return a collection of (shared) pointers to \c FOREST::Node.
|
||||||
|
* @param visitorPre \c visitorPre(node, parentData) will be called at every node, before
|
||||||
|
* visiting its children, and will be passed, by reference, the \c DATA object returned
|
||||||
|
* by the visit to its parent. Likewise, \c visitorPre should return the \c DATA object
|
||||||
|
* to pass to the children. The returned \c DATA object will be copy-constructed only
|
||||||
|
* upon returning to store internally, thus may be modified by visiting the children.
|
||||||
|
* Regarding efficiency, this copy-on-return is usually optimized out by the compiler.
|
||||||
|
* @param visitorPost \c visitorPost(node, data) will be called at every node, after visiting
|
||||||
|
* its children, and will be passed, by reference, the \c DATA object returned by the
|
||||||
|
* call to \c visitorPre (the \c DATA object may be modified by visiting the children).
|
||||||
|
* @param rootData The data to pass by reference to \c visitorPre when it is called on each
|
||||||
|
* root node. */
|
||||||
|
template<class FOREST, typename DATA, typename VISITOR_PRE,
|
||||||
|
typename VISITOR_POST>
|
||||||
|
void DepthFirstForest(FOREST& forest, DATA& rootData, VISITOR_PRE& visitorPre,
|
||||||
|
VISITOR_POST& visitorPost) {
|
||||||
|
// Typedefs
|
||||||
|
typedef typename FOREST::Node Node;
|
||||||
|
typedef boost::shared_ptr<Node> sharedNode;
|
||||||
|
|
||||||
|
// Depth first traversal stack
|
||||||
|
typedef TraversalNode<typename FOREST::Node, DATA> TraversalNode;
|
||||||
|
typedef FastList<TraversalNode> Stack;
|
||||||
|
Stack stack;
|
||||||
|
FastList<DATA> dataList; // List to store node data as it is returned from the pre-order visitor
|
||||||
|
|
||||||
|
// Add roots to stack (insert such that they are visited and processed in order
|
||||||
|
{
|
||||||
|
typename Stack::iterator insertLocation = stack.begin();
|
||||||
|
BOOST_FOREACH(const sharedNode& root, forest.roots())
|
||||||
|
stack.insert(insertLocation, TraversalNode(root, rootData));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse
|
||||||
|
while (!stack.empty()) {
|
||||||
|
// Get next node
|
||||||
|
TraversalNode& node = stack.front();
|
||||||
|
|
||||||
|
if (node.expanded) {
|
||||||
|
// If already expanded, then the data stored in the node is no longer needed, so visit
|
||||||
|
// then delete it.
|
||||||
|
(void) visitorPost(node.treeNode, *node.dataPointer);
|
||||||
|
dataList.erase(node.dataPointer);
|
||||||
|
stack.pop_front();
|
||||||
|
} else {
|
||||||
|
// If not already visited, visit the node and add its children (use reverse iterators so
|
||||||
|
// children are processed in the order they appear)
|
||||||
|
node.dataPointer = dataList.insert(dataList.end(),
|
||||||
|
visitorPre(node.treeNode, node.parentData));
|
||||||
|
typename Stack::iterator insertLocation = stack.begin();
|
||||||
|
BOOST_FOREACH(const sharedNode& child, node.treeNode->children)
|
||||||
|
stack.insert(insertLocation, TraversalNode(child, *node.dataPointer));
|
||||||
|
node.expanded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(dataList.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Traverse a forest depth-first, with a pre-order visit but no post-order visit.
|
||||||
|
* @param forest The forest of trees to traverse. The method \c forest.roots() should exist
|
||||||
|
* and return a collection of (shared) pointers to \c FOREST::Node.
|
||||||
|
* @param visitorPre \c visitorPre(node, parentData) will be called at every node, before
|
||||||
|
* visiting its children, and will be passed, by reference, the \c DATA object returned
|
||||||
|
* by the visit to its parent. Likewise, \c visitorPre should return the \c DATA object
|
||||||
|
* to pass to the children. The returned \c DATA object will be copy-constructed only
|
||||||
|
* upon returning to store internally, thus may be modified by visiting the children.
|
||||||
|
* Regarding efficiency, this copy-on-return is usually optimized out by the compiler.
|
||||||
|
* @param rootData The data to pass by reference to \c visitorPre when it is called on each
|
||||||
|
* root node. */
|
||||||
|
template<class FOREST, typename DATA, typename VISITOR_PRE>
|
||||||
|
void DepthFirstForest(FOREST& forest, DATA& rootData, VISITOR_PRE& visitorPre) {
|
||||||
|
no_op visitorPost;
|
||||||
|
DepthFirstForest(forest, rootData, visitorPre, visitorPost);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Traverse a forest depth-first with pre-order and post-order visits.
|
||||||
|
* @param forest The forest of trees to traverse. The method \c forest.roots() should exist
|
||||||
|
* and return a collection of (shared) pointers to \c FOREST::Node.
|
||||||
|
* @param visitorPre \c visitorPre(node, parentData) will be called at every node, before
|
||||||
|
* visiting its children, and will be passed, by reference, the \c DATA object returned
|
||||||
|
* by the visit to its parent. Likewise, \c visitorPre should return the \c DATA object
|
||||||
|
* to pass to the children. The returned \c DATA object will be copy-constructed only
|
||||||
|
* upon returning to store internally, thus may be modified by visiting the children.
|
||||||
|
* Regarding efficiency, this copy-on-return is usually optimized out by the compiler.
|
||||||
|
* @param visitorPost \c visitorPost(node, data) will be called at every node, after visiting
|
||||||
|
* its children, and will be passed, by reference, the \c DATA object returned by the
|
||||||
|
* call to \c visitorPre (the \c DATA object may be modified by visiting the children).
|
||||||
|
* @param rootData The data to pass by reference to \c visitorPre when it is called on each
|
||||||
|
* root node. */
|
||||||
|
template<class FOREST, typename DATA, typename VISITOR_PRE,
|
||||||
|
typename VISITOR_POST>
|
||||||
|
void DepthFirstForestParallel(FOREST& forest, DATA& rootData,
|
||||||
|
VISITOR_PRE& visitorPre, VISITOR_POST& visitorPost,
|
||||||
|
int problemSizeThreshold = 10) {
|
||||||
|
#ifdef GTSAM_USE_TBB
|
||||||
|
// Typedefs
|
||||||
|
typedef typename FOREST::Node Node;
|
||||||
|
typedef boost::shared_ptr<Node> sharedNode;
|
||||||
|
|
||||||
|
tbb::task::spawn_root_and_wait(
|
||||||
|
internal::CreateRootTask<Node>(forest.roots(), rootData, visitorPre,
|
||||||
|
visitorPost, problemSizeThreshold));
|
||||||
|
#else
|
||||||
|
DepthFirstForest(forest, rootData, visitorPre, visitorPost);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
/** Traversal function for CloneForest */
|
||||||
|
namespace {
|
||||||
|
template<typename NODE>
|
||||||
|
boost::shared_ptr<NODE> CloneForestVisitorPre(
|
||||||
|
const boost::shared_ptr<NODE>& node,
|
||||||
|
const boost::shared_ptr<NODE>& parentPointer) {
|
||||||
|
// Clone the current node and add it to its cloned parent
|
||||||
|
boost::shared_ptr<NODE> clone = boost::make_shared<NODE>(*node);
|
||||||
|
clone->children.clear();
|
||||||
|
parentPointer->children.push_back(clone);
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Clone a tree, copy-constructing new nodes (calling boost::make_shared) and setting up child
|
||||||
|
* pointers for a clone of the original tree.
|
||||||
|
* @param forest The forest of trees to clone. The method \c forest.roots() should exist and
|
||||||
|
* return a collection of shared pointers to \c FOREST::Node.
|
||||||
|
* @return The new collection of roots. */
|
||||||
|
template<class FOREST>
|
||||||
|
FastVector<boost::shared_ptr<typename FOREST::Node> > CloneForest(
|
||||||
|
const FOREST& forest) {
|
||||||
|
typedef typename FOREST::Node Node;
|
||||||
|
boost::shared_ptr<Node> rootContainer = boost::make_shared<Node>();
|
||||||
|
DepthFirstForest(forest, rootContainer, CloneForestVisitorPre<Node>);
|
||||||
|
return FastVector<boost::shared_ptr<Node> >(rootContainer->children.begin(),
|
||||||
|
rootContainer->children.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
/** Traversal function for PrintForest */
|
||||||
|
namespace {
|
||||||
|
struct PrintForestVisitorPre {
|
||||||
|
const KeyFormatter& formatter;
|
||||||
|
PrintForestVisitorPre(const KeyFormatter& formatter) :
|
||||||
|
formatter(formatter) {
|
||||||
|
}
|
||||||
|
template<typename NODE> std::string operator()(
|
||||||
|
const boost::shared_ptr<NODE>& node, const std::string& parentString) {
|
||||||
|
// Print the current node
|
||||||
|
node->print(parentString + "-", formatter);
|
||||||
|
// Increment the indentation
|
||||||
|
return parentString + "| ";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Print a tree, prefixing each line with \c str, and formatting keys using \c keyFormatter.
|
||||||
|
* To print each node, this function calls the \c print function of the tree nodes. */
|
||||||
|
template<class FOREST>
|
||||||
|
void PrintForest(const FOREST& forest, std::string str,
|
||||||
|
const KeyFormatter& keyFormatter) {
|
||||||
|
PrintForestVisitorPre visitor(keyFormatter);
|
||||||
|
DepthFirstForest(forest, str, visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,178 +7,227 @@
|
||||||
* @brief Collects factorgraph fragments defined on variable clusters, arranged in a tree
|
* @brief Collects factorgraph fragments defined on variable clusters, arranged in a tree
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <gtsam/base/timing.h>
|
|
||||||
#include <gtsam/base/treeTraversal-inst.h>
|
|
||||||
#include <gtsam/inference/ClusterTree.h>
|
#include <gtsam/inference/ClusterTree.h>
|
||||||
#include <gtsam/inference/BayesTree.h>
|
#include <gtsam/inference/BayesTree.h>
|
||||||
#include <gtsam/inference/Ordering.h>
|
#include <gtsam/inference/Ordering.h>
|
||||||
|
#include <gtsam/base/timing.h>
|
||||||
|
#include <gtsam/base/treeTraversal-inst.h>
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
|
|
||||||
namespace gtsam
|
namespace gtsam {
|
||||||
{
|
|
||||||
namespace
|
/* ************************************************************************* */
|
||||||
{
|
// Elimination traversal data - stores a pointer to the parent data and collects the factors
|
||||||
/* ************************************************************************* */
|
// resulting from elimination of the children. Also sets up BayesTree cliques with parent and
|
||||||
// Elimination traversal data - stores a pointer to the parent data and collects the factors
|
// child pointers.
|
||||||
// resulting from elimination of the children. Also sets up BayesTree cliques with parent and
|
template<class CLUSTERTREE>
|
||||||
// child pointers.
|
struct EliminationData {
|
||||||
template<class CLUSTERTREE>
|
// Typedefs
|
||||||
struct EliminationData {
|
typedef typename CLUSTERTREE::sharedFactor sharedFactor;
|
||||||
EliminationData* const parentData;
|
typedef typename CLUSTERTREE::FactorType FactorType;
|
||||||
size_t myIndexInParent;
|
typedef typename CLUSTERTREE::FactorGraphType FactorGraphType;
|
||||||
FastVector<typename CLUSTERTREE::sharedFactor> childFactors;
|
typedef typename CLUSTERTREE::ConditionalType ConditionalType;
|
||||||
boost::shared_ptr<typename CLUSTERTREE::BayesTreeType::Node> bayesTreeNode;
|
typedef typename CLUSTERTREE::BayesTreeType::Node BTNode;
|
||||||
EliminationData(EliminationData* _parentData, size_t nChildren) :
|
|
||||||
parentData(_parentData),
|
EliminationData* const parentData;
|
||||||
bayesTreeNode(boost::make_shared<typename CLUSTERTREE::BayesTreeType::Node>())
|
size_t myIndexInParent;
|
||||||
{
|
FastVector<sharedFactor> childFactors;
|
||||||
if(parentData) {
|
boost::shared_ptr<BTNode> bayesTreeNode;
|
||||||
myIndexInParent = parentData->childFactors.size();
|
EliminationData(EliminationData* _parentData, size_t nChildren) :
|
||||||
parentData->childFactors.push_back(typename CLUSTERTREE::sharedFactor());
|
parentData(_parentData), bayesTreeNode(boost::make_shared<BTNode>()) {
|
||||||
} else {
|
if (parentData) {
|
||||||
myIndexInParent = 0;
|
myIndexInParent = parentData->childFactors.size();
|
||||||
}
|
parentData->childFactors.push_back(sharedFactor());
|
||||||
// Set up BayesTree parent and child pointers
|
} else {
|
||||||
if(parentData) {
|
myIndexInParent = 0;
|
||||||
if(parentData->parentData) // If our parent is not the dummy node
|
}
|
||||||
bayesTreeNode->parent_ = parentData->bayesTreeNode;
|
// Set up BayesTree parent and child pointers
|
||||||
parentData->bayesTreeNode->children.push_back(bayesTreeNode);
|
if (parentData) {
|
||||||
|
if (parentData->parentData) // If our parent is not the dummy node
|
||||||
|
bayesTreeNode->parent_ = parentData->bayesTreeNode;
|
||||||
|
parentData->bayesTreeNode->children.push_back(bayesTreeNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elimination pre-order visitor - creates the EliminationData structure for the visited node.
|
||||||
|
static EliminationData EliminationPreOrderVisitor(
|
||||||
|
const typename CLUSTERTREE::sharedNode& node,
|
||||||
|
EliminationData& parentData) {
|
||||||
|
assert(node);
|
||||||
|
EliminationData myData(&parentData, node->children.size());
|
||||||
|
myData.bayesTreeNode->problemSize_ = node->problemSize();
|
||||||
|
return myData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elimination post-order visitor - combine the child factors with our own factors, add the
|
||||||
|
// resulting conditional to the BayesTree, and add the remaining factor to the parent.
|
||||||
|
struct EliminationPostOrderVisitor {
|
||||||
|
const typename CLUSTERTREE::Eliminate& eliminationFunction;
|
||||||
|
typename CLUSTERTREE::BayesTreeType::Nodes& nodesIndex;
|
||||||
|
EliminationPostOrderVisitor(
|
||||||
|
const typename CLUSTERTREE::Eliminate& eliminationFunction,
|
||||||
|
typename CLUSTERTREE::BayesTreeType::Nodes& nodesIndex) :
|
||||||
|
eliminationFunction(eliminationFunction), nodesIndex(nodesIndex) {
|
||||||
|
}
|
||||||
|
void operator()(const typename CLUSTERTREE::sharedNode& node,
|
||||||
|
EliminationData& myData) {
|
||||||
|
assert(node);
|
||||||
|
|
||||||
|
// Gather factors
|
||||||
|
FactorGraphType gatheredFactors;
|
||||||
|
gatheredFactors.reserve(node->factors.size() + node->children.size());
|
||||||
|
gatheredFactors += node->factors;
|
||||||
|
gatheredFactors += myData.childFactors;
|
||||||
|
|
||||||
|
// Check for Bayes tree orphan subtrees, and add them to our children
|
||||||
|
BOOST_FOREACH(const sharedFactor& f, node->factors) {
|
||||||
|
if (const BayesTreeOrphanWrapper<BTNode>* asSubtree =
|
||||||
|
dynamic_cast<const BayesTreeOrphanWrapper<BTNode>*>(f.get())) {
|
||||||
|
myData.bayesTreeNode->children.push_back(asSubtree->clique);
|
||||||
|
asSubtree->clique->parent_ = myData.bayesTreeNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
// Do dense elimination step
|
||||||
// Elimination pre-order visitor - just creates the EliminationData structure for the visited
|
std::pair<boost::shared_ptr<ConditionalType>,
|
||||||
// node.
|
boost::shared_ptr<FactorType> > eliminationResult =
|
||||||
template<class CLUSTERTREE>
|
|
||||||
EliminationData<CLUSTERTREE> eliminationPreOrderVisitor(
|
|
||||||
const typename CLUSTERTREE::sharedNode& node, EliminationData<CLUSTERTREE>& parentData)
|
|
||||||
{
|
|
||||||
EliminationData<CLUSTERTREE> myData(&parentData, node->children.size());
|
|
||||||
myData.bayesTreeNode->problemSize_ = node->problemSize();
|
|
||||||
return myData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
|
||||||
// Elimination post-order visitor - combine the child factors with our own factors, add the
|
|
||||||
// resulting conditional to the BayesTree, and add the remaining factor to the parent.
|
|
||||||
template<class CLUSTERTREE>
|
|
||||||
struct EliminationPostOrderVisitor
|
|
||||||
{
|
|
||||||
const typename CLUSTERTREE::Eliminate& eliminationFunction;
|
|
||||||
typename CLUSTERTREE::BayesTreeType::Nodes& nodesIndex;
|
|
||||||
EliminationPostOrderVisitor(const typename CLUSTERTREE::Eliminate& eliminationFunction,
|
|
||||||
typename CLUSTERTREE::BayesTreeType::Nodes& nodesIndex) :
|
|
||||||
eliminationFunction(eliminationFunction), nodesIndex(nodesIndex) {}
|
|
||||||
void operator()(const typename CLUSTERTREE::sharedNode& node, EliminationData<CLUSTERTREE>& myData)
|
|
||||||
{
|
|
||||||
// Typedefs
|
|
||||||
typedef typename CLUSTERTREE::sharedFactor sharedFactor;
|
|
||||||
typedef typename CLUSTERTREE::FactorType FactorType;
|
|
||||||
typedef typename CLUSTERTREE::FactorGraphType FactorGraphType;
|
|
||||||
typedef typename CLUSTERTREE::ConditionalType ConditionalType;
|
|
||||||
typedef typename CLUSTERTREE::BayesTreeType::Node BTNode;
|
|
||||||
|
|
||||||
// Gather factors
|
|
||||||
FactorGraphType gatheredFactors;
|
|
||||||
gatheredFactors.reserve(node->factors.size() + node->children.size());
|
|
||||||
gatheredFactors += node->factors;
|
|
||||||
gatheredFactors += myData.childFactors;
|
|
||||||
|
|
||||||
// Check for Bayes tree orphan subtrees, and add them to our children
|
|
||||||
BOOST_FOREACH(const sharedFactor& f, node->factors)
|
|
||||||
{
|
|
||||||
if(const BayesTreeOrphanWrapper<BTNode>* asSubtree = dynamic_cast<const BayesTreeOrphanWrapper<BTNode>*>(f.get()))
|
|
||||||
{
|
|
||||||
myData.bayesTreeNode->children.push_back(asSubtree->clique);
|
|
||||||
asSubtree->clique->parent_ = myData.bayesTreeNode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do dense elimination step
|
|
||||||
std::pair<boost::shared_ptr<ConditionalType>, boost::shared_ptr<FactorType> > eliminationResult =
|
|
||||||
eliminationFunction(gatheredFactors, node->orderedFrontalKeys);
|
eliminationFunction(gatheredFactors, node->orderedFrontalKeys);
|
||||||
|
|
||||||
// Store conditional in BayesTree clique, and in the case of ISAM2Clique also store the remaining factor
|
// Store conditional in BayesTree clique, and in the case of ISAM2Clique also store the remaining factor
|
||||||
myData.bayesTreeNode->setEliminationResult(eliminationResult);
|
myData.bayesTreeNode->setEliminationResult(eliminationResult);
|
||||||
|
|
||||||
// Fill nodes index - we do this here instead of calling insertRoot at the end to avoid
|
// Fill nodes index - we do this here instead of calling insertRoot at the end to avoid
|
||||||
// putting orphan subtrees in the index - they'll already be in the index of the ISAM2
|
// putting orphan subtrees in the index - they'll already be in the index of the ISAM2
|
||||||
// object they're added to.
|
// object they're added to.
|
||||||
BOOST_FOREACH(const Key& j, myData.bayesTreeNode->conditional()->frontals())
|
BOOST_FOREACH(const Key& j, myData.bayesTreeNode->conditional()->frontals())
|
||||||
nodesIndex.insert(std::make_pair(j, myData.bayesTreeNode));
|
nodesIndex.insert(std::make_pair(j, myData.bayesTreeNode));
|
||||||
|
|
||||||
// Store remaining factor in parent's gathered factors
|
// Store remaining factor in parent's gathered factors
|
||||||
if(!eliminationResult.second->empty())
|
if (!eliminationResult.second->empty())
|
||||||
myData.parentData->childFactors[myData.myIndexInParent] = eliminationResult.second;
|
myData.parentData->childFactors[myData.myIndexInParent] =
|
||||||
}
|
eliminationResult.second;
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
|
||||||
template<class BAYESTREE, class GRAPH>
|
|
||||||
void ClusterTree<BAYESTREE,GRAPH>::Cluster::print(
|
|
||||||
const std::string& s, const KeyFormatter& keyFormatter) const
|
|
||||||
{
|
|
||||||
std::cout << s << " (" << problemSize_ << ")" ;
|
|
||||||
PrintKeyVector(orderedFrontalKeys);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
|
||||||
template<class BAYESTREE, class GRAPH>
|
|
||||||
void ClusterTree<BAYESTREE,GRAPH>::print(
|
|
||||||
const std::string& s, const KeyFormatter& keyFormatter) const
|
|
||||||
{
|
|
||||||
treeTraversal::PrintForest(*this, s, keyFormatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
|
||||||
template<class BAYESTREE, class GRAPH>
|
|
||||||
ClusterTree<BAYESTREE,GRAPH>& ClusterTree<BAYESTREE,GRAPH>::operator=(const This& other)
|
|
||||||
{
|
|
||||||
// Start by duplicating the tree.
|
|
||||||
roots_ = treeTraversal::CloneForest(other);
|
|
||||||
|
|
||||||
// Assign the remaining factors - these are pointers to factors in the original factor graph and
|
|
||||||
// we do not clone them.
|
|
||||||
remainingFactors_ = other.remainingFactors_;
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
|
||||||
template<class BAYESTREE, class GRAPH>
|
|
||||||
std::pair<boost::shared_ptr<BAYESTREE>, boost::shared_ptr<GRAPH> >
|
|
||||||
ClusterTree<BAYESTREE,GRAPH>::eliminate(const Eliminate& function) const
|
|
||||||
{
|
|
||||||
gttic(ClusterTree_eliminate);
|
|
||||||
// Do elimination (depth-first traversal). The rootsContainer stores a 'dummy' BayesTree node
|
|
||||||
// that contains all of the roots as its children. rootsContainer also stores the remaining
|
|
||||||
// uneliminated factors passed up from the roots.
|
|
||||||
boost::shared_ptr<BayesTreeType> result = boost::make_shared<BayesTreeType>();
|
|
||||||
EliminationData<This> rootsContainer(0, roots_.size());
|
|
||||||
EliminationPostOrderVisitor<This> visitorPost(function, result->nodes_);
|
|
||||||
{
|
|
||||||
TbbOpenMPMixedScope threadLimiter; // Limits OpenMP threads since we're mixing TBB and OpenMP
|
|
||||||
treeTraversal::DepthFirstForestParallel(*this, rootsContainer,
|
|
||||||
eliminationPreOrderVisitor<This>, visitorPost, 10);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// Create BayesTree from roots stored in the dummy BayesTree node.
|
/* ************************************************************************* */
|
||||||
result->roots_.insert(result->roots_.end(), rootsContainer.bayesTreeNode->children.begin(), rootsContainer.bayesTreeNode->children.end());
|
template<class BAYESTREE, class GRAPH>
|
||||||
|
void ClusterTree<BAYESTREE, GRAPH>::Cluster::print(const std::string& s,
|
||||||
|
const KeyFormatter& keyFormatter) const {
|
||||||
|
std::cout << s << " (" << problemSize_ << ")";
|
||||||
|
PrintKeyVector(orderedFrontalKeys);
|
||||||
|
}
|
||||||
|
|
||||||
// Add remaining factors that were not involved with eliminated variables
|
/* ************************************************************************* */
|
||||||
boost::shared_ptr<FactorGraphType> allRemainingFactors = boost::make_shared<FactorGraphType>();
|
template<class BAYESTREE, class GRAPH>
|
||||||
allRemainingFactors->reserve(remainingFactors_.size() + rootsContainer.childFactors.size());
|
void ClusterTree<BAYESTREE, GRAPH>::Cluster::mergeChildren(
|
||||||
allRemainingFactors->push_back(remainingFactors_.begin(), remainingFactors_.end());
|
const std::vector<bool>& merge) {
|
||||||
BOOST_FOREACH(const sharedFactor& factor, rootsContainer.childFactors)
|
gttic(Cluster::mergeChildren);
|
||||||
if(factor)
|
|
||||||
allRemainingFactors->push_back(factor);
|
|
||||||
|
|
||||||
// Return result
|
// Count how many keys, factors and children we'll end up with
|
||||||
return std::make_pair(result, allRemainingFactors);
|
size_t nrKeys = orderedFrontalKeys.size();
|
||||||
|
size_t nrFactors = factors.size();
|
||||||
|
size_t nrNewChildren = 0;
|
||||||
|
// Loop over children
|
||||||
|
size_t i = 0;
|
||||||
|
BOOST_FOREACH(const sharedNode& child, children) {
|
||||||
|
if (merge[i]) {
|
||||||
|
nrKeys += child->orderedFrontalKeys.size();
|
||||||
|
nrFactors += child->factors.size();
|
||||||
|
nrNewChildren += child->children.size();
|
||||||
|
} else {
|
||||||
|
nrNewChildren += 1; // we keep the child
|
||||||
|
}
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// now reserve space, and really merge
|
||||||
|
orderedFrontalKeys.reserve(nrKeys);
|
||||||
|
factors.reserve(nrFactors);
|
||||||
|
typename Node::Children newChildren;
|
||||||
|
newChildren.reserve(nrNewChildren);
|
||||||
|
i = 0;
|
||||||
|
BOOST_FOREACH(const sharedNode& child, children) {
|
||||||
|
if (merge[i]) {
|
||||||
|
// Merge keys. For efficiency, we add keys in reverse order at end, calling reverse after..
|
||||||
|
orderedFrontalKeys.insert(orderedFrontalKeys.end(),
|
||||||
|
child->orderedFrontalKeys.rbegin(), child->orderedFrontalKeys.rend());
|
||||||
|
// Merge keys, factors, and children.
|
||||||
|
factors.insert(factors.end(), child->factors.begin(),
|
||||||
|
child->factors.end());
|
||||||
|
newChildren.insert(newChildren.end(), child->children.begin(),
|
||||||
|
child->children.end());
|
||||||
|
// Increment problem size
|
||||||
|
problemSize_ = std::max(problemSize_, child->problemSize_);
|
||||||
|
// Increment number of frontal variables
|
||||||
|
} else {
|
||||||
|
newChildren.push_back(child); // we keep the child
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
children = newChildren;
|
||||||
|
std::reverse(orderedFrontalKeys.begin(), orderedFrontalKeys.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
template<class BAYESTREE, class GRAPH>
|
||||||
|
void ClusterTree<BAYESTREE, GRAPH>::print(const std::string& s,
|
||||||
|
const KeyFormatter& keyFormatter) const {
|
||||||
|
treeTraversal::PrintForest(*this, s, keyFormatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
template<class BAYESTREE, class GRAPH>
|
||||||
|
ClusterTree<BAYESTREE, GRAPH>& ClusterTree<BAYESTREE, GRAPH>::operator=(
|
||||||
|
const This& other) {
|
||||||
|
// Start by duplicating the tree.
|
||||||
|
roots_ = treeTraversal::CloneForest(other);
|
||||||
|
|
||||||
|
// Assign the remaining factors - these are pointers to factors in the original factor graph and
|
||||||
|
// we do not clone them.
|
||||||
|
remainingFactors_ = other.remainingFactors_;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
template<class BAYESTREE, class GRAPH>
|
||||||
|
std::pair<boost::shared_ptr<BAYESTREE>, boost::shared_ptr<GRAPH> > ClusterTree<
|
||||||
|
BAYESTREE, GRAPH>::eliminate(const Eliminate& function) const {
|
||||||
|
gttic(ClusterTree_eliminate);
|
||||||
|
// Do elimination (depth-first traversal). The rootsContainer stores a 'dummy' BayesTree node
|
||||||
|
// that contains all of the roots as its children. rootsContainer also stores the remaining
|
||||||
|
// uneliminated factors passed up from the roots.
|
||||||
|
boost::shared_ptr<BayesTreeType> result = boost::make_shared<BayesTreeType>();
|
||||||
|
typedef EliminationData<This> Data;
|
||||||
|
Data rootsContainer(0, roots_.size());
|
||||||
|
typename Data::EliminationPostOrderVisitor visitorPost(function,
|
||||||
|
result->nodes_);
|
||||||
|
{
|
||||||
|
TbbOpenMPMixedScope threadLimiter; // Limits OpenMP threads since we're mixing TBB and OpenMP
|
||||||
|
treeTraversal::DepthFirstForestParallel(*this, rootsContainer,
|
||||||
|
Data::EliminationPreOrderVisitor, visitorPost, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create BayesTree from roots stored in the dummy BayesTree node.
|
||||||
|
result->roots_.insert(result->roots_.end(),
|
||||||
|
rootsContainer.bayesTreeNode->children.begin(),
|
||||||
|
rootsContainer.bayesTreeNode->children.end());
|
||||||
|
|
||||||
|
// Add remaining factors that were not involved with eliminated variables
|
||||||
|
boost::shared_ptr<FactorGraphType> remaining = boost::make_shared<
|
||||||
|
FactorGraphType>();
|
||||||
|
remaining->reserve(
|
||||||
|
remainingFactors_.size() + rootsContainer.childFactors.size());
|
||||||
|
remaining->push_back(remainingFactors_.begin(), remainingFactors_.end());
|
||||||
|
BOOST_FOREACH(const sharedFactor& factor, rootsContainer.childFactors) {
|
||||||
|
if (factor)
|
||||||
|
remaining->push_back(factor);
|
||||||
|
}
|
||||||
|
// Return result
|
||||||
|
return std::make_pair(result, remaining);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,118 +13,122 @@
|
||||||
#include <gtsam/base/FastVector.h>
|
#include <gtsam/base/FastVector.h>
|
||||||
#include <gtsam/inference/Ordering.h>
|
#include <gtsam/inference/Ordering.h>
|
||||||
|
|
||||||
namespace gtsam
|
namespace gtsam {
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A cluster-tree is associated with a factor graph and is defined as in Koller-Friedman:
|
* A cluster-tree is associated with a factor graph and is defined as in Koller-Friedman:
|
||||||
* each node k represents a subset \f$ C_k \sub X \f$, and the tree is family preserving, in that
|
* each node k represents a subset \f$ C_k \sub X \f$, and the tree is family preserving, in that
|
||||||
* each factor \f$ f_i \f$ is associated with a single cluster and \f$ scope(f_i) \sub C_k \f$.
|
* each factor \f$ f_i \f$ is associated with a single cluster and \f$ scope(f_i) \sub C_k \f$.
|
||||||
* \nosubgrouping
|
* \nosubgrouping
|
||||||
*/
|
*/
|
||||||
template<class BAYESTREE, class GRAPH>
|
template<class BAYESTREE, class GRAPH>
|
||||||
class ClusterTree
|
class ClusterTree {
|
||||||
{
|
public:
|
||||||
public:
|
typedef GRAPH FactorGraphType; ///< The factor graph type
|
||||||
typedef GRAPH FactorGraphType; ///< The factor graph type
|
typedef typename GRAPH::FactorType FactorType; ///< The type of factors
|
||||||
typedef typename GRAPH::FactorType FactorType; ///< The type of factors
|
typedef ClusterTree<BAYESTREE, GRAPH> This; ///< This class
|
||||||
typedef ClusterTree<BAYESTREE, GRAPH> This; ///< This class
|
typedef boost::shared_ptr<This> shared_ptr; ///< Shared pointer to this class
|
||||||
typedef boost::shared_ptr<This> shared_ptr; ///< Shared pointer to this class
|
typedef boost::shared_ptr<FactorType> sharedFactor; ///< Shared pointer to a factor
|
||||||
typedef boost::shared_ptr<FactorType> sharedFactor; ///< Shared pointer to a factor
|
typedef BAYESTREE BayesTreeType; ///< The BayesTree type produced by elimination
|
||||||
typedef BAYESTREE BayesTreeType; ///< The BayesTree type produced by elimination
|
typedef typename BayesTreeType::ConditionalType ConditionalType; ///< The type of conditionals
|
||||||
typedef typename BayesTreeType::ConditionalType ConditionalType; ///< The type of conditionals
|
typedef boost::shared_ptr<ConditionalType> sharedConditional; ///< Shared pointer to a conditional
|
||||||
typedef boost::shared_ptr<ConditionalType> sharedConditional; ///< Shared pointer to a conditional
|
typedef typename FactorGraphType::Eliminate Eliminate; ///< Typedef for an eliminate subroutine
|
||||||
typedef typename FactorGraphType::Eliminate Eliminate; ///< Typedef for an eliminate subroutine
|
|
||||||
|
|
||||||
struct Cluster {
|
struct Cluster {
|
||||||
typedef Ordering Keys;
|
typedef Ordering Keys;
|
||||||
typedef FastVector<sharedFactor> Factors;
|
typedef FastVector<sharedFactor> Factors;
|
||||||
typedef FastVector<boost::shared_ptr<Cluster> > Children;
|
typedef FastVector<boost::shared_ptr<Cluster> > Children;
|
||||||
|
|
||||||
Cluster() {}
|
Cluster() {
|
||||||
Cluster(Key key, const Factors& factors) : factors(factors) {
|
}
|
||||||
orderedFrontalKeys.push_back(key);
|
Cluster(Key key, const Factors& factors) :
|
||||||
}
|
factors(factors) {
|
||||||
|
orderedFrontalKeys.push_back(key);
|
||||||
|
}
|
||||||
|
|
||||||
Keys orderedFrontalKeys; ///< Frontal keys of this node
|
Keys orderedFrontalKeys; ///< Frontal keys of this node
|
||||||
Factors factors; ///< Factors associated with this node
|
Factors factors; ///< Factors associated with this node
|
||||||
Children children; ///< sub-trees
|
Children children; ///< sub-trees
|
||||||
int problemSize_;
|
int problemSize_;
|
||||||
|
|
||||||
int problemSize() const { return problemSize_; }
|
int problemSize() const {
|
||||||
|
return problemSize_;
|
||||||
|
}
|
||||||
|
|
||||||
/** print this node */
|
/// print this node
|
||||||
void print(const std::string& s = "", const KeyFormatter& keyFormatter = DefaultKeyFormatter) const;
|
void print(const std::string& s = "", const KeyFormatter& keyFormatter =
|
||||||
};
|
DefaultKeyFormatter) const;
|
||||||
|
|
||||||
typedef boost::shared_ptr<Cluster> sharedCluster; ///< Shared pointer to Cluster
|
|
||||||
typedef Cluster Node; ///< Define Node=Cluster for compatibility with tree traversal functions
|
|
||||||
typedef sharedCluster sharedNode; ///< Define Node=Cluster for compatibility with tree traversal functions
|
|
||||||
|
|
||||||
/** concept check */
|
|
||||||
GTSAM_CONCEPT_TESTABLE_TYPE(FactorType);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
FastVector<sharedNode> roots_;
|
|
||||||
FastVector<sharedFactor> remainingFactors_;
|
|
||||||
|
|
||||||
/// @name Standard Constructors
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
/** Copy constructor - makes a deep copy of the tree structure, but only pointers to factors are
|
|
||||||
* copied, factors are not cloned. */
|
|
||||||
ClusterTree(const This& other) { *this = other; }
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// @name Testable
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
/** Print the cluster tree */
|
|
||||||
void print(const std::string& s = "", const KeyFormatter& keyFormatter = DefaultKeyFormatter) const;
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
/// @name Standard Interface
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
/** Eliminate the factors to a Bayes tree and remaining factor graph
|
|
||||||
* @param function The function to use to eliminate, see the namespace functions
|
|
||||||
* in GaussianFactorGraph.h
|
|
||||||
* @return The Bayes tree and factor graph resulting from elimination
|
|
||||||
*/
|
|
||||||
std::pair<boost::shared_ptr<BayesTreeType>, boost::shared_ptr<FactorGraphType> >
|
|
||||||
eliminate(const Eliminate& function) const;
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
/// @name Advanced Interface
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
/** Return the set of roots (one for a tree, multiple for a forest) */
|
|
||||||
const FastVector<sharedNode>& roots() const { return roots_; }
|
|
||||||
|
|
||||||
/** Return the remaining factors that are not pulled into elimination */
|
|
||||||
const FastVector<sharedFactor>& remainingFactors() const { return remainingFactors_; }
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/// @name Details
|
|
||||||
|
|
||||||
/// Assignment operator - makes a deep copy of the tree structure, but only pointers to factors
|
|
||||||
/// are copied, factors are not cloned.
|
|
||||||
This& operator=(const This& other);
|
|
||||||
|
|
||||||
/// Default constructor to be used in derived classes
|
|
||||||
ClusterTree() {}
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
|
/// Merge all children for which bit is set into this node
|
||||||
|
void mergeChildren(const std::vector<bool>& merge);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef boost::shared_ptr<Cluster> sharedCluster; ///< Shared pointer to Cluster
|
||||||
|
typedef Cluster Node; ///< Define Node=Cluster for compatibility with tree traversal functions
|
||||||
|
typedef sharedCluster sharedNode; ///< Define Node=Cluster for compatibility with tree traversal functions
|
||||||
|
|
||||||
|
/** concept check */
|
||||||
|
GTSAM_CONCEPT_TESTABLE_TYPE(FactorType);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
FastVector<sharedNode> roots_;
|
||||||
|
FastVector<sharedFactor> remainingFactors_;
|
||||||
|
|
||||||
|
/// @name Standard Constructors
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/** Copy constructor - makes a deep copy of the tree structure, but only pointers to factors are
|
||||||
|
* copied, factors are not cloned. */
|
||||||
|
ClusterTree(const This& other) {*this = other;}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// @name Testable
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/** Print the cluster tree */
|
||||||
|
void print(const std::string& s = "", const KeyFormatter& keyFormatter = DefaultKeyFormatter) const;
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// @name Standard Interface
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/** Eliminate the factors to a Bayes tree and remaining factor graph
|
||||||
|
* @param function The function to use to eliminate, see the namespace functions
|
||||||
|
* in GaussianFactorGraph.h
|
||||||
|
* @return The Bayes tree and factor graph resulting from elimination
|
||||||
|
*/
|
||||||
|
std::pair<boost::shared_ptr<BayesTreeType>, boost::shared_ptr<FactorGraphType> >
|
||||||
|
eliminate(const Eliminate& function) const;
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// @name Advanced Interface
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/** Return the set of roots (one for a tree, multiple for a forest) */
|
||||||
|
const FastVector<sharedNode>& roots() const {return roots_;}
|
||||||
|
|
||||||
|
/** Return the remaining factors that are not pulled into elimination */
|
||||||
|
const FastVector<sharedFactor>& remainingFactors() const {return remainingFactors_;}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// @name Details
|
||||||
|
|
||||||
|
/// Assignment operator - makes a deep copy of the tree structure, but only pointers to factors
|
||||||
|
/// are copied, factors are not cloned.
|
||||||
|
This& operator=(const This& other);
|
||||||
|
|
||||||
|
/// Default constructor to be used in derived classes
|
||||||
|
ClusterTree() {}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
|
||||||
template <class BAYESTREE, class GRAPH, class ETREE_NODE>
|
template<class BAYESTREE, class GRAPH, class ETREE_NODE>
|
||||||
struct ConstructorTraversalData {
|
struct ConstructorTraversalData {
|
||||||
typedef typename JunctionTree<BAYESTREE, GRAPH>::Node Node;
|
typedef typename JunctionTree<BAYESTREE, GRAPH>::Node Node;
|
||||||
typedef typename JunctionTree<BAYESTREE, GRAPH>::sharedNode sharedNode;
|
typedef typename JunctionTree<BAYESTREE, GRAPH>::sharedNode sharedNode;
|
||||||
|
@ -37,8 +37,13 @@ struct ConstructorTraversalData {
|
||||||
FastVector<SymbolicConditional::shared_ptr> childSymbolicConditionals;
|
FastVector<SymbolicConditional::shared_ptr> childSymbolicConditionals;
|
||||||
FastVector<SymbolicFactor::shared_ptr> childSymbolicFactors;
|
FastVector<SymbolicFactor::shared_ptr> childSymbolicFactors;
|
||||||
|
|
||||||
ConstructorTraversalData(ConstructorTraversalData* _parentData)
|
// Small inner class to store symbolic factors
|
||||||
: parentData(_parentData) {}
|
class SymbolicFactors: public FactorGraph<Factor> {
|
||||||
|
};
|
||||||
|
|
||||||
|
ConstructorTraversalData(ConstructorTraversalData* _parentData) :
|
||||||
|
parentData(_parentData) {
|
||||||
|
}
|
||||||
|
|
||||||
// Pre-order visitor function
|
// Pre-order visitor function
|
||||||
static ConstructorTraversalData ConstructorTraversalVisitorPre(
|
static ConstructorTraversalData ConstructorTraversalVisitorPre(
|
||||||
|
@ -64,13 +69,12 @@ struct ConstructorTraversalData {
|
||||||
// our number of symbolic elimination parents is exactly 1 less than
|
// our number of symbolic elimination parents is exactly 1 less than
|
||||||
// our child's symbolic elimination parents - this condition indicates that
|
// our child's symbolic elimination parents - this condition indicates that
|
||||||
// eliminating the current node did not introduce any parents beyond those
|
// eliminating the current node did not introduce any parents beyond those
|
||||||
// already in the child.
|
// already in the child->
|
||||||
|
|
||||||
// Do symbolic elimination for this node
|
// Do symbolic elimination for this node
|
||||||
class : public FactorGraph<Factor> {}
|
SymbolicFactors symbolicFactors;
|
||||||
symbolicFactors;
|
symbolicFactors.reserve(
|
||||||
symbolicFactors.reserve(ETreeNode->factors.size() +
|
ETreeNode->factors.size() + myData.childSymbolicFactors.size());
|
||||||
myData.childSymbolicFactors.size());
|
|
||||||
// Add ETree node factors
|
// Add ETree node factors
|
||||||
symbolicFactors += ETreeNode->factors;
|
symbolicFactors += ETreeNode->factors;
|
||||||
// Add symbolic factors passed up from children
|
// Add symbolic factors passed up from children
|
||||||
|
@ -78,60 +82,47 @@ struct ConstructorTraversalData {
|
||||||
|
|
||||||
Ordering keyAsOrdering;
|
Ordering keyAsOrdering;
|
||||||
keyAsOrdering.push_back(ETreeNode->key);
|
keyAsOrdering.push_back(ETreeNode->key);
|
||||||
std::pair<SymbolicConditional::shared_ptr, SymbolicFactor::shared_ptr>
|
SymbolicConditional::shared_ptr myConditional;
|
||||||
symbolicElimResult =
|
SymbolicFactor::shared_ptr mySeparatorFactor;
|
||||||
internal::EliminateSymbolic(symbolicFactors, keyAsOrdering);
|
boost::tie(myConditional, mySeparatorFactor) = internal::EliminateSymbolic(
|
||||||
|
symbolicFactors, keyAsOrdering);
|
||||||
|
|
||||||
// Store symbolic elimination results in the parent
|
// Store symbolic elimination results in the parent
|
||||||
myData.parentData->childSymbolicConditionals.push_back(
|
myData.parentData->childSymbolicConditionals.push_back(myConditional);
|
||||||
symbolicElimResult.first);
|
myData.parentData->childSymbolicFactors.push_back(mySeparatorFactor);
|
||||||
myData.parentData->childSymbolicFactors.push_back(
|
|
||||||
symbolicElimResult.second);
|
|
||||||
sharedNode node = myData.myJTNode;
|
sharedNode node = myData.myJTNode;
|
||||||
|
const FastVector<SymbolicConditional::shared_ptr>& childConditionals =
|
||||||
|
myData.childSymbolicConditionals;
|
||||||
|
node->problemSize_ = (int) (myConditional->size() * symbolicFactors.size());
|
||||||
|
|
||||||
// Merge our children if they are in our clique - if our conditional has
|
// Merge our children if they are in our clique - if our conditional has
|
||||||
// exactly one fewer parent than our child's conditional.
|
// exactly one fewer parent than our child's conditional.
|
||||||
size_t myNrFrontals = 1;
|
const size_t myNrParents = myConditional->nrParents();
|
||||||
const size_t myNrParents = symbolicElimResult.first->nrParents();
|
const size_t nrChildren = node->children.size();
|
||||||
size_t nrMergedChildren = 0;
|
assert(childConditionals.size() == nrChildren);
|
||||||
assert(node->children.size() == myData.childSymbolicConditionals.size());
|
|
||||||
// Loop over children
|
// decide which children to merge, as index into children
|
||||||
int combinedProblemSize =
|
std::vector<bool> merge(nrChildren, false);
|
||||||
(int)(symbolicElimResult.first->size() * symbolicFactors.size());
|
size_t myNrFrontals = 1, i = 0;
|
||||||
gttic(merge_children);
|
BOOST_FOREACH(const sharedNode& child, node->children) {
|
||||||
for (size_t i = 0; i < myData.childSymbolicConditionals.size(); ++i) {
|
|
||||||
// Check if we should merge the i^th child
|
// Check if we should merge the i^th child
|
||||||
if (myNrParents + myNrFrontals ==
|
if (myNrParents + myNrFrontals == childConditionals[i]->nrParents()) {
|
||||||
myData.childSymbolicConditionals[i]->nrParents()) {
|
|
||||||
// Get a reference to the i, adjusting the index to account for children
|
|
||||||
// previously merged and removed from the i list.
|
|
||||||
const Node& child = *node->children[i - nrMergedChildren];
|
|
||||||
// Merge keys. For efficiency, we add keys in reverse order at end, calling reverse after..
|
|
||||||
node->orderedFrontalKeys.insert(node->orderedFrontalKeys.end(),
|
|
||||||
child.orderedFrontalKeys.rbegin(),
|
|
||||||
child.orderedFrontalKeys.rend());
|
|
||||||
// Merge keys, factors, and children.
|
|
||||||
node->factors.insert(node->factors.end(), child.factors.begin(), child.factors.end());
|
|
||||||
node->children.insert(node->children.end(), child.children.begin(), child.children.end());
|
|
||||||
// Increment problem size
|
|
||||||
combinedProblemSize = std::max(combinedProblemSize, child.problemSize_);
|
|
||||||
// Increment number of frontal variables
|
// Increment number of frontal variables
|
||||||
myNrFrontals += child.orderedFrontalKeys.size();
|
myNrFrontals += child->orderedFrontalKeys.size();
|
||||||
// Remove i from list.
|
merge[i] = true;
|
||||||
node->children.erase(node->children.begin() + (i - nrMergedChildren));
|
|
||||||
// Increment number of merged children
|
|
||||||
++nrMergedChildren;
|
|
||||||
}
|
}
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
std::reverse(node->orderedFrontalKeys.begin(), node->orderedFrontalKeys.end());
|
|
||||||
gttoc(merge_children);
|
// now really merge
|
||||||
node->problemSize_ = combinedProblemSize;
|
node->mergeChildren(merge);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
template <class BAYESTREE, class GRAPH>
|
template<class BAYESTREE, class GRAPH>
|
||||||
template <class ETREE_BAYESNET, class ETREE_GRAPH>
|
template<class ETREE_BAYESNET, class ETREE_GRAPH>
|
||||||
JunctionTree<BAYESTREE, GRAPH>::JunctionTree(
|
JunctionTree<BAYESTREE, GRAPH>::JunctionTree(
|
||||||
const EliminationTree<ETREE_BAYESNET, ETREE_GRAPH>& eliminationTree) {
|
const EliminationTree<ETREE_BAYESNET, ETREE_GRAPH>& eliminationTree) {
|
||||||
gttic(JunctionTree_FromEliminationTree);
|
gttic(JunctionTree_FromEliminationTree);
|
||||||
|
@ -147,18 +138,20 @@ JunctionTree<BAYESTREE, GRAPH>::JunctionTree(
|
||||||
typedef typename EliminationTree<ETREE_BAYESNET, ETREE_GRAPH>::Node ETreeNode;
|
typedef typename EliminationTree<ETREE_BAYESNET, ETREE_GRAPH>::Node ETreeNode;
|
||||||
typedef ConstructorTraversalData<BAYESTREE, GRAPH, ETreeNode> Data;
|
typedef ConstructorTraversalData<BAYESTREE, GRAPH, ETreeNode> Data;
|
||||||
Data rootData(0);
|
Data rootData(0);
|
||||||
rootData.myJTNode =
|
rootData.myJTNode = boost::make_shared<typename Base::Node>(); // Make a dummy node to gather
|
||||||
boost::make_shared<typename Base::Node>(); // Make a dummy node to gather
|
// the junction tree roots
|
||||||
// the junction tree roots
|
|
||||||
treeTraversal::DepthFirstForest(eliminationTree, rootData,
|
treeTraversal::DepthFirstForest(eliminationTree, rootData,
|
||||||
Data::ConstructorTraversalVisitorPre,
|
Data::ConstructorTraversalVisitorPre,
|
||||||
Data::ConstructorTraversalVisitorPostAlg2);
|
Data::ConstructorTraversalVisitorPostAlg2);
|
||||||
|
|
||||||
// Assign roots from the dummy node
|
// Assign roots from the dummy node
|
||||||
Base::roots_ = rootData.myJTNode->children;
|
typedef typename JunctionTree<BAYESTREE, GRAPH>::Node Node;
|
||||||
|
const typename Node::Children& children = rootData.myJTNode->children;
|
||||||
|
Base::roots_.reserve(children.size());
|
||||||
|
Base::roots_.insert(Base::roots_.begin(), children.begin(), children.end());
|
||||||
|
|
||||||
// Transfer remaining factors from elimination tree
|
// Transfer remaining factors from elimination tree
|
||||||
Base::remainingFactors_ = eliminationTree.remainingFactors();
|
Base::remainingFactors_ = eliminationTree.remainingFactors();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace gtsam
|
} // namespace gtsam
|
||||||
|
|
Loading…
Reference in New Issue