aGrUM  0.16.0
cliqueGraph.cpp
Go to the documentation of this file.
1 
29 #include <sstream>
30 
31 #ifdef GUM_NO_INLINE
33 #endif // GUM_NOINLINE
34 
35 #ifndef DOXYGEN_SHOULD_SKIP_THIS
36 
37 namespace gum {
38 
39  /* ===========================================================================
40  */
41  /* ===========================================================================
42  */
43  /* === IMPLEMENTATION OF GUM_CLIQUE_GRAPH ===
44  */
45  /* ===========================================================================
46  */
47  /* ===========================================================================
48  */
49 
51 
52  CliqueGraph::CliqueGraph(Size nodes_size,
53  bool nodes_resize_policy,
54  Size edges_size,
55  bool edges_resize_policy) :
56  NodeGraphPart(nodes_size, nodes_resize_policy),
57  UndiGraph(nodes_size, nodes_resize_policy, edges_size, edges_resize_policy) {
58  // for debugging purposes
59  GUM_CONSTRUCTOR(CliqueGraph);
60  }
61 
63 
64  CliqueGraph::CliqueGraph(const CliqueGraph& from) :
65  NodeGraphPart(from), // needed because NodeGraphPart is a virtual inherited
66  UndiGraph(from), // class (see C++ FAQ Lite #25.12 for details)
67  __cliques(from.__cliques), __separators(from.__separators) {
68  // for debugging purposes
69  GUM_CONS_CPY(CliqueGraph);
70  }
71 
73 
75  // for debugging purposes
76  GUM_DESTRUCTOR(CliqueGraph);
77  }
78 
80 
81  std::vector< NodeId > CliqueGraph::containerPath(const NodeId node1,
82  const NodeId node2) const {
83  // get a path from a __clique containing node1 to a __clique containing
84  // node2
85  std::vector< NodeId > path =
86  undirectedPath(container(node1), container(node2));
87 
88  // it may happen that the path contains several nodes containing node1 and
89  // node2. Hence we shall remove the superfluous nodes
90  while ((path.size() >= 2) && (clique(path[path.size() - 2]).contains(node2)))
91  path.pop_back();
92 
93  while ((path.size() >= 2) && (clique(path[1]).contains(node1)))
94  path.erase(path.begin());
95 
96  return path;
97  }
98 
102 
103  void CliqueGraph::addToClique(const NodeId clique_id, const NodeId node_id) {
104  // get the current clique set
105  NodeSet& clique = __cliques[clique_id];
106 
107  // check if the node already exists, in which case throw an exception
108  if (clique.contains(node_id)) {
109  GUM_ERROR(DuplicateElement,
110  "the clique set already contains the node " << node_id);
111  }
112 
113  clique.insert(node_id);
114 
115  // update the __separators adjacent to clique 'id'
116  for (const auto nei : neighbours(clique_id))
117  if (__cliques[nei].contains(node_id))
118  __separators[Edge(nei, clique_id)].insert(node_id);
119  }
120 
122 
123  void CliqueGraph::eraseFromClique(const NodeId clique_id, const NodeId node_id) {
124  // get the current __clique set
125  NodeSet& clique = __cliques[clique_id];
126 
127  // check if the node does not exist, in which case throw an exception
128  if (clique.contains(node_id)) {
129  clique.erase(node_id);
130 
131  // update the __separators adjacent to __clique 'id'
132  for (const auto nei : neighbours(clique_id)) {
133  Edge edge(nei, clique_id);
134 
135  if (__separators[edge].contains(node_id))
136  __separators[edge].erase(node_id);
137  }
138  }
139  }
140 
142 
144  const NodeId clique,
145  const NodeId from,
146  CliqueGraph::__RunningIntersect& infos_DFS) const {
147  // check that no node in the clique belongs to the set of nodes belonging to
148  // other connected components of the cliqueGraph
149  const NodeSet& nodes_clique = __cliques[clique];
150 
151  for (const auto node : nodes_clique)
152  if (infos_DFS.nodes_other_components.contains(node)) return false;
153 
154  // update the structure that keeps track of the cliques that still require
155  // chains to access some of their nodes
156  for (const auto node : nodes_clique)
157  if (!infos_DFS.nodes_DFS_forbidden.contains(node))
158  infos_DFS.cliques_DFS_chain[clique].erase(node);
159 
160  // if the clique had already been visited, no need to see its neighbours
161  // or to update the list of visited nodes
162  if (infos_DFS.visited_cliques.contains(clique)) return true;
163 
164  // update the list of nodes visited during the DFS
165  for (const auto node : nodes_clique)
166  if (!infos_DFS.nodes_DFS_seen.contains(node))
167  infos_DFS.nodes_DFS_seen.insert(node);
168 
169  // update the fact that the clique has been visited
170  infos_DFS.visited_cliques.insert(clique);
171 
172  // check the neighbours that are different from "from" and that have not
173  // been visited yet
174 
175  for (const auto otherID : neighbours(clique))
176  if (otherID != from) {
177  // update the list of forbidden nodes in the DFS, i.e., the nodes that
178  // belong to the clique but not to the separator
179  const Edge edge(otherID, clique);
180  const NodeSet& from_separ = __separators[edge];
181 
182  for (const auto node : nodes_clique) {
183  if (!from_separ.contains(node))
184  infos_DFS.nodes_DFS_forbidden.insert(node);
185  }
186 
187  // check the neighbour
188  if (!__runningIntersectionDFS(otherID, clique, infos_DFS)) return false;
189 
190  // remove from the forbidden list the nodes that belong to clique
191  for (const auto node : nodes_clique)
192  infos_DFS.nodes_DFS_forbidden.erase(node);
193 
194  // check again the structure that keeps track of the cliques that still
195  // require chains to access some of their nodes: the chain may be
196  // the neighbour we just encountered
197  for (const auto node : nodes_clique) {
198  if (!infos_DFS.nodes_DFS_forbidden.contains(node))
199  infos_DFS.cliques_DFS_chain[clique].erase(node);
200  }
201  }
202 
203  // when a node is terminal, i.e., it has at most one neighbour, add its
204  // nodes
205  // to the nodes forbidden by the DFS. It will prevent non adjacent extremal
206  // cliques to contain the same node while this one does not belong to any
207  // separator
208  if (neighbours(clique).size() <= 1)
209  for (const auto node : nodes_clique)
210  if (!infos_DFS.nodes_DFS_forbidden.contains(node))
211  infos_DFS.nodes_DFS_forbidden.insert(node);
212 
213  // here everything was OK. A priori, the running intersection property holds
214  return true;
215  }
216 
218 
220  // create a RunningIntersect structure and initialize it
221  __RunningIntersect infos_DFS;
222  infos_DFS.cliques_DFS_chain = __cliques;
223 
224  // while there exist unvisited cliques, perform a DFS on them
225  for (const auto DFSnode : nodes())
226  if (!infos_DFS.visited_cliques.contains(DFSnode)) {
227  // no nodes are forbidden a priori in the DFS
228  infos_DFS.nodes_DFS_forbidden.clear();
229 
230  // no node has already been seen in the DFS
231  infos_DFS.nodes_DFS_seen.clear();
232 
233  // here iter_DFS points on a clique that has not been visited yet
234  // visit the clique graph from this clique
235  if (!__runningIntersectionDFS(DFSnode, DFSnode, infos_DFS)) return false;
236 
237  // the nodes that were seen during the DFS belong to a connected
238  // component
239  // that is different from the connected components of the subsequent DFS
240  for (const auto node : infos_DFS.nodes_DFS_seen)
241  if (!infos_DFS.nodes_other_components.contains(node))
242  infos_DFS.nodes_other_components.insert(node);
243  }
244 
245  // check that no clique requires an additional chain to guarantee the
246  // running intersection property
247  for (const auto& elt : infos_DFS.cliques_DFS_chain)
248  if (!elt.second.empty()) return false;
249 
250  return true;
251  }
252 
254 
255  bool CliqueGraph::operator==(const CliqueGraph& from) const {
256  // check if both graphical structures are identical
257  if (UndiGraph::operator!=(from)) return false;
258 
259  // check if the __cliques are identical
260  for (const auto& elt : __cliques)
261  if (elt.second != from.__cliques[elt.first]) return false;
262 
263  return true;
264  }
265 
266  const std::string CliqueGraph::toString() const {
267  std::stringstream stream;
268  stream << "list of nodes:" << std::endl;
269 
270  for (const auto node : nodes()) {
271  stream << " -- node: " << node << std::endl << " clique:";
272 
273  for (const auto cliq : clique(node))
274  stream << " " << cliq;
275 
276  stream << std::endl;
277  }
278 
279  stream << "\n\nlist of edges:\n";
280 
281  for (const auto edge : edges())
282  stream << edge << " ";
283 
284  return stream.str();
285  }
286 
287  const std::string expandCliqueContent(const NodeSet& clique) {
288  std::stringstream stream;
289  bool first = true;
290 
291  for (auto node : clique) {
292  if (!first) { stream << "-"; }
293 
294  stream << node;
295  first = false;
296  }
297 
298  return stream.str();
299  }
300  const std::string expandClique(const NodeId n, const NodeSet& clique) {
301  std::stringstream stream;
302  stream << '(' << n << ") " << expandCliqueContent(clique);
303  return stream.str();
304  }
305  const std::string expandSeparator(const NodeId n1,
306  const NodeSet& clique1,
307  const NodeId n2,
308  const NodeSet& clique2) {
309  std::stringstream stream;
310  stream << expandClique(n1, clique1) << "^" << expandClique(n2, clique2);
311  return stream.str();
312  }
313 
314  const std::string CliqueGraph::toDot() const {
315  std::stringstream stream;
316  stream << "graph {" << std::endl;
317 
318  // cliques as nodes
319  for (auto node : nodes()) {
320  std::string nom = '"' + expandClique(node, clique(node)) + '"';
321  stream << " " << nom << " [label=\"" << expandCliqueContent(clique(node))
322  << "\",fillcolor =\"burlywood\",style=\"filled\"];" << std::endl;
323  }
324 
325  stream << std::endl;
326 
327  // separator as nodes
328  for (auto edge : edges()) {
329  stream << " \""
330  << expandSeparator(edge.first(),
331  clique(edge.first()),
332  edge.second(),
333  clique(edge.second()))
334  << "\" [label=\"" << expandCliqueContent(separator(edge)) << "\""
335  << ",shape=box,fillcolor=\"palegreen\",style=\"filled\",fontsize=8,"
336  "width=0,height=0];"
337  << std::endl;
338  }
339 
340  stream << std::endl;
341 
342  // edges now as c1--sep--c2
343  for (auto edge : edges())
344  stream << " \"" << expandClique(edge.first(), clique(edge.first()))
345  << "\"--\""
346  << expandSeparator(edge.first(),
347  clique(edge.first()),
348  edge.second(),
349  clique(edge.second()))
350  << "\"--\"" << expandClique(edge.second(), clique(edge.second()))
351  << "\";" << std::endl;
352 
353  stream << "}" << std::endl;
354 
355  return stream.str();
356  }
357 
359 
360  std::ostream& operator<<(std::ostream& stream, const CliqueGraph& graph) {
361  stream << graph.toString();
362  return stream;
363  }
364 
365 } /* namespace gum */
366 
367 #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:656
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
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:605
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:48
virtual void eraseFromClique(const NodeId clique_id, const NodeId node_id)
remove a node from a clique
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
Size NodeId
Type for node ids.
Definition: graphElements.h:98
void insert(const Key &k)
Inserts a new element into the set.
Definition: set_tpl.h:613
bool hasRunningIntersection() const
indicates whether the running intersection property holds
#define GUM_ERROR(type, msg)
Definition: exceptions.h:55
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.