aGrUM  0.20.3
a C++ library for (probabilistic) graphical models
gum::BinaryJoinTreeConverterDefault Class Reference

#include <binaryJoinTreeConverterDefault.h>

+ Collaboration diagram for gum::BinaryJoinTreeConverterDefault:

Public Member Functions

Constructors / Destructors
 BinaryJoinTreeConverterDefault ()
 default constructor More...
 
virtual ~BinaryJoinTreeConverterDefault ()
 destructor More...
 
Accessors/Modifiers
CliqueGraph convert (const CliqueGraph &JT, const NodeProperty< Size > &domain_sizes, const NodeSet &roots)
 returns a binary join tree corresponding to clique graph JT More...
 
const NodeSetroots () const
 returns all the roots considered for all the connected components More...
 

Detailed Description

Definition at line 34 of file binaryJoinTreeConverterDefault.h.

Constructor & Destructor Documentation

◆ BinaryJoinTreeConverterDefault() [1/2]

gum::BinaryJoinTreeConverterDefault::BinaryJoinTreeConverterDefault ( )

default constructor

Definition at line 36 of file binaryJoinTreeConverterDefault.cpp.

References gum::Set< Key, Alloc >::emplace().

36  {
37  GUM_CONSTRUCTOR(BinaryJoinTreeConverterDefault);
38  }
+ Here is the call graph for this function:

◆ ~BinaryJoinTreeConverterDefault()

gum::BinaryJoinTreeConverterDefault::~BinaryJoinTreeConverterDefault ( )
virtual

destructor

Definition at line 41 of file binaryJoinTreeConverterDefault.cpp.

References gum::Set< Key, Alloc >::emplace().

41  {
42  GUM_DESTRUCTOR(BinaryJoinTreeConverterDefault);
43  }
+ Here is the call graph for this function:

◆ BinaryJoinTreeConverterDefault() [2/2]

gum::BinaryJoinTreeConverterDefault::BinaryJoinTreeConverterDefault ( const BinaryJoinTreeConverterDefault )
private

forbid copy constructor

Member Function Documentation

◆ _combinedSize_()

float gum::BinaryJoinTreeConverterDefault::_combinedSize_ ( const NodeSet nodes1,
const NodeSet nodes2,
const NodeProperty< Size > &  domain_sizes 
) const
private

returns the domain size of the union of two cliques

Definition at line 81 of file binaryJoinTreeConverterDefault.cpp.

References gum::Set< Key, Alloc >::emplace().

84  {
85  float result = 1;
86 
87  for (const auto node: nodes1)
88  result *= domain_sizes[node];
89 
90  for (const auto node: nodes2)
91  if (!nodes1.exists(node)) result *= domain_sizes[node];
92 
93  return result;
94  }
+ Here is the call graph for this function:

◆ _convertClique_()

void gum::BinaryJoinTreeConverterDefault::_convertClique_ ( CliqueGraph JT,
NodeId  clique,
NodeId  from,
const NodeProperty< Size > &  domain_sizes 
) const
private

convert a clique and its adjacent cliques into a binary join tree

Definition at line 100 of file binaryJoinTreeConverterDefault.cpp.

References gum::Set< Key, Alloc >::emplace().

