aGrUM 2.3.2
a C++ library for (probabilistic) graphical models
loopyBeliefPropagation_tpl.h
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#pragma once
41
42
47#ifndef DOXYGEN_SHOULD_SKIP_THIS
48
49# include <algorithm>
50# include <sstream>
51# include <string>
52
53constexpr auto LBP_DEFAULT_MAXITER = 100;
54constexpr auto LBP_DEFAULT_EPSILON = 1e-8;
55constexpr auto LBP_DEFAULT_MIN_EPSILON_RATE = 1e-10;
56constexpr auto LBP_DEFAULT_PERIOD_SIZE = 1;
57constexpr auto LBP_DEFAULT_VERBOSITY = false;
58
59
60// to ease parsing for IDE
62
63namespace gum {
64
66 template < typename GUM_SCALAR >
68 ApproximateInference< GUM_SCALAR >(bn) {
69 // for debugging purposes
70 GUM_CONSTRUCTOR(LoopyBeliefPropagation)
71
72 this->setEpsilon(LBP_DEFAULT_EPSILON);
73 this->setMinEpsilonRate(LBP_DEFAULT_MIN_EPSILON_RATE);
74 this->setMaxIter(LBP_DEFAULT_MAXITER);
75 this->setVerbosity(LBP_DEFAULT_VERBOSITY);
76 this->setPeriodSize(LBP_DEFAULT_PERIOD_SIZE);
77
78 _init_messages_();
79 }
80
82 template < typename GUM_SCALAR >
83 INLINE LoopyBeliefPropagation< GUM_SCALAR >::~LoopyBeliefPropagation() {
84 GUM_DESTRUCTOR(LoopyBeliefPropagation)
85 }
86
87 template < typename GUM_SCALAR >
88 void LoopyBeliefPropagation< GUM_SCALAR >::_init_messages_() {
89 _messages_.clear();
90 for (const auto& tail: this->BN().nodes()) {
91 Tensor< GUM_SCALAR > p;
92 p.add(this->BN().variable(tail));
93 p.fill(static_cast< GUM_SCALAR >(1));
94
95 for (const auto& head: this->BN().children(tail)) {
96 _messages_.insert(Arc(head, tail), p);
97 _messages_.insert(Arc(tail, head), p);
98 }
99 }
100 }
101
102 template < typename GUM_SCALAR >
103 void LoopyBeliefPropagation< GUM_SCALAR >::updateOutdatedStructure_() {
104 _init_messages_();
105 }
106
107 template < typename GUM_SCALAR >
108 Tensor< GUM_SCALAR > LoopyBeliefPropagation< GUM_SCALAR >::_computeProdPi_(NodeId X) {
109 const auto& varX = this->BN().variable(X);
110
111 auto piX = this->BN().cpt(X);
112 for (const auto& U: this->BN().parents(X)) {
113 piX *= _messages_[Arc(U, X)];
114 }
115 piX = piX.sumIn({&varX});
116
117 return piX;
118 }
119
120 template < typename GUM_SCALAR >
121 Tensor< GUM_SCALAR > LoopyBeliefPropagation< GUM_SCALAR >::_computeProdPi_(NodeId X,
122 NodeId except) {
123 const auto& varX = this->BN().variable(X);
124 const auto& varExcept = this->BN().variable(except);
125 auto piXexcept = this->BN().cpt(X);
126 for (const auto& U: this->BN().parents(X)) {
127 if (U != except) { piXexcept *= _messages_[Arc(U, X)]; }
128 }
129 piXexcept = piXexcept.sumIn({&varX, &varExcept});
130 return piXexcept;
131 }
132
133 template < typename GUM_SCALAR >
134 Tensor< GUM_SCALAR > LoopyBeliefPropagation< GUM_SCALAR >::_computeProdLambda_(NodeId X) {
135 Tensor< GUM_SCALAR > lamX;
136 if (this->hasEvidence(X)) {
137 lamX = *(this->evidence()[X]);
138 } else {
139 lamX.add(this->BN().variable(X));
140 lamX.fill(1);
141 }
142 for (const auto& Y: this->BN().children(X)) {
143 lamX *= _messages_[Arc(Y, X)];
144 }
145
146 return lamX;
147 }
148
149 template < typename GUM_SCALAR >
150 Tensor< GUM_SCALAR > LoopyBeliefPropagation< GUM_SCALAR >::_computeProdLambda_(NodeId X,
151 NodeId except) {
152 Tensor< GUM_SCALAR > lamXexcept;
153 if (this->hasEvidence(X)) { //
154 lamXexcept = *this->evidence()[X];
155 } else {
156 lamXexcept.add(this->BN().variable(X));
157 lamXexcept.fill(1);
158 }
159 for (const auto& Y: this->BN().children(X)) {
160 if (Y != except) { lamXexcept *= _messages_[Arc(Y, X)]; }
161 }
162
163 return lamXexcept;
164 }
165
166 template < typename GUM_SCALAR >
167 GUM_SCALAR LoopyBeliefPropagation< GUM_SCALAR >::_updateNodeMessage_(NodeId X) {
168 auto piX = _computeProdPi_(X);
169 auto lamX = _computeProdLambda_(X);
170
171 GUM_SCALAR KL = 0;
172 Arc argKL(0, 0);
173
174 // update lambda_par (for arc U->x)
175 for (const auto& U: this->BN().parents(X)) {
176 auto newLambda = (_computeProdPi_(X, U) * lamX).sumIn({&this->BN().variable(U)});
177 newLambda.normalize();
178 auto ekl = static_cast< GUM_SCALAR >(0);
179 try {
180 ekl = _messages_[Arc(X, U)].KL(newLambda);
181 } catch (InvalidArgument const&) {
182 GUM_ERROR(InvalidArgument, "Not compatible pi during computation")
183 } catch (FatalError const&) { // 0 misplaced
184 ekl = std::numeric_limits< GUM_SCALAR >::infinity();
185 }
186 if (ekl > KL) {
187 KL = ekl;
188 argKL = Arc(X, U);
189 }
190 _messages_.set(Arc(X, U), newLambda);
191 }
192
193 // update pi_child (for arc x->child)
194 for (const auto& Y: this->BN().children(X)) {
195 auto newPi = (piX * _computeProdLambda_(X, Y));
196 newPi.normalize();
197 GUM_SCALAR ekl = KL;
198 try {
199 ekl = _messages_[Arc(X, Y)].KL(newPi);
200 } catch (InvalidArgument const&) {
201 GUM_ERROR(InvalidArgument, "Not compatible pi during computation")
202 } catch (FatalError const&) { // 0 misplaced
203 ekl = std::numeric_limits< GUM_SCALAR >::infinity();
204 }
205 if (ekl > KL) {
206 KL = ekl;
207 argKL = Arc(X, Y);
208 }
209 _messages_.set(Arc(X, Y), newPi);
210 }
211
212 return KL;
213 }
214
215 template < typename GUM_SCALAR >
216 INLINE void LoopyBeliefPropagation< GUM_SCALAR >::_initStats_() {
217 _init_messages_();
218 for (const auto& node: this->BN().topologicalOrder()) {
219 _updateNodeMessage_(node);
220 }
221 }
222
224 template < typename GUM_SCALAR >
225 void LoopyBeliefPropagation< GUM_SCALAR >::makeInference_() {
226 _initStats_();
227 this->initApproximationScheme();
228
229 std::vector< NodeId > shuffleIds;
230 for (const auto& node: this->BN().nodes())
231 shuffleIds.push_back(node);
232
233 auto engine = std::default_random_engine{};
234
235 GUM_SCALAR error = 0.0;
236 do {
237 std::shuffle(std::begin(shuffleIds), std::end(shuffleIds), engine);
238 this->updateApproximationScheme();
239 for (const auto& node: shuffleIds) {
240 GUM_SCALAR e = _updateNodeMessage_(node);
241 if (e > error) error = e;
242 }
243 } while (this->continueApproximationScheme(error));
244 }
245
247 template < typename GUM_SCALAR >
248 INLINE const Tensor< GUM_SCALAR >& LoopyBeliefPropagation< GUM_SCALAR >::posterior_(NodeId id) {
249 auto p = _computeProdPi_(id) * _computeProdLambda_(id);
250 p.normalize();
251 _posteriors_.set(id, p);
252
253 return _posteriors_[id];
254 }
255} /* namespace gum */
256
257#endif // DOXYGEN_SHOULD_SKIP_THIS
KL is the base class for KL computation betweens 2 BNs.
Exception : fatal (unknown ?) error.
Class representing the minimal interface for Bayesian network with no numerical data.
Definition IBayesNet.h:75
Exception: at least one argument passed to a function is not what was expected.
LoopyBeliefPropagation(const IBayesNet< GUM_SCALAR > *bn)
Default constructor.
#define GUM_ERROR(type, msg)
Definition exceptions.h:72
This file contains gibbs sampling (for BNs) class definitions.
gum is the global namespace for all aGrUM entities
Definition agrum.h:46