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