104  {
105  // get the neighbors of clique. If there are fewer than 3 neighbors,
106  // there is nothing to do
107  const NodeSet& neighbors = JT.neighbours(clique);
108 
109  if (neighbors.size() <= 2) return;
110 
111  if ((neighbors.size() == 3) && (clique != from)) return;
112 
113  // here we need to transform the neighbors into a binary tree
114  // create a vector with all the ids of the cliques to combine
115  std::vector< NodeId > cliques;
116  cliques.reserve(neighbors.size());
117 
118  for (const auto nei: neighbors)
119  if (nei != from) cliques.push_back(nei);
120 
121  // create a vector indicating wether the elements in cliques contain
122  // relevant information or not (during the execution of the for
123  // loop below, a cell of vector cliques may actually contain only
124  // trash data)
125  std::vector< bool > is_cliques_relevant(cliques.size(), true);
126 
127  // for each pair of cliques (i,j), compute the size of the clique that would
128  // result from the combination of clique i with clique j and store the
129  // result
130  // into a priorityQueue
131  std::pair< NodeId, NodeId > pair;
132 
133  PriorityQueue< std::pair< NodeId, NodeId >, float > queue;
134 
135  for (NodeId i = 0; i < cliques.size(); ++i) {
136  pair.first = i;
137  const NodeSet& nodes1 = JT.separator(cliques[i], clique);
138 
139  for (NodeId j = i + 1; j < cliques.size(); ++j) {
140  pair.second = j;
141  queue.insert(pair, _combinedSize_(nodes1, JT.separator(cliques[j], clique), domain_sizes));
142  }
143  }
144 
145  // now parse the priority queue: the top element (i,j) gives the combination
146  // to perform. When the result R has been computed, substitute i by R,
147  // remove
148  // table j and recompute all the priorities of all the pairs (R,k) still
149  // available.
150  for (NodeId k = 2; k < cliques.size(); ++k) {
151  // get the combination to perform and do it
152  pair = queue.pop();
153  NodeId ti = pair.first;
154  NodeId tj = pair.second;
155 
156  // create a new clique that will become adjacent to ti and tj
157  // and remove the edges between ti, tj and clique
158  const NodeSet& nodes1 = JT.separator(cliques[ti], clique);
159  const NodeSet& nodes2 = JT.separator(cliques[tj], clique);
160  NodeId new_node = JT.addNode(nodes1 + nodes2);
161  JT.addEdge(cliques[ti], new_node);
162  JT.addEdge(cliques[tj], new_node);
163  JT.addEdge(clique, new_node);
164  JT.eraseEdge(Edge(cliques[ti], clique));
165  JT.eraseEdge(Edge(cliques[tj], clique));
166 
167  // substitute cliques[pair.first] by the result
168  cliques[ti] = new_node;
169  is_cliques_relevant[tj] = false; // now tj is no more a neighbor of clique
170 
171  // remove all the pairs involving tj in the priority queue
172 
173  for (NodeId ind = 0; ind < tj; ++ind) {
174  if (is_cliques_relevant[ind]) {
175  pair.first = ind;
176  queue.erase(pair);
177  }
178  }
179 
180  pair.first = tj;
181 
182  for (NodeId ind = tj + 1; ind < cliques.size(); ++ind) {
183  if (is_cliques_relevant[ind]) {
184  pair.second = ind;
185  queue.erase(pair);
186  }
187  }
188 
189  // update the "combined" size of all the pairs involving "new_node"
190  {
191  const NodeSet& nodes1 = JT.separator(cliques[ti], clique);
192  pair.second = ti;
193  float newsize;
194 
195  for (NodeId ind = 0; ind < ti; ++ind) {
196  if (is_cliques_relevant[ind]) {
197  pair.first = ind;
198  newsize = _combinedSize_(nodes1, JT.separator(cliques[ind], clique), domain_sizes);
199  queue.setPriority(pair, newsize);
200  }
201  }
202 
203  pair.first = ti;
204 
205  for (NodeId ind = ti + 1; ind < cliques.size(); ++ind) {
206  if (is_cliques_relevant[ind]) {
207  pair.second = ind;
208  newsize = _combinedSize_(nodes1, JT.separator(cliques[ind], clique), domain_sizes);
209  queue.setPriority(pair, newsize);
210  }
211  }
212  }
213  }
214  }
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
float _combinedSize_(const NodeSet &nodes1, const NodeSet &nodes2, const NodeProperty< Size > &domain_sizes) const
returns the domain size of the union of two cliques
Size NodeId
Type for node ids.
Definition: graphElements.h:97
+ Here is the call graph for this function:

◆ _convertConnectedComponent_()

void gum::BinaryJoinTreeConverterDefault::_convertConnectedComponent_ ( CliqueGraph JT,
NodeId  current_node,
NodeId  from,
const NodeProperty< Size > &  domain_sizes,
NodeProperty< bool > &  mark 
) const
private

convert a whole connected component into a binary join tree

Definition at line 217 of file binaryJoinTreeConverterDefault.cpp.

References gum::Set< Key, Alloc >::emplace().

222  {
223  // first, indicate that the node has been marked (this avoids looping
224  // if JT is not a tree
225  mark[current_node] = true;
226 
227  // parse all the neighbors except nodes already converted and convert them
228  for (const auto neigh: JT.neighbours(current_node)) {
229  if (!mark[neigh]) {
230  _convertConnectedComponent_(JT, neigh, current_node, domain_sizes, mark);
231  }
232  }
233 
234  // convert the current node
235  _convertClique_(JT, current_node, from, domain_sizes);
236  }
void _convertConnectedComponent_(CliqueGraph &JT, NodeId current_node, NodeId from, const NodeProperty< Size > &domain_sizes, NodeProperty< bool > &mark) const
convert a whole connected component into a binary join tree
void _convertClique_(CliqueGraph &JT, NodeId clique, NodeId from, const NodeProperty< Size > &domain_sizes) const
convert a clique and its adjacent cliques into a binary join tree
+ Here is the call graph for this function:

◆ _markConnectedComponent_()

void gum::BinaryJoinTreeConverterDefault::_markConnectedComponent_ ( const CliqueGraph JT,
NodeId  root,
NodeProperty< bool > &  mark 
) const
private

a function used to mark the nodes belonging to a given connected component

Definition at line 47 of file binaryJoinTreeConverterDefault.cpp.

References gum::Set< Key, Alloc >::emplace().

