1 /**
2  *
3  *   Copyright (c) 2005-2021 by Pierre-Henri WUILLEMIN(_at_LIP6) & Christophe GONZALES(_at_AMU)
4  *   info_at_agrum_dot_org
5  *
6  *  This library is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU Lesser General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public License
17  *  along with this library.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 
22 /** @file
23  * @brief Source implementation of MCBayesNetGenerator
24  *
25  * @author Pierre-Henri WUILLEMIN(_at_LIP6) and Ariele-Paolo MAESANO
26  *
27  */
28 
29 #include <agrum/BN/generator/MCBayesNetGenerator.h>
30 
31 namespace gum {
32 
33 #ifdef _MSC_VER
34 #  define IBNG IBayesNetGenerator
35 #else
36 #  define IBNG IBayesNetGenerator< GUM_SCALAR, ICPTGenerator >
37 #endif
38 
39   template < typename GUM_SCALAR >
getMaxModality(gum::BayesNet<GUM_SCALAR> & bayesNet)40   gum::Size getMaxModality(gum::BayesNet< GUM_SCALAR >& bayesNet) {
41     gum::Size maxMod = 0;
42 
43     for (auto node: bayesNet.nodes())
44       if (maxMod < bayesNet.variable(node).domainSize())
45         maxMod = bayesNet.variable(node).domainSize();
46 
47     return maxMod;
48   }
49 
50   // Default constructor.
51   // Use the SimpleCPTGenerator for generating the BNs CPT.
52   template < typename GUM_SCALAR,
53              template < typename >
54              class ICPTGenerator,
55              template < typename >
56              class ICPTDisturber >
MCBayesNetGenerator(Size nbrNodes,Size maxArcs,Idx maxModality,Size iteration,Idx p,Idx q)57   MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::MCBayesNetGenerator(
58      Size nbrNodes,
59      Size maxArcs,
60      Idx  maxModality,
61      Size iteration,
62      Idx  p,
63      Idx  q) :
64       IBNG(nbrNodes, maxArcs, maxModality),
65       bayesNettemp_(), hashMarginal_() {
66     if (p + q > 100)
67       GUM_ERROR(OperationNotAllowed,
68                 "the sum of the probabilities p and q must be at most equal to 100");
69 
70     iteration_  = iteration;
71     p_          = p;
72     q_          = q;
73     disturbing_ = false;
74 
75     GUM_CONSTRUCTOR(MCBayesNetGenerator);
76   }
77 
78   template < typename GUM_SCALAR,
79              template < typename >
80              class ICPTGenerator,
81              template < typename >
82              class ICPTDisturber >
MCBayesNetGenerator(BayesNet<GUM_SCALAR> bayesNet,Size iteration,Idx p,Idx q)83   MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::MCBayesNetGenerator(
84      BayesNet< GUM_SCALAR > bayesNet,
85      Size                   iteration,
86      Idx                    p,
87      Idx                    q) :
88       MCBayesNetGenerator(bayesNet.size(),
89                           (Size)(bayesNet.sizeArcs() * 1.1),
90                           getMaxModality(bayesNet)) {
91     iteration_  = iteration;
92     p_          = p;
93     q_          = q;
94     disturbing_ = false;
95   }
96 
97   // Destructor.
98   template < typename GUM_SCALAR,
99              template < typename >
100              class ICPTGenerator,
101              template < typename >
102              class ICPTDisturber >
~MCBayesNetGenerator()103   MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::~MCBayesNetGenerator() {
104     GUM_DESTRUCTOR(MCBayesNetGenerator);
105   }
106 
107   template < typename GUM_SCALAR,
108              template < typename >
109              class ICPTGenerator,
110              template < typename >
111              class ICPTDisturber >
generateBN(BayesNet<GUM_SCALAR> & bayesNet)112   void MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::generateBN(
113      BayesNet< GUM_SCALAR >& bayesNet) {
114     Idx iteration = iteration_;
115 
116     // this->bayesNet_ = bayesNet;
117     _createTree_(this->nbrNodes_);
118     _transformPoly_(this->nbrNodes_ / 2);
119     bayesNettemp_ = this->bayesNet_;
120     _PMMx_poly_();
121 
122     this->fillCPT();
123     iteration_ = iteration;
124 
125     bayesNet = this->bayesNet_;
126   }
127 
128   // density represent de
129   template < typename GUM_SCALAR,
130              template < typename >
131              class ICPTGenerator,
132              template < typename >
133              class ICPTDisturber >
disturbBN(BayesNet<GUM_SCALAR> & bayesNetinit,Size iteration)134   void MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::disturbBN(
135      BayesNet< GUM_SCALAR >& bayesNetinit,
136      Size                    iteration) {   // insert option for the variation
137     disturbing_ = true;
138     Size iter   = iteration_;
139 
140     if (iteration) iteration_ = iteration;
141 
142     this->bayesNet_ = bayesNetinit;
143 
144     if (_checkConditions_()) {
145       LazyPropagation< GUM_SCALAR > inf(&bayesNetinit);
146       inf.makeInference();
147 
148       for (auto node: bayesNetinit.nodes()) {
149         auto pottemp = new Potential< GUM_SCALAR >();
150         pottemp->copy(inf.posterior(node));
151         hashMarginal_.insert(node, pottemp);
152       }
153 
154       bayesNettemp_ = this->bayesNet_;
155 
156       if (_isPolytree_())
157         _PMMx_poly_();
158       else
159         _PMMx_multi_();
160 
161       bayesNetinit = (this->bayesNet_);
162 
163       while (hashMarginal_.size()) {
164         delete (hashMarginal_.begin().val());
165         hashMarginal_.erase(hashMarginal_.beginSafe());   // safe iterator needed here.
166       }
167 
168     } else {
169       std::cout << this->bayesNet_.toDot() << std::endl;
170       GUM_ERROR(OperationNotAllowed, "BN is not valid cause it does not respect constraint ")
171     }
172 
173     iteration_  = iter;
174     disturbing_ = false;
175   }
176 
177   template < typename GUM_SCALAR,
178              template < typename >
179              class ICPTGenerator,
180              template < typename >
181              class ICPTDisturber >
_checkConditions_()182   INLINE bool MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_checkConditions_() {
183     return this->maxArcs_ >= this->bayesNet_.sizeArcs();
184   }
185 
186   // main algorithme for moving between state of the IBayesNet according on the
187   // nature of the topology polytree or multi-connected
188 
189   template < typename GUM_SCALAR,
190              template < typename >
191              class ICPTGenerator,
192              template < typename >
193              class ICPTDisturber >
_PMMx_poly_()194   void MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_PMMx_poly_() {
195     if (!iteration_--) return;
196 
197     Idx per = randomValue(100);
198 
199     if (per < p_) {
200       _AorR_();
201 
202       if (_checkConditions_()) {
203         bayesNettemp_ = this->bayesNet_;
204         _PMMx_multi_();
205       } else {
206         this->bayesNet_ = bayesNettemp_;
207         _PMMx_poly_();
208       }
209     } else {
210       if (per < p_ + q_) {
211         _AR_();
212 
213         if (!_checkConditions_()) {
214           this->bayesNet_ = bayesNettemp_;
215         } else
216           bayesNettemp_ = this->bayesNet_;
217 
218         _PMMx_poly_();
219       } else {
220         _jump_poly_();
221 
222         if (_checkConditions_()) {
223           bayesNettemp_ = this->bayesNet_;
224           _PMMx_multi_();
225 
226         } else {
227           this->bayesNet_ = bayesNettemp_;
228           _PMMx_poly_();
229         }
230       }
231     }
232   }
233 
234   template < typename GUM_SCALAR,
235              template < typename >
236              class ICPTGenerator,
237              template < typename >
238              class ICPTDisturber >
_PMMx_multi_()239   void MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_PMMx_multi_() {
240     if (!iteration_--) return;
241 
242     Idx per = randomValue(100);
243 
244     if (per < p_ + q_) {
245       _AorR_();
246 
247       if (_checkConditions_()) {
248         if (_isPolytree_()) {
249           if (per < p_) {
250             bayesNettemp_ = this->bayesNet_;
251             _PMMx_poly_();
252           } else {
253             this->bayesNet_ = bayesNettemp_;
254             _PMMx_multi_();
255           }
256         } else {
257           bayesNettemp_ = this->bayesNet_;
258           _PMMx_multi_();
259         }
260       } else {
261         this->bayesNet_ = bayesNettemp_;
262         _PMMx_multi_();
263       }
264     } else {
265       _jump_multi_();
266 
267       if (_checkConditions_()) {
268         bayesNettemp_ = this->bayesNet_;
269 
270         if (_isPolytree_())
271           _PMMx_poly_();
272         else
273           _PMMx_multi_();
274 
275       } else {
276         this->bayesNet_ = bayesNettemp_;
277         _PMMx_multi_();
278       }
279     }
280   }
281 
282   template < typename GUM_SCALAR,
283              template < typename >
284              class ICPTGenerator,
285              template < typename >
286              class ICPTDisturber >
_AorR_()287   void MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_AorR_() {
288     NodeId i, j;
289     _chooseNodes_(i, j);
290     const DAG _dag_ = this->bayesNet_.dag();
291 
292     if (_dag_.existsArc(i, j)) {
293       _eraseArc_(i, j);
294 
295       return;
296     } else
297       _insertArc_(i, j);
298   }
299 
300   template < typename GUM_SCALAR,
301              template < typename >
302              class ICPTGenerator,
303              template < typename >
304              class ICPTDisturber >
_AR_()305   void MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_AR_() {
306     NodeId i, j, head, tail;
307     _chooseNodes_(i, j);
308     const DAG _dag_ = this->bayesNet_.dag();
309 
310     if (_dag_.existsArc(i, j) || _dag_.existsArc(j, i)) {
311       return;
312     } else {
313       Idx per = randomValue(100);
314 
315       if (per < 50) {
316         head = i;
317         tail = j;
318       } else {
319         head = j;
320         tail = i;
321       }
322 
323       for (auto node: _dag_.parents(j)) {
324         NodeSet excluded;
325         excluded.insert(j);
326 
327         if (_connect_(node, i, excluded)) {
328           this->bayesNet_.eraseArc(node, j);
329           this->bayesNet_.addArc(head, tail);
330           return;
331         }
332       }
333 
334       for (auto node: _dag_.children(j)) {
335         NodeSet excluded;
336         excluded.insert(j);
337 
338         if (_connect_(node, i, excluded)) {
339           this->bayesNet_.eraseArc(j, node);
340           this->bayesNet_.addArc(head, tail);
341           return;
342         }
343       }
344     }
345   }
346 
347   template < typename GUM_SCALAR,
348              template < typename >
349              class ICPTGenerator,
350              template < typename >
351              class ICPTDisturber >
_jump_poly_()352   INLINE void MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_jump_poly_() {
353     NodeId i, j;
354     _chooseNodes_(i, j);
355     const DAG _dag_ = this->bayesNet_.dag();
356 
357     if (!_dag_.existsArc(i, j)) _insertArc_(i, j);
358   }
359 
360   template < typename GUM_SCALAR,
361              template < typename >
362              class ICPTGenerator,
363              template < typename >
364              class ICPTDisturber >
_jump_multi_()365   void MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_jump_multi_() {
366     NodeId i, j;
367     _chooseNodes_(i, j);
368     const DAG _dag_ = this->bayesNet_.dag();
369 
370     if (_dag_.existsArc(i, j)) { _eraseArc_(i, j); }
371   }
372 
373   template < typename GUM_SCALAR,
374              template < typename >
375              class ICPTGenerator,
376              template < typename >
377              class ICPTDisturber >
378   INLINE void
_insertArc_(NodeId i,NodeId j)379      MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_insertArc_(NodeId i,
380                                                                                   NodeId j) {
381     if (_directedPath_(j, i)) return;
382 
383     if (disturbing_) {
384       auto potj = this->bayesNet_.cpt(j);
385       this->bayesNet_.addArc(i, j);
386 
387       this->disturbAugmCPT(j, this->bayesNet_, potj, (GUM_SCALAR)0.5);
388     } else
389       this->bayesNet_.addArc(i, j);
390   }
391 
392   template < typename GUM_SCALAR,
393              template < typename >
394              class ICPTGenerator,
395              template < typename >
396              class ICPTDisturber >
_eraseArc_(NodeId i,NodeId j,bool mustbeconnex)397   INLINE void MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_eraseArc_(
398      NodeId i,
399      NodeId j,
400      bool   mustbeconnex) {
401     if (disturbing_) {
402       const BayesNet< GUM_SCALAR > bayesNet(this->bayesNet_);
403       Potential< GUM_SCALAR >      potj;
404       potj.copy(this->bayesNet_.cpt(j));
405       this->bayesNet_.eraseArc(i, j);
406 
407       if (_connect_(i, j) || !mustbeconnex) {
408         auto marg = *hashMarginal_[i];
409 
410         this->disturbReducCPT(j, this->bayesNet_, potj, marg);
411       } else
412         this->bayesNet_.addArc(i, j);
413     } else {
414       this->bayesNet_.eraseArc(i, j);
415 
416       if (!_connect_(i, j) && mustbeconnex) { this->bayesNet_.addArc(i, j); }
417     }
418   }
419 
420   template < typename GUM_SCALAR,
421              template < typename >
422              class ICPTGenerator,
423              template < typename >
424              class ICPTDisturber >
425   INLINE void
_chooseNodes_(NodeId & i,NodeId & j)426      MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_chooseNodes_(NodeId& i,
427                                                                                     NodeId& j) {
428     i = randomValue(this->bayesNet_.size());
429     j = randomValue(this->bayesNet_.size());
430 
431     while (i == j)
432       j = randomValue(this->bayesNet_.size());
433   }
434 
435   template < typename GUM_SCALAR,
436              template < typename >
437              class ICPTGenerator,
438              template < typename >
439              class ICPTDisturber >
_chooseCloseNodes_(NodeId & i,NodeId & j)440   void MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_chooseCloseNodes_(
441      NodeId& i,
442      NodeId& j) {
443     NodeId temp = randomValue(this->bayesNet_.size());
444     Size   co   = 0;
445 
446     if (this->bayesNet_.parents(temp).size()) {
447       j       = temp;
448       auto it = this->bayesNet_.parents(j).begin();
449       co      = randomValue(this->bayesNet_.parents(j).size());
450 
451       while (co--) {
452         ++it;
453       }
454 
455       i = *it;
456     } else if (this->bayesNet_.children(temp).size()) {
457       i       = temp;
458       auto it = this->bayesNet_.children(i).begin();
459       co      = randomValue(this->bayesNet_.children(i).size());
460 
461       while (co--) {
462         ++it;
463       }
464 
465       j = *it;
466     } else {
467       GUM_ERROR(FatalError, "Sorry Misconstructed BN because of isolated node.")
468     }
469   }
470 
471   template < typename GUM_SCALAR,
472              template < typename >
473              class ICPTGenerator,
474              template < typename >
475              class ICPTDisturber >
_createTree_(Size BNSize)476   void MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_createTree_(Size BNSize) {
477     Idx               n      = 0;
478     Size              nb_mod = 2 + randomValue(this->maxModality_ - 1);
479     std::stringstream strBuff;
480     strBuff << "n_" << n++;
481     NodeId root     = this->bayesNet_.add(LabelizedVariable(strBuff.str(), "", nb_mod));
482     Size   maxNodes = BNSize - 1;
483     Size   SubG     = 0;
484 
485     while (maxNodes) {
486       SubG         = randomValue(maxNodes) + 1;
487       maxNodes     = maxNodes - SubG;
488       NodeId rootS = _createPartTree_(SubG, n);
489       this->bayesNet_.addArc(root, rootS);
490     }
491   }
492 
493   template < typename GUM_SCALAR,
494              template < typename >
495              class ICPTGenerator,
496              template < typename >
497              class ICPTDisturber >
498   NodeId
_createPartTree_(Size BNSize,Idx & n)499      MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_createPartTree_(Size BNSize,
500                                                                                        Idx& n) {
501     Size              nb_mod = 2 + randomValue(this->maxModality_ - 1);
502     std::stringstream strBuff;
503     strBuff << "n_" << n++;
504     NodeId root     = this->bayesNet_.add(LabelizedVariable(strBuff.str(), "", nb_mod));
505     Size   maxNodes = BNSize - 1;
506     Size   SubG     = 0;
507 
508     while (maxNodes) {
509       SubG         = randomValue(maxNodes) + 1;
510       maxNodes     = maxNodes - SubG;
511       NodeId rootS = _createPartTree_(SubG, n);
512       this->bayesNet_.addArc(root, rootS);
513     }
514 
515     return root;
516   }
517 
518   // Allow to invert maximum nbiter arc to use from polytree only
519   template < typename GUM_SCALAR,
520              template < typename >
521              class ICPTGenerator,
522              template < typename >
523              class ICPTDisturber >
524   void
_transformPoly_(Idx nbiter)525      MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_transformPoly_(Idx nbiter) {
526     while (nbiter--) {
527       NodeId i, j;
528       _chooseCloseNodes_(i, j);
529       bayesNettemp_ = this->bayesNet_;
530       _eraseArc_(i, j, false);
531       this->bayesNet_.addArc(j, i);
532 
533       if (!_checkConditions_()) this->bayesNet_ = bayesNettemp_;
534     }
535   }
536 
537   template < typename GUM_SCALAR,
538              template < typename >
539              class ICPTGenerator,
540              template < typename >
541              class ICPTDisturber >
_isPolytree_()542   INLINE bool MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_isPolytree_() {
543     const DAG _dag_ = this->bayesNet_.dag();
544     return this->bayesNet_.size() - 1 == this->bayesNet_.sizeArcs();
545   }
546 
547   template < typename GUM_SCALAR,
548              template < typename >
549              class ICPTGenerator,
550              template < typename >
551              class ICPTDisturber >
_connect_(const NodeId i,const NodeId j)552   bool MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_connect_(const NodeId i,
553                                                                                   const NodeId j) {
554     const DAG _dag_ = this->bayesNet_.dag();
555 
556     if (_dag_.existsArc(i, j) || _dag_.existsArc(j, i))
557       return true;
558     else {
559       NodeSet excluded;
560       excluded.insert(i);
561 
562       for (auto par: _dag_.parents(i)) {
563         if (!excluded.exists(par) && _connect_(par, j, excluded)) return true;
564       }
565 
566       for (auto chi: _dag_.children(i)) {
567         if (!excluded.exists(chi) && _connect_(chi, j, excluded)) return true;
568       }
569 
570       return false;
571     }
572   }
573 
574   template < typename GUM_SCALAR,
575              template < typename >
576              class ICPTGenerator,
577              template < typename >
578              class ICPTDisturber >
579   bool
_connect_(const NodeId i,const NodeId j,NodeSet & excluded)580      MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_connect_(const NodeId i,
581                                                                                 const NodeId j,
582                                                                                 NodeSet& excluded) {
583     const DAG _dag_ = this->bayesNet_.dag();
584 
585     if (_dag_.existsArc(i, j) || _dag_.existsArc(j, i))
586       return true;
587     else {
588       excluded.insert(i);
589 
590       for (auto par: _dag_.parents(i)) {
591         if (!excluded.exists(par) && _connect_(par, j, excluded)) return true;
592       }
593 
594       for (auto chi: _dag_.children(i)) {
595         if (!excluded.exists(chi) && _connect_(chi, j, excluded)) return true;
596       }
597 
598       return false;
599     }
600   }
601 
602   template < typename GUM_SCALAR,
603              template < typename >
604              class ICPTGenerator,
605              template < typename >
606              class ICPTDisturber >
607   bool
_directedPath_(NodeId tail,NodeId head)608      MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_directedPath_(NodeId tail,
609                                                                                      NodeId head) {
610     const DAG _dag_ = this->bayesNet_.dag();
611 
612     if (_dag_.existsArc(tail, head))
613       return true;
614     else {
615       NodeSet excluded;
616       excluded.insert(tail);
617 
618       for (auto node: _dag_.children(tail)) {
619         if (_directedPath_(node, head, excluded)) return true;
620       }
621 
622       return false;
623     }
624   }
625 
626   template < typename GUM_SCALAR,
627              template < typename >
628              class ICPTGenerator,
629              template < typename >
630              class ICPTDisturber >
_directedPath_(NodeId tail,NodeId head,NodeSet & excluded)631   bool MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::_directedPath_(
632      NodeId   tail,
633      NodeId   head,
634      NodeSet& excluded) {
635     const DAG _dag_ = this->bayesNet_.dag();
636 
637     if (_dag_.existsArc(tail, head))
638       return true;
639     else {
640       excluded.insert(tail);
641 
642       for (auto node: _dag_.children(tail)) {
643         if (!excluded.exists(node) && _directedPath_(node, head, excluded)) return true;
644       }
645 
646       return false;
647     }
648   }
649 
650   template < typename GUM_SCALAR,
651              template < typename >
652              class ICPTGenerator,
653              template < typename >
654              class ICPTDisturber >
iteration()655   INLINE Size MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::iteration() const {
656     return iteration_;
657   }
658 
659   template < typename GUM_SCALAR,
660              template < typename >
661              class ICPTGenerator,
662              template < typename >
663              class ICPTDisturber >
p()664   INLINE Idx MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::p() const {
665     return p_;
666   }
667 
668   template < typename GUM_SCALAR,
669              template < typename >
670              class ICPTGenerator,
671              template < typename >
672              class ICPTDisturber >
q()673   INLINE Idx MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::q() const {
674     return q_;
675   }
676 
677   template < typename GUM_SCALAR,
678              template < typename >
679              class ICPTGenerator,
680              template < typename >
681              class ICPTDisturber >
682   INLINE void
setIteration(Size iteration)683      MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::setIteration(Size iteration) {
684     iteration_ = iteration;
685   }
686   template < typename GUM_SCALAR,
687              template < typename >
688              class ICPTGenerator,
689              template < typename >
690              class ICPTDisturber >
setP(Idx p)691   INLINE void MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::setP(Idx p) {
692     p_ = p;
693 
694     if (p + q_ > 100)
695       GUM_ERROR(OperationNotAllowed,
696                 "the sum of the probabilities p and q must be at most equal to 100");
697   }
698   template < typename GUM_SCALAR,
699              template < typename >
700              class ICPTGenerator,
701              template < typename >
702              class ICPTDisturber >
setQ(Idx q)703   INLINE void MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::setQ(Idx q) {
704     q_ = q;
705 
706     if (p_ + q > 100)
707       GUM_ERROR(OperationNotAllowed,
708                 "the sum of the probabilities p and q must be at most equal to 100");
709   }
710 
711 } /* namespace gum */
712