aGrUM 2.3.2
a C++ library for (probabilistic) graphical models
DAG.cpp
Go to the documentation of this file.
1/****************************************************************************
2 * This file is part of the aGrUM/pyAgrum library. *
3 * *
4 * Copyright (c) 2005-2025 by *
5 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
6 * - Christophe GONZALES(_at_AMU) *
7 * *
8 * The aGrUM/pyAgrum library is free software; you can redistribute it *
9 * and/or modify it under the terms of either : *
10 * *
11 * - the GNU Lesser General Public License as published by *
12 * the Free Software Foundation, either version 3 of the License, *
13 * or (at your option) any later version, *
14 * - the MIT license (MIT), *
15 * - or both in dual license, as here. *
16 * *
17 * (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) *
18 * *
19 * This aGrUM/pyAgrum library is distributed in the hope that it will be *
20 * useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
21 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
25 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
26 * OTHER DEALINGS IN THE SOFTWARE. *
27 * *
28 * See LICENCES for more details. *
29 * *
30 * SPDX-FileCopyrightText: Copyright 2005-2025 *
31 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
32 * - Christophe GONZALES(_at_AMU) *
33 * SPDX-License-Identifier: LGPL-3.0-or-later OR MIT *
34 * *
35 * Contact : info_at_agrum_dot_org *
36 * homepage : http://agrum.gitlab.io *
37 * gitlab : https://gitlab.com/agrumery/agrum *
38 * *
39 ****************************************************************************/
40
41
49
50#ifdef GUM_NO_INLINE
52#endif // GU%_NO_INLINE
53
54namespace gum {
55
56 // diamond structure require to explicitly initialize NodeGraphPart
57 DAG::DAG(Size nodes_size, bool nodes_resize_policy, Size arcs_size, bool arcs_resize_policy) :
58 NodeGraphPart(nodes_size, nodes_resize_policy),
59 DiGraph(nodes_size, nodes_resize_policy, arcs_size, arcs_resize_policy) {
60 GUM_CONSTRUCTOR(DAG);
61 }
62
63 // diamond structure require to explicitly initialize NodeGraphPart
64 DAG::DAG(const DAG& g) : NodeGraphPart(g), DiGraph(g) { GUM_CONS_CPY(DAG); }
65
66 DAG::~DAG() { GUM_DESTRUCTOR(DAG); }
67
69 UndiGraph moralgraph;
70 moralgraph.populateNodes(*this);
71 // transform the arcs into edges
72 for (const auto& arc: arcs())
73 moralgraph.addEdge(arc.first(), arc.second());
74
75 // marry the parents
76 for (const auto node: nodes()) {
77 const auto& par = parents(node);
78
79 for (auto it1 = par.begin(); it1 != par.end(); ++it1) {
80 auto it2 = it1;
81
82 for (++it2; it2 != par.end(); ++it2) {
83 // will automatically check if this edge already exists
84 moralgraph.addEdge(*it1, *it2);
85 }
86 }
87 }
88 return moralgraph;
89 }
90
92 UndiGraph res;
93 NodeSet tmp{nodes};
94
95 // findings all nodes
96 while (!tmp.empty()) {
97 auto current = *(tmp.begin());
98 tmp.erase(current);
99
100 res.addNodeWithId(current);
101 for (auto next: parents(current))
102 if (!tmp.contains(next) && !res.exists(next)) tmp.insert(next);
103 }
104
105 // finding all edges and moralizing
106 for (auto current: res)
107 for (auto father: parents(current)) {
108 res.addEdge(current,
109 father); // addEdge does not complain if edge already exists
110 for (auto other_father: parents(current))
111 if (other_father != father) res.addEdge(father, other_father);
112 }
113
114 return res;
115 }
116
117 bool DAG::dSeparation(NodeId X, NodeId Y, const NodeSet& Z) const {
118 NodeSet cumul{Z};
119 cumul << X << Y;
120 auto g = moralizedAncestralGraph(cumul);
121 for (auto node: Z)
122 g.eraseNode(node);
123 return !g.hasUndirectedPath(X, Y);
124 }
125
126 bool DAG::dSeparation(const NodeSet& X, const NodeSet& Y, const NodeSet& Z) const {
127 if (!(X * Y).empty())
128 GUM_ERROR(InvalidArgument, "NodeSets " << X << ", " << Y << " should have no intersection")
129
130 NodeSet cumul{Z};
131 cumul += X;
132 cumul += Y;
133 auto g = moralizedAncestralGraph(cumul);
134 for (auto node: Z)
135 g.eraseNode(node);
136 auto cc = g.nodes2ConnectedComponent();
137
138 NodeSet Xcc, Ycc;
139 for (const auto node: X)
140 if (g.existsNode(node)) // it may be in Z too
141 if (!Xcc.exists(cc[node])) Xcc.insert(cc[node]);
142 for (const auto node: Y)
143 if (g.existsNode(node)) // it may be in Z too
144 if (!Ycc.exists(cc[node])) Ycc.insert(cc[node]);
145
146 return (Xcc * Ycc).empty();
147 }
148
149 // visit the nodes and add some of node from soids in minimal
151 const NodeSet& soids,
152 NodeSet& minimal,
153 NodeSet& alreadyVisitedUp,
154 NodeSet& alreadyVisitedDn) const {
155 if (alreadyVisitedUp.contains(node)) return;
156 alreadyVisitedUp << node;
157
158 if (soids.contains(node)) {
159 minimal << node;
160 } else {
161 for (auto fath: parents(node))
162 _minimalCondSetVisitUp_(fath, soids, minimal, alreadyVisitedUp, alreadyVisitedDn);
163 for (auto chil: children(node))
164 _minimalCondSetVisitDn_(chil, soids, minimal, alreadyVisitedUp, alreadyVisitedDn);
165 }
166 }
167
168 // visit the nodes and add some of node from soids in minimal
170 const NodeSet& soids,
171 NodeSet& minimal,
172 NodeSet& alreadyVisitedUp,
173 NodeSet& alreadyVisitedDn) const {
174 if (alreadyVisitedDn.contains(node)) return;
175 alreadyVisitedDn << node;
176
177 if (soids.contains(node)) {
178 minimal << node;
179 for (auto fath: parents(node))
180 _minimalCondSetVisitUp_(fath, soids, minimal, alreadyVisitedUp, alreadyVisitedDn);
181 } else {
182 for (auto chil: children(node))
183 _minimalCondSetVisitDn_(chil, soids, minimal, alreadyVisitedUp, alreadyVisitedDn);
184 }
185 }
186
187 NodeSet DAG::minimalCondSet(NodeId target, const NodeSet& soids) const {
188 if (soids.contains(target)) return NodeSet({target});
189
190 NodeSet res;
191 NodeSet alreadyVisitedUp;
192 NodeSet alreadyVisitedDn;
193 alreadyVisitedDn << target;
194 alreadyVisitedUp << target;
195
196 for (auto fath: parents(target))
197 _minimalCondSetVisitUp_(fath, soids, res, alreadyVisitedUp, alreadyVisitedDn);
198 for (auto chil: children(target))
199 _minimalCondSetVisitDn_(chil, soids, res, alreadyVisitedUp, alreadyVisitedDn);
200 return res;
201 }
202
203 NodeSet DAG::minimalCondSet(const NodeSet& targets, const NodeSet& soids) const {
204 NodeSet res;
205 for (auto node: targets) {
206 res += minimalCondSet(node, soids);
207 }
208 return res;
209 }
210} /* namespace gum */
Base classes for directed acyclic graphs.
Inline implementation of Base classes for directed acylic graphs.
const NodeSet & parents(NodeId id) const
returns the set of nodes with arc ingoing to a given node
NodeSet children(const NodeSet &ids) const
returns the set of children of a set of nodes
const ArcSet & arcs() const
returns the set of arcs stored within the ArcGraphPart
bool dSeparation(NodeId X, NodeId Y, const NodeSet &Z) const
check if node X and node Y are independent given nodes Z (in the sense of d-separation)
Definition DAG.cpp:117
void _minimalCondSetVisitUp_(NodeId node, const NodeSet &soids, NodeSet &minimal, NodeSet &alreadyVisitedUp, NodeSet &alreadyVisitedDn) const
Definition DAG.cpp:150
void _minimalCondSetVisitDn_(NodeId node, const NodeSet &soids, NodeSet &minimal, NodeSet &alreadyVisitedUp, NodeSet &alreadyVisitedDn) const
Definition DAG.cpp:169
UndiGraph moralGraph() const
build a UndiGraph by moralizing the dag
Definition DAG.cpp:68
NodeSet minimalCondSet(NodeId target, const NodeSet &soids) const
Definition DAG.cpp:187
DAG(Size nodes_size=HashTableConst::default_size, bool nodes_resize_policy=true, Size arcs_size=HashTableConst::default_size, bool arcs_resize_policy=true)
default constructor
Definition DAG.cpp:57
virtual ~DAG()
destructor
Definition DAG.cpp:66
UndiGraph moralizedAncestralGraph(const NodeSet &nodes) const
build a UndiGraph by moralizing the Ancestral Graph of a set of Nodes
Definition DAG.cpp:91
DiGraph(Size nodes_size=HashTableConst::default_size, bool nodes_resize_policy=true, Size arcs_size=HashTableConst::default_size, bool arcs_resize_policy=true)
default constructor
Definition diGraph.cpp:69
Exception: at least one argument passed to a function is not what was expected.
Class for node sets in graph.
void populateNodes(const NodeGraphPart &s)
populateNodes clears *this and fills it with the same nodes as "s"
const NodeGraphPart & nodes() const
return *this as a NodeGraphPart
bool exists(const NodeId id) const
alias for existsNode
bool empty() const
alias for emptyNodes
virtual void addNodeWithId(const NodeId id)
try to insert a node with the given id
iterator begin() const
The usual unsafe begin iterator to parse the set.
Definition set_tpl.h:438
bool exists(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:533
bool contains(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:497
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:539
bool empty() const noexcept
Indicates whether the set is the empty set.
Definition set_tpl.h:642
void erase(const Key &k)
Erases an element from the set.
Definition set_tpl.h:582
Base class for undirected graphs.
Definition undiGraph.h:128
void addEdge(NodeId first, NodeId second) override
insert a new edge into the undirected graph
#define GUM_ERROR(type, msg)
Definition exceptions.h:72
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition types.h:74
Size NodeId
Type for node ids.
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
gum is the global namespace for all aGrUM entities
Definition agrum.h:46