49#ifndef DOXYGEN_SHOULD_SKIP_THIS
55constexpr auto LBP_DEFAULT_MAXITER = 100;
56constexpr auto LBP_DEFAULT_EPSILON = 1e-8;
57constexpr auto LBP_DEFAULT_MIN_EPSILON_RATE = 1e-10;
58constexpr auto LBP_DEFAULT_PERIOD_SIZE = 1;
59constexpr auto LBP_DEFAULT_VERBOSITY =
false;
68 template < GUM_Numeric GUM_SCALAR >
72 GUM_CONSTRUCTOR(LoopyBeliefPropagation)
74 this->setEpsilon(LBP_DEFAULT_EPSILON);
75 this->setMinEpsilonRate(LBP_DEFAULT_MIN_EPSILON_RATE);
76 this->setMaxIter(LBP_DEFAULT_MAXITER);
77 this->setVerbosity(LBP_DEFAULT_VERBOSITY);
78 this->setPeriodSize(LBP_DEFAULT_PERIOD_SIZE);
84 template < GUM_Numeric GUM_SCALAR >
85 LoopyBeliefPropagation< GUM_SCALAR >::~LoopyBeliefPropagation() {
86 GUM_DESTRUCTOR(LoopyBeliefPropagation)
89 template < GUM_Numeric GUM_SCALAR >
90 void LoopyBeliefPropagation< GUM_SCALAR >::_init_messages_() {
92 for (
const auto& tail: this->BN().nodes()) {
93 Tensor< GUM_SCALAR > p;
94 p.add(this->BN().variable(tail));
95 p.fill(
static_cast< GUM_SCALAR
>(1));
97 for (
const auto& head: this->BN().children(tail)) {
98 _messages_.insert(
Arc(head, tail), p);
99 _messages_.insert(
Arc(tail, head), p);
104 template < GUM_Numeric GUM_SCALAR >
105 void LoopyBeliefPropagation< GUM_SCALAR >::updateOutdatedStructure_() {
109 template < GUM_Numeric GUM_SCALAR >
110 Tensor< GUM_SCALAR > LoopyBeliefPropagation< GUM_SCALAR >::_computeProdPi_(NodeId X) {
111 const auto& varX = this->BN().variable(X);
113 auto piX = this->BN().cpt(X);
114 for (
const auto& U: this->BN().parents(X)) {
115 piX *= _messages_[
Arc(U, X)];
117 piX = piX.sumIn({&varX});
122 template < GUM_Numeric GUM_SCALAR >
123 Tensor< GUM_SCALAR > LoopyBeliefPropagation< GUM_SCALAR >::_computeProdPi_(NodeId X,
125 const auto& varX = this->BN().variable(X);
126 const auto& varExcept = this->BN().variable(except);
127 auto piXexcept = this->BN().cpt(X);
128 for (
const auto& U: this->BN().parents(X)) {
129 if (U != except) { piXexcept *= _messages_[
Arc(U, X)]; }
131 piXexcept = piXexcept.sumIn({&varX, &varExcept});
135 template < GUM_Numeric GUM_SCALAR >
136 Tensor< GUM_SCALAR > LoopyBeliefPropagation< GUM_SCALAR >::_computeProdLambda_(NodeId X) {
137 Tensor< GUM_SCALAR > lamX;
138 if (this->hasEvidence(X)) {
139 lamX = *(this->evidence()[X]);
141 lamX.add(this->BN().variable(X));
144 for (
const auto& Y: this->BN().children(X)) {
145 lamX *= _messages_[
Arc(Y, X)];
151 template < GUM_Numeric GUM_SCALAR >
152 Tensor< GUM_SCALAR > LoopyBeliefPropagation< GUM_SCALAR >::_computeProdLambda_(NodeId X,
154 Tensor< GUM_SCALAR > lamXexcept;
155 if (this->hasEvidence(X)) {
156 lamXexcept = *this->evidence()[X];
158 lamXexcept.add(this->BN().variable(X));
161 for (
const auto& Y: this->BN().children(X)) {
162 if (Y != except) { lamXexcept *= _messages_[
Arc(Y, X)]; }
168 template < GUM_Numeric GUM_SCALAR >
169 GUM_SCALAR LoopyBeliefPropagation< GUM_SCALAR >::_updateNodeMessage_(NodeId X) {
170 auto piX = _computeProdPi_(X);
171 auto lamX = _computeProdLambda_(X);
177 for (
const auto& U: this->BN().parents(X)) {
178 auto newLambda = (_computeProdPi_(X, U) * lamX).sumIn({&this->BN().variable(U)});
179 newLambda.normalize();
180 auto ekl =
static_cast< GUM_SCALAR
>(0);
182 ekl = _messages_[
Arc(X, U)].KL(newLambda);
186 ekl = std::numeric_limits< GUM_SCALAR >::infinity();
192 _messages_.set(
Arc(X, U), newLambda);
196 for (
const auto& Y: this->BN().children(X)) {
197 auto newPi = (piX * _computeProdLambda_(X, Y));
201 ekl = _messages_[
Arc(X, Y)].KL(newPi);
205 ekl = std::numeric_limits< GUM_SCALAR >::infinity();
211 _messages_.set(
Arc(X, Y), newPi);
217 template < GUM_Numeric GUM_SCALAR >
218 void LoopyBeliefPropagation< GUM_SCALAR >::_initStats_() {
221 _updateNodeMessage_(node);
226 template < GUM_Numeric GUM_SCALAR >
227 void LoopyBeliefPropagation< GUM_SCALAR >::makeInference_() {
229 this->initApproximationScheme();
231 std::vector< NodeId > shuffleIds;
232 for (
const auto& node: this->BN().nodes())
233 shuffleIds.push_back(node);
235 auto engine = std::default_random_engine{};
237 GUM_SCALAR error = 0.0;
239 std::shuffle(std::begin(shuffleIds), std::end(shuffleIds), engine);
240 this->updateApproximationScheme();
241 for (
const auto& node: shuffleIds) {
242 GUM_SCALAR e = _updateNodeMessage_(node);
243 if (e > error) error = e;
245 }
while (this->continueApproximationScheme(error));
249 template < GUM_Numeric GUM_SCALAR >
250 const Tensor< GUM_SCALAR >& LoopyBeliefPropagation< GUM_SCALAR >::posterior_(NodeId
id) {
251 auto p = _computeProdPi_(
id) * _computeProdLambda_(
id);
253 _posteriors_.set(
id, p);
255 return _posteriors_[id];
258 template < GUM_Numeric GUM_SCALAR >
259 void LoopyBeliefPropagation< GUM_SCALAR >::onStateChanged_() {}
261 template < GUM_Numeric GUM_SCALAR >
262 void LoopyBeliefPropagation< GUM_SCALAR >::onEvidenceAdded_(
const NodeId
id,
263 bool isHardEvidence) {}
265 template < GUM_Numeric GUM_SCALAR >
266 void LoopyBeliefPropagation< GUM_SCALAR >::onEvidenceErased_(
const NodeId
id,
267 bool isHardEvidence) {}
269 template < GUM_Numeric GUM_SCALAR >
270 void LoopyBeliefPropagation< GUM_SCALAR >::onAllEvidenceErased_(
bool contains_hard_evidence) {}
272 template < GUM_Numeric GUM_SCALAR >
273 void LoopyBeliefPropagation< GUM_SCALAR >::onEvidenceChanged_(
const NodeId
id,
274 bool hasChangedSoftHard) {}
276 template < GUM_Numeric GUM_SCALAR >
277 void LoopyBeliefPropagation< GUM_SCALAR >::onModelChanged_(
const GraphicalModel* bn) {}
279 template < GUM_Numeric GUM_SCALAR >
280 void LoopyBeliefPropagation< GUM_SCALAR >::updateOutdatedTensors_() {}
282 template < GUM_Numeric GUM_SCALAR >
283 void LoopyBeliefPropagation< GUM_SCALAR >::onMarginalTargetAdded_(
const NodeId
id) {}
285 template < GUM_Numeric GUM_SCALAR >
286 void LoopyBeliefPropagation< GUM_SCALAR >::onMarginalTargetErased_(
const NodeId
id) {}
288 template < GUM_Numeric GUM_SCALAR >
289 void LoopyBeliefPropagation< GUM_SCALAR >::onAllMarginalTargetsAdded_() {}
291 template < GUM_Numeric GUM_SCALAR >
292 void LoopyBeliefPropagation< GUM_SCALAR >::onAllMarginalTargetsErased_() {}
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.
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)
This file contains gibbs sampling (for BNs) class definitions.
Sequence< NodeId > topologicalOrder(const G &g)
Returns a topological ordering of the nodes of g (Kahn's algorithm).
gum is the global namespace for all aGrUM entities