49  {
50  // we mark the nodes in a depth first search manner. To avoid a recursive
51  // algorithm, use a vector to simulate a stack of nodes to inspect.
52  // stack => depth first search
53  std::vector< NodeId > nodes_to_inspect;
54  nodes_to_inspect.reserve(JT.sizeNodes());
55 
56  // the idea to populate the marks is to use the stack: root is
57  // put onto the stack. Then, while the stack is not empty, remove
58  // the top of the stack and mark it and put into the stack its
59  // adjacent nodes.
60  nodes_to_inspect.push_back(root);
61 
62  while (!nodes_to_inspect.empty()) {
63  // process the top of the stack
64  NodeId current_node = nodes_to_inspect.back();
65  nodes_to_inspect.pop_back();
66 
67  // only process the node if it has not been processed yet (actually,
68  // this should not occur unless the clique graph is not singly connected)
69 
70  if (!mark[current_node]) {
71  mark[current_node] = true;
72 
73  // put the neighbors onto the stack
74  for (const auto neigh: JT.neighbours(current_node))
75  if (!mark[neigh]) nodes_to_inspect.push_back(neigh);
76  }
77  }
78  }
Size NodeId
Type for node ids.
Definition: graphElements.h:97
+ Here is the call graph for this function:

◆ convert()

CliqueGraph gum::BinaryJoinTreeConverterDefault::convert ( const CliqueGraph JT,
const NodeProperty< Size > &  domain_sizes,
const NodeSet roots 
)

returns a binary join tree corresponding to clique graph JT

computes the binary join tree

This method creates and returns a new binary join tree compatible with that passed in argument (JT) and optimized for inference. As such, this requires knowing the join tree to be converted (of course), but also which roots will be used by the collect/diffusion inference engine and the domain size of the variables contained in the cliques of JT (to optimize the combination of the potentials contained in the cliques.

Exceptions
InvalidNodeexception is thrown if some roots do not belong to JT or if several roots belong to the same connected component.
Warning
If you do not pass in argument a root for each connected component, then for those with unspecified roots, an arbitrary root will be computed and used for the binarization.

Definition at line 239 of file binaryJoinTreeConverterDefault.cpp.

References gum::Set< Key, Alloc >::emplace().

241  {
242  // first, we copy the current clique graph. By default, this is what we
243  // will return
244  CliqueGraph binJT = JT;
245 
246  // check that there is no connected component without a root. In such a
247  // case,
248  // assign an arbitrary root to it
249  _roots_ = specified_roots;
250 
251  NodeProperty< bool > mark = JT.nodesProperty(false, JT.sizeNodes());
252 
253  // for each specified root, populate its connected component
254  for (const auto root: specified_roots) {
255  // check that the root has not already been marked
256  // in this case, this means that more than one root has been specified
257  // for a given connected component
258  if (mark[root])
259  GUM_ERROR(InvalidNode,
260  "several roots have been specified for a given "
261  "connected component (last : "
262  << root << ")");
263 
264  _markConnectedComponent_(JT, root, mark);
265  }
266 
267  // check that all nodes have been marked. If this is not the case, then
268  // this means that we need to add new roots
269  for (const auto& elt: mark)
270  if (!elt.second) {
271  _roots_ << elt.first;
272  _markConnectedComponent_(JT, elt.first, mark);
273  }
274 
275  // here, we know that each connected component has one and only one root.
276  // Now we can apply a recursive collect algorithm starting from root
277  // that transforms each clique with more than 3 neighbors into a set of
278  // cliques having at most 3 neighbors.
279  NodeProperty< bool > mark2 = JT.nodesProperty(false, JT.sizeNodes());
280 
281  for (const auto root: _roots_)
282  _convertConnectedComponent_(binJT, root, root, domain_sizes, mark2);
283 
284  // binJT is now a binary join tree, so we can return it
285  return binJT;
286  }
NodeSet _roots_
the new roots that have been created to compute the last query
void _convertConnectedComponent_(CliqueGraph &JT, NodeId current_node, NodeId from, const NodeProperty< Size > &domain_sizes, NodeProperty< bool > &mark) const
convert a whole connected component into a binary join tree
void _markConnectedComponent_(const CliqueGraph &JT, NodeId root, NodeProperty< bool > &mark) const
a function used to mark the nodes belonging to a given connected component
#define GUM_ERROR(type, msg)
Definition: exceptions.h:51
+ Here is the call graph for this function:

◆ operator=()

BinaryJoinTreeConverterDefault& gum::BinaryJoinTreeConverterDefault::operator= ( const BinaryJoinTreeConverterDefault )
private

forbid copy operator

◆ roots()

const NodeSet & gum::BinaryJoinTreeConverterDefault::roots ( ) const

returns all the roots considered for all the connected components

Definition at line 97 of file binaryJoinTreeConverterDefault.cpp.

References gum::Set< Key, Alloc >::emplace().

97 { return _roots_; }
NodeSet _roots_
the new roots that have been created to compute the last query
+ Here is the call graph for this function:

Member Data Documentation

◆ _roots_

NodeSet gum::BinaryJoinTreeConverterDefault::_roots_
private

the new roots that have been created to compute the last query

Definition at line 78 of file binaryJoinTreeConverterDefault.h.


The documentation for this class was generated from the following files: