aGrUM  0.14.2
cliqueGraph.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2005 by Christophe GONZALES and Pierre-Henri WUILLEMIN *
3  * {prenom.nom}_at_lip6.fr *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the *
17  * Free Software Foundation, Inc., *
18  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19  ***************************************************************************/
26 #include <sstream>
27 
28 #ifdef GUM_NO_INLINE
30 #endif // GUM_NOINLINE
31 
32 #ifndef DOXYGEN_SHOULD_SKIP_THIS
33 
34 namespace gum {
35 
36  /* ===========================================================================
37  */
38  /* ===========================================================================
39  */
40  /* === IMPLEMENTATION OF GUM_CLIQUE_GRAPH ===
41  */
42  /* ===========================================================================
43  */
44  /* ===========================================================================
45  */
46 
48 
49  CliqueGraph::CliqueGraph(Size nodes_size,
50  bool nodes_resize_policy,
51  Size edges_size,
52  bool edges_resize_policy) :
53  NodeGraphPart(nodes_size, nodes_resize_policy),
54  UndiGraph(nodes_size, nodes_resize_policy, edges_size, edges_resize_policy) {
55  // for debugging purposes
56  GUM_CONSTRUCTOR(CliqueGraph);
57  }
58 
60 
61  CliqueGraph::CliqueGraph(const CliqueGraph& from) :
62  NodeGraphPart(from), // needed because NodeGraphPart is a virtual inherited
63  UndiGraph(from), // class (see C++ FAQ Lite #25.12 for details)
64  __cliques(from.__cliques), __separators(from.__separators) {
65  // for debugging purposes
66  GUM_CONS_CPY(CliqueGraph);
67  }
68 
70 
72  // for debugging purposes
73  GUM_DESTRUCTOR(CliqueGraph);
74  }
75 
77 
78  std::vector< NodeId > CliqueGraph::containerPath(const NodeId node1,
79  const NodeId node2) const {
80  // get a path from a __clique containing node1 to a __clique containing
81  // node2
82  std::vector< NodeId > path =
83  undirectedPath(container(node1), container(node2));
84 
85  // it may happen that the path contains several nodes containing node1 and
86  // node2. Hence we shall remove the superfluous nodes
87  while ((path.size() >= 2) && (clique(path[path.size() - 2]).contains(node2)))
88  path.pop_back();
89 
90  while ((path.size() >= 2) && (clique(path[1]).contains(node1)))
91  path.erase(path.begin());
92 
93  return path;
94  }
95 
99 
100  void CliqueGraph::addToClique(const NodeId clique_id, const NodeId node_id) {
101  // get the current clique set
102  NodeSet& clique = __cliques[clique_id];
103 
104  // check if the node already exists, in which case throw an exception
105  if (clique.contains(node_id)) {
106  GUM_ERROR(DuplicateElement,
107  "the clique set already contains the node " << node_id);
108  }
109 
110  clique.insert(node_id);
111 
112  // update the __separators adjacent to clique 'id'
113  for (const auto nei : neighbours(clique_id))
114  if (__cliques[nei].contains(node_id))
115  __separators[Edge(nei, clique_id)].insert(node_id);
116  }
117 
119 
120  void CliqueGraph::eraseFromClique(const NodeId clique_id, const NodeId node_id) {
121  // get the current __clique set
122  NodeSet& clique = __cliques[clique_id];
123 
124  // check if the node does not exist, in which case throw an exception
125  if (clique.contains(node_id)) {
126  clique.erase(node_id);
127 
128  // update the __separators adjacent to __clique 'id'
129  for (const auto nei : neighbours(clique_id)) {
130  Edge edge(nei, clique_id);
131 
132  if (__separators[edge].contains(node_id))
133  __separators[edge].erase(node_id);
134  }
135  }
136  }
137 
139 
141  const NodeId clique,
142  const NodeId from,
143  CliqueGraph::__RunningIntersect& infos_DFS) const {
144  // check that no node in the clique belongs to the set of nodes belonging to
145  // other connected components of the cliqueGraph
146  const NodeSet& nodes_clique = __cliques[clique];
147 
148  for (const auto node : nodes_clique)
149  if (infos_DFS.nodes_other_components.contains(node)) return false;
150 
151  // update the structure that keeps track of the cliques that still require
152  // chains to access some of their nodes
153  for (const auto node : nodes_clique)
154  if (!infos_DFS.nodes_DFS_forbidden.contains(node))
155  infos_DFS.cliques_DFS_chain[clique].erase(node);
156 
157  // if the clique had already been visited, no need to see its neighbours
158  // or to update the list of visited nodes
159  if (infos_DFS.visited_cliques.contains(clique)) return true;
160 
161  // update the list of nodes visited during the DFS
162  for (const auto node : nodes_clique)
163  if (!infos_DFS.nodes_DFS_seen.contains(node))
164  infos_DFS.nodes_DFS_seen.insert(node);
165 
166  // update the fact that the clique has been visited
167  infos_DFS.visited_cliques.insert(clique);
168 
169  // check the neighbours that are different from "from" and that have not
170  // been visited yet
171 
172  for (const auto otherID : neighbours(clique))
173  if (otherID != from) {
174  // update the list of forbidden nodes in the DFS, i.e., the nodes that
175  // belong to the clique but not to the separator
176  const Edge edge(otherID, clique);
177  const NodeSet& from_separ = __separators[edge];
178 
179  for (const auto node : nodes_clique) {
180  if (!from_separ.contains(node))
181  infos_DFS.nodes_DFS_forbidden.insert(node);
182  }
183 
184  // check the neighbour
185  if (!__runningIntersectionDFS(otherID, clique, infos_DFS)) return false;
186 
187  // remove from the forbidden list the nodes that belong to clique
188  for (const auto node : nodes_clique)
189  infos_DFS.nodes_DFS_forbidden.erase(node);
190 
191  // check again the structure that keeps track of the cliques that still
192  // require chains to access some of their nodes: the chain may be
193  // the neighbour we just encountered
194  for (const auto node : nodes_clique) {
195  if (!infos_DFS.nodes_DFS_forbidden.contains(node))
196  infos_DFS.cliques_DFS_chain[clique].erase(node);
197  }
198  }
199 
200  // when a node is terminal, i.e., it has at most one neighbour, add its
201  // nodes
202  // to the nodes forbidden by the DFS. It will prevent non adjacent extremal
203  // cliques to contain the same node while this one does not belong to any
204  // separator
205  if (neighbours(clique).size() <= 1)
206  for (const auto node : nodes_clique)
207  if (!infos_DFS.nodes_DFS_forbidden.contains(node))
208  infos_DFS.nodes_DFS_forbidden.insert(node);
209 
210  // here everything was OK. A priori, the running intersection property holds
211  return true;
212  }
213 
215 
217  // create a RunningIntersect structure and initialize it
218  __RunningIntersect infos_DFS;
219  infos_DFS.cliques_DFS_chain = __cliques;
220 
221  // while there exist unvisited cliques, perform a DFS on them
222  for (const auto DFSnode : nodes())
223  if (!infos_DFS.visited_cliques.contains(DFSnode)) {
224  // no nodes are forbidden a priori in the DFS
225  infos_DFS.nodes_DFS_forbidden.clear();
226 
227  // no node has already been seen in the DFS
228  infos_DFS.nodes_DFS_seen.clear();
229 
230  // here iter_DFS points on a clique that has not been visited yet
231  // visit the clique graph from this clique
232  if (!__runningIntersectionDFS(DFSnode, DFSnode, infos_DFS)) return false;
233 
234  // the nodes that were seen during the DFS belong to a connected
235  // component
236  // that is different from the connected components of the subsequent DFS
237  for (const auto node : infos_DFS.nodes_DFS_seen)
238  if (!infos_DFS.nodes_other_components.contains(node))
239  infos_DFS.nodes_other_components.insert(node);
240  }
241 
242  // check that no clique requires an additional chain to guarantee the
243  // running intersection property
244  for (const auto& elt : infos_DFS.cliques_DFS_chain)
245  if (!elt.second.empty()) return false;
246 
247  return true;
248  }
249 
251 
252  bool CliqueGraph::operator==(const CliqueGraph& from) const {
253  // check if both graphical structures are identical
254  if (UndiGraph::operator!=(from)) return false;
255 
256  // check if the __cliques are identical
257  for (const auto& elt : __cliques)
258  if (elt.second != from.__cliques[elt.first]) return false;
259 
260  return true;
261  }
262 
263  const std::string CliqueGraph::toString() const {
264  std::stringstream stream;
265  stream << "list of nodes:" << std::endl;
266 
267  for (const auto node : nodes()) {
268  stream << " -- node: " << node << std::endl << " clique:";
269 
270  for (const auto cliq : clique(node))
271  stream << " " << cliq;
272 
273  stream << std::endl;
274  }
275 
276  stream << "\n\nlist of edges:\n";
277 
278  for (const auto edge : edges())
279  stream << edge << " ";
280 
281  return stream.str();
282  }
283 
284  const std::string expandCliqueContent(const NodeSet& clique) {
285  std::stringstream stream;
286  bool first = true;
287 
288  for (auto node : clique) {
289  if (!first) { stream << "-"; }
290 
291  stream << node;
292  first = false;
293  }
294 
295  return stream.str();
296  }
297  const std::string expandClique(const NodeId n, const NodeSet& clique) {
298  std::stringstream stream;
299  stream << '(' << n << ") " << expandCliqueContent(clique);
300  return stream.str();
301  }
302  const std::string expandSeparator(const NodeId n1,
303  const NodeSet& clique1,
304  const NodeId n2,
305  const NodeSet& clique2) {
306  std::stringstream stream;
307  stream << expandClique(n1, clique1) << "^" << expandClique(n2, clique2);
308  return stream.str();
309  }
310 
311  const std::string CliqueGraph::toDot() const {
312  std::stringstream stream;
313  stream << "graph {" << std::endl;
314 
315  // cliques as nodes
316  for (auto node : nodes()) {
317  std::string nom = '"' + expandClique(node, clique(node)) + '"';
318  stream << " " << nom << " [label=\"" << expandCliqueContent(clique(node))
319  << "\",fillcolor =\"burlywood\",style=\"filled\"];" << std::endl;
320  }
321 
322  stream << std::endl;
323 
324  // separator as nodes
325  for (auto edge : edges()) {
326  stream << " \""
327  << expandSeparator(edge.first(),
328  clique(edge.first()),
329  edge.second(),
330  clique(edge.second()))
331  << "\" [label=\"" << expandCliqueContent(separator(edge)) << "\""
332  << ",shape=box,fillcolor=\"palegreen\",style=\"filled\",fontsize=8,"
333  "width=0,height=0];"
334  << std::endl;
335  }
336 
337  stream << std::endl;
338 
339  // edges now as c1--sep--c2
340  for (auto edge : edges())
341  stream << " \"" << expandClique(edge.first(), clique(edge.first()))
342  << "\"--\""
343  << expandSeparator(edge.first(),
344  clique(edge.first()),
345  edge.second(),
346  clique(edge.second()))
347  << "\"--\"" << expandClique(edge.second(), clique(edge.second()))
348  << "\";" << std::endl;
349 
350  stream << "}" << std::endl;
351 
352  return stream.str();
353  }
354 
356 
357  std::ostream& operator<<(std::ostream& stream, const CliqueGraph& graph) {
358  stream << graph.toString();
359  return stream;
360  }
361 
362 } /* namespace gum */
363 
364 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
bool operator==(const CliqueGraph &from) const
checks whether two clique graphs are equal
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
virtual const std::string toDot() const
friendly displays the content of the CliqueGraph in DOT format
void erase(const Key &k)
Erases an element from the set.
Definition: set_tpl.h:653
gum is the global namespace for all aGrUM entities
Definition: agrum.h:25
bool __runningIntersectionDFS(const NodeId clique, const NodeId from, __RunningIntersect &infos_DFS) const
function used for the computation of the running intersection property
virtual ~CliqueGraph()
destructor
std::ostream & operator<<(std::ostream &output, const BayesNet< GUM_SCALAR > &bn)
Prints map&#39;s DAG in output using the Graphviz-dot format.
Definition: BayesNet_tpl.h:583
virtual void addToClique(const NodeId clique_id, const NodeId node_id)
changes the set of nodes included into a given clique and returns the new set
virtual const std::string toString() const
friendly displays the content of the CliqueGraph
std::vector< NodeId > containerPath(const NodeId node1, const NodeId node2) const
returns a path from a clique containing node1 to a clique containing node2
CliqueGraph(Size nodes_size=HashTableConst::default_size, bool nodes_resize_policy=true, Size edges_size=HashTableConst::default_size, bool edges_resize_policy=true)
basic constructor: creates an empty clique graph
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition: types.h:45
virtual void eraseFromClique(const NodeId clique_id, const NodeId node_id)
remove a node from a clique
Basic class for all graphs of cliques (join trees, etc)
Size NodeId
Type for node ids.
Definition: graphElements.h:97
void insert(const Key &k)
Inserts a new element into the set.
Definition: set_tpl.h:610
bool hasRunningIntersection() const
indicates whether the running intersection property holds
#define GUM_ERROR(type, msg)
Definition: exceptions.h:52
inline source of basic clique graphs