57#ifndef DOXYGEN_SHOULD_SKIP_THIS
70 bool nodes_resize_policy,
72 bool edges_resize_policy) :
74 UndiGraph(nodes_size, nodes_resize_policy, edges_size, edges_resize_policy) {
76 GUM_CONSTRUCTOR(CliqueGraph)
81 CliqueGraph::CliqueGraph(
const CliqueGraph& from) :
84 _cliques_(from._cliques_), _separators_(from._separators_) {
85 GUM_CONS_CPY(CliqueGraph)
88 CliqueGraph::CliqueGraph(CliqueGraph&& from) :
89 NodeGraphPart(
std::move(from)),
90 UndiGraph(
std::move(from)), _cliques_(
std::move(from._cliques_)),
91 _separators_(
std::move(from._separators_)) {
92 GUM_CONS_MOV(CliqueGraph)
97 CliqueGraph::~CliqueGraph() {
98 GUM_DESTRUCTOR(CliqueGraph)
104 std::vector< NodeId > CliqueGraph::containerPath(
const NodeId node1,
const NodeId node2)
const {
108 if (!opt)
GUM_ERROR(
NotFound,
"no path between cliques containing the given nodes")
109 std::vector< NodeId > path =
std::move(*opt);
113 while ((path.size() >= 2) && (clique(path[path.size() - 2]).contains(node2)))
116 while ((path.size() >= 2) && (clique(path[1]).contains(node1)))
117 path.erase(path.begin());
126 void CliqueGraph::addToClique(const NodeId clique_id, const NodeId node_id) {
128 NodeSet& clique = _cliques_[clique_id];
131 if (clique.contains(node_id)) {
132 GUM_ERROR(DuplicateElement,
"the clique set already contains the node " << node_id)
138 for (
const auto nei: neighbours(clique_id))
139 if (_cliques_[nei].
contains(node_id)) _separators_[
Edge(nei, clique_id)].insert(node_id);
144 void CliqueGraph::eraseFromClique(
const NodeId clique_id,
const NodeId node_id) {
146 NodeSet& clique = _cliques_[clique_id];
149 if (clique.contains(node_id)) {
150 clique.erase(node_id);
153 for (
const auto nei: neighbours(clique_id)) {
154 Edge edge(nei, clique_id);
156 if (_separators_[edge].
contains(node_id)) _separators_[edge].erase(node_id);
163 bool CliqueGraph::_runningIntersectionDFS_(
const NodeId clique,
165 CliqueGraph::_RunningIntersect_& infos_DFS)
const {
168 const NodeSet& nodes_clique = _cliques_[clique];
170 for (
const auto node: nodes_clique)
171 if (infos_DFS.nodes_other_components.contains(node))
return false;
175 for (
const auto node: nodes_clique)
176 if (!infos_DFS.nodes_DFS_forbidden.contains(node))
177 infos_DFS.cliques_DFS_chain[clique].
erase(node);
181 if (infos_DFS.visited_cliques.contains(clique))
return true;
184 for (
const auto node: nodes_clique)
185 if (!infos_DFS.nodes_DFS_seen.contains(node)) infos_DFS.nodes_DFS_seen.insert(node);
188 infos_DFS.visited_cliques.insert(clique);
193 for (
const auto otherID: neighbours(clique))
194 if (otherID != from) {
197 const Edge edge(otherID, clique);
198 const NodeSet& from_separ = _separators_[edge];
200 for (
const auto node: nodes_clique) {
201 if (!from_separ.contains(node)) infos_DFS.nodes_DFS_forbidden.insert(node);
205 if (!_runningIntersectionDFS_(otherID, clique, infos_DFS))
return false;
208 for (
const auto node: nodes_clique)
209 infos_DFS.nodes_DFS_forbidden.
erase(node);
214 for (
const auto node: nodes_clique) {
215 if (!infos_DFS.nodes_DFS_forbidden.contains(node))
216 infos_DFS.cliques_DFS_chain[clique].erase(node);
225 if (neighbours(clique).size() <= 1)
226 for (
const auto node: nodes_clique)
227 if (!infos_DFS.nodes_DFS_forbidden.contains(node))
228 infos_DFS.nodes_DFS_forbidden.insert(node);
236 bool CliqueGraph::hasRunningIntersection()
const {
238 _RunningIntersect_ infos_DFS;
239 infos_DFS.cliques_DFS_chain = _cliques_;
242 for (
const auto DFSnode: nodes())
243 if (!infos_DFS.visited_cliques.contains(DFSnode)) {
245 infos_DFS.nodes_DFS_forbidden.clear();
248 infos_DFS.nodes_DFS_seen.clear();
252 if (!_runningIntersectionDFS_(DFSnode, DFSnode, infos_DFS))
return false;
257 for (
const auto node: infos_DFS.nodes_DFS_seen)
258 if (!infos_DFS.nodes_other_components.contains(node))
259 infos_DFS.nodes_other_components.insert(node);
264 for (
const auto& [node, nodes]: infos_DFS.cliques_DFS_chain)
265 if (!nodes.empty())
return false;
272 bool CliqueGraph::operator==(
const CliqueGraph& from)
const {
274 if (!UndiGraph::operator==(from))
return false;
277 for (
const auto& [node, nodes]: _cliques_)
278 if (nodes != from._cliques_[node])
return false;
283 std::string CliqueGraph::toString()
const {
284 std::stringstream stream;
285 stream <<
"list of nodes:\n";
287 for (
const auto node: nodes()) {
288 stream << std::format(
" -- node: {}\n clique:", node);
290 for (
const auto cliq: clique(node))
291 stream <<
" " << cliq;
296 stream <<
"\n\nlist of edges:\n";
298 for (
const auto& edge: edges())
299 stream << edge <<
" ";
304 std::string expandCliqueContent(
const NodeSet& clique, std::string_view delim =
"-") {
308 std::vector< NodeId > sorted(clique.begin(), clique.end());
309 std::sort(sorted.begin(), sorted.end());
310 for (
auto node: sorted) {
311 if (!first) { result += delim; }
312 result += std::to_string(node);
319 std::string expandCliqueTooltip(
const NodeSet& clique) {
320 return std::format(
"size : {}\\n{}", clique.size(), expandCliqueContent(clique,
"\\n"));
323 std::string expandClique(
const NodeId n,
const NodeSet& clique) {
324 return std::format(
"({}) {}", n, expandCliqueContent(clique));
327 std::string expandSeparator(
const NodeId n1,
328 const NodeSet& clique1,
330 const NodeSet& clique2) {
331 return std::format(
"{}^{}", expandClique(n1, clique1), expandClique(n2, clique2));
334 std::string CliqueGraph::toDot()
const {
335 std::stringstream stream;
336 stream <<
"graph {" <<
'\n';
337 stream << R
"( node [style="filled", fontcolor="black"];)" << '\n';
340 for (
auto node: nodes()) {
341 std::string nom =
'"' + expandClique(node, clique(node)) +
'"';
342 stream <<
" " << nom <<
" [label=\"" << expandCliqueContent(clique(node)) <<
"\",tooltip=\""
343 << expandCliqueTooltip(clique(node)) << R
"(",fillcolor ="burlywood"];)" << '\n';
349 for (
const auto& edge: edges()) {
351 << expandSeparator(edge.first(),
352 clique(edge.first()),
354 clique(edge.second()))
355 <<
"\" [label=\"" << expandCliqueContent(separator(edge)) <<
"\",tooltip=\""
356 << expandCliqueTooltip(separator(edge))
357 << R
"(",shape=box,fillcolor="palegreen",fontsize=8,width=0,height=0];)" << '\n';
363 for (
const auto& edge: edges())
364 stream <<
" \"" << expandClique(edge.first(), clique(edge.first())) <<
"\"--\""
365 << expandSeparator(edge.first(),
366 clique(edge.first()),
368 clique(edge.second()))
369 <<
"\"--\"" << expandClique(edge.second(), clique(edge.second())) <<
"\";" <<
'\n';
371 stream <<
"}" <<
'\n';
376 std::string CliqueGraph::mapToDot(
double scaleClique,
379 std::string_view colorClique,
380 std::string_view colorSep)
const {
381 std::stringstream stream;
382 stream <<
"graph {" <<
'\n';
383 stream <<
" bgcolor=transparent;" <<
'\n';
384 stream <<
" layout=neato;" <<
'\n' <<
'\n';
385 stream <<
" node [shape=point,style=filled, fillcolor =" << colorClique <<
"];" <<
'\n';
386 stream <<
" edge [len=" << lenEdge <<
"];" <<
'\n' <<
'\n';
389 for (
auto node: nodes()) {
390 const auto& clik = clique(node);
391 stream <<
" " << node <<
" [tooltip=\"" << expandCliqueTooltip(clik)
392 <<
"\", width=" << scaleClique *
double(clik.size()) <<
"];" <<
'\n';
395 stream <<
" node [shape=square,style=filled, fillcolor =" << colorSep <<
",label=\"\"];"
400 for (
const auto& edge: edges()) {
402 const auto sep = clique(edge.first()) * clique(edge.second());
403 stream <<
" \"" << edge.first() <<
"~" << edge.second() <<
"\" [tooltip=\""
404 << expandCliqueTooltip(sep) <<
"\""
405 <<
", width=" << scaleSep *
double(sep.size()) <<
"];" <<
'\n';
407 stream <<
" \"" << edge.first() <<
"\"--\"" << edge.first() <<
"~" << edge.second()
408 <<
"\"--\"" << edge.second() <<
"\";" <<
'\n'
412 stream <<
"}" <<
'\n';
419 std::ostream&
operator<<(std::ostream& stream,
const CliqueGraph& graph) {
420 stream << graph.toString();
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
Class for node sets in graph.
Exception : the element we looked for cannot be found.
void insert(const Key &k)
Inserts a new element into the set.
void erase(const Key &k)
Erases an element from the set.
Base class for undirected graphs.
Basic class for all graphs of cliques (join trees, etc).
inline source of basic clique graphs
#define GUM_ERROR(type, msg)
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
bool contains(std::string_view s, std::string_view needle)
true if needle in s
std::optional< std::vector< NodeId > > undirectedPath(const G &g, NodeId n1, NodeId n2)
Shortest undirected path from n1 to n2 (BFS).
gum is the global namespace for all aGrUM entities
std::ostream & operator<<(std::ostream &out, const TiXmlNode &base)