aGrUM  0.16.0
binaryJoinTreeConverterDefault.cpp
Go to the documentation of this file.
1 
29 #include <agrum/agrum.h>
30 
33 
34 namespace gum {
35 
38  // for debugging purposes
39  GUM_CONSTRUCTOR(BinaryJoinTreeConverterDefault);
40  }
41 
44  GUM_DESTRUCTOR(BinaryJoinTreeConverterDefault);
45  }
46 
50  const CliqueGraph& JT, NodeId root, NodeProperty< bool >& mark) const {
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  }
80 
83  const NodeSet& nodes1,
84  const NodeSet& nodes2,
85  const NodeProperty< Size >& domain_sizes) const {
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  }
96 
99 
102  CliqueGraph& JT,
103  NodeId clique,
104  NodeId from,
105  const NodeProperty< Size >& domain_sizes) const {
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 
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  }
220 
223  CliqueGraph& JT,
224  NodeId current_node,
225  NodeId from,
226  const NodeProperty< Size >& domain_sizes,
227  NodeProperty< bool >& mark) const {
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  }
242 
245  const CliqueGraph& JT,
246  const NodeProperty< Size >& domain_sizes,
247  const NodeSet& specified_roots) {
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])
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  }
292 
293 } /* namespace gum */
NodeId addNode(const NodeSet &clique)
adds a new clique to the graph
Val pop()
Removes the top element from the priority queue and return it.
CliqueGraph convert(const CliqueGraph &JT, const NodeProperty< Size > &domain_sizes, const NodeSet &roots)
returns a binary join tree corresponding to clique graph JT
const NodeSet & separator(const Edge &edge) const
returns the separator included in a given edge
NodeProperty< VAL > nodesProperty(VAL(*f)(const NodeId &), Size size=0) const
a method to create a HashTable with key:NodeId and value:VAL
void __markConnectedComponent(const CliqueGraph &JT, NodeId root, NodeProperty< bool > &mark) const
a function used to mark the nodes belonging to a given connected component
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
Definition: agrum.h:25
The class for generic Hash Tables.
Definition: hashTable.h:679
void erase(const Val &val)
Removes a given element from the priority queue (but does not return it).
const NodeSet & neighbours(const NodeId id) const
returns the set of edges adjacent to a given node
void setPriority(const Val &elt, const Priority &new_priority)
Modifies the priority of each instance of a given element.
const NodeSet & roots() const
returns all the roots considered for all the connected components
A priorityQueue is a heap in which each element has a mutable priorityA priority queue is quite simil...
Definition: priorityQueue.h:51
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
virtual void eraseEdge(const Edge &edge)
removes an edge (and its separator) from the clique graph
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
virtual void addEdge(const NodeId first, const NodeId second)
inserts a new edge between two cliques
Size insert(const Val &val, const Priority &priority)
Inserts a new (a copy) element in the priority queue.
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
Basic graph of cliques.
Definition: cliqueGraph.h:58
Size sizeNodes() const
returns the number of nodes in the NodeGraphPart
The base class for all undirected edges.
NodeSet __roots
the new roots that have been created to compute the last query
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
Size size() const noexcept
Returns the number of elements in the set.
Definition: set_tpl.h:701
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
#define GUM_ERROR(type, msg)
Definition: exceptions.h:55