aGrUM  0.20.2
a C++ library for (probabilistic) graphical models
binaryJoinTreeConverterDefault.cpp
Go to the documentation of this file.
1 /**
2  *
3  * Copyright 2005-2020 Pierre-Henri WUILLEMIN(@LIP6) & Christophe GONZALES(@AMU)
4  * info_at_agrum_dot_org
5  *
6  * This library is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library. If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 
22 /** @file
23  * @brief An algorithm for converting a join tree into a binary join tree
24  *
25  * @author Christophe GONZALES(@AMU) and Pierre-Henri WUILLEMIN(@LIP6)
26  */
27 
28 #include <agrum/agrum.h>
29 
30 #include <agrum/tools/core/priorityQueue.h>
31 #include <agrum/tools/graphs/algorithms/binaryJoinTreeConverterDefault.h>
32 
33 namespace gum {
34 
35  /// default constructor
37  // for debugging purposes
39  }
40 
41  /// destructor
44  }
45 
46  /** @brief a function used to mark the nodes belonging to a given
47  * connected component */
49  const CliqueGraph& JT,
50  NodeId root,
51  NodeProperty< bool >& mark) const {
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
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.
63 
64  while (!nodes_to_inspect.empty()) {
65  // process the top of the stack
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))
78  }
79  }
80  }
81 
82  /// returns the domain size of the union of two cliques
84  const NodeSet& nodes1,
85  const NodeSet& nodes2,
86  const NodeProperty< Size >& domain_sizes) const {
87  float result = 1;
88 
89  for (const auto node: nodes1)
91 
92  for (const auto node: nodes2)
94 
95  return result;
96  }
97 
98  /// returns all the roots considered for all the connected components
100 
101  /// convert a clique and its adjacent cliques into a binary join tree
103  CliqueGraph& JT,
104  NodeId clique,
105  NodeId from,
106  const NodeProperty< Size >& domain_sizes) const {
107  // get the neighbors of clique. If there are fewer than 3 neighbors,
108  // there is nothing to do
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;
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,
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);
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;
204  domain_sizes);
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;
216  domain_sizes);
218  }
219  }
220  }
221  }
222  }
223 
224  /// convert a whole connected component into a binary join tree
226  CliqueGraph& JT,
228  NodeId from,
229  const NodeProperty< Size >& domain_sizes,
230  NodeProperty< bool >& mark) const {
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]) {
239  }
240  }
241 
242  // convert the current node
244  }
245 
246  /// computes the binary join tree
248  const CliqueGraph& JT,
249  const NodeProperty< Size >& domain_sizes,
250  const NodeSet& specified_roots) {
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
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])
269  "several roots have been specified for a given "
270  "connected component (last : "
271  << root << ")");
272 
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;
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__)
292 
293  // binJT is now a binary join tree, so we can return it
294  return binJT;
295  }
296 
297 } /* namespace gum */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:669