aGrUM  0.16.0
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 35 of file binaryJoinTreeConverterDefault.h.

Constructor & Destructor Documentation

◆ BinaryJoinTreeConverterDefault() [1/2]

gum::BinaryJoinTreeConverterDefault::BinaryJoinTreeConverterDefault ( )

default constructor

Definition at line 37 of file binaryJoinTreeConverterDefault.cpp.

37  {
38  // for debugging purposes
39  GUM_CONSTRUCTOR(BinaryJoinTreeConverterDefault);
40  }

◆ ~BinaryJoinTreeConverterDefault()

gum::BinaryJoinTreeConverterDefault::~BinaryJoinTreeConverterDefault ( )
virtual

destructor

Definition at line 43 of file binaryJoinTreeConverterDefault.cpp.

43  {
44  GUM_DESTRUCTOR(BinaryJoinTreeConverterDefault);
45  }

◆ 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 82 of file binaryJoinTreeConverterDefault.cpp.

Referenced by __convertClique().

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

References __combinedSize(), gum::CliqueGraph::addEdge(), gum::CliqueGraph::addNode(), gum::PriorityQueueImplementation< Val, Priority, Cmp, Alloc, std::is_scalar< Val >::value >::erase(), gum::CliqueGraph::eraseEdge(), gum::PriorityQueueImplementation< Val, Priority, Cmp, Alloc, std::is_scalar< Val >::value >::insert(), gum::EdgeGraphPart::neighbours(), gum::PriorityQueueImplementation< Val, Priority, Cmp, Alloc, std::is_scalar< Val >::value >::pop(), gum::CliqueGraph::separator(), gum::PriorityQueueImplementation< Val, Priority, Cmp, Alloc, std::is_scalar< Val >::value >::setPriority(), and gum::Set< Key, Alloc >::size().

Referenced by __convertConnectedComponent().

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

References __convertClique(), and gum::EdgeGraphPart::neighbours().

Referenced by convert().

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

References gum::EdgeGraphPart::neighbours(), and gum::NodeGraphPart::sizeNodes().

Referenced by convert().

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

References __convertConnectedComponent(), __markConnectedComponent(), __roots, GUM_ERROR, gum::NodeGraphPart::nodesProperty(), and gum::NodeGraphPart::sizeNodes().

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

References __roots.

98 { return __roots; }
NodeSet __roots
the new roots that have been created to compute the last query

Member Data Documentation

◆ __roots

NodeSet gum::BinaryJoinTreeConverterDefault::__roots
private

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

Definition at line 79 of file binaryJoinTreeConverterDefault.h.

Referenced by convert(), and roots().


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