aGrUM 3.1.1
a C++ library for (probabilistic) graphical models
credalNet_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
44#include <agrum/agrum.h>
45
46#include <agrum/CN/credalNet.h>
47
50
51namespace gum {
52 namespace credal {
53
54 template < GUM_Numeric GUM_SCALAR >
57
58 _src_bn_ = BayesNet< GUM_SCALAR >();
59 _src_bn_min_ = BayesNet< GUM_SCALAR >();
60 _src_bn_max_ = BayesNet< GUM_SCALAR >();
61
62 GUM_CONSTRUCTOR(CredalNet);
63 }
64
65 template < GUM_Numeric GUM_SCALAR >
66 NodeId CredalNet< GUM_SCALAR >::addVariable(std::string_view name, const Size& card) {
67 LabelizedVariable var(name, "node " + std::string{name}, card);
68
69 NodeId a = _src_bn_.add(var);
70 NodeId b = _src_bn_min_.add(var);
71 NodeId c = _src_bn_max_.add(var);
72
73 if (a != b || a != c /*|| b != c*/)
75 "addVariable : not the same id over all networks : " << a << ", " << b << ", "
76 << c);
77
78 return a;
79 }
80
81 template < GUM_Numeric GUM_SCALAR >
82 void CredalNet< GUM_SCALAR >::addArc(const NodeId& tail, const NodeId& head) {
83 _src_bn_.addArc(tail, head);
84 _src_bn_min_.addArc(tail, head);
85 _src_bn_max_.addArc(tail, head);
86 }
87
88 template < GUM_Numeric GUM_SCALAR >
90 const NodeId& id,
91 const std::vector< std::vector< std::vector< GUM_SCALAR > > >& cpt) {
92 const Tensor< GUM_SCALAR >* const tensor(&_src_bn_.cpt(id));
93
94 auto var_dSize = _src_bn_.variable(id).domainSize();
95
96 if (auto entry_size = tensor->domainSize() / var_dSize; cpt.size() != entry_size)
98 "setCPTs : entry sizes of cpts does not match for node id : "
99 << id << " : " << cpt.size() << " != " << entry_size);
100
101 for (const auto& cset: cpt) {
102 if (cset.size() == 0)
104 "setCPTs : vertices in credal set does not match for node id : "
105 << id << " with 0 vertices");
106
107 for (const auto& vertex: cset) {
108 if (vertex.size() != var_dSize)
110 "setCPTs : variable modalities in cpts does "
111 "not match for node id : "
112 << id << " with vertex " << vertex << " : " << vertex.size()
113 << " != " << var_dSize);
114
115 GUM_SCALAR sum = 0;
116
117 for (const auto& prob: vertex) {
118 sum += prob;
119 }
120
121 if (!gum::isCloseToOne(sum, GUM_SCALAR(1e-6)))
123 "setCPTs : a vertex coordinates does not "
124 "sum to one for node id : "
125 << id << " with vertex " << vertex);
126 }
127 }
128
129 _credalNet_src_cpt_.insert(id, cpt);
130 }
131
132 template < GUM_Numeric GUM_SCALAR >
134 const Size& entry,
135 const std::vector< std::vector< GUM_SCALAR > >& cpt) {
136 const Tensor< GUM_SCALAR >* const tensor(&_src_bn_.cpt(id));
137
138 auto var_dSize = _src_bn_.variable(id).domainSize();
139 auto entry_size = tensor->domainSize() / var_dSize;
140
141 if (entry >= entry_size)
143 "setCPT : entry is greater or equal than entry size "
144 "(entries start at 0 up to entry_size - 1) : "
145 << entry << " >= " << entry_size);
146
147 if (cpt.size() == 0) GUM_ERROR(SizeError, "setCPT : empty credal set for entry : " << entry)
148
149 for (const auto& vertex: cpt) {
150 if (vertex.size() != var_dSize)
152 "setCPT : variable modalities in cpts does not "
153 "match for node id : "
154 << id << " with vertex " << vertex << " at entry " << entry << " : "
155 << vertex.size() << " != " << var_dSize);
156
157 GUM_SCALAR sum = 0;
158
159 for (const auto& prob: vertex) {
160 sum += prob;
161 }
162
163 if (!gum::isCloseToOne(sum, GUM_SCALAR(1e-6)))
165 "setCPT : a vertex coordinates does not sum to one for node id : "
166 << id << " at entry " << entry << " with vertex " << vertex);
167 }
168
169 // !! auto does NOT use adress (if available) unless explicitly asked !!
170 auto& node_cpt = _credalNet_src_cpt_.getWithDefault(
171 id,
172 std::vector< std::vector< std::vector< GUM_SCALAR > > >(entry_size));
173
174 if (node_cpt[entry].size() != 0)
176 "setCPT : vertices of entry id " << entry
177 << " already set to : " << node_cpt[entry]
178 << ", cannot insert : " << cpt);
179
180 node_cpt[entry] = cpt;
181
183 }
184
185 template < GUM_Numeric GUM_SCALAR >
187 Instantiation ins,
188 const std::vector< std::vector< GUM_SCALAR > >& cpt) {
189 const Tensor< GUM_SCALAR >* const tensor(&_src_bn_.cpt(id));
190
191 auto var_dSize = _src_bn_.variable(id).domainSize();
192 auto entry_size = tensor->domainSize() / var_dSize;
193
194 // to be sure of entry index reorder ins according to the bayes net
195 // tensors
196 // ( of the credal net )
197 // it WONT throw an error if the sequences are not equal not because of
198 // order
199 // but content, so we double check (before & after order correction)
200 // beware of slaves & master
201 Instantiation ref(tensor);
202 ref.forgetMaster();
203
204 ins.forgetMaster();
205
206 const auto& vseq = ref.variablesSequence();
207
208 if (ins.variablesSequence() != vseq) {
209 ins.reorder(ref);
210
211 if (ins.variablesSequence() != vseq)
213 "setCPT : instantiation : "
214 << ins << " is not valid for node id " << id
215 << " which accepts instantiations such as (order is not "
216 "important) : "
217 << ref);
218 }
219
220 Idx entry = 0, jump = 1;
221
222 for (Idx i = 0, end = ins.nbrDim(); i < end; i++) {
223 if (_src_bn_.nodeId(ins.variable(i)) == id) continue;
224
225 entry += ins.val(i) * jump;
226
227 jump *= ins.variable(i).domainSize();
228 }
229
230 if (entry >= entry_size)
232 "setCPT : entry is greater or equal than entry size "
233 "(entries start at 0 up to entry_size - 1) : "
234 << entry << " >= " << entry_size);
235
236 if (cpt.size() == 0) GUM_ERROR(SizeError, "setCPT : empty credal set for entry : " << entry)
237
238 for (const auto& vertex: cpt) {
239 if (vertex.size() != var_dSize)
241 "setCPT : variable modalities in cpts does not "
242 "match for node id : "
243 << id << " with vertex " << vertex << " at entry " << entry << " : "
244 << vertex.size() << " != " << var_dSize);
245
246 GUM_SCALAR sum = 0;
247
248 for (const auto& prob: vertex) {
249 sum += prob;
250 }
251
252 if (!gum::isCloseToOne(sum, GUM_SCALAR(1e-6)))
254 "setCPT : a vertex coordinates does not sum to one for node id : "
255 << id << " at entry " << entry << " with vertex " << vertex);
256 }
257
258 auto& node_cpt = _credalNet_src_cpt_.getWithDefault(
259 id,
260 std::vector< std::vector< std::vector< GUM_SCALAR > > >(entry_size));
261
262 if (node_cpt[entry].size() != 0)
264 "setCPT : vertices of entry : " << ins << " id " << entry
265 << " already set to : " << node_cpt[entry]
266 << ", cannot insert : " << cpt);
267
268 node_cpt[entry] = cpt;
269
271 }
272
273 template < GUM_Numeric GUM_SCALAR >
275 const std::vector< GUM_SCALAR >& lower,
276 const std::vector< GUM_SCALAR >& upper) {
277 try {
278 _src_bn_min_.cpt(id).fillWith(lower);
279 _src_bn_max_.cpt(id).fillWith(upper);
280 } catch (const SizeError&) {
282 "fillConstraints : sizes does not match in fillWith for node id : " << id);
283 }
284 }
285
286 template < GUM_Numeric GUM_SCALAR >
288 const Idx& entry,
289 const std::vector< GUM_SCALAR >& lower,
290 const std::vector< GUM_SCALAR >& upper) {
291 Tensor< GUM_SCALAR >* const tensor_min(
292 const_cast< Tensor< GUM_SCALAR >* const >(&_src_bn_min_.cpt(id)));
293 Tensor< GUM_SCALAR >* const tensor_max(
294 const_cast< Tensor< GUM_SCALAR >* const >(&_src_bn_max_.cpt(id)));
295
296 auto var_dSize = _src_bn_.variable(id).domainSize();
297
298 if (lower.size() != var_dSize || upper.size() != var_dSize)
300 "setCPT : variable modalities in cpts does not match for node id : "
301 << id << " with sizes of constraints : ( " << lower.size() << " || "
302 << upper.size() << " ) != " << var_dSize);
303
304 auto entry_size = tensor_min->domainSize() / var_dSize;
305
306 if (entry >= entry_size)
308 "setCPT : entry is greater or equal than entry size "
309 "(entries start at 0 up to entry_size - 1) : "
310 << entry << " >= " << entry_size);
311
312 Instantiation min(tensor_min);
313 Instantiation max(tensor_max);
314 min.setFirst();
315 max.setFirst();
316
317 Idx pos = 0;
318
319 while (pos != entry) {
320 ++min;
321 ++max;
322 ++pos;
323 }
324
325 for (Size i = 0; i < var_dSize; i++) {
326 tensor_min->set(min, lower[i]);
327 tensor_max->set(max, upper[i]);
328 ++min;
329 ++max;
330 }
331 }
332
333 template < GUM_Numeric GUM_SCALAR >
335 Instantiation ins,
336 const std::vector< GUM_SCALAR >& lower,
337 const std::vector< GUM_SCALAR >& upper) {
338 const Tensor< GUM_SCALAR >* const tensor(&_src_bn_.cpt(id));
339 /*
340 auto var_dSize = _src_bn_.variable ( id ).domainSize();
341 auto entry_size = tensor->domainSize() / var_dSize;
342 */
343 // to be sure of entry index reorder ins according to the bayes net
344 // tensors
345 // ( of the credal net )
346 // it WONT throw an error if the sequences are not equal not because of
347 // order
348 // but content, so we double check (before & after order correction)
349 // beware of slaves & master
350 Instantiation ref(tensor);
351 ref.forgetMaster();
352
353 ins.forgetMaster();
354
355 const auto& vseq = ref.variablesSequence();
356
357 if (ins.variablesSequence() != vseq) {
358 ins.reorder(ref);
359
360 if (ins.variablesSequence() != vseq)
362 "setCPT : instantiation : "
363 << ins << " is not valid for node id " << id
364 << " which accepts instantiations such as (order is not "
365 "important) : "
366 << ref);
367 }
368
369 Idx entry = 0, jump = 1;
370
371 for (Idx i = 0, end = ins.nbrDim(); i < end; i++) {
372 if (_src_bn_.nodeId(ins.variable(i)) == id) continue;
373
374 entry += ins.val(i) * jump;
375
376 jump *= ins.variable(i).domainSize();
377 }
378
379 /*
380 if ( entry >= entry_size )
381 GUM_ERROR ( SizeError, "setCPT : entry is greater or equal than entry
382 size
383 (entries start at 0 up to entry_size - 1) : " << entry << " >= " <<
384 entry_size
385 );
386
387 if ( lower.size() != var_dSize || upper.size() != var_dSize )
388 GUM_ERROR ( SizeError, "setCPT : variable modalities in cpts does not
389 match
390 for node id : " << id << " with sizes of constraints : ( "<< lower.size()
391 << "
392 || " << upper.size() << " ) != " << var_dSize );
393 */
394 fillConstraint(id, entry, lower, upper);
395 }
396
399
400 template < GUM_Numeric GUM_SCALAR >
404
405 template < GUM_Numeric GUM_SCALAR >
407 return _src_bn_.variable(id).domainSize();
408 }
409
411
412 template < GUM_Numeric GUM_SCALAR >
413 CredalNet< GUM_SCALAR >::CredalNet(std::string_view src_min_num, std::string_view src_max_den) {
414 _initParams_();
415 _initCNNets_(src_min_num, src_max_den);
416
417 GUM_CONSTRUCTOR(CredalNet);
418 }
419
420 template < GUM_Numeric GUM_SCALAR >
421 CredalNet< GUM_SCALAR >::CredalNet(const BayesNet< GUM_SCALAR >& src_min_num,
422 const BayesNet< GUM_SCALAR >& src_max_den) {
423 _initParams_();
424 _initCNNets_(src_min_num, src_max_den);
425
426 GUM_CONSTRUCTOR(CredalNet);
427 }
428
429 template < GUM_Numeric GUM_SCALAR >
431 if (_current_bn_ != nullptr) delete _current_bn_;
432
434
435 if (_current_nodeType_ != nullptr) delete _current_nodeType_;
436
437 GUM_DESTRUCTOR(CredalNet);
438 }
439
440 // from BNs with numerators & denominators or cpts & denominators to credal
441 template < GUM_Numeric GUM_SCALAR >
442 void CredalNet< GUM_SCALAR >::bnToCredal(GUM_SCALAR beta, bool oneNet) {
443 this->bnToCredal(beta, oneNet, false);
444 }
445
446 template < GUM_Numeric GUM_SCALAR >
447 void CredalNet< GUM_SCALAR >::bnToCredal(GUM_SCALAR beta, bool oneNet, bool keepZeroes) {
448 GUM_SCALAR epsi_min = 1.;
449 GUM_SCALAR epsi_max = 0.;
450 GUM_SCALAR epsi_moy = 0.;
451 GUM_SCALAR epsi_den = 0.;
452
453 for (auto node: src_bn().nodes()) {
454 const Tensor< GUM_SCALAR >* const tensor(&_src_bn_.cpt(node));
455
456 Tensor< GUM_SCALAR >* const tensor_min(
457 const_cast< Tensor< GUM_SCALAR >* const >(&_src_bn_min_.cpt(node)));
458 Tensor< GUM_SCALAR >* const tensor_max(
459 const_cast< Tensor< GUM_SCALAR >* const >(&_src_bn_max_.cpt(node)));
460
461 Size var_dSize = _src_bn_.variable(node).domainSize();
462 Size entry_size = tensor->domainSize() / var_dSize;
463
464 Instantiation ins(tensor);
465 Instantiation ins_min(tensor_min);
466 Instantiation ins_max(tensor_max);
467
468 ins.setFirst();
469 ins_min.setFirst();
470 ins_max.setFirst();
471
472 std::vector< GUM_SCALAR > vertex(var_dSize);
473
474 for (Size entry = 0; entry < entry_size; entry++) {
475 GUM_SCALAR den;
476
477 if (oneNet) den = 0;
478 else den = tensor_max->get(ins_max);
479
480 Size nbm = 0;
481
482 for (Size modality = 0; modality < var_dSize; modality++) {
483 vertex[modality] = tensor->get(ins);
484
485 if (oneNet) {
486 den += vertex[modality];
487
488 if (vertex[modality] < 1 && vertex[modality] > 0)
490 "bnToCredal : the BayesNet contains "
491 "probabilities and not event counts "
492 "although user precised oneNet = "
493 << oneNet);
494 }
495
496 if (vertex[modality] > 0) nbm++;
497
498 ++ins;
499 }
500
502 if (!oneNet) {
503 GUM_SCALAR sum = 0;
504
505 for (auto modality = vertex.cbegin(), theEnd = vertex.cend(); modality != theEnd;
506 ++modality) {
507 sum += *modality;
508 }
509
510 if (std::fabs(1. - sum) > _epsRedund_) {
512 _src_bn_.variable(node).name()
513 << "(" << _epsRedund_ << ") does not sum to one for" << " " << entry
514 << std::endl
515 << vertex << std::endl
516 << ins << std::endl);
517 }
518 }
519
521
522 GUM_SCALAR epsilon;
523
524 if (beta == 0) epsilon = 0;
525 else if (den == 0 || beta == 1) epsilon = GUM_SCALAR(1.0);
526 else epsilon = GUM_SCALAR(std::pow(beta, std::log1p(den)));
527
528 epsi_moy += epsilon;
529 epsi_den += 1;
530
531 if (epsilon > epsi_max) epsi_max = epsilon;
532
533 if (epsilon < epsi_min) epsi_min = epsilon;
534
535 GUM_SCALAR min, max;
536
537 for (Size modality = 0; modality < var_dSize; modality++) {
538 if ((vertex[modality] > 0 && nbm > 1) || !keepZeroes) {
539 min = GUM_SCALAR((1. - epsilon) * vertex[modality]);
540
541 if (oneNet) min = GUM_SCALAR(min * 1.0 / den);
542
543 max = GUM_SCALAR(min + epsilon);
544 } else {
545 // if ( ( vertex[modality] == 0 && keepZeroes ) || (
546 // vertex[modality] > 0 && nbm <= 1 ) || ( vertex[modality] == 0
547 // && nbm <= 1 ) ) {
548 min = vertex[modality];
549
550 if (oneNet) min = GUM_SCALAR(min * 1.0 / den);
551
552 max = min;
553 }
554
555 tensor_min->set(ins_min, min);
556 tensor_max->set(ins_max, max);
557
558 ++ins_min;
559 ++ins_max;
560 } // end of : for each modality
561 } // end of : for each entry
562 } // end of : for each variable
563
564 _epsilonMin_ = epsi_min;
565 _epsilonMax_ = epsi_max;
566 _epsilonMoy_ = (GUM_SCALAR)epsi_moy / (GUM_SCALAR)epsi_den;
567
569 }
570
571 template < GUM_Numeric GUM_SCALAR >
573 for (auto node: _src_bn_.nodes()) {
574 const Tensor< GUM_SCALAR >* const tensor(&_src_bn_.cpt(node));
575
576 auto var_dSize = _src_bn_.variable(node).domainSize();
577 auto entry_size = tensor->domainSize() / var_dSize;
578
579 Instantiation ins(tensor);
580
581 ins.setFirst();
582
583 std::vector< GUM_SCALAR > vertex(var_dSize);
584
585 for (Size entry = 0; entry < entry_size; entry++) {
586 bool zeroes = false;
587 Instantiation ins_prev = ins;
588
589 for (Size modality = 0; modality < var_dSize; modality++) {
590 vertex[modality] = tensor->get(ins);
591
592 if (vertex[modality] < 1 && vertex[modality] > 0)
594 "lagrangeNormalization : the BayesNet "
595 "contains probabilities and not event "
596 "counts.");
597
598 if (!zeroes && vertex[modality] == 0) { zeroes = true; }
599
600 ++ins;
601 }
602
603 if (zeroes) {
604 ins = ins_prev;
605
606 for (Size modality = 0; modality < var_dSize; modality++) {
607 tensor->set(ins, tensor->get(ins) + 1);
608 ++ins;
609 }
610 }
611 } // end of : for each entry
612 } // end of : for each variable
613 }
614
615 template < GUM_Numeric GUM_SCALAR >
616 void CredalNet< GUM_SCALAR >::idmLearning(const Idx s, const bool keepZeroes) {
617 for (auto node: _src_bn_.nodes()) {
618 const Tensor< GUM_SCALAR >* const tensor(&_src_bn_.cpt(node));
619
620 Tensor< GUM_SCALAR >* const tensor_min(
621 const_cast< Tensor< GUM_SCALAR >* const >(&_src_bn_min_.cpt(node)));
622 Tensor< GUM_SCALAR >* const tensor_max(
623 const_cast< Tensor< GUM_SCALAR >* const >(&_src_bn_max_.cpt(node)));
624
625 Size var_dSize = _src_bn_.variable(node).domainSize();
626 Size entry_size = tensor->domainSize() / var_dSize;
627
628 Instantiation ins(tensor);
629 Instantiation ins_min(tensor_min);
630 Instantiation ins_max(tensor_max);
631
632 ins.setFirst();
633 ins_min.setFirst();
634 ins_max.setFirst();
635
636 std::vector< GUM_SCALAR > vertex(var_dSize);
637
638 for (Size entry = 0; entry < entry_size; entry++) {
639 GUM_SCALAR den = 0;
640 Size nbm = 0;
641
642 for (Size modality = 0; modality < var_dSize; modality++) {
643 vertex[modality] = tensor->get(ins);
644
645 if (vertex[modality] < 1 && vertex[modality] > 0)
647 "idmLearning : the BayesNet contains "
648 "probabilities and not event counts.");
649
650 den += vertex[modality];
651
652 if (vertex[modality] > 0) nbm++;
653
654 ++ins;
655 }
656
657 if (nbm > 1 || !keepZeroes) den += s;
658
659 GUM_SCALAR min, max;
660
661 for (Size modality = 0; modality < var_dSize; modality++) {
662 min = vertex[modality];
663 max = min;
664
665 if ((vertex[modality] > 0 && nbm > 1) || !keepZeroes) { max += s; }
666
667 min = GUM_SCALAR(min * 1.0 / den);
668 max = GUM_SCALAR(max * 1.0 / den);
669
670 tensor_min->set(ins_min, min);
671 tensor_max->set(ins_max, max);
672
673 ++ins_min;
674 ++ins_max;
675 } // end of : for each modality
676 } // end of : for each entry
677 } // end of : for each variable
678
679 _epsilonMin_ = GUM_SCALAR(s);
680 _epsilonMax_ = GUM_SCALAR(s);
681 _epsilonMoy_ = GUM_SCALAR(s);
683 }
684
685 /* no need for lrs : (max ... min ... max) vertices from bnToCredal() */
686 template < GUM_Numeric GUM_SCALAR >
688 if (!_credalNet_src_cpt_.empty()) _credalNet_src_cpt_.clear();
689
690 _credalNet_src_cpt_.resize(_src_bn_.size());
691
692 for (auto node: _src_bn_.nodes()) {
693 const Tensor< GUM_SCALAR >* const tensor_min(&_src_bn_min_.cpt(node));
694 const Tensor< GUM_SCALAR >* const tensor_max(&_src_bn_max_.cpt(node));
695
696 Size var_dSize = _src_bn_.variable(node).domainSize();
697 Size entry_size = tensor_min->domainSize() / var_dSize;
698
699 std::vector< std::vector< std::vector< GUM_SCALAR > > > var_cpt(entry_size);
700
701 Instantiation ins_min(tensor_min);
702 Instantiation ins_max(tensor_max);
703
704 ins_min.setFirst();
705 ins_max.setFirst();
706
707 std::vector< GUM_SCALAR > lower(var_dSize);
708 std::vector< GUM_SCALAR > upper(var_dSize);
709
710 for (Size entry = 0; entry < entry_size; entry++) {
711 for (Size modality = 0; modality < var_dSize; modality++, ++ins_min, ++ins_max) {
712 lower[modality] = tensor_min->get(ins_min);
713 upper[modality] = tensor_max->get(ins_max);
714 }
715
716 bool all_equals = true;
717 std::vector< std::vector< GUM_SCALAR > > vertices;
718
719 for (Size modality = 0; modality < var_dSize; modality++) {
720 if (std::fabs(upper[modality] - lower[modality]) < 1e-6) continue;
721
722 all_equals = false;
723 std::vector< GUM_SCALAR > vertex(var_dSize);
724 vertex[modality] = upper[modality];
725
726 for (Size mod = 0; mod < var_dSize; mod++) {
727 if (modality != mod) vertex[mod] = lower[mod];
728 }
729
730 GUM_SCALAR total = 0;
731
732 auto vsize = vertex.size();
733
734 for (Size i = 0; i < vsize; i++)
735 total += vertex[i];
736
737 if (std::fabs(total - 1.) > 1e-6)
739 _src_bn_.variable(node).name()
740 << " does not sum to one for " << entry << std::endl
741 << vertex << std::endl);
742
743 vertices.push_back(vertex);
744 }
745
746 if (all_equals) {
747 std::vector< GUM_SCALAR > vertex(var_dSize);
748
749 for (Size modality = 0; modality < var_dSize; modality++)
750 vertex[modality] = lower[modality];
751
752 GUM_SCALAR total = 0.;
753
754 auto vsize = vertex.size();
755
756 for (Size i = 0; i < vsize; i++)
757 total += vertex[i];
758
759 if (std::fabs(total - 1.) > 1e-6)
761 _src_bn_.variable(node).name()
762 << " does not sum to one for " << entry << std::endl
763 << vertex << std::endl);
764
765 vertices.push_back(vertex);
766 }
767
768 var_cpt[entry] = vertices;
769 }
770
771 _credalNet_src_cpt_.insert(node, var_cpt);
772 } // end of : for each variable (node)
773
774 // get precise/credal/vacuous status of each variable
777 }
778
779 /* uses lrsWrapper */
780 template < GUM_Numeric GUM_SCALAR >
782 if (!_credalNet_src_cpt_.empty()) _credalNet_src_cpt_.clear();
783
784 _credalNet_src_cpt_.resize(_src_bn_.size());
785
786 LRSWrapper< GUM_SCALAR > lrsWrapper;
787
788 for (auto node: _src_bn_.nodes()) {
789 const Tensor< GUM_SCALAR >* const tensor_min(&_src_bn_min_.cpt(node));
790 const Tensor< GUM_SCALAR >* const tensor_max(&_src_bn_max_.cpt(node));
791
792 Size var_dSize = _src_bn_.variable(node).domainSize();
793 Size entry_size = tensor_min->domainSize() / var_dSize;
794
795 std::vector< std::vector< std::vector< GUM_SCALAR > > > var_cpt(entry_size);
796
797 Instantiation ins_min(tensor_min);
798 Instantiation ins_max(tensor_max);
799
800 ins_min.setFirst();
801 ins_max.setFirst();
802
803 lrsWrapper.setUpH(var_dSize);
804
805 for (Size entry = 0; entry < entry_size; entry++) {
806 for (Size modality = 0; modality < var_dSize; modality++) {
807 if (tensor_min->get(ins_min) > tensor_max->get(ins_max)) {
809 "For variable "
810 << _src_bn_.variable(node).name() << " (at " << ins_min
811 << "), the min is greater than the max : " << tensor_min->get(ins_min)
812 << ">" << tensor_max->get(ins_max) << ".");
813 }
814 lrsWrapper.fillH(tensor_min->get(ins_min), tensor_max->get(ins_max), modality);
815 ++ins_min;
816 ++ins_max;
817 }
818
819 lrsWrapper.H2V();
820 var_cpt[entry] = lrsWrapper.getOutput();
821 lrsWrapper.nextHInput();
822 }
823
824 _credalNet_src_cpt_.insert(node, var_cpt);
825 } // end of : for each variable (node)
826
827 // get precise/credal/vacuous status of each variable
830 }
831
832 /* call lrs */
833 template < GUM_Numeric GUM_SCALAR >
835 if (!_credalNet_src_cpt_.empty()) _credalNet_src_cpt_.clear();
836
837 _credalNet_src_cpt_.resize(_src_bn_.size());
838
839 for (auto node: _src_bn_.nodes()) {
840 const Tensor< GUM_SCALAR >* const tensor_min(&_src_bn_min_.cpt(node));
841 const Tensor< GUM_SCALAR >* const tensor_max(&_src_bn_max_.cpt(node));
842
843 auto var_dSize = _src_bn_.variable(node).domainSize();
844 auto entry_size = tensor_min->domainSize() / var_dSize;
845
846 std::vector< std::vector< std::vector< GUM_SCALAR > > > var_cpt(entry_size);
847
848 Instantiation ins_min(tensor_min);
849 Instantiation ins_max(tensor_max);
850
851 ins_min.setFirst();
852 ins_max.setFirst();
853
854 // use iterator
855 for (Size entry = 0; entry < entry_size; entry++) {
856 std::vector< std::vector< GUM_SCALAR > > vertices;
857 std::vector< GUM_SCALAR > vertex(var_dSize); // if not interval
858
859 std::vector< std::vector< GUM_SCALAR > > inequalities(
860 var_dSize * 2,
861 std::vector< GUM_SCALAR >(var_dSize + 1, 0));
862
863 std::vector< GUM_SCALAR > sum_ineq1(var_dSize + 1, -1);
864 std::vector< GUM_SCALAR > sum_ineq2(var_dSize + 1, 1);
865 sum_ineq1[0] = 1;
866 sum_ineq2[0] = -1;
867
868 bool isInterval = false;
869
870 for (Size modality = 0; modality < var_dSize; modality++) {
871 inequalities[modality * 2][0] = -tensor_min->get(ins_min);
872 inequalities[modality * 2 + 1][0] = tensor_max->get(ins_max);
873 inequalities[modality * 2][modality + 1] = 1;
874 inequalities[modality * 2 + 1][modality + 1] = -1;
875
876 vertex[modality] = inequalities[modality * 2 + 1][0];
877
878 if (!isInterval
879 && (-inequalities[modality * 2][0] != inequalities[modality * 2 + 1][0]))
880 isInterval = true;
881
882 ++ins_min;
883 ++ins_max;
884 }
885
886 inequalities.push_back(sum_ineq1);
887 inequalities.push_back(sum_ineq2);
888
889 if (!isInterval) {
890 vertices.push_back(vertex);
891 } else {
892 try {
893 _H2Vlrs_(inequalities, vertices);
894 // __H2Vcdd ( inequalities, vertices );
895 } catch (const std::exception& err) {
896 std::cout << err.what() << std::endl;
897 throw;
898 }
899 } // end of : is interval
900
901 if (entry == 0 && vertices.size() >= 2) {
902 auto tmp = vertices[0];
903 vertices[0] = vertices[1];
904 vertices[1] = tmp;
905 }
906
907 var_cpt[entry] = vertices;
908 } // end of : for each entry
909
910 _credalNet_src_cpt_.insert(node, var_cpt);
911 // std::cout << _src_bn_.variable(node_idIt).name() << std::endl;
912 // std::cout << var_cpt << std::endl;
913 } // end of : for each variable (node)
914
915 // get precise/credal/vacuous status of each variable
918 }
919
924 template < GUM_Numeric GUM_SCALAR >
925 void CredalNet< GUM_SCALAR >::saveBNsMinMax(std::string_view min_path,
926 std::string_view max_path) {
928
929 std::string minfilename(min_path); //"min.bif";
930 std::string maxfilename(max_path); //"max.bif";
931 std::ofstream min_file(minfilename.c_str(), std::ios::out | std::ios::trunc);
932 std::ofstream max_file(maxfilename.c_str(), std::ios::out | std::ios::trunc);
933
934 if (!min_file.good())
935 GUM_ERROR(IOError, "bnToCredal() : could not open stream : min_file : " << minfilename);
936
937 if (!max_file.good()) {
938 min_file.close();
939 GUM_ERROR(IOError, "bnToCredal() : could not open stream : min_file : " << maxfilename);
940 }
941
942 try {
943 writer.write(min_file, _src_bn_min_);
944 writer.write(max_file, _src_bn_max_);
945 } catch (Exception& err) {
946 GUM_SHOWERROR(err);
947 min_file.close();
948 max_file.close();
949 throw(err);
950 }
951
952 min_file.close();
953 max_file.close();
954 }
955
956 template < GUM_Numeric GUM_SCALAR >
958 // don't forget to delete the old one ( _current_), if necessary at the end
959 auto bin_bn = new BayesNet< GUM_SCALAR >();
960
961 // __bnCopy ( * _bin_bn_ );
962 // delete old one too
963 auto credalNet_bin_cpt
965
966 // delete old one too
967 auto bin_nodeType = new NodeProperty< NodeType >();
968
969 const BayesNet< GUM_SCALAR >* current_bn;
971 credalNet_current_cpt;
972
973 if (this->_current_bn_ == nullptr) current_bn = &this->_src_bn_;
974 else current_bn = this->_current_bn_;
975
976 if (this->_credalNet_current_cpt_ == nullptr)
977 credalNet_current_cpt = &this->_credalNet_src_cpt_;
978 else credalNet_current_cpt = this->_credalNet_current_cpt_;
979
980 if (!_var_bits_.empty()) _var_bits_.clear();
981
982 bin_bn->beginTopologyTransformation();
983
984 for (auto node: current_bn->nodes()) {
985 auto var_dSize = current_bn->variable(node).domainSize();
986
987 if (var_dSize != 2) {
988 unsigned long b;
989 unsigned long c;
990 superiorPow(static_cast< unsigned long >(var_dSize), b, c);
991 Size nb_bits{b};
992
993 std::string bit_name;
994 std::vector< NodeId > bits(nb_bits);
995
996 for (Size bit = 0; bit < nb_bits; bit++) {
997 bit_name = current_bn->variable(node).name() + "-b";
998 bit_name += std::to_string(bit);
999
1000 LabelizedVariable var_bit(bit_name, "node " + bit_name, 2);
1001 NodeId iD = bin_bn->add(var_bit);
1002
1003 bits[bit] = iD;
1004 } // end of : for each bit
1005
1006 _var_bits_.insert(node, bits);
1007 } // end of : if variable is not binary
1008 else {
1009 const std::string bit_name = current_bn->variable(node).name();
1010 LabelizedVariable var_bit(bit_name, "node " + bit_name, 2);
1011 const NodeId iD = bin_bn->add(var_bit);
1012
1013 _var_bits_.insert(node, std::vector< NodeId >(1, iD));
1014 }
1015 } // end of : for each original variable
1016
1017 for (auto node: current_bn->nodes()) {
1018 if (NodeSet parents = current_bn->parents(node); !parents.empty()) {
1019 for (auto par: current_bn->parents(node)) {
1020 for (Size parent_bit = 0, spbits = static_cast< Size >(_var_bits_[par].size());
1021 parent_bit < spbits;
1022 parent_bit++)
1023 for (Size var_bit = 0, mbits = static_cast< Size >(_var_bits_[node].size());
1024 var_bit < mbits;
1025 var_bit++)
1026 bin_bn->addArc(_var_bits_[par][parent_bit], _var_bits_[node][var_bit]);
1027 }
1028 }
1029
1030 // arcs with one's bits
1031 const auto bitsize = _var_bits_[node].size();
1032
1033 for (Size bit_c = 1; bit_c < bitsize; bit_c++)
1034 for (Size bit_p = 0; bit_p < bit_c; bit_p++)
1035 bin_bn->addArc(_var_bits_[node][bit_p], _var_bits_[node][bit_c]);
1036 } // end of : for each original variable
1037
1038 bin_bn->endTopologyTransformation();
1039
1040 // binarization of cpts
1041
1042 const auto varsize = current_bn->size();
1043
1044 for (Size var = 0; var < varsize; var++) {
1045 const auto bitsize = _var_bits_[var].size();
1046
1047 for (Size i = 0; i < bitsize; i++) {
1048 Tensor< GUM_SCALAR > const* tensor(&bin_bn->cpt(_var_bits_[var][i]));
1049 Instantiation ins(tensor);
1050 ins.setFirst();
1051
1052 auto entry_size = tensor->domainSize() / 2;
1053 std::vector< std::vector< std::vector< GUM_SCALAR > > > var_cpt(entry_size);
1054
1055 Size old_conf = 0;
1056
1057 for (Size conf = 0; conf < entry_size; conf++) {
1058 std::vector< std::vector< GUM_SCALAR > > pvar_cpt;
1059 auto verticessize = (*credalNet_current_cpt)[var][old_conf].size();
1060
1061 for (Size old_distri = 0; old_distri < verticessize; old_distri++) {
1062 const std::vector< GUM_SCALAR >& vertex
1063 = (*credalNet_current_cpt)[var][old_conf][old_distri];
1064 auto vertexsize = vertex.size();
1065
1066 std::vector< Idx > incc(vertexsize, 0);
1067
1068 for (Size preced = 0; preced < i; preced++) {
1069 auto bit_pos = ins.pos(bin_bn->variable(_var_bits_[var][preced]));
1070 auto val = ins.val(bit_pos);
1071
1072 Size pas = Size(int2Pow(preced));
1073 Size elem;
1074
1075 if (val == 0) elem = 0;
1076 else elem = pas;
1077
1078 while (elem < vertexsize) {
1079 incc[elem]++;
1080 elem++;
1081
1082 if (elem % pas == 0) elem += pas;
1083 }
1084 }
1085
1086 Size pas = Size(int2Pow(i));
1087
1088 std::vector< GUM_SCALAR > distri(2, 0);
1089 int pos = 1;
1090
1091 for (Size elem = 0; elem < vertexsize; elem++) {
1092 if (elem % pas == 0) pos = -pos;
1093
1094 if (incc[elem] == i)
1095 (pos < 0) ? (distri[0] += vertex[elem]) : (distri[1] += vertex[elem]);
1096 }
1097
1098 if (i > 0) {
1099 GUM_SCALAR den = distri[0] + distri[1];
1100
1101 if (den == 0) {
1102 distri[0] = 0;
1103 distri[1] = 0;
1104 } else {
1105 distri[0] /= den;
1106 distri[1] /= den;
1107 }
1108 }
1109
1110 pvar_cpt.push_back(distri);
1111 } // end of old distris
1112
1113 // get min/max approx, 2 vertices
1114 std::vector< std::vector< GUM_SCALAR > > vertices(2, std::vector< GUM_SCALAR >(2, 1));
1115 vertices[1][1] = 0;
1116
1117 const auto new_verticessize = pvar_cpt.size();
1118
1119 for (Size v = 0; v < new_verticessize; v++) {
1120 if (pvar_cpt[v][1] < vertices[0][1]) vertices[0][1] = pvar_cpt[v][1];
1121
1122 if (pvar_cpt[v][1] > vertices[1][1]) vertices[1][1] = pvar_cpt[v][1];
1123 }
1124
1125 vertices[0][0] = 1 - vertices[0][1];
1126 vertices[1][0] = 1 - vertices[1][1];
1127
1128 pvar_cpt = vertices;
1129
1130 var_cpt[conf] = pvar_cpt;
1131
1132 ++ins;
1133 ++ins;
1134
1135 old_conf++;
1136
1137 if (old_conf == (*credalNet_current_cpt)[var].size()) old_conf = 0;
1138 } // end of new parent conf
1139
1140 credalNet_bin_cpt->insert(_var_bits_[var][i], var_cpt);
1141 } // end of bit i
1142 } // end of old variable
1143
1144 bin_bn->beginTopologyTransformation();
1145
1146 /* indicatrices variables */
1147 const auto old_varsize = _var_bits_.size();
1148
1149 for (Size i = 0; i < old_varsize; i++) {
1150 auto bitsize = _var_bits_[i].size();
1151
1152 // binary variable
1153 if (bitsize == 1) continue;
1154
1155 auto old_card = _src_bn_.variable(i).domainSize();
1156
1157 for (Size mod = 0; mod < old_card; mod++) {
1158 auto ss_str = std::format("{}-v{}", _src_bn_.variable(i).name(), mod);
1159
1160 LabelizedVariable var(ss_str, "node " + ss_str, 2);
1161 const NodeId indic = bin_bn->add(var);
1162
1163 // arcs from one's bits
1164 for (Size bit = 0; bit < bitsize; bit++)
1165 bin_bn->addArc(_var_bits_[i][bit], indic);
1166
1167 // cpt
1168 Size num = Size(int2Pow(long(bitsize)));
1169
1170 std::vector< std::vector< std::vector< GUM_SCALAR > > > icpt(num);
1171
1172 for (Size entry = 0; entry < num; entry++) {
1173 std::vector< std::vector< GUM_SCALAR > > vertices(1, std::vector< GUM_SCALAR >(2, 0));
1174
1175 if (mod == entry) vertices[0][1] = 1;
1176 else vertices[0][0] = 1;
1177
1178 icpt[entry] = vertices;
1179 }
1180
1181 credalNet_bin_cpt->insert(indic, icpt);
1182
1183 bin_nodeType->insert(indic, NodeType::Indic);
1184 } // end of each modality, i.e. as many indicatrice
1185 }
1186
1187 bin_bn->endTopologyTransformation();
1188
1189 if (this->_current_bn_ != nullptr) delete this->_current_bn_;
1190
1191 this->_current_bn_ = bin_bn;
1192
1193 if (this->_credalNet_current_cpt_ != nullptr) delete this->_credalNet_current_cpt_;
1194
1195 this->_credalNet_current_cpt_ = credalNet_bin_cpt;
1196
1197 if (this->_current_nodeType_ != nullptr) delete this->_current_nodeType_;
1198
1199 this->_current_nodeType_ = bin_nodeType;
1200
1201 _sort_varType_(); // will fill _bin_nodeType_ except for NodeType::Indic
1202 // variables
1203
1205 }
1206
1207 template < GUM_Numeric GUM_SCALAR >
1214
1215 template < GUM_Numeric GUM_SCALAR >
1220
1221 template < GUM_Numeric GUM_SCALAR >
1224 if (_current_nodeType_ != nullptr) return (*(_current_nodeType_))[id];
1225
1226 return _original_nodeType_[id];
1227 }
1228
1229 template < GUM_Numeric GUM_SCALAR >
1232 return _original_nodeType_[id];
1233 }
1234
1235 template < GUM_Numeric GUM_SCALAR >
1239
1240 template < GUM_Numeric GUM_SCALAR >
1244
1245 // only if CN is binary !!
1246 template < GUM_Numeric GUM_SCALAR >
1248 // ASSUMPTION: NodeIds are dense 0..N-1 (no nodes ever removed from the BN).
1249 // If sparse NodeIds are introduced, indexing by node will go out of bounds.
1250 _binCptMin_.resize(current_bn().size());
1251 _binCptMax_.resize(current_bn().size());
1252
1253 for (auto node: current_bn().nodes()) {
1254 auto pConf = credalNet_currentCpt()[node].size();
1255 std::vector< GUM_SCALAR > min(pConf);
1256 std::vector< GUM_SCALAR > max(pConf);
1257
1258 for (Size pconf = 0; pconf < pConf; pconf++) {
1259 GUM_SCALAR v1, v2;
1260 v1 = credalNet_currentCpt()[node][pconf][0][1];
1261
1262 if (credalNet_currentCpt()[node][pconf].size() > 1)
1263 v2 = credalNet_currentCpt()[node][pconf][1][1];
1264 else v2 = v1;
1265
1266 GUM_SCALAR delta = v1 - v2;
1267 min[pconf] = (delta >= 0) ? v2 : v1;
1268 max[pconf] = (delta >= 0) ? v1 : v2;
1269 }
1270
1271 _binCptMin_[node] = min;
1272 _binCptMax_[node] = max;
1273 }
1274
1276 }
1277
1278 template < GUM_Numeric GUM_SCALAR >
1279 const std::vector< std::vector< GUM_SCALAR > >&
1283
1284 template < GUM_Numeric GUM_SCALAR >
1285 const std::vector< std::vector< GUM_SCALAR > >&
1289
1290 template < GUM_Numeric GUM_SCALAR >
1291 const GUM_SCALAR& CredalNet< GUM_SCALAR >::epsilonMin() const {
1292 return _epsilonMin_;
1293 }
1294
1295 template < GUM_Numeric GUM_SCALAR >
1296 const GUM_SCALAR& CredalNet< GUM_SCALAR >::epsilonMax() const {
1297 return _epsilonMax_;
1298 }
1299
1300 template < GUM_Numeric GUM_SCALAR >
1301 const GUM_SCALAR& CredalNet< GUM_SCALAR >::epsilonMean() const {
1302 return _epsilonMoy_;
1303 }
1304
1305 template < GUM_Numeric GUM_SCALAR >
1307 std::stringstream output;
1308 const BayesNet< GUM_SCALAR >* bn_ptr = (_current_bn_ != nullptr) ? _current_bn_ : &_src_bn_;
1311
1312 for (auto node: bn_ptr->nodes()) {
1313 const Tensor< GUM_SCALAR >* tensor(&bn_ptr->cpt(node));
1314 auto pconfs = tensor->domainSize() / bn_ptr->variable(node).domainSize();
1315
1316 output << "\n" << bn_ptr->variable(node) << "\n";
1317
1318 Instantiation ins(tensor);
1319 ins.forgetMaster();
1320 ins.erase(bn_ptr->variable(node));
1321 ins.setFirst();
1322
1323 for (Size pconf = 0; pconf < pconfs; pconf++) {
1324 output << ins << " : ";
1325 output << (*cpt_ptr)[node][pconf] << "\n";
1326
1327 if (pconf < pconfs - 1) ++ins;
1328 }
1329 }
1330
1331 output << "\n";
1332
1333 return output.str();
1334 }
1335
1336 template < GUM_Numeric GUM_SCALAR >
1337 const BayesNet< GUM_SCALAR >& CredalNet< GUM_SCALAR >::current_bn() const {
1338 if (_current_bn_ != nullptr) return *_current_bn_;
1339
1340 return _src_bn_;
1341 }
1342
1343 template < GUM_Numeric GUM_SCALAR >
1344 const BayesNet< GUM_SCALAR >& CredalNet< GUM_SCALAR >::src_bn() const {
1345 return _src_bn_;
1346 }
1347
1349
1351
1352 template < GUM_Numeric GUM_SCALAR >
1354 _epsilonMin_ = 0;
1355 _epsilonMax_ = 0;
1356 _epsilonMoy_ = 0;
1357
1358 _epsRedund_ = GUM_SCALAR(1e-6);
1359
1360 // farey algorithm
1361 _epsF_ = GUM_SCALAR(1e-6);
1362 _denMax_ = GUM_SCALAR(1e6); // beware LRSWrapper
1363
1364 // continued fractions, beware LRSWrapper
1365 // decimal paces ( _epsC_ * _precisionC_ == 1)
1366 _precisionC_ = GUM_SCALAR(1e6);
1367 _deltaC_ = 5;
1368
1369 // old custom algorithm
1370 _precision_ = GUM_SCALAR(1e6); // beware LRSWrapper
1371
1372 _current_bn_ = nullptr;
1373 _credalNet_current_cpt_ = nullptr;
1374 _current_nodeType_ = nullptr;
1375
1377 }
1378
1379 template < GUM_Numeric GUM_SCALAR >
1380 void CredalNet< GUM_SCALAR >::_initCNNets_(std::string_view src_min_num,
1381 std::string_view src_max_den) {
1382 BIFReader< GUM_SCALAR > reader(&_src_bn_, src_min_num);
1383 std::string other;
1384
1385 if (src_max_den.compare("") != 0) other = src_max_den;
1386 else other = src_min_num;
1387
1388 BIFReader< GUM_SCALAR > reader_min(&_src_bn_min_, src_min_num);
1389 BIFReader< GUM_SCALAR > reader_max(&_src_bn_max_, other);
1390
1391 reader.proceed();
1392 reader_min.proceed();
1393 reader_max.proceed();
1394 }
1395
1396 template < GUM_Numeric GUM_SCALAR >
1397 void CredalNet< GUM_SCALAR >::_initCNNets_(const BayesNet< GUM_SCALAR >& src_min_num,
1398 const BayesNet< GUM_SCALAR >& src_max_den) {
1399 _src_bn_ = src_min_num;
1400 _src_bn_min_ = src_min_num;
1401
1402 if (src_max_den.size() > 0) _src_bn_max_ = src_max_den;
1403 else _src_bn_max_ = src_min_num;
1404 }
1405
1406 template < GUM_Numeric GUM_SCALAR >
1408 const std::vector< std::vector< std::vector< GUM_SCALAR > > >& var_cpt) const {
1409 Size vertices_size = 0;
1410
1411 for (auto entry = var_cpt.cbegin(), theEnd = var_cpt.cend(); entry != theEnd; ++entry) {
1412 if (entry->size() > vertices_size) vertices_size = Size(entry->size());
1413 }
1414
1415 return int(vertices_size);
1416 }
1417
1418 template < GUM_Numeric GUM_SCALAR >
1419 void CredalNet< GUM_SCALAR >::_bnCopy_(BayesNet< GUM_SCALAR >& dest) {
1420 const BayesNet< GUM_SCALAR >* bn_ptr = (_current_bn_ != nullptr) ? _current_bn_ : &_src_bn_;
1421
1422 for (auto node: bn_ptr->nodes())
1423 dest.add(bn_ptr->variable(node));
1424
1425 dest.beginTopologyTransformation();
1426
1427 for (auto node: bn_ptr->nodes()) {
1428 for (auto parent_idIt: bn_ptr->cpt(node).variablesSequence()) {
1429 if (bn_ptr->nodeId(*parent_idIt) != node) dest.addArc(bn_ptr->nodeId(*parent_idIt), node);
1430 } // end of : for each parent in order of appearence
1431 } // end of : for each variable
1432
1433 dest.endTopologyTransformation();
1434 }
1435
1436 /*
1437 // cdd can use real values, not just rationals / integers
1438 template< GumScalar GUM_SCALAR >
1439 void CredalNet< GUM_SCALAR >:: _H2Vcdd_ ( const std::vector< std::vector<
1440 GUM_SCALAR > > & h_rep, std::vector< std::vector< GUM_SCALAR > > & v_rep )
1441 const {
1442 dd_set_global_constants();
1443
1444 dd_MatrixPtr M, G;
1445 dd_PolyhedraPtr poly;
1446 dd_ErrorType err;
1447
1448 unsigned int rows = h_rep.size();
1449 unsigned int cols = 0;
1450 if( h_rep.size() > 0 )
1451 cols = h_rep[0].size();
1452
1453 M = dd_CreateMatrix( rows, cols);
1454
1455 for ( unsigned int row = 0; row < rows; row++ )
1456 for ( unsigned int col = 0; col < cols; col++ )
1457 dd_set_d( M->matrix[row][col], h_rep[row][col] );
1458
1459 M->representation = dd_Inequality;
1460
1461 poly = dd_DDMatrix2Poly(M, &err);
1462 G = dd_CopyGenerators(poly);
1463
1464 rows = G->rowsize;
1465 cols = G->colsize;
1466
1467 v_rep.clear();
1468 for ( unsigned int row = 0; row < rows; row++ ) {
1469 std::vector< GUM_SCALAR > aRow(cols - 1);
1470
1471 if ( *G->matrix[row][0] != 1 )
1472 GUM_ERROR(OperationNotAllowed, " __H2Vcdd : not reading a vertex")
1473
1474 for ( unsigned int col = 0; col < cols - 1; col++ )
1475 aRow[col] = *G->matrix[row][ col + 1 ];
1476
1477 v_rep.push_back(aRow);
1478 }
1479
1480 dd_FreeMatrix(M);
1481 dd_FreeMatrix(G);
1482 dd_FreePolyhedra(poly);
1483
1484 dd_free_global_constants();
1485 }
1486 */
1487
1488 template < GUM_Numeric GUM_SCALAR >
1489 void CredalNet< GUM_SCALAR >::_H2Vlrs_(const std::vector< std::vector< GUM_SCALAR > >& h_rep,
1490 std::vector< std::vector< GUM_SCALAR > >& v_rep) const {
1491 // write H rep file
1492 int64_t num, den;
1493
1494 std::string sinefile = getUniqueFileName(); // generate unique file name, we
1495 // need to add .ine or .ext for lrs
1496 // to know which input it is (Hrep
1497 // to Vrep or Vrep to Hrep)
1498 sinefile += ".ine";
1499
1500 std::ofstream h_file(sinefile.c_str(), std::ios::out | std::ios::trunc);
1501
1502 if (!h_file.good())
1503 GUM_ERROR(IOError, " __H2Vlrs : could not open lrs input file : " << sinefile)
1504
1505 h_file << "H - representation\n";
1506 h_file << "begin\n";
1507 h_file << h_rep.size() << ' ' << h_rep[0].size() << " rational\n";
1508
1509 for (auto it = h_rep.cbegin(), theEnd = h_rep.cend(); it != theEnd; ++it) {
1510 for (auto it2 = it->cbegin(), theEnd2 = it->cend(); it2 != theEnd2; ++it2) {
1511 // get integer fraction from decimal value
1512 // smallest numerator & denominator is farley, also
1513 // best precision
1515 den,
1516 ((*it2 > 0) ? *it2 : -*it2),
1517 int64_t(_denMax_),
1518 _epsF_);
1519
1520 h_file << ((*it2 > 0) ? num : -num) << '/' << den << ' ';
1521 }
1522
1523 h_file << '\n';
1524 }
1525
1526 h_file << "end\n";
1527 h_file.close();
1528
1529 // call lrs
1530 std::string extfile(sinefile);
1531 extfile += ".ext";
1532
1533 std::string lrs_arg0 = "lrs";
1534 std::string lrs_arg1 = sinefile;
1535 std::string lrs_arg2 = extfile;
1536 char* lrs_argv[3] = {lrs_arg0.data(), lrs_arg1.data(), lrs_arg2.data()};
1537
1538 // it may need to redirect stdout to a file
1539 lrs_main(3, lrs_argv);
1540
1541 // read V rep file
1542 std::ifstream v_file(extfile.c_str() /*extfilename.c_str()*/, std::ios::in);
1543
1544 if (!v_file.good()) GUM_ERROR(IOError, " __H2Vlrs : could not open lrs output file : ")
1545
1546 std::string line, tmp;
1547 char * cstr, *p;
1548 GUM_SCALAR probability;
1549
1550 std::string::size_type pos;
1551 bool keep_going = true;
1552 // int vertices;
1553
1554 std::vector< GUM_SCALAR > vertex;
1555
1556 v_file.ignore(256, 'l');
1557
1558 while (v_file.good() && keep_going) {
1559 getline(v_file, line);
1560
1561 if (line.size() == 0) continue;
1562 else if (line.compare("end") == 0) {
1563 keep_going = false;
1564 // this is to get vertices number :
1565 /*getline ( v_file, line );
1566 std::string::size_type pos, end_pos;
1567 pos = line.find ( "vertices = " );
1568 end_pos = line.find ( "rays", pos + 9 );
1569 vertices = atoi ( line.substr ( pos + 9, end_pos - pos - 9 ).c_str()
1570 );*/
1571 break;
1572 } else if (line[1] != '1') {
1574 " __H2Vlrs : reading something other than a vertex from "
1575 "lrs output file : ");
1576 }
1577
1578 line = line.substr(2);
1579 cstr = new char[line.size() + 1];
1580 strcpy(cstr, line.c_str());
1581
1582 p = strtok(cstr, " ");
1583
1584 while (p != nullptr) {
1585 tmp = p;
1586
1587 if (tmp.compare("1") == 0 || tmp.compare("0") == 0)
1588 probability = GUM_SCALAR(atof(tmp.c_str()));
1589 else {
1590 pos = tmp.find("/");
1591 probability = GUM_SCALAR(atof(tmp.substr(0, pos).c_str())
1592 / atof(tmp.substr(pos + 1, tmp.size()).c_str()));
1593 }
1594
1595 vertex.push_back(probability);
1596 p = strtok(nullptr, " ");
1597 } // end of : for all tokens
1598
1599 delete[] cstr;
1600
1601 // compute is_redund using multiple threads:
1602 // compute the max number of threads to use (avoid nested threads)
1603 const Size nb_threads = ThreadExecutor::nbRunningThreadsExecutors() == 0
1605 : 1; // no nested multithreading
1606
1607 const auto nsize = v_rep.size();
1608 const auto real_nb_threads = std::min(nb_threads, nsize);
1609
1610 // prepare the data used by the threads
1611 const auto ranges = gum::dispatchRangeToThreads(0, nsize, (unsigned int)(real_nb_threads));
1612 std::vector< Size > t_redund(real_nb_threads); // use Size to avoid false sharing
1613
1614 // create the function to be executed by the threads
1615 auto threadedExec = [this, ranges, &t_redund, vertex, v_rep](const std::size_t this_thread,
1616 const std::size_t nb_threads) {
1617 const auto vsize = vertex.size();
1618 auto& thread_redund = t_redund[this_thread];
1619
1620 for (Idx i = ranges[this_thread].first, end = ranges[this_thread].second; i < end; i++) {
1621 thread_redund = 1;
1622 for (Idx modality = 0; modality < vsize; ++modality) {
1623 if (std::fabs(vertex[modality] - v_rep[i][modality]) > _epsRedund_) {
1624 thread_redund = 0;
1625 break;
1626 }
1627 }
1628
1629 if (thread_redund) return;
1630 }
1631 };
1632
1633 // launch the threads
1634 ThreadExecutor::execute(real_nb_threads, threadedExec);
1635
1636 // aggregate the results
1637 bool is_redund = false;
1638 for (const auto thread_redund: t_redund) {
1639 if (thread_redund) {
1640 is_redund = true;
1641 break;
1642 }
1643 }
1644
1645
1646 /*
1647 // old openMP code:
1648 #pragma omp parallel
1649 {
1650 int this_thread = threadsOMP::getThreadNumber();
1651 int num_threads = threadsOMP::getNumberOfRunningThreads();
1652
1653 auto begin_pos = (this_thread + 0) * v_rep.size() / num_threads;
1654 auto end_pos = (this_thread + 1) * v_rep.size() / num_threads;
1655
1656 for (auto p = begin_pos; p < end_pos; p++) {
1657 #pragma omp flush(is_redund)
1658
1659 if (is_redund) break;
1660
1661 bool thread_redund = true;
1662
1663 auto vsize = vertex.size();
1664
1665 for (Size modality = 0; modality < vsize; modality++) {
1666 if (std::fabs(vertex[modality] - v_rep[p][modality]) > _epsRedund_) {
1667 thread_redund = false;
1668 break;
1669 }
1670 }
1671
1672 if (thread_redund) {
1673 is_redund = true;
1674 #pragma omp flush(is_redund)
1675 int i=0; // this line to work around a weird syntax error with msvc
1676 }
1677 } // end of : each thread for
1678 } // end of : parallel
1679 */
1680
1681 if (!is_redund) v_rep.push_back(vertex);
1682
1683 vertex.clear();
1684 } // end of : file
1685
1686 v_file.close();
1687
1688 if (std::remove(sinefile.c_str()) != 0) GUM_ERROR(IOError, "error removing : " + sinefile)
1689
1690 if (std::remove(extfile.c_str()) != 0) GUM_ERROR(IOError, "error removing : " + extfile)
1691 }
1692
1693 template < GUM_Numeric GUM_SCALAR >
1695 const BayesNet< GUM_SCALAR >* bn_ptr = (_current_bn_ != nullptr) ? _current_bn_ : &_src_bn_;
1698 NodeProperty< NodeType >* nodeType_ptr
1700
1701 for (auto node: bn_ptr->nodes()) {
1702 // indicatrices are already present
1703 if (nodeType_ptr->exists(node)) continue;
1704
1705 bool precise = true, vacuous = true;
1706
1707 for (auto entry = (*cpt_ptr)[node].cbegin(), theEnd2 = (*cpt_ptr)[node].cend();
1708 entry != theEnd2;
1709 ++entry) {
1710 auto vertices = entry->size();
1711 auto var_dSize = (*entry)[0].size();
1712
1713 if (precise && vertices > 1) precise = false;
1714
1715 if (vacuous && vertices == var_dSize) {
1716 std::vector< bool > elem(var_dSize, false);
1717
1718 for (auto vertex = entry->cbegin(), vEnd = entry->cend(); vertex != vEnd; ++vertex) {
1719 for (auto probability = vertex->cbegin(), pEnd = vertex->cend(); probability != pEnd;
1720 ++probability) {
1721 if (*probability == 1) {
1722 elem[probability - vertex->begin()] = true;
1723 break;
1724 }
1725 } // end of : for each modality
1726 } // end of : for each vertex
1727
1728 for (auto probability = elem.cbegin(); probability != elem.cend(); ++probability)
1729 if (*probability == false) vacuous = false;
1730 } // end of : if vertices == dSize
1731 else
1732 vacuous = false;
1733
1734 if (vacuous == false && precise == false) {
1735 nodeType_ptr->insert(node, NodeType::Credal);
1736 break;
1737 }
1738 } // end of : for each parents entry
1739
1740 if (vacuous) nodeType_ptr->insert(node, NodeType::Vacuous);
1741 else if (precise) nodeType_ptr->insert(node, NodeType::Precise);
1742 } // end of : for each variable
1743 }
1744 } // namespace credal
1745} // namespace gum
Definition of templatized reader of BIF files for Bayesian networks.
Definition BIFReader.h:143
Size proceed() override
parse.
Writes a IBayesNet in the BIF format.
Definition BIFWriter.h:79
void write(std::ostream &output, IBayesNet< GUM_SCALAR > &bn)
Writes a Bayesian network in the output stream.
Exception base for CPT error.
virtual Size domainSize() const =0
Exception : a similar element already exists.
Base class for all aGrUM's exceptions.
Definition exceptions.h:122
value_type & insert(const Key &key, const Val &val)
Adds a new element (actually a copy of this element) into the hash table.
bool exists(const Key &key) const
Checks whether there exists an element with a given key in the hashtable.
Exception : input/output problem.
Class for assigning/browsing values to tuples of discrete variables.
const Sequence< const DiscreteVariable * > & variablesSequence() const final
Returns the sequence of DiscreteVariable of this instantiation.
Idx pos(const DiscreteVariable &v) const final
Returns the position of the variable v.
void erase(const DiscreteVariable &v) final
Removes a variable from the Instantiation.
void reorder(const Sequence< const DiscreteVariable * > &v)
Reorder vars of this instantiation giving the order in v.
Idx val(Idx i) const
Returns the current value of the variable at position i.
void setFirst()
Assign the first values to the tuple of the Instantiation.
const DiscreteVariable & variable(Idx i) const final
Returns the variable at position i in the tuple.
Idx nbrDim() const final
Returns the number of variables in the Instantiation.
bool forgetMaster()
Deassociate the master MultiDimAdressable, if any.
class LabelizedVariable
Exception : operation not allowed.
static void farey(int64_t &numerator, int64_t &denominator, const GUM_SCALAR &number, const int64_t &den_max=1000000L, const GUM_SCALAR &zero=1e-6)
Find the rational close enough to a given ( decimal ) number in [-1,1] and whose denominator is not h...
Exception : problem with size.
void _H2Vlrs_(const std::vector< std::vector< GUM_SCALAR > > &h_rep, std::vector< std::vector< GUM_SCALAR > > &v_rep) const
void _initParams_()
Initialize private constant variables after the Constructor has been called.
GUM_SCALAR _epsilonMoy_
The average perturbation of the BayesNet provided as input for this CredalNet.
Definition credalNet.h:566
void setCPTs(const NodeId &id, const std::vector< std::vector< std::vector< GUM_SCALAR > > > &cpt)
Set the vertices of the credal sets ( all of the conditionals ) of a given node
GUM_SCALAR _deltaC_
5 by default, used by fracC as number of decimals.
Definition credalNet.h:553
Size domainSize(const NodeId &id)
Get the cardinality of a node
BayesNet< GUM_SCALAR > _src_bn_max_
BayesNet used to store upper probabilities.
Definition credalNet.h:596
void _initCNNets_(std::string_view src_min_num, std::string_view src_max_den)
Initialize private BayesNet variables after the Constructor has been called.
BayesNet< GUM_SCALAR > * _current_bn_
Up-to-date BayesNet (used as a DAG).
Definition credalNet.h:599
void _intervalToCredal_()
Computes the vertices of each credal set according to their interval definition (does not use lrs).
std::string toString() const
bool _hasComputedBinaryCPTMinMax_
Used by L2U, to know if lower and upper probabilities over the second modality has been stored in ord...
Definition credalNet.h:618
NodeProperty< std::vector< std::vector< std::vector< GUM_SCALAR > > > > _credalNet_src_cpt_
This CredalNet original CPTs.
Definition credalNet.h:602
const GUM_SCALAR & epsilonMax() const
void _bnCopy_(BayesNet< GUM_SCALAR > &bn_dest)
bool hasComputedBinaryCPTMinMax() const
GUM_SCALAR _precision_
Precision used by frac.
Definition credalNet.h:583
const NodeProperty< std::vector< std::vector< std::vector< GUM_SCALAR > > > > & credalNet_currentCpt() const
void bnToCredal(GUM_SCALAR beta, bool oneNet, bool keepZeroes)
Perturbates the BayesNet provided as input for this CredalNet by generating intervals instead of poin...
void intervalToCredal()
Computes the vertices of each credal set according to their interval definition (uses lrs).
const GUM_SCALAR & epsilonMean() const
std::vector< std::vector< GUM_SCALAR > > _binCptMin_
Used with binary networks to speed-up L2U inference.
Definition credalNet.h:625
void saveBNsMinMax(std::string_view min_path, std::string_view max_path)
If this CredalNet was built over a perturbed BayesNet, one can save the intervals as two BayesNet.
void approximatedBinarization()
Approximate binarization.
const BayesNet< GUM_SCALAR > & src_bn() const
int _find_dNode_card_(const std::vector< std::vector< std::vector< GUM_SCALAR > > > &var_cpt) const
BayesNet< GUM_SCALAR > _src_bn_
Original BayesNet (used as a DAG).
Definition credalNet.h:591
void computeBinaryCPTMinMax()
Used with binary networks to speed-up L2U inference.
const NodeProperty< std::vector< std::vector< std::vector< GUM_SCALAR > > > > & credalNet_srcCpt() const
NodeType currentNodeType(const NodeId &id) const
NodeProperty< NodeType > _original_nodeType_
The NodeType of each node from the ORIGINAL network.
Definition credalNet.h:612
void lagrangeNormalization()
Normalize counts of a BayesNet storing counts of each events such that no probability is 0.
NodeProperty< std::vector< std::vector< std::vector< GUM_SCALAR > > > > * _credalNet_current_cpt_
This CredalNet up-to-date CPTs.
Definition credalNet.h:606
bool isSeparatelySpecified() const
void _sort_varType_()
Set the NodeType of each node
void idmLearning(const Idx s=0, const bool keepZeroes=false)
Learns parameters from a BayesNet storing counts of events.
GUM_SCALAR _epsilonMax_
The highest perturbation of the BayesNet provided as input for this CredalNet.
Definition credalNet.h:562
std::vector< std::vector< GUM_SCALAR > > _binCptMax_
Used with binary networks to speed-up L2U inference.
Definition credalNet.h:633
NodeType nodeType(const NodeId &id) const
Instantiation instantiation(const NodeId &id)
Get an Instantiation from a node id, useful to fill the constraints of the network
bool _separatelySpecified_
TRUE if this CredalNet is separately and interval specified, FALSE otherwise.
Definition credalNet.h:588
void setCPT(const NodeId &id, const Size &entry, const std::vector< std::vector< GUM_SCALAR > > &cpt)
Set the vertices of one credal set of a given node ( any instantiation index )
NodeProperty< std::vector< NodeId > > _var_bits_
Corresponding bits of each variable.
Definition credalNet.h:609
const std::vector< std::vector< GUM_SCALAR > > & get_binaryCPT_max() const
Used with binary networks to speed-up L2U inference.
BayesNet< GUM_SCALAR > _src_bn_min_
BayesNet used to store lower probabilities.
Definition credalNet.h:594
NodeId addVariable(std::string_view name, const Size &card)
Adds a discrete node into the network.
const BayesNet< GUM_SCALAR > & current_bn() const
void addArc(const NodeId &tail, const NodeId &head)
Adds an arc between two nodes.
const std::vector< std::vector< GUM_SCALAR > > & get_binaryCPT_min() const
Used with binary networks to speed-up L2U inference.
void fillConstraints(const NodeId &id, const std::vector< GUM_SCALAR > &lower, const std::vector< GUM_SCALAR > &upper)
Set the interval constraints of the credal sets of a given node (all instantiations )
GUM_SCALAR _epsilonMin_
The lowest perturbation of the BayesNet provided as input for this CredalNet.
Definition credalNet.h:558
const GUM_SCALAR & epsilonMin() const
NodeProperty< NodeType > * _current_nodeType_
The NodeType of each node from the up-to-date network.
Definition credalNet.h:614
GUM_SCALAR _denMax_
Highest possible denominator allowed when using farey.
Definition credalNet.h:580
void fillConstraint(const NodeId &id, const Idx &entry, const std::vector< GUM_SCALAR > &lower, const std::vector< GUM_SCALAR > &upper)
Set the interval constraints of a credal set of a given node ( from an instantiation index )
GUM_SCALAR _epsRedund_
Value under which a decimal number is considered to be zero when computing redundant vertices.
Definition credalNet.h:571
GUM_SCALAR _precisionC_
1e6 by default, used by fracC as precision.
Definition credalNet.h:551
NodeType
NodeType to speed-up computations in some algorithms.
Definition credalNet.h:100
CredalNet()
Constructor used to create a CredalNet step by step, i.e.
GUM_SCALAR _epsF_
Value under which a decimal number is considered to be zero when using farey.
Definition credalNet.h:576
Class template acting as a wrapper for Lexicographic Reverse Search by David Avis.
Definition LrsWrapper.h:121
const matrix & getOutput() const
Get the output matrix solution of the problem.
void H2V()
H-representation to V-representation.
void fillH(const GUM_SCALAR &min, const GUM_SCALAR &max, const Size &modal)
Creates the H-representation of min <= p(X=modal | .) <= max and add it to the problem input _input_.
void setUpH(const Size &card)
Sets up an H-representation.
void nextHInput()
Reset the wrapper for next computation for a H-representation with the same variable cardinality and ...
Class representing Credal Networks.
#define GUM_ERROR(type, msg)
Definition exceptions.h:76
#define GUM_SHOWERROR(e)
Definition exceptions.h:89
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.
HashTable< NodeId, VAL > NodeProperty
Property on graph elements.
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
uint64_t int2Pow(uint64_t exponent)
Specialized base 2 pow function with integer.
Definition pow_inl.h:70
void superiorPow(unsigned long card, unsigned long &num_bits, unsigned long &new_card)
Compute the superior and closest power of two of an integer.
Definition pow_inl.h:79
std::string getUniqueFileName()
Returns a path to a unique file name.
Useful macros for maths.
namespace for all credal networks entities
Definition agrum.h:61
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
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
bool isCloseToOne(T x, T tol=T(1e-9))
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
Utilities for manipulating strings.