aGrUM 2.3.2
a C++ library for (probabilistic) graphical models
MarkovBlanket.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
50
51#ifdef GUM_NO_INLINE
53#endif // GU%_NO_INLINE
54
55namespace gum {
56 MarkovBlanket::MarkovBlanket(const DAGmodel& m, NodeId id, int level) : _model_(m), _node_(id) {
57 if (level < 1) GUM_ERROR(InvalidArgument, "Argument level(=" << level << ") must be >0.")
58
59 NodeSet done;
61 done.insert(_node_);
62
63 while (level > 1) {
64 level--;
65 auto todo = _mb_.nodes().asNodeSet() - done;
66 bool anythingnew = false;
67 for (NodeId n: todo) {
68 done.insert(n);
69 if (_buildMarkovBlanket_(n)) anythingnew = true;
70 }
71 if (!anythingnew) break;
72 }
73
74 // we add now some arcs that are between the nodes in _mb_ but are not part of
75 // the last ones.
76 // For instance, an arc between a parent and a parent of children
77 for (const auto node: _mb_.nodes()) {
78 for (const auto child: _model_.children(node)) {
79 if (_mb_.existsNode(child) && !_mb_.existsArc(Arc(node, child))) {
80 _mb_.addArc(node, child);
81 _specialArcs_.insert(Arc(node, child));
82 }
83 }
84 }
85 }
86
87 MarkovBlanket::MarkovBlanket(const DAGmodel& m, const std::string& name, int level) :
88 MarkovBlanket(m, m.idFromName(name), level) {}
89
91
93 bool change = false;
94 if (!_model_.nodes().exists(node))
95 GUM_ERROR(InvalidArgument, "Node " << node << " does not exist.")
96
97 if (!_mb_.nodes().exists(node)) {
98 _mb_.addNodeWithId(node);
99 change = true;
100 }
101
102 for (const auto& parent: _model_.parents(node)) {
103 if (!_mb_.nodes().exists(parent)) {
104 _mb_.addNodeWithId(parent);
105 change = true;
106 }
107 _mb_.addArc(parent, node);
108 }
109
110 for (const auto& child: _model_.children(node)) {
111 if (!_mb_.nodes().exists(child)) {
112 _mb_.addNodeWithId(child);
113 change = true;
114 }
115 _mb_.addArc(node, child);
116 for (const auto& opar: _model_.parents(child)) {
117 if (opar != node) {
118 if (!_mb_.nodes().exists(opar)) {
119 _mb_.addNodeWithId(opar);
120 change = true;
121 }
122 _mb_.addArc(opar, child);
123 }
124 }
125 }
126
127 return change;
128 }
129
130 bool MarkovBlanket::hasSameStructure(const DAGmodel& other) const {
131 if (size() != other.size()) return false;
132
133 if (sizeArcs() != other.sizeArcs()) return false;
134
135 for (const auto& nid: nodes()) {
136 try {
137 other.idFromName(_model_.variable(nid).name());
138 } catch (NotFound const&) { return false; }
139 }
140
141 for (const auto& arc: arcs()) {
142 if (!other.arcs().exists(Arc(other.idFromName(_model_.variable(arc.tail()).name()),
143 other.idFromName(_model_.variable(arc.head()).name()))))
144 return false;
145 }
146
147 return true;
148 }
149
150 std::string MarkovBlanket::toDot() const {
151 std::stringstream output;
152 std::stringstream nodeStream;
153 std::stringstream arcStream;
154 List< NodeId > treatedNodes;
155 output << "digraph \""
156 << "no_name\" {" << std::endl;
157 nodeStream << "node [shape = ellipse];" << std::endl;
158 std::string tab = " ";
159
160 for (const auto node: _mb_.nodes()) {
161 nodeStream << tab << node << "[label=\"" << _model_.variable(node).name() << "\"";
162 if (node == _node_) { nodeStream << ", color=red"; }
163 nodeStream << "];" << std::endl;
164
165 for (const auto chi: _mb_.children(node)) {
166 arcStream << tab << node << " -> " << chi;
167 if (_specialArcs_.exists(Arc(node, chi))) { arcStream << " [color=grey]"; }
168 arcStream << ";" << std::endl;
169 }
170 }
171
172 output << nodeStream.str() << std::endl << arcStream.str() << std::endl << "}" << std::endl;
173
174 return output.str();
175 }
176} // namespace gum
Class building the markovBlanket from a DAGmodel and a node name.
Inline implementation of the class building the essential Graph from a DAGmodel.
The base class for all directed edges.
Virtual base class for PGMs using a DAG.
Definition DAGmodel.h:64
const ArcSet & arcs() const
return true if the arc tail->head exists in the DAGmodel
virtual Size size() const final
Returns the number of variables in this Directed Graphical Model.
Size sizeArcs() const
Returns the number of arcs in this Directed Graphical Model.
virtual NodeId idFromName(const std::string &name) const =0
Getter by name.
Exception: at least one argument passed to a function is not what was expected.
Generic doubly linked lists.
Definition list.h:379
const DAGmodel & _model_
MarkovBlanket(const DAGmodel &m, NodeId n, int level=1)
std::string toDot() const
bool hasSameStructure(const DAGmodel &other) const
const NodeId _node_
Size sizeArcs() const
wrapping DAG::sizeArcs()
const ArcSet & arcs() const
wrapping DAG::arcs()
bool _buildMarkovBlanket_(const NodeId id)
const NodeGraphPart & nodes() const
wrapping DAG::nodes()
Size size() const
wrapping DAG::size()
Exception : the element we looked for cannot be found.
bool exists(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:533
#define GUM_ERROR(type, msg)
Definition exceptions.h:72
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