aGrUM 3.1.1
a C++ library for (probabilistic) graphical models
CNLoopyPropagation_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-2026 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-2026 *
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#pragma once
42
43
45
46namespace gum::credal {
47
48 template < GUM_Numeric GUM_SCALAR >
50 const std::string spath(path);
51 std::string path_name = spath.substr(0, spath.size() - 4);
52 path_name = path_name + ".res";
53
54 std::ofstream res(path_name.c_str(), std::ios::out | std::ios::trunc);
55
56 if (!res.good()) {
58 "CNLoopyPropagation<GUM_SCALAR>::saveInference(std::"
59 "string & path) : could not open file : "
60 + path_name)
61 }
62
63
64 if (std::string ext = spath.substr(spath.size() - 3, spath.size());
65 std::strcmp(ext.c_str(), "evi") == 0) {
66 std::ifstream evi(spath.c_str(), std::ios::in);
67 std::string ligne;
68
69 if (!evi.good()) {
71 "CNLoopyPropagation<GUM_SCALAR>::saveInference(std::"
72 "string & path) : could not open file : "
73 + ext)
74 }
75
76 while (evi.good()) {
77 getline(evi, ligne);
78 res << ligne << "\n";
79 }
80
81 evi.close();
82 }
83
84 res << "[RESULTATS]"
85 << "\n";
86
87 for (auto node: _bnet_->nodes()) {
88 // calcul distri posteriori
89 GUM_SCALAR msg_p_min = 1.0;
90 GUM_SCALAR msg_p_max = 0.0;
91
92 // cas evidence, calcul immediat
93 if (_infE_::evidence_.exists(node)) {
94 if (_infE_::evidence_[node][1] == 0.) {
95 msg_p_min = 0.;
96 } else if (_infE_::evidence_[node][1] == 1.) {
97 msg_p_min = 1.;
98 }
99
100 msg_p_max = msg_p_min;
101 }
102 // sinon depuis node P et node L
103 else {
104 GUM_SCALAR min = NodesP_min_[node];
105 GUM_SCALAR max;
106
107 if (NodesP_max_.exists(node)) {
108 max = NodesP_max_[node];
109 } else {
110 max = min;
111 }
112
113 GUM_SCALAR lmin = NodesL_min_[node];
114 GUM_SCALAR lmax;
115
116 if (NodesL_max_.exists(node)) {
117 lmax = NodesL_max_[node];
118 } else {
119 lmax = lmin;
120 }
121
122 // cas limites sur min
123 if (min == INF_ && lmin == 0.) {
124 std::cout << "proba ERR (negatif) : pi = inf, l = 0" << std::endl;
125 }
126
127 if (lmin == INF_) { // cas infini
128 msg_p_min = GUM_SCALAR(1.);
129 } else if (min == 0. || lmin == 0.) {
130 msg_p_min = GUM_SCALAR(0.);
131 } else {
132 msg_p_min = GUM_SCALAR(1. / (1. + ((1. / min - 1.) * 1. / lmin)));
133 }
134
135 // cas limites sur max
136 if (max == INF_ && lmax == 0.) {
137 std::cout << "proba ERR (negatif) : pi = inf, l = 0" << std::endl;
138 }
139
140 if (lmax == INF_) { // cas infini
141 msg_p_max = GUM_SCALAR(1.);
142 } else if (max == 0. || lmax == 0.) {
143 msg_p_max = GUM_SCALAR(0.);
144 } else {
145 msg_p_max = GUM_SCALAR(1. / (1. + ((1. / max - 1.) * 1. / lmax)));
146 }
147 }
148
149 if (msg_p_min != msg_p_min && msg_p_max == msg_p_max) { msg_p_min = msg_p_max; }
150
151 if (msg_p_max != msg_p_max && msg_p_min == msg_p_min) { msg_p_max = msg_p_min; }
152
153 if (msg_p_max != msg_p_max && msg_p_min != msg_p_min) {
154 std::cout << std::endl;
155 std::cout << "pas de proba calculable (verifier observations)" << std::endl;
156 }
157
158 res << "P(" << _bnet_->variable(node).name() << " | e) = ";
159
160 if (_infE_::evidence_.exists(node)) {
161 res << "(observe)" << std::endl;
162 } else {
163 res << std::endl;
164 }
165
166 res << "\t\t" << _bnet_->variable(node).label(0) << " [ " << (GUM_SCALAR)1. - msg_p_max;
167
168 if (msg_p_min != msg_p_max) {
169 res << ", " << (GUM_SCALAR)1. - msg_p_min << " ] | ";
170 } else {
171 res << " ] | ";
172 }
173
174 res << _bnet_->variable(node).label(1) << " [ " << msg_p_min;
175
176 if (msg_p_min != msg_p_max) {
177 res << ", " << msg_p_max << " ]" << std::endl;
178 } else {
179 res << " ]" << std::endl;
180 }
181 } // end of : for each node
182
183 res.close();
184 }
185
191
195 template < GUM_Numeric GUM_SCALAR >
197 GUM_SCALAR& msg_l_max,
198 std::vector< GUM_SCALAR >& lx,
199 GUM_SCALAR& num_min,
200 GUM_SCALAR& num_max,
201 GUM_SCALAR& den_min,
202 GUM_SCALAR& den_max) {
203 GUM_SCALAR num_min_tmp = 1.;
204 GUM_SCALAR den_min_tmp = 1.;
205 GUM_SCALAR num_max_tmp = 1.;
206 GUM_SCALAR den_max_tmp = 1.;
207
208 GUM_SCALAR res_min = 1.0;
209 GUM_SCALAR res_max = 0.0;
210
211 auto lsize = lx.size();
212
213 for (decltype(lsize) i = 0; i < lsize; i++) {
214 bool non_defini_min = false;
215 bool non_defini_max = false;
216
217 if (lx[i] == INF_) {
218 num_min_tmp = num_min;
219 den_min_tmp = den_max;
220 num_max_tmp = num_max;
221 den_max_tmp = den_min;
222 } else if (lx[i] == (GUM_SCALAR)1.) {
223 num_min_tmp = GUM_SCALAR(1.);
224 den_min_tmp = GUM_SCALAR(1.);
225 num_max_tmp = GUM_SCALAR(1.);
226 den_max_tmp = GUM_SCALAR(1.);
227 } else if (lx[i] > (GUM_SCALAR)1.) {
228 GUM_SCALAR li = GUM_SCALAR(1.) / (lx[i] - GUM_SCALAR(1.));
229 num_min_tmp = num_min + li;
230 den_min_tmp = den_max + li;
231 num_max_tmp = num_max + li;
232 den_max_tmp = den_min + li;
233 } else if (lx[i] < (GUM_SCALAR)1.) {
234 GUM_SCALAR li = GUM_SCALAR(1.) / (lx[i] - GUM_SCALAR(1.));
235 num_min_tmp = num_max + li;
236 den_min_tmp = den_min + li;
237 num_max_tmp = num_min + li;
238 den_max_tmp = den_max + li;
239 }
240
241 if (den_min_tmp == 0. && num_min_tmp == 0.) {
242 non_defini_min = true;
243 } else if (den_min_tmp == 0. && num_min_tmp != 0.) {
244 res_min = INF_;
245 } else if (den_min_tmp != INF_ || num_min_tmp != INF_) {
246 res_min = num_min_tmp / den_min_tmp;
247 }
248
249 if (den_max_tmp == 0. && num_max_tmp == 0.) {
250 non_defini_max = true;
251 } else if (den_max_tmp == 0. && num_max_tmp != 0.) {
252 res_max = INF_;
253 } else if (den_max_tmp != INF_ || num_max_tmp != INF_) {
254 res_max = num_max_tmp / den_max_tmp;
255 }
256
257 if (non_defini_max && non_defini_min) {
258 std::cout << "undefined msg" << std::endl;
259 continue;
260 } else if (non_defini_min && !non_defini_max) {
261 res_min = res_max;
262 } else if (non_defini_max && !non_defini_min) {
263 res_max = res_min;
264 }
265
266 if (res_min < 0.) { res_min = 0.; }
267
268 if (res_max < 0.) { res_max = 0.; }
269
270 if (msg_l_min == msg_l_max && msg_l_min == -2.) {
271 msg_l_min = res_min;
272 msg_l_max = res_max;
273 }
274
275 if (res_max > msg_l_max) { msg_l_max = res_max; }
276
277 if (res_min < msg_l_min) { msg_l_min = res_min; }
278
279 } // end of : for each lx
280 }
281
285 template < GUM_Numeric GUM_SCALAR >
287 std::vector< std::vector< GUM_SCALAR > >& combi_msg_p,
288 const NodeId& id,
289 GUM_SCALAR& msg_l_min,
290 GUM_SCALAR& msg_l_max,
291 std::vector< GUM_SCALAR >& lx,
292 const Idx& pos) {
293 GUM_SCALAR num_min = 0.;
294 GUM_SCALAR num_max = 0.;
295 GUM_SCALAR den_min = 0.;
296 GUM_SCALAR den_max = 0.;
297
298 auto taille = combi_msg_p.size();
299
300 std::vector< typename std::vector< GUM_SCALAR >::iterator > it(taille);
301
302 for (decltype(taille) i = 0; i < taille; i++) {
303 it[i] = combi_msg_p[i].begin();
304 }
305
306 Size pp = pos;
307
308 Size combi_den = 0;
309 Size combi_num = pp;
310
311 // marginalisation
312 while (it[taille - 1] != combi_msg_p[taille - 1].end()) {
313 GUM_SCALAR prod = 1.;
314
315 for (decltype(taille) k = 0; k < taille; k++) {
316 prod *= *it[k];
317 }
318
319 den_min += (_cn_->get_binaryCPT_min()[id][combi_den] * prod);
320 den_max += (_cn_->get_binaryCPT_max()[id][combi_den] * prod);
321
322 num_min += (_cn_->get_binaryCPT_min()[id][combi_num] * prod);
323 num_max += (_cn_->get_binaryCPT_max()[id][combi_num] * prod);
324
325 combi_den++;
326 combi_num++;
327
328 if (pp != 0) {
329 if (combi_den % pp == 0) {
330 combi_den += pp;
331 combi_num += pp;
332 }
333 }
334
335 // incrementation
336 ++it[0];
337
338 for (decltype(taille) i = 0; (i < taille - 1) && (it[i] == combi_msg_p[i].end()); ++i) {
339 it[i] = combi_msg_p[i].begin();
340 ++it[i + 1];
341 }
342 } // end of : marginalisation
343
344 compute_ext_(msg_l_min, msg_l_max, lx, num_min, num_max, den_min, den_max);
345 }
346
351 template < GUM_Numeric GUM_SCALAR >
353 std::vector< std::vector< GUM_SCALAR > >& combi_msg_p,
354 const NodeId& id,
355 GUM_SCALAR& msg_p_min,
356 GUM_SCALAR& msg_p_max) {
357 GUM_SCALAR min = 0.;
358 GUM_SCALAR max = 0.;
359
360 auto taille = combi_msg_p.size();
361
362 std::vector< typename std::vector< GUM_SCALAR >::iterator > it(taille);
363
364 for (decltype(taille) i = 0; i < taille; i++) {
365 it[i] = combi_msg_p[i].begin();
366 }
367
368 int combi = 0;
369 auto theEnd = combi_msg_p[taille - 1].end();
370
371 while (it[taille - 1] != theEnd) {
372 GUM_SCALAR prod = 1.;
373
374 for (decltype(taille) k = 0; k < taille; k++) {
375 prod *= *it[k];
376 }
377
378 min += (_cn_->get_binaryCPT_min()[id][combi] * prod);
379 max += (_cn_->get_binaryCPT_max()[id][combi] * prod);
380
381 combi++;
382
383 // incrementation
384 ++it[0];
385
386 for (decltype(taille) i = 0; (i < taille - 1) && (it[i] == combi_msg_p[i].end()); ++i) {
387 it[i] = combi_msg_p[i].begin();
388 ++it[i + 1];
389 }
390 }
391
392 if (min < msg_p_min) { msg_p_min = min; }
393
394 if (max > msg_p_max) { msg_p_max = max; }
395 }
396
400 template < GUM_Numeric GUM_SCALAR >
402 std::vector< std::vector< std::vector< GUM_SCALAR > > >& msgs_p,
403 const NodeId& id,
404 GUM_SCALAR& msg_p_min,
405 GUM_SCALAR& msg_p_max) {
406 auto taille = msgs_p.size();
407
408 // source node
409 if (taille == 0) {
410 msg_p_min = _cn_->get_binaryCPT_min()[id][0];
411 msg_p_max = _cn_->get_binaryCPT_max()[id][0];
412 return;
413 }
414
415 Size msgPerm = 1;
416 for (Size i = 0; i < taille; i++) {
417 msgPerm *= msgs_p[i].size();
418 }
419
420 // dispatch the messages among the threads and prepare the data
421 // they will process
423 ? this->getNumberOfThreads()
424 : 1; // no nested multithreading
425 nb_threads = std::min(msgPerm * taille / this->threadMinimalNbOps_, nb_threads);
426 if (nb_threads < 1) nb_threads = 1;
427
428 const auto ranges = gum::dispatchRangeToThreads(0, msgPerm, (unsigned int)(nb_threads));
429 const auto real_nb_threads = ranges.size();
430 std::vector< GUM_SCALAR > msg_pmin(real_nb_threads, msg_p_min);
431 std::vector< GUM_SCALAR > msg_pmax(real_nb_threads, msg_p_max);
432
433 // create the function to be executed by the threads
434 auto threadedExec
435 = [this, &msg_pmin, &msg_pmax, msgs_p, taille, ranges, id](const std::size_t this_thread,
436 const std::size_t nb_threads) {
437 std::vector< std::vector< GUM_SCALAR > > combi_msg_p(taille);
438
439 const auto& [first, second] = ranges[this_thread];
440 for (Idx j = first; j < second; ++j) {
441 // get jth msg :
442 auto jvalue = j;
443
444 for (Idx i = 0; i < taille; i++) {
445 if (msgs_p[i].size() == 2) {
446 combi_msg_p[i] = (jvalue & 1) ? msgs_p[i][1] : msgs_p[i][0];
447 jvalue /= 2;
448 } else {
449 combi_msg_p[i] = msgs_p[i][0];
450 }
451 }
452
453 compute_ext_(combi_msg_p, id, msg_pmin[this_thread], msg_pmax[this_thread]);
454 }
455 };
456
457 // launch the threads
458 ThreadExecutor::execute(real_nb_threads, threadedExec);
459
460 for (Idx j = 0; j < real_nb_threads; ++j) {
461 if (msg_p_min > msg_pmin[j]) { msg_p_min = msg_pmin[j]; }
462 if (msg_p_max < msg_pmax[j]) { msg_p_max = msg_pmax[j]; }
463 }
464 }
465
470 template < GUM_Numeric GUM_SCALAR >
472 std::vector< std::vector< std::vector< GUM_SCALAR > > >& msgs_p,
473 const NodeId& id,
474 GUM_SCALAR& real_msg_l_min,
475 GUM_SCALAR& real_msg_l_max,
476 std::vector< GUM_SCALAR >& lx,
477 const Idx& pos) {
478 GUM_SCALAR msg_l_min = real_msg_l_min;
479 GUM_SCALAR msg_l_max = real_msg_l_max;
480
481 auto taille = msgs_p.size();
482
483 // one parent node, the one receiving the message
484 if (taille == 0) {
485 GUM_SCALAR num_min = _cn_->get_binaryCPT_min()[id][1];
486 GUM_SCALAR num_max = _cn_->get_binaryCPT_max()[id][1];
487 GUM_SCALAR den_min = _cn_->get_binaryCPT_min()[id][0];
488 GUM_SCALAR den_max = _cn_->get_binaryCPT_max()[id][0];
489
490 compute_ext_(msg_l_min, msg_l_max, lx, num_min, num_max, den_min, den_max);
491
492 real_msg_l_min = msg_l_min;
493 real_msg_l_max = msg_l_max;
494 return;
495 }
496
497 Size msgPerm = 1;
498 for (Size i = 0; i < taille; i++) {
499 msgPerm *= msgs_p[i].size();
500 }
501
502 // dispatch the messages among the threads and prepare the data
503 // they will process
505 ? this->getNumberOfThreads()
506 : 1; // no nested multithreading
507 nb_threads = std::min(msgPerm * taille / this->threadMinimalNbOps_, nb_threads);
508 if (nb_threads < 1) nb_threads = 1;
509
510 const auto ranges = gum::dispatchRangeToThreads(0, msgPerm, (unsigned int)(nb_threads));
511 const auto real_nb_threads = ranges.size();
512 std::vector< GUM_SCALAR > msg_lmin(real_nb_threads, msg_l_min);
513 std::vector< GUM_SCALAR > msg_lmax(real_nb_threads, msg_l_max);
514
515 // create the function to be executed by the threads
516 auto threadedExec = [this, &msg_lmin, &msg_lmax, msgs_p, taille, ranges, id, &lx, pos](
517 const std::size_t this_thread,
518 const std::size_t nb_threads) {
519 std::vector< std::vector< GUM_SCALAR > > combi_msg_p(taille);
520
521 const auto& [first, second] = ranges[this_thread];
522 for (Idx j = first; j < second; ++j) {
523 // get jth msg :
524 auto jvalue = j;
525
526 for (Idx i = 0; i < taille; i++) {
527 if (msgs_p[i].size() == 2) {
528 combi_msg_p[i] = (jvalue & 1) ? msgs_p[i][1] : msgs_p[i][0];
529 jvalue /= 2;
530 } else {
531 combi_msg_p[i] = msgs_p[i][0];
532 }
533 }
534 compute_ext_(combi_msg_p, id, msg_lmin[this_thread], msg_lmax[this_thread], lx, pos);
535 }
536 };
537
538 // launch the threads
539 ThreadExecutor::execute(real_nb_threads, threadedExec);
540
541 for (Idx j = 0; j < real_nb_threads; ++j) {
542 if ((msg_l_min > msg_lmin[j] || msg_l_min == -2) && msg_lmin[j] > 0) {
543 msg_l_min = msg_lmin[j];
544 }
545 if ((msg_l_max < msg_lmax[j] || msg_l_max == -2) && msg_lmax[j] > 0) {
546 msg_l_max = msg_lmax[j];
547 }
548 }
549
550 real_msg_l_min = msg_l_min;
551 real_msg_l_max = msg_l_max;
552 }
553
554 template < GUM_Numeric GUM_SCALAR >
556 if (inference_up_to_date_) { return; }
557
558 initialize_();
559
561
562 switch (_inferenceType_) {
564
566
568 }
569
570 //_updateMarginals();
571 updateIndicatrices_(); // will call updateMarginals_()
572
574
576 }
577
578 template < GUM_Numeric GUM_SCALAR >
581
582 ArcsL_min_.clear();
583 ArcsL_max_.clear();
584 ArcsP_min_.clear();
585 ArcsP_max_.clear();
586 NodesL_min_.clear();
587 NodesL_max_.clear();
588 NodesP_min_.clear();
589 NodesP_max_.clear();
590
591 inference_up_to_date_ = false;
592
593 if (!msg_l_sent_.empty()) {
594 for (auto node: _bnet_->nodes()) {
595 delete msg_l_sent_[node];
596 }
597 }
598
599 msg_l_sent_.clear();
600 update_l_.clear();
601 update_p_.clear();
602
603 active_nodes_set_.clear();
605 }
606
607 template < GUM_Numeric GUM_SCALAR >
609 const DAG& graphe = _bnet_->dag();
610
611 // use const iterators with cbegin when available
612 for (auto node: _bnet_->topologicalOrder()) {
613 update_p_.set(node, false);
614 update_l_.set(node, false);
615 auto parents_ = new NodeSet();
616 msg_l_sent_.set(node, parents_);
617
618 // accelerer init pour evidences
619 if (_infE_::evidence_.exists(node)) {
620 if (_infE_::evidence_[node][1] != 0. && _infE_::evidence_[node][1] != 1.) {
621 GUM_ERROR(OperationNotAllowed, "CNLoopyPropagation can only handle HARD evidences")
622 }
623
624 active_nodes_set_.insert(node);
625 update_l_.set(node, true);
626 update_p_.set(node, true);
627
628 if (_infE_::evidence_[node][1] == (GUM_SCALAR)1.) {
629 NodesL_min_.set(node, INF_);
630 NodesP_min_.set(node, (GUM_SCALAR)1.);
631 } else if (_infE_::evidence_[node][1] == (GUM_SCALAR)0.) {
632 NodesL_min_.set(node, (GUM_SCALAR)0.);
633 NodesP_min_.set(node, (GUM_SCALAR)0.);
634 }
635
636 std::vector< GUM_SCALAR > marg(2);
637 marg[1] = NodesP_min_[node];
638 marg[0] = 1 - marg[1];
639
640 _infE_::oldMarginalMin_.set(node, marg);
641 _infE_::oldMarginalMax_.set(node, marg);
642
643 continue;
644 }
645
646 NodeSet par_ = graphe.parents(node);
647 NodeSet enf_ = graphe.children(node);
648
649 if (par_.empty()) {
650 active_nodes_set_.insert(node);
651 update_p_.set(node, true);
652 update_l_.set(node, true);
653 }
654
655 if (enf_.empty()) {
656 active_nodes_set_.insert(node);
657 update_p_.set(node, true);
658 update_l_.set(node, true);
659 }
660
665 const auto parents = &_bnet_->cpt(node).variablesSequence();
666
667 std::vector< std::vector< std::vector< GUM_SCALAR > > > msgs_p;
668 std::vector< std::vector< GUM_SCALAR > > msg_p;
669 std::vector< GUM_SCALAR > distri(2);
670
671 // +1 from start to avoid counting_ itself
672 // use const iterators when available with cbegin
673 for (auto jt = ++parents->begin(), theEnd = parents->end(); jt != theEnd; ++jt) {
674 // compute probability distribution to avoid doing it multiple times
675 // (at
676 // each combination of messages)
677 distri[1] = NodesP_min_[_bnet_->nodeId(**jt)];
678 distri[0] = (GUM_SCALAR)1. - distri[1];
679 msg_p.push_back(distri);
680
681 if (NodesP_max_.exists(_bnet_->nodeId(**jt))) {
682 distri[1] = NodesP_max_[_bnet_->nodeId(**jt)];
683 distri[0] = (GUM_SCALAR)1. - distri[1];
684 msg_p.push_back(distri);
685 }
686
687 msgs_p.push_back(msg_p);
688 msg_p.clear();
689 }
690
691 GUM_SCALAR msg_p_min = 1.;
692 GUM_SCALAR msg_p_max = 0.;
693
694 if (_cn_->currentNodeType(node) != CredalNet< GUM_SCALAR >::NodeType::Indic) {
695 enum_combi_(msgs_p, node, msg_p_min, msg_p_max);
696 }
697
698 if (msg_p_min <= (GUM_SCALAR)0.) { msg_p_min = (GUM_SCALAR)0.; }
699
700 if (msg_p_max <= (GUM_SCALAR)0.) { msg_p_max = (GUM_SCALAR)0.; }
701
702 NodesP_min_.set(node, msg_p_min);
703 std::vector< GUM_SCALAR > marg(2);
704 marg[1] = msg_p_min;
705 marg[0] = 1 - msg_p_min;
706
707 _infE_::oldMarginalMin_.set(node, marg);
708
709 if (msg_p_min != msg_p_max) {
710 marg[1] = msg_p_max;
711 marg[0] = 1 - msg_p_max;
712 NodesP_max_.insert(node, msg_p_max);
713 }
714
715 _infE_::oldMarginalMax_.set(node, marg);
716
717 NodesL_min_.set(node, (GUM_SCALAR)1.);
718 }
719
720 for (auto arc: _bnet_->arcs()) {
721 ArcsP_min_.set(arc, NodesP_min_[arc.tail()]);
722
723 if (NodesP_max_.exists(arc.tail())) { ArcsP_max_.set(arc, NodesP_max_[arc.tail()]); }
724
725 ArcsL_min_.set(arc, NodesL_min_[arc.tail()]);
726 }
727 }
728
729 template < GUM_Numeric GUM_SCALAR >
731 const DAG& graphe = _bnet_->dag();
732
733 GUM_SCALAR eps;
734 // to validate TestSuite
736
737 do {
738 for (auto node: active_nodes_set_) {
739 for (auto chil: graphe.children(node)) {
740 if (_cn_->currentNodeType(chil) == CredalNet< GUM_SCALAR >::NodeType::Indic) { continue; }
741
742 msgP_(node, chil);
743 }
744
745 for (auto par: graphe.parents(node)) {
746 if (_cn_->currentNodeType(node) == CredalNet< GUM_SCALAR >::NodeType::Indic) { continue; }
747
748 msgL_(node, par);
749 }
750 }
751
752 eps = calculateEpsilon_();
753
755
756 active_nodes_set_.clear();
759
760 } while (_infE_::continueApproximationScheme(eps) && active_nodes_set_.size() > 0);
761
762 _infE_::stopApproximationScheme(); // just to be sure of the
763 // approximationScheme has been notified of
764 // the end of looop
765 }
766
767 template < GUM_Numeric GUM_SCALAR >
769 Size nbrArcs = _bnet_->dag().sizeArcs();
770
771 std::vector< cArcP > seq;
772 seq.reserve(nbrArcs);
773
774 for (const auto& arc: _bnet_->arcs()) {
775 seq.push_back(&arc);
776 }
777
778 GUM_SCALAR eps;
779 // validate TestSuite
781
782 do {
783 for (Size j = 0, theEnd = nbrArcs / 2; j < theEnd; j++) {
784 auto w1 = randomValue(nbrArcs);
785 auto w2 = randomValue(nbrArcs);
786
787 if (w1 == w2) { continue; }
788
789 std::swap(seq[w1], seq[w2]);
790 }
791
792 for (const auto it: seq) {
793 if (_cn_->currentNodeType(it->tail()) == CredalNet< GUM_SCALAR >::NodeType::Indic
794 || _cn_->currentNodeType(it->head()) == CredalNet< GUM_SCALAR >::NodeType::Indic) {
795 continue;
796 }
797
798 msgP_(it->tail(), it->head());
799 msgL_(it->head(), it->tail());
800 }
801
802 eps = calculateEpsilon_();
803
805
807 }
808
809 // gives slightly worse results for some variable/modalities than other
810 // inference
811 // types (node D on 2U network loose 0.03 precision)
812 template < GUM_Numeric GUM_SCALAR >
814 Size nbrArcs = _bnet_->dag().sizeArcs();
815
816 std::vector< cArcP > seq;
817 seq.reserve(nbrArcs);
818
819 for (const auto& arc: _bnet_->arcs()) {
820 seq.push_back(&arc);
821 }
822
823 GUM_SCALAR eps;
824 // validate TestSuite
826
827 do {
828 for (const auto it: seq) {
829 if (_cn_->currentNodeType(it->tail()) == CredalNet< GUM_SCALAR >::NodeType::Indic
830 || _cn_->currentNodeType(it->head()) == CredalNet< GUM_SCALAR >::NodeType::Indic) {
831 continue;
832 }
833
834 msgP_(it->tail(), it->head());
835 msgL_(it->head(), it->tail());
836 }
837
838 eps = calculateEpsilon_();
839
841
843 }
844
845 template < GUM_Numeric GUM_SCALAR >
847 NodeSet const& children = _bnet_->children(Y);
848 NodeSet const& parents_ = _bnet_->parents(Y);
849
850 const auto parents = &_bnet_->cpt(Y).variablesSequence();
851
852 if (((children.size() + parents->size() - 1) == 1) && (!_infE_::evidence_.exists(Y))) {
853 return;
854 }
855
856 bool update_l = update_l_[Y];
857 bool update_p = update_p_[Y];
858
859 if (!update_p && !update_l) { return; }
860
861 msg_l_sent_[Y]->insert(X);
862
863 // for future refresh LM/PI
864 if (msg_l_sent_[Y]->size() == parents_.size()) {
865 msg_l_sent_[Y]->clear();
866 update_l_[Y] = false;
867 }
868
869 // refresh LM_part
870 if (update_l) {
871 if (!children.empty() && !_infE_::evidence_.exists(Y)) {
872 GUM_SCALAR lmin = 1.;
873 GUM_SCALAR lmax = 1.;
874
875 for (const NodeId chil: children) {
876 const Arc arc_YC{Y, chil};
877 lmin *= ArcsL_min_[arc_YC];
878
879 if (ArcsL_max_.exists(arc_YC)) {
880 lmax *= ArcsL_max_[arc_YC];
881 } else {
882 lmax *= ArcsL_min_[arc_YC];
883 }
884 }
885
886 lmin = lmax;
887
888 if (lmax != lmax && lmin == lmin) { lmax = lmin; }
889
890 if (lmax != lmax && lmin != lmin) {
891 std::cout << "no likelihood defined [lmin, lmax] (incompatibles "
892 "evidence ?)"
893 << std::endl;
894 }
895
896 if (lmin < 0.) { lmin = 0.; }
897
898 if (lmax < 0.) { lmax = 0.; }
899
900 // no need to update nodeL if evidence since nodeL will never be used
901
902 NodesL_min_[Y] = lmin;
903
904 if (lmin != lmax) {
905 NodesL_max_.set(Y, lmax);
906 } else if (NodesL_max_.exists(Y)) {
907 NodesL_max_.erase(Y);
908 }
909
910 } // end of : node has children & no evidence
911
912 } // end of : if update_l
913
914 GUM_SCALAR lmin = NodesL_min_[Y];
915 GUM_SCALAR lmax;
916
917 if (NodesL_max_.exists(Y)) {
918 lmax = NodesL_max_[Y];
919 } else {
920 lmax = lmin;
921 }
922
926
927 const Arc arc_XY{X, Y};
928 if (lmin == lmax && lmin == 1.) {
929 ArcsL_min_[arc_XY] = lmin;
930
931 if (ArcsL_max_.exists(arc_XY)) { ArcsL_max_.erase(arc_XY); }
932
933 return;
934 }
935
936 // garder pour chaque noeud un table des parents maj, une fois tous maj,
937 // stop
938 // jusque notification msg L ou P
939
940 if (update_p || update_l) {
941 std::vector< std::vector< std::vector< GUM_SCALAR > > > msgs_p;
942 std::vector< std::vector< GUM_SCALAR > > msg_p;
943 std::vector< GUM_SCALAR > distri(2);
944
945 Idx pos;
946
947 // +1 from start to avoid counting_ itself
948 // use const iterators with cbegin when available
949 for (auto jt = ++parents->begin(), theEnd = parents->end(); jt != theEnd; ++jt) {
950 if (_bnet_->nodeId(**jt) == X) {
951 // retirer la variable courante de la taille
952 pos = parents->pos(*jt) - 1;
953 continue;
954 }
955
956 // compute probability distribution to avoid doing it multiple times
957 // (at each combination of messages)
958 const Arc arc_PY{_bnet_->nodeId(**jt), Y};
959 distri[1] = ArcsP_min_[arc_PY];
960 distri[0] = GUM_SCALAR(1.) - distri[1];
961 msg_p.push_back(distri);
962
963 if (ArcsP_max_.exists(arc_PY)) {
964 distri[1] = ArcsP_max_[arc_PY];
965 distri[0] = GUM_SCALAR(1.) - distri[1];
966 msg_p.push_back(distri);
967 }
968
969 msgs_p.push_back(msg_p);
970 msg_p.clear();
971 }
972
973 GUM_SCALAR min = -2.;
974 GUM_SCALAR max = -2.;
975
976 std::vector< GUM_SCALAR > lx;
977 lx.push_back(lmin);
978
979 if (lmin != lmax) { lx.push_back(lmax); }
980
981 enum_combi_(msgs_p, Y, min, max, lx, pos);
982
983 if (min == -2. || max == -2.) {
984 if (min != -2.) {
985 max = min;
986 } else if (max != -2.) {
987 min = max;
988 } else {
989 std::cout << std::endl;
990 std::cout << "!!!! pas de message L calculable !!!!" << std::endl;
991 return;
992 }
993 }
994
995 if (min < 0.) { min = 0.; }
996
997 if (max < 0.) { max = 0.; }
998
999 bool update = false;
1000
1001 if (min != ArcsL_min_[arc_XY]) {
1002 ArcsL_min_[arc_XY] = min;
1003 update = true;
1004 }
1005
1006 if (ArcsL_max_.exists(arc_XY)) {
1007 if (max != ArcsL_max_[arc_XY]) {
1008 if (max != min) {
1009 ArcsL_max_[arc_XY] = max;
1010 } else { // if ( max == min )
1011 ArcsL_max_.erase(arc_XY);
1012 }
1013
1014 update = true;
1015 }
1016 } else {
1017 if (max != min) {
1018 ArcsL_max_.insert(arc_XY, max);
1019 update = true;
1020 }
1021 }
1022
1023 if (update) {
1024 update_l_.set(X, true);
1025 next_active_nodes_set_.insert(X);
1026 }
1027
1028 } // end of update_p || update_l
1029 }
1030
1031 template < GUM_Numeric GUM_SCALAR >
1032 void CNLoopyPropagation< GUM_SCALAR >::msgP_(const NodeId X, const NodeId demanding_child) {
1033 NodeSet const& children = _bnet_->children(X);
1034
1035 const auto parents = &_bnet_->cpt(X).variablesSequence();
1036
1037 if (((children.size() + parents->size() - 1) == 1) && (!_infE_::evidence_.exists(X))) {
1038 return;
1039 }
1040
1041 // LM_part ---- from all children but one --- the lonely one will get the
1042 // message
1043
1044 const Arc arc_XDC{X, demanding_child};
1045 if (_infE_::evidence_.exists(X)) {
1046 ArcsP_min_[arc_XDC] = _infE_::evidence_[X][1];
1047
1048 if (ArcsP_max_.exists(arc_XDC)) { ArcsP_max_.erase(arc_XDC); }
1049
1050 return;
1051 }
1052
1053 bool update_l = update_l_[X];
1054 bool update_p = update_p_[X];
1055
1056 if (!update_p && !update_l) { return; }
1057
1058 GUM_SCALAR lmin = 1.;
1059 GUM_SCALAR lmax = 1.;
1060
1061 // use cbegin if available
1062 for (auto chil: children) {
1063 if (chil == demanding_child) { continue; }
1064
1065 const Arc arc_XC{X, chil};
1066 lmin *= ArcsL_min_[arc_XC];
1067
1068 if (ArcsL_max_.exists(arc_XC)) {
1069 lmax *= ArcsL_max_[arc_XC];
1070 } else {
1071 lmax *= ArcsL_min_[arc_XC];
1072 }
1073 }
1074
1075 if (lmin != lmin && lmax == lmax) { lmin = lmax; }
1076
1077 if (lmax != lmax && lmin == lmin) { lmax = lmin; }
1078
1079 if (lmax != lmax && lmin != lmin) {
1080 std::cout << "pas de vraisemblance definie [lmin, lmax] (observations "
1081 "incompatibles ?)"
1082 << std::endl;
1083 return;
1084 }
1085
1086 if (lmin < 0.) { lmin = 0.; }
1087
1088 if (lmax < 0.) { lmax = 0.; }
1089
1090 // refresh PI_part
1091 GUM_SCALAR min = INF_;
1092 GUM_SCALAR max = 0.;
1093
1094 if (update_p) {
1095 std::vector< std::vector< std::vector< GUM_SCALAR > > > msgs_p;
1096 std::vector< std::vector< GUM_SCALAR > > msg_p;
1097 std::vector< GUM_SCALAR > distri(2);
1098
1099 // +1 from start to avoid counting_ itself
1100 // use const_iterators if available
1101 for (auto jt = ++parents->begin(), theEnd = parents->end(); jt != theEnd; ++jt) {
1102 // compute probability distribution to avoid doing it multiple times
1103 // (at each combination of messages)
1104 const Arc arc_PX{_bnet_->nodeId(**jt), X};
1105 distri[1] = ArcsP_min_[arc_PX];
1106 distri[0] = GUM_SCALAR(1.) - distri[1];
1107 msg_p.push_back(distri);
1108
1109 if (ArcsP_max_.exists(arc_PX)) {
1110 distri[1] = ArcsP_max_[arc_PX];
1111 distri[0] = GUM_SCALAR(1.) - distri[1];
1112 msg_p.push_back(distri);
1113 }
1114
1115 msgs_p.push_back(msg_p);
1116 msg_p.clear();
1117 }
1118
1119 enum_combi_(msgs_p, X, min, max);
1120
1121 if (min < 0.) { min = 0.; }
1122
1123 if (max < 0.) { max = 0.; }
1124
1125 if (min == INF_ || max == INF_) {
1126 std::cout << " ERREUR msg P min = max = INF " << std::endl;
1127 std::cout.flush();
1128 return;
1129 }
1130
1131 NodesP_min_[X] = min;
1132
1133 if (min != max) {
1134 NodesP_max_.set(X, max);
1135 } else if (NodesP_max_.exists(X)) {
1136 NodesP_max_.erase(X);
1137 }
1138
1139 update_p_.set(X, false);
1140
1141 } // end of update_p
1142 else {
1143 min = NodesP_min_[X];
1144
1145 if (NodesP_max_.exists(X)) {
1146 max = NodesP_max_[X];
1147 } else {
1148 max = min;
1149 }
1150 }
1151
1152 if (update_p || update_l) {
1153 GUM_SCALAR msg_p_min;
1154 GUM_SCALAR msg_p_max;
1155
1156 // cas limites sur min
1157 if (min == INF_ && lmin == 0.) {
1158 std::cout << "MESSAGE P ERR (negatif) : pi = inf, l = 0" << std::endl;
1159 }
1160
1161 if (lmin == INF_) { // cas infini
1162 msg_p_min = GUM_SCALAR(1.);
1163 } else if (min == 0. || lmin == 0.) {
1164 msg_p_min = 0;
1165 } else {
1166 msg_p_min = GUM_SCALAR(1. / (1. + ((1. / min - 1.) * 1. / lmin)));
1167 }
1168
1169 // cas limites sur max
1170 if (max == INF_ && lmax == 0.) {
1171 std::cout << "MESSAGE P ERR (negatif) : pi = inf, l = 0" << std::endl;
1172 }
1173
1174 if (lmax == INF_) { // cas infini
1175 msg_p_max = GUM_SCALAR(1.);
1176 } else if (max == 0. || lmax == 0.) {
1177 msg_p_max = 0;
1178 } else {
1179 msg_p_max = GUM_SCALAR(1. / (1. + ((1. / max - 1.) * 1. / lmax)));
1180 }
1181
1182 if (msg_p_min != msg_p_min && msg_p_max == msg_p_max) {
1183 msg_p_min = msg_p_max;
1184 std::cout << std::endl;
1185 std::cout << "msg_p_min is NaN" << std::endl;
1186 }
1187
1188 if (msg_p_max != msg_p_max && msg_p_min == msg_p_min) {
1189 msg_p_max = msg_p_min;
1190 std::cout << std::endl;
1191 std::cout << "msg_p_max is NaN" << std::endl;
1192 }
1193
1194 if (msg_p_max != msg_p_max && msg_p_min != msg_p_min) {
1195 std::cout << std::endl;
1196 std::cout << "pas de message P calculable (verifier observations)" << std::endl;
1197 return;
1198 }
1199
1200 if (msg_p_min < 0.) { msg_p_min = 0.; }
1201
1202 if (msg_p_max < 0.) { msg_p_max = 0.; }
1203
1204 bool update = false;
1205
1206 if (msg_p_min != ArcsP_min_[arc_XDC]) {
1207 ArcsP_min_[arc_XDC] = msg_p_min;
1208 update = true;
1209 }
1210
1211 if (ArcsP_max_.exists(arc_XDC)) {
1212 if (msg_p_max != ArcsP_max_[arc_XDC]) {
1213 if (msg_p_max != msg_p_min) {
1214 ArcsP_max_[arc_XDC] = msg_p_max;
1215 } else { // if ( msg_p_max == msg_p_min )
1216 ArcsP_max_.erase(arc_XDC);
1217 }
1218
1219 update = true;
1220 }
1221 } else {
1222 if (msg_p_max != msg_p_min) {
1223 ArcsP_max_.insert(arc_XDC, msg_p_max);
1224 update = true;
1225 }
1226 }
1227
1228 if (update) {
1229 update_p_.set(demanding_child, true);
1230 next_active_nodes_set_.insert(demanding_child);
1231 }
1232
1233 } // end of : update_l || update_p
1234 }
1235
1236 template < GUM_Numeric GUM_SCALAR >
1238 for (auto node: _bnet_->nodes()) {
1239 if ((!refreshIndic)
1240 && _cn_->currentNodeType(node) == CredalNet< GUM_SCALAR >::NodeType::Indic) {
1241 continue;
1242 }
1243
1244 NodeSet const& children = _bnet_->children(node);
1245
1246 auto parents = &_bnet_->cpt(node).variablesSequence();
1247
1248 if (update_l_[node]) {
1249 GUM_SCALAR lmin = 1.;
1250 GUM_SCALAR lmax = 1.;
1251
1252 if (!children.empty() && !_infE_::evidence_.exists(node)) {
1253 for (auto chil: children) {
1254 const Arc arc_NC{node, chil};
1255 lmin *= ArcsL_min_[arc_NC];
1256
1257 if (ArcsL_max_.exists(arc_NC)) {
1258 lmax *= ArcsL_max_[arc_NC];
1259 } else {
1260 lmax *= ArcsL_min_[arc_NC];
1261 }
1262 }
1263
1264 if (lmin != lmin && lmax == lmax) { lmin = lmax; }
1265
1266 lmax = lmin;
1267
1268 if (lmax != lmax && lmin != lmin) {
1269 std::cout << "pas de vraisemblance definie [lmin, lmax] (observations "
1270 "incompatibles ?)"
1271 << std::endl;
1272 return;
1273 }
1274
1275 if (lmin < 0.) { lmin = 0.; }
1276
1277 if (lmax < 0.) { lmax = 0.; }
1278
1279 NodesL_min_[node] = lmin;
1280
1281 if (lmin != lmax) {
1282 NodesL_max_.set(node, lmax);
1283 } else if (NodesL_max_.exists(node)) {
1284 NodesL_max_.erase(node);
1285 }
1286 }
1287
1288 } // end of : update_l
1289
1290 if (update_p_[node]) {
1291 if ((parents->size() - 1) > 0 && !_infE_::evidence_.exists(node)) {
1292 std::vector< std::vector< std::vector< GUM_SCALAR > > > msgs_p;
1293 std::vector< std::vector< GUM_SCALAR > > msg_p;
1294 std::vector< GUM_SCALAR > distri(2);
1295
1296 // +1 from start to avoid counting_ itself
1297 // cbegin
1298 for (auto jt = ++parents->begin(), theEnd = parents->end(); jt != theEnd; ++jt) {
1299 // compute probability distribution to avoid doing it multiple
1300 // times (at each combination of messages)
1301 const Arc arc_PN{_bnet_->nodeId(**jt), node};
1302 distri[1] = ArcsP_min_[arc_PN];
1303 distri[0] = GUM_SCALAR(1.) - distri[1];
1304 msg_p.push_back(distri);
1305
1306 if (ArcsP_max_.exists(arc_PN)) {
1307 distri[1] = ArcsP_max_[arc_PN];
1308 distri[0] = GUM_SCALAR(1.) - distri[1];
1309 msg_p.push_back(distri);
1310 }
1311
1312 msgs_p.push_back(msg_p);
1313 msg_p.clear();
1314 }
1315
1316 GUM_SCALAR min = INF_;
1317 GUM_SCALAR max = 0.;
1318
1319 enum_combi_(msgs_p, node, min, max);
1320
1321 if (min < 0.) { min = 0.; }
1322
1323 if (max < 0.) { max = 0.; }
1324
1325 NodesP_min_[node] = min;
1326
1327 if (min != max) {
1328 NodesP_max_.set(node, max);
1329 } else if (NodesP_max_.exists(node)) {
1330 NodesP_max_.erase(node);
1331 }
1332
1333 update_p_[node] = false;
1334 }
1335 } // end of update_p
1336
1337 } // end of : for each node
1338 }
1339
1340 template < GUM_Numeric GUM_SCALAR >
1342 for (auto node: _bnet_->nodes()) {
1343 GUM_SCALAR msg_p_min = 1.;
1344 GUM_SCALAR msg_p_max = 0.;
1345
1346 if (_infE_::evidence_.exists(node)) {
1347 if (_infE_::evidence_[node][1] == 0.) {
1348 msg_p_min = (GUM_SCALAR)0.;
1349 } else if (_infE_::evidence_[node][1] == 1.) {
1350 msg_p_min = 1.;
1351 }
1352
1353 msg_p_max = msg_p_min;
1354 } else {
1355 GUM_SCALAR min = NodesP_min_[node];
1356 GUM_SCALAR max;
1357
1358 if (NodesP_max_.exists(node)) {
1359 max = NodesP_max_[node];
1360 } else {
1361 max = min;
1362 }
1363
1364 GUM_SCALAR lmin = NodesL_min_[node];
1365 GUM_SCALAR lmax;
1366 if (NodesL_max_.exists(node)) {
1367 lmax = NodesL_max_[node];
1368 } else {
1369 lmax = lmin;
1370 }
1371
1372 if (min == INF_ || max == INF_) {
1373 std::cout << " min ou max === INF_ !!!!!!!!!!!!!!!!!!!!!!!!!! " << std::endl;
1374 return;
1375 }
1376
1377 if (min == INF_ && lmin == 0.) {
1378 std::cout << "proba ERR (negatif) : pi = inf, l = 0" << std::endl;
1379 return;
1380 }
1381
1382 if (lmin == INF_) {
1383 msg_p_min = GUM_SCALAR(1.);
1384 } else if (min == 0. || lmin == 0.) {
1385 msg_p_min = GUM_SCALAR(0.);
1386 } else {
1387 msg_p_min = GUM_SCALAR(1. / (1. + ((1. / min - 1.) * 1. / lmin)));
1388 }
1389
1390 if (max == INF_ && lmax == 0.) {
1391 std::cout << "proba ERR (negatif) : pi = inf, l = 0" << std::endl;
1392 return;
1393 }
1394
1395 if (lmax == INF_) {
1396 msg_p_max = GUM_SCALAR(1.);
1397 } else if (max == 0. || lmax == 0.) {
1398 msg_p_max = GUM_SCALAR(0.);
1399 } else {
1400 msg_p_max = GUM_SCALAR(1. / (1. + ((1. / max - 1.) * 1. / lmax)));
1401 }
1402 }
1403
1404 if (msg_p_min != msg_p_min && msg_p_max == msg_p_max) {
1405 msg_p_min = msg_p_max;
1406 std::cout << std::endl;
1407 std::cout << "msg_p_min is NaN" << std::endl;
1408 }
1409
1410 if (msg_p_max != msg_p_max && msg_p_min == msg_p_min) {
1411 msg_p_max = msg_p_min;
1412 std::cout << std::endl;
1413 std::cout << "msg_p_max is NaN" << std::endl;
1414 }
1415
1416 if (msg_p_max != msg_p_max && msg_p_min != msg_p_min) {
1417 std::cout << std::endl;
1418 std::cout << "Please check the observations (no proba can be computed)" << std::endl;
1419 return;
1420 }
1421
1422 if (msg_p_min < 0.) { msg_p_min = 0.; }
1423
1424 if (msg_p_max < 0.) { msg_p_max = 0.; }
1425
1426 _infE_::marginalMin_[node][0] = 1 - msg_p_max;
1427 _infE_::marginalMax_[node][0] = 1 - msg_p_min;
1428 _infE_::marginalMin_[node][1] = msg_p_min;
1429 _infE_::marginalMax_[node][1] = msg_p_max;
1430 }
1431 }
1432
1433 template < GUM_Numeric GUM_SCALAR >
1440
1441 template < GUM_Numeric GUM_SCALAR >
1443 for (auto node: _bnet_->nodes()) {
1444 if (_cn_->currentNodeType(node) != CredalNet< GUM_SCALAR >::NodeType::Indic) { continue; }
1445
1446 for (auto pare: _bnet_->parents(node)) {
1447 msgP_(pare, node);
1448 }
1449 }
1450
1451 refreshLMsPIs_(true);
1453 }
1454
1455 template < GUM_Numeric GUM_SCALAR >
1457 if (_infE_::modal_.empty()) { return; }
1458
1459 std::vector< std::vector< GUM_SCALAR > > vertices(2, std::vector< GUM_SCALAR >(2));
1460
1461 for (auto node: _bnet_->nodes()) {
1462 vertices[0][0] = _infE_::marginalMin_[node][0];
1463 vertices[0][1] = _infE_::marginalMax_[node][1];
1464
1465 vertices[1][0] = _infE_::marginalMax_[node][0];
1466 vertices[1][1] = _infE_::marginalMin_[node][1];
1467
1468 for (auto vertex = 0, vend = 2; vertex != vend; vertex++) {
1470 // test credal sets vertices elim
1471 // remove with L2U since variables are binary
1472 // but does the user know that ?
1474 vertices[vertex]); // no redundancy elimination with 2 vertices
1475 }
1476 }
1477 }
1478
1479 template < GUM_Numeric GUM_SCALAR >
1481 InferenceEngine< GUM_SCALAR >::InferenceEngine(credalNet) {
1482 if (!credalNet.isSeparatelySpecified()) {
1483 GUM_ERROR(OperationNotAllowed,
1484 "CNLoopyPropagation is only available "
1485 "with separately specified nets");
1486 }
1487
1488 // test for binary cn
1489 for (auto node: credalNet.current_bn().nodes())
1490 if (credalNet.current_bn().variable(node).domainSize() != 2) {
1491 GUM_ERROR(OperationNotAllowed,
1492 "CNLoopyPropagation is only available "
1493 "with binary credal networks")
1494 }
1495
1496 // test if compute CPTMinMax has been called
1497 if (!credalNet.hasComputedBinaryCPTMinMax()) {
1498 GUM_ERROR(OperationNotAllowed,
1499 "CNLoopyPropagation only works when "
1500 "\"computeBinaryCPTMinMax()\" has been called for "
1501 "this credal net")
1502 }
1503
1504 _cn_ = &credalNet;
1505 _bnet_ = &credalNet.current_bn();
1506
1508 inference_up_to_date_ = false;
1509
1510 GUM_CONSTRUCTOR(CNLoopyPropagation)
1511 }
1512
1513 template < GUM_Numeric GUM_SCALAR >
1515 inference_up_to_date_ = false;
1516
1517 for (auto& [node, pset]: msg_l_sent_) {
1518 delete pset;
1519 }
1520
1521 GUM_DESTRUCTOR(CNLoopyPropagation)
1522 }
1523
1524 template < GUM_Numeric GUM_SCALAR >
1528
1529 template < GUM_Numeric GUM_SCALAR >
1534
1535 template < GUM_Numeric GUM_SCALAR >
1539
1540} // namespace gum::credal
Class implementing loopy-propagation with binary networks - L2U algorithm.
#define INF_
void updateApproximationScheme(unsigned int incr=1)
Update the scheme w.r.t the new error and increment steps.
bool continueApproximationScheme(double error)
Update the scheme w.r.t the new error.
void initApproximationScheme()
Initialise the scheme.
void stopApproximationScheme()
Stop the approximation scheme.
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 nodes which consists in the node and its parents returns the set of children of a ...
The base class for all directed edges.
Base class for dag.
Definition DAG.h:121
Exception : the element we looked for cannot be found.
Exception : operation not allowed.
bool empty() const noexcept
Indicates whether the set is the empty set.
Definition set_tpl.h:613
Size size() const noexcept
Returns the number of elements in the set.
Definition set_tpl.h:607
void insertEvidenceFile(std::string_view path) override
Starts the inference.
void makeInference() override
Starts the inference.
NodeProperty< GUM_SCALAR > NodesL_min_
"Lower" node information obtained by combinaison of children messages.
void eraseAllEvidence() override
Erase all inference related data to perform another one.
NodeProperty< GUM_SCALAR > NodesP_min_
"Lower" node information obtained by combinaison of parent's messages.
NodeProperty< GUM_SCALAR > NodesL_max_
"Upper" node information obtained by combinaison of children messages.
void msgL_(const NodeId X, const NodeId demanding_parent)
Sends a message to one's parent, i.e.
NodeProperty< NodeSet * > msg_l_sent_
Used to keep track of one's messages sent to it's parents.
InferenceType _inferenceType_
The chosen inference type.
void compute_ext_(GUM_SCALAR &msg_l_min, GUM_SCALAR &msg_l_max, std::vector< GUM_SCALAR > &lx, GUM_SCALAR &num_min, GUM_SCALAR &num_max, GUM_SCALAR &den_min, GUM_SCALAR &den_max)
Used by msgL_.
NodeProperty< bool > update_p_
Used to keep track of which node needs to update it's information coming from it's parents.
void refreshLMsPIs_(bool refreshIndic=false)
Get the last messages from one's parents and children.
NodeProperty< bool > update_l_
Used to keep track of which node needs to update it's information coming from it's children.
void makeInferenceNodeToNeighbours_()
Starts the inference with this inference type.
void initialize_()
Topological forward propagation to initialize old marginals & messages.
GUM_SCALAR calculateEpsilon_()
Compute epsilon.
void saveInference(std::string_view path)
void makeInferenceByRandomOrder_()
Starts the inference with this inference type.
const IBayesNet< GUM_SCALAR > * _bnet_
A pointer to it's IBayesNet used as a DAG.
ArcProperty< GUM_SCALAR > ArcsP_min_
"Lower" information coming from one's parent.
InferenceType
Inference type to be used by the algorithm.
@ nodeToNeighbours
Uses a node-set so we don't iterate on nodes that can't send a new message.
@ randomOrder
Chooses a random arc ordering and sends messages accordingly.
@ ordered
Chooses an arc ordering and sends messages accordingly at all steps.
void msgP_(const NodeId X, const NodeId demanding_child)
Sends a message to one's child, i.e.
ArcProperty< GUM_SCALAR > ArcsL_max_
"Upper" information coming from one's children.
void updateMarginals_()
Compute marginals from up-to-date messages.
const CredalNet< GUM_SCALAR > * _cn_
A pointer to the CredalNet to be used.
void computeExpectations_()
Since the network is binary, expectations can be computed from the final marginals which give us the ...
NodeProperty< GUM_SCALAR > NodesP_max_
"Upper" node information obtained by combinaison of parent's messages.
void enum_combi_(std::vector< std::vector< std::vector< GUM_SCALAR > > > &msgs_p, const NodeId &id, GUM_SCALAR &msg_l_min, GUM_SCALAR &msg_l_max, std::vector< GUM_SCALAR > &lx, const Idx &pos)
Used by msgL_.
InferenceType inferenceType()
Get the inference type.
void makeInferenceByOrderedArcs_()
Starts the inference with this inference type.
NodeSet active_nodes_set_
The current node-set to iterate through at this current step.
void updateIndicatrices_()
Only update indicatrices variables at the end of computations ( calls msgP_ ).
NodeSet next_active_nodes_set_
The next node-set, i.e.
CNLoopyPropagation(const CredalNet< GUM_SCALAR > &credalNet)
Constructor.
bool inference_up_to_date_
TRUE if inference has already been performed, FALSE otherwise.
ArcProperty< GUM_SCALAR > ArcsL_min_
"Lower" information coming from one's children.
ArcProperty< GUM_SCALAR > ArcsP_max_
"Upper" information coming from one's parent.
Class template representing a Credal Network.
Definition credalNet.h:97
void updateExpectations_(const NodeId &id, const std::vector< GUM_SCALAR > &vertex)
Given a node id and one of it's possible vertex obtained during inference, update this node lower and...
margi oldMarginalMax_
Old upper marginals used to compute epsilon.
margi evidence_
Holds observed variables states.
margi marginalMax_
Upper marginals.
void updateCredalSets_(const NodeId &id, const std::vector< GUM_SCALAR > &vertex, const bool &elimRedund=false)
Given a node id and one of it's possible vertex, update it's credal set.
virtual const GUM_SCALAR computeEpsilon_()
Compute approximation scheme epsilon using the old marginals and the new ones.
const std::vector< std::vector< GUM_SCALAR > > & vertices(const NodeId id) const
Get the vertice of a given node id.
InferenceEngine(const CredalNet< GUM_SCALAR > &credalNet)
Construtor.
margi oldMarginalMin_
Old lower marginals used to compute epsilon.
virtual void eraseAllEvidence()
removes all the evidence entered into the network
virtual void insertEvidenceFile(std::string_view path)
Insert evidence from file.
const CredalNet< GUM_SCALAR > & credalNet() const
Get this credal network.
margi marginalMin_
Lower marginals.
dynExpe modal_
Variables modalities used to compute expectations.
#define GUM_ERROR(type, msg)
Definition exceptions.h:76
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition types.h:74
Size Idx
Type for indexes.
Definition types.h:79
Size NodeId
Type for node ids.
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
Idx randomValue(const Size max=2)
Returns a random Idx between 0 and max-1 included.
namespace for all credal networks entities
Definition agrum.h:61
std::vector< std::pair< Idx, Idx > > dispatchRangeToThreads(const Idx beg, const Idx end, const unsigned int nb_threads)
returns a vector equally splitting elements of a range among threads
Definition threads.cpp:76
unsigned int getNumberOfThreads()
returns the max number of threads used by default when entering the next parallel region
static void execute(std::size_t nb_threads, FUNCTION exec_func, ARGS &&... func_args)
executes a function using several threads
static int nbRunningThreadsExecutors()
indicates how many threadExecutors are currently running