gtsam  3.2.1
gtsam
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
BayesTree-inst.h
1 /* ----------------------------------------------------------------------------
2 
3  * GTSAM Copyright 2010, Georgia Tech Research Corporation,
4  * Atlanta, Georgia 30332-0415
5  * All Rights Reserved
6  * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
7 
8  * See LICENSE for the license information
9 
10  * -------------------------------------------------------------------------- */
11 
21 #pragma once
22 
26 #include <gtsam/base/timing.h>
27 
28 #include <boost/optional.hpp>
29 #include <boost/foreach.hpp>
30 #include <boost/assign/list_of.hpp>
31 #include <fstream>
32 
33 using boost::assign::cref_list_of;
34 
35 namespace gtsam {
36 
37  /* ************************************************************************* */
38  template<class CLIQUE>
41  BOOST_FOREACH(const sharedClique& root, roots_)
42  getCliqueData(data, root);
43  return data;
44  }
45 
46  /* ************************************************************************* */
47  template<class CLIQUE>
49  data.conditionalSizes.push_back(clique->conditional()->nrFrontals());
50  data.separatorSizes.push_back(clique->conditional()->nrParents());
51  BOOST_FOREACH(sharedClique c, clique->children) {
52  getCliqueData(data, c);
53  }
54  }
55 
56  /* ************************************************************************* */
57  template<class CLIQUE>
59  size_t count = 0;
60  BOOST_FOREACH(const sharedClique& root, roots_)
61  count += root->numCachedSeparatorMarginals();
62  return count;
63  }
64 
65  /* ************************************************************************* */
66  template<class CLIQUE>
67  void BayesTree<CLIQUE>::saveGraph(const std::string &s, const KeyFormatter& keyFormatter) const {
68  if (roots_.empty()) throw std::invalid_argument("the root of Bayes tree has not been initialized!");
69  std::ofstream of(s.c_str());
70  of<< "digraph G{\n";
71  BOOST_FOREACH(const sharedClique& root, roots_)
72  saveGraph(of, root, keyFormatter);
73  of<<"}";
74  of.close();
75  }
76 
77  /* ************************************************************************* */
78  template<class CLIQUE>
79  void BayesTree<CLIQUE>::saveGraph(std::ostream &s, sharedClique clique, const KeyFormatter& indexFormatter, int parentnum) const {
80  static int num = 0;
81  bool first = true;
82  std::stringstream out;
83  out << num;
84  std::string parent = out.str();
85  parent += "[label=\"";
86 
87  BOOST_FOREACH(Key index, clique->conditional_->frontals()) {
88  if(!first) parent += ","; first = false;
89  parent += indexFormatter(index);
90  }
91 
92  if(clique->parent()){
93  parent += " : ";
94  s << parentnum << "->" << num << "\n";
95  }
96 
97  first = true;
98  BOOST_FOREACH(Key sep, clique->conditional_->parents()) {
99  if(!first) parent += ","; first = false;
100  parent += indexFormatter(sep);
101  }
102  parent += "\"];\n";
103  s << parent;
104  parentnum = num;
105 
106  BOOST_FOREACH(sharedClique c, clique->children) {
107  num++;
108  saveGraph(s, c, indexFormatter, parentnum);
109  }
110  }
111 
112  /* ************************************************************************* */
113  template<class CLIQUE>
114  size_t BayesTree<CLIQUE>::size() const {
115  size_t size = 0;
116  BOOST_FOREACH(const sharedClique& clique, roots_)
117  size += clique->treeSize();
118  return size;
119  }
120 
121  /* ************************************************************************* */
122  template<class CLIQUE>
123  void BayesTree<CLIQUE>::addClique(const sharedClique& clique, const sharedClique& parent_clique) {
124  BOOST_FOREACH(Key j, clique->conditional()->frontals())
125  nodes_[j] = clique;
126  if (parent_clique != NULL) {
127  clique->parent_ = parent_clique;
128  parent_clique->children.push_back(clique);
129  } else {
130  roots_.push_back(clique);
131  }
132  }
133 
134  /* ************************************************************************* */
135  // TODO: Clean up
136  namespace {
137  template<class FACTOR, class CLIQUE>
138  int _pushClique(FactorGraph<FACTOR>& fg, const boost::shared_ptr<CLIQUE>& clique) {
139  fg.push_back(clique->conditional_);
140  return 0;
141  }
142 
143  template<class FACTOR, class CLIQUE>
144  struct _pushCliqueFunctor {
145  _pushCliqueFunctor(FactorGraph<FACTOR>& graph_) : graph(graph_) {}
146  FactorGraph<FACTOR>& graph;
147  int operator()(const boost::shared_ptr<CLIQUE>& clique, int dummy) {
148  graph.push_back(clique->conditional_);
149  return 0;
150  }
151  };
152  }
153 
154  /* ************************************************************************* */
155  template<class CLIQUE>
157  {
158  // Traverse the BayesTree and add all conditionals to this graph
159  int data = 0; // Unused
160  _pushCliqueFunctor<FactorType,CLIQUE> functor(graph);
161  treeTraversal::DepthFirstForest(*this, data, functor); // FIXME: sort of works?
162 // treeTraversal::DepthFirstForest(*this, data, boost::bind(&_pushClique<FactorType,CLIQUE>, boost::ref(graph), _1));
163  }
164 
165  /* ************************************************************************* */
166  template<class CLIQUE>
168  *this = other;
169  }
170 
171  /* ************************************************************************* */
172  namespace {
173  template<typename NODE>
174  boost::shared_ptr<NODE>
175  BayesTreeCloneForestVisitorPre(const boost::shared_ptr<NODE>& node, const boost::shared_ptr<NODE>& parentPointer)
176  {
177  // Clone the current node and add it to its cloned parent
178  boost::shared_ptr<NODE> clone = boost::make_shared<NODE>(*node);
179  clone->children.clear();
180  clone->parent_ = parentPointer;
181  parentPointer->children.push_back(clone);
182  return clone;
183  }
184  }
185 
186  /* ************************************************************************* */
187  template<class CLIQUE>
189  this->clear();
190  boost::shared_ptr<Clique> rootContainer = boost::make_shared<Clique>();
191  treeTraversal::DepthFirstForest(other, rootContainer, BayesTreeCloneForestVisitorPre<Clique>);
192  BOOST_FOREACH(const sharedClique& root, rootContainer->children) {
193  root->parent_ = typename Clique::weak_ptr(); // Reset the parent since it's set to the dummy clique
194  insertRoot(root);
195  }
196  return *this;
197  }
198 
199  /* ************************************************************************* */
200  template<class CLIQUE>
201  void BayesTree<CLIQUE>::print(const std::string& s, const KeyFormatter& keyFormatter) const {
202  std::cout << s << ": cliques: " << size() << ", variables: " << nodes_.size() << std::endl;
203  treeTraversal::PrintForest(*this, s, keyFormatter);
204  }
205 
206  /* ************************************************************************* */
207  // binary predicate to test equality of a pair for use in equals
208  template<class CLIQUE>
209  bool check_sharedCliques(
210  const std::pair<Key, typename BayesTree<CLIQUE>::sharedClique>& v1,
211  const std::pair<Key, typename BayesTree<CLIQUE>::sharedClique>& v2
212  ) {
213  return v1.first == v2.first &&
214  ((!v1.second && !v2.second) || (v1.second && v2.second && v1.second->equals(*v2.second)));
215  }
216 
217  /* ************************************************************************* */
218  template<class CLIQUE>
219  bool BayesTree<CLIQUE>::equals(const BayesTree<CLIQUE>& other, double tol) const {
220  return size()==other.size() &&
221  std::equal(nodes_.begin(), nodes_.end(), other.nodes_.begin(), &check_sharedCliques<CLIQUE>);
222  }
223 
224  /* ************************************************************************* */
225  template<class CLIQUE>
226  template<class CONTAINER>
227  Key BayesTree<CLIQUE>::findParentClique(const CONTAINER& parents) const {
228  typename CONTAINER::const_iterator lowestOrderedParent = min_element(parents.begin(), parents.end());
229  assert(lowestOrderedParent != parents.end());
230  return *lowestOrderedParent;
231  }
232 
233  /* ************************************************************************* */
234  template<class CLIQUE>
236  // Add each frontal variable of this root node
237  BOOST_FOREACH(const Key& j, subtree->conditional()->frontals()) {
238  bool inserted = nodes_.insert(std::make_pair(j, subtree)).second;
239  assert(inserted); (void)inserted;
240  }
241  // Fill index for each child
243  BOOST_FOREACH(const sharedClique& child, subtree->children) {
244  fillNodesIndex(child); }
245  }
246 
247  /* ************************************************************************* */
248  template<class CLIQUE>
250  roots_.push_back(subtree); // Add to roots
251  fillNodesIndex(subtree); // Populate nodes index
252  }
253 
254  /* ************************************************************************* */
255  // First finds clique marginal then marginalizes that
256  /* ************************************************************************* */
257  template<class CLIQUE>
258  typename BayesTree<CLIQUE>::sharedConditional
259  BayesTree<CLIQUE>::marginalFactor(Key j, const Eliminate& function) const
260  {
261  gttic(BayesTree_marginalFactor);
262 
263  // get clique containing Key j
264  sharedClique clique = this->clique(j);
265 
266  // calculate or retrieve its marginal P(C) = P(F,S)
267  FactorGraphType cliqueMarginal = clique->marginal2(function);
268 
269  // Now, marginalize out everything that is not variable j
270  BayesNetType marginalBN = *cliqueMarginal.marginalMultifrontalBayesNet(
271  Ordering(cref_list_of<1,Key>(j)), boost::none, function);
272 
273  // The Bayes net should contain only one conditional for variable j, so return it
274  return marginalBN.front();
275  }
276 
277  /* ************************************************************************* */
278  // Find two cliques, their joint, then marginalizes
279  /* ************************************************************************* */
280  template<class CLIQUE>
281  typename BayesTree<CLIQUE>::sharedFactorGraph
282  BayesTree<CLIQUE>::joint(Key j1, Key j2, const Eliminate& function) const
283  {
284  gttic(BayesTree_joint);
285  return boost::make_shared<FactorGraphType>(*jointBayesNet(j1, j2, function));
286  }
287 
288  /* ************************************************************************* */
289  template<class CLIQUE>
290  typename BayesTree<CLIQUE>::sharedBayesNet
291  BayesTree<CLIQUE>::jointBayesNet(Key j1, Key j2, const Eliminate& function) const
292  {
293  gttic(BayesTree_jointBayesNet);
294  // get clique C1 and C2
295  sharedClique C1 = (*this)[j1], C2 = (*this)[j2];
296 
297  gttic(Lowest_common_ancestor);
298  // Find lowest common ancestor clique
299  sharedClique B; {
300  // Build two paths to the root
301  FastList<sharedClique> path1, path2; {
302  sharedClique p = C1;
303  while(p) {
304  path1.push_front(p);
305  p = p->parent();
306  }
307  } {
308  sharedClique p = C2;
309  while(p) {
310  path2.push_front(p);
311  p = p->parent();
312  }
313  }
314  // Find the path intersection
315  typename FastList<sharedClique>::const_iterator p1 = path1.begin(), p2 = path2.begin();
316  if(*p1 == *p2)
317  B = *p1;
318  while(p1 != path1.end() && p2 != path2.end() && *p1 == *p2) {
319  B = *p1;
320  ++p1;
321  ++p2;
322  }
323  }
324  gttoc(Lowest_common_ancestor);
325 
326  // Build joint on all involved variables
327  FactorGraphType p_BC1C2;
328 
329  if(B)
330  {
331  // Compute marginal on lowest common ancestor clique
332  gttic(LCA_marginal);
333  FactorGraphType p_B = B->marginal2(function);
334  gttoc(LCA_marginal);
335 
336  // Compute shortcuts of the requested cliques given the lowest common ancestor
337  gttic(Clique_shortcuts);
338  BayesNetType p_C1_Bred = C1->shortcut(B, function);
339  BayesNetType p_C2_Bred = C2->shortcut(B, function);
340  gttoc(Clique_shortcuts);
341 
342  // Factor the shortcuts to be conditioned on the full root
343  // Get the set of variables to eliminate, which is C1\B.
344  gttic(Full_root_factoring);
345  boost::shared_ptr<typename EliminationTraitsType::BayesTreeType> p_C1_B; {
346  FastVector<Key> C1_minus_B; {
347  FastSet<Key> C1_minus_B_set(C1->conditional()->beginParents(), C1->conditional()->endParents());
348  BOOST_FOREACH(const Key j, *B->conditional()) {
349  C1_minus_B_set.erase(j); }
350  C1_minus_B.assign(C1_minus_B_set.begin(), C1_minus_B_set.end());
351  }
352  // Factor into C1\B | B.
353  sharedFactorGraph temp_remaining;
354  boost::tie(p_C1_B, temp_remaining) =
355  FactorGraphType(p_C1_Bred).eliminatePartialMultifrontal(Ordering(C1_minus_B), function);
356  }
357  boost::shared_ptr<typename EliminationTraitsType::BayesTreeType> p_C2_B; {
358  FastVector<Key> C2_minus_B; {
359  FastSet<Key> C2_minus_B_set(C2->conditional()->beginParents(), C2->conditional()->endParents());
360  BOOST_FOREACH(const Key j, *B->conditional()) {
361  C2_minus_B_set.erase(j); }
362  C2_minus_B.assign(C2_minus_B_set.begin(), C2_minus_B_set.end());
363  }
364  // Factor into C2\B | B.
365  sharedFactorGraph temp_remaining;
366  boost::tie(p_C2_B, temp_remaining) =
367  FactorGraphType(p_C2_Bred).eliminatePartialMultifrontal(Ordering(C2_minus_B), function);
368  }
369  gttoc(Full_root_factoring);
370 
371  gttic(Variable_joint);
372  p_BC1C2 += p_B;
373  p_BC1C2 += *p_C1_B;
374  p_BC1C2 += *p_C2_B;
375  if(C1 != B)
376  p_BC1C2 += C1->conditional();
377  if(C2 != B)
378  p_BC1C2 += C2->conditional();
379  gttoc(Variable_joint);
380  }
381  else
382  {
383  // The nodes have no common ancestor, they're in different trees, so they're joint is just the
384  // product of their marginals.
385  gttic(Disjoint_marginals);
386  p_BC1C2 += C1->marginal2(function);
387  p_BC1C2 += C2->marginal2(function);
388  gttoc(Disjoint_marginals);
389  }
390 
391  // now, marginalize out everything that is not variable j1 or j2
392  return p_BC1C2.marginalMultifrontalBayesNet(Ordering(cref_list_of<2,Key>(j1)(j2)), boost::none, function);
393  }
394 
395  /* ************************************************************************* */
396  template<class CLIQUE>
398  // Remove all nodes and clear the root pointer
399  nodes_.clear();
400  roots_.clear();
401  }
402 
403  /* ************************************************************************* */
404  template<class CLIQUE>
406  BOOST_FOREACH(const sharedClique& root, roots_) {
407  root->deleteCachedShortcuts();
408  }
409  }
410 
411  /* ************************************************************************* */
412  template<class CLIQUE>
414  {
415  if (clique->isRoot()) {
416  typename Roots::iterator root = std::find(roots_.begin(), roots_.end(), clique);
417  if(root != roots_.end())
418  roots_.erase(root);
419  } else { // detach clique from parent
420  sharedClique parent = clique->parent_.lock();
421  typename Roots::iterator child = std::find(parent->children.begin(), parent->children.end(), clique);
422  assert(child != parent->children.end());
423  parent->children.erase(child);
424  }
425 
426  // orphan my children
427  BOOST_FOREACH(sharedClique child, clique->children)
428  child->parent_ = typename Clique::weak_ptr();
429 
430  BOOST_FOREACH(Key j, clique->conditional()->frontals()) {
431  nodes_.unsafe_erase(j);
432  }
433  }
434 
435  /* ************************************************************************* */
436  template<class CLIQUE>
437  void BayesTree<CLIQUE>::removePath(sharedClique clique, BayesNetType& bn, Cliques& orphans)
438  {
439  // base case is NULL, if so we do nothing and return empties above
440  if (clique) {
441 
442  // remove the clique from orphans in case it has been added earlier
443  orphans.remove(clique);
444 
445  // remove me
446  this->removeClique(clique);
447 
448  // remove path above me
449  this->removePath(typename Clique::shared_ptr(clique->parent_.lock()), bn, orphans);
450 
451  // add children to list of orphans (splice also removed them from clique->children_)
452  orphans.insert(orphans.begin(), clique->children.begin(), clique->children.end());
453  clique->children.clear();
454 
455  bn.push_back(clique->conditional_);
456 
457  }
458  }
459 
460  /* ************************************************************************* */
461  template<class CLIQUE>
462  void BayesTree<CLIQUE>::removeTop(const FastVector<Key>& keys, BayesNetType& bn, Cliques& orphans)
463  {
464  // process each key of the new factor
465  BOOST_FOREACH(const Key& j, keys)
466  {
467  // get the clique
468  // TODO: Nodes will be searched again in removeClique
469  typename Nodes::const_iterator node = nodes_.find(j);
470  if(node != nodes_.end()) {
471  // remove path from clique to root
472  this->removePath(node->second, bn, orphans);
473  }
474  }
475 
476  // Delete cachedShortcuts for each orphan subtree
477  //TODO: Consider Improving
478  BOOST_FOREACH(sharedClique& orphan, orphans)
479  orphan->deleteCachedShortcuts();
480  }
481 
482  /* ************************************************************************* */
483  template<class CLIQUE>
485  const sharedClique& subtree)
486  {
487  // Result clique list
488  Cliques cliques;
489  cliques.push_back(subtree);
490 
491  // Remove the first clique from its parents
492  if(!subtree->isRoot())
493  subtree->parent()->children.erase(std::find(
494  subtree->parent()->children.begin(), subtree->parent()->children.end(), subtree));
495  else
496  roots_.erase(std::find(roots_.begin(), roots_.end(), subtree));
497 
498  // Add all subtree cliques and erase the children and parent of each
499  for(typename Cliques::iterator clique = cliques.begin(); clique != cliques.end(); ++clique)
500  {
501  // Add children
502  BOOST_FOREACH(const sharedClique& child, (*clique)->children) {
503  cliques.push_back(child); }
504 
505  // Delete cached shortcuts
506  (*clique)->deleteCachedShortcutsNonRecursive();
507 
508  // Remove this node from the nodes index
509  BOOST_FOREACH(Key j, (*clique)->conditional()->frontals()) {
510  nodes_.unsafe_erase(j); }
511 
512  // Erase the parent and children pointers
513  (*clique)->parent_.reset();
514  (*clique)->children.clear();
515  }
516 
517  return cliques;
518  }
519 
520 }
Nodes nodes_
Map from indices to Clique.
Definition: BayesTree.h:95
boost::shared_ptr< Clique > sharedClique
Shared pointer to a clique.
Definition: BayesTree.h:72
Definition: BayesTree.h:64
Bayes Tree is a tree of cliques of a Bayes Chain.
void fillNodesIndex(const sharedClique &subtree)
Fill the nodes index for a subtree.
Definition: BayesTree-inst.h:235
A factor graph is a bipartite graph with factor nodes connected to variable nodes.
Definition: BayesTree.h:32
void clear()
Remove all nodes.
Definition: BayesTree-inst.h:397
void addFactorsToGraph(FactorGraph< FactorType > &graph) const
Add all cliques in this BayesTree to the specified factor graph.
Definition: BayesTree-inst.h:156
size_t size() const
number of cliques
Definition: BayesTree-inst.h:114
BayesTree()
Create an empty Bayes Tree.
Definition: BayesTree.h:107
void DepthFirstForest(FOREST &forest, DATA &rootData, VISITOR_PRE &visitorPre, VISITOR_POST &visitorPost)
Traverse a forest depth-first with pre-order and post-order visits.
Definition: treeTraversal-inst.h:75
boost::enable_if< boost::is_base_of< FactorType, DERIVEDFACTOR > >::type push_back(boost::shared_ptr< DERIVEDFACTOR > factor)
Add a factor directly using a shared_ptr.
Definition: FactorGraph.h:155
void insertRoot(const sharedClique &subtree)
Insert a new subtree with known parent clique.
Definition: BayesTree-inst.h:249
Timing utilities.
void removePath(sharedClique clique, BayesNetType &bn, Cliques &orphans)
Remove path from clique to root and return that path as factors plus a list of orphaned subtree roots...
Definition: BayesTree-inst.h:437
store all the sizes
Definition: BayesTree.h:46
Cliques removeSubtree(const sharedClique &subtree)
Remove the requested subtree.
Definition: BayesTree-inst.h:484
sharedConditional marginalFactor(Key j, const Eliminate &function=EliminationTraitsType::DefaultEliminate) const
Return marginal on any variable.
Definition: BayesTree-inst.h:259
sharedFactorGraph joint(Key j1, Key j2, const Eliminate &function=EliminationTraitsType::DefaultEliminate) const
return joint on two variables Limitation: can only calculate joint if cliques are disjoint or one of ...
Definition: BayesTree-inst.h:282
Key findParentClique(const CONTAINER &parents) const
Find parent clique of a conditional.
Definition: BayesTree-inst.h:227
size_t Key
Integer nonlinear key type.
Definition: types.h:59
void removeClique(sharedClique clique)
remove a clique: warning, can result in a forest
Definition: BayesTree-inst.h:413
void removeTop(const FastVector< Key > &keys, BayesNetType &bn, Cliques &orphans)
Given a list of indices, turn "contaminated" part of the tree back into a factor graph.
Definition: BayesTree-inst.h:462
void print(const std::string &s="", const KeyFormatter &keyFormatter=DefaultKeyFormatter) const
print
Definition: BayesTree-inst.h:201
bool equal(const T &obj1, const T &obj2, double tol)
Call equal on the object.
Definition: Testable.h:75
sharedBayesNet jointBayesNet(Key j1, Key j2, const Eliminate &function=EliminationTraitsType::DefaultEliminate) const
return joint on two variables as a BayesNet Limitation: can only calculate joint if cliques are disjo...
Definition: BayesTree-inst.h:291
bool equals(const This &other, double tol=1e-9) const
check equality
Definition: BayesTree-inst.h:219
void deleteCachedShortcuts()
Clear all shortcut caches - use before timing on marginal calculation to avoid residual cache data...
Definition: BayesTree-inst.h:405
BayesTreeCliqueData getCliqueData() const
Gather data on all cliques.
Definition: BayesTree-inst.h:39
Definition: Ordering.h:30
size_t numCachedSeparatorMarginals() const
Collect number of cliques with cached separator marginals.
Definition: BayesTree-inst.h:58
Definition: FastList.h:38
void addClique(const sharedClique &clique, const sharedClique &parent_clique=sharedClique())
add a clique (top down)
Definition: BayesTree-inst.h:123
void saveGraph(const std::string &s, const KeyFormatter &keyFormatter=DefaultKeyFormatter) const
Read only with side effects.
Definition: BayesTree-inst.h:67
void PrintForest(const FOREST &forest, std::string str, const KeyFormatter &keyFormatter)
Print a tree, prefixing each line with str, and formatting keys using keyFormatter.
Definition: treeTraversal-inst.h:218
This & operator=(const This &other)
Assignment operator.
Definition: BayesTree-inst.h:188
boost::function< std::string(Key)> KeyFormatter
Typedef for a function to format a key, i.e. to convert it to a string.
Definition: types.h:62