aGrUM  0.20.2
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  // for debugging purposes
38  GUM_CONSTRUCTOR(BinaryJoinTreeConverterDefault);
39  }
+ Here is the call graph for this function:

◆ ~BinaryJoinTreeConverterDefault()

gum::BinaryJoinTreeConverterDefault::~BinaryJoinTreeConverterDefault ( )
virtual

destructor

Definition at line 42 of file binaryJoinTreeConverterDefault.cpp.

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

42  {
43  GUM_DESTRUCTOR(BinaryJoinTreeConverterDefault);
44  }
+ 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 83 of file binaryJoinTreeConverterDefault.cpp.

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

86  {
87  float result = 1;
88 
89  for (const auto node: nodes1)
90  result *= domain_sizes[node];
91 
92  for (const auto node: nodes2)
93  if (!nodes1.exists(node)) result *= domain_sizes[node];
94 
95  return result;
96  }
+ 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 247 of file binaryJoinTreeConverterDefault.cpp.

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

250  {
251  // first, we copy the current clique graph. By default, this is what we
252  // will return
253  CliqueGraph binJT = JT;
254 
255  // check that there is no connected component without a root. In such a
256  // case,
257  // assign an arbitrary root to it
258  roots__ = specified_roots;
259 
260  NodeProperty< bool > mark = JT.nodesProperty(false, JT.sizeNodes());
261 
262  // for each specified root, populate its connected component
263  for (const auto root: specified_roots) {
264  // check that the root has not already been marked
265  // in this case, this means that more than one root has been specified
266  // for a given connected component
267  if (mark[root])
268  GUM_ERROR(InvalidNode,
269  "several roots have been specified for a given "
270  "connected component (last : "
271  << root << ")");
272 
273  markConnectedComponent__(JT, root, mark);
274  }
275 
276  // check that all nodes have been marked. If this is not the case, then
277  // this means that we need to add new roots
278  for (const auto& elt: mark)
279  if (!elt.second) {
280  roots__ << elt.first;
281  markConnectedComponent__(JT, elt.first, mark);
282  }
283 
284  // here, we know that each connected component has one and only one root.
285  // Now we can apply a recursive collect algorithm starting from root
286  // that transforms each clique with more than 3 neighbors into a set of
287  // cliques having at most 3 neighbors.
288  NodeProperty< bool > mark2 = JT.nodesProperty(false, JT.sizeNodes());
289 
290  for (const auto root: roots__)
291  convertConnectedComponent__(binJT, root, root, domain_sizes, mark2);
292 
293  // binJT is now a binary join tree, so we can return it
294  return binJT;
295  }
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:54
+ 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 102 of file binaryJoinTreeConverterDefault.cpp.

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

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

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

230  {
231  // first, indicate that the node has been marked (this avoids looping
232  // if JT is not a tree
233  mark[current_node] = true;
234 
235  // parse all the neighbors except nodes already converted and convert them
236  for (const auto neigh: JT.neighbours(current_node)) {
237  if (!mark[neigh]) {
238  convertConnectedComponent__(JT, neigh, current_node, domain_sizes, mark);
239  }
240  }
241 
242  // convert the current node
243  convertClique__(JT, current_node, from, domain_sizes);
244  }
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 48 of file binaryJoinTreeConverterDefault.cpp.

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

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

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

99 { 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: