aGrUM 3.1.1
a C++ library for (probabilistic) graphical models
MCBayesNetGenerator_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
50
52#define IBNG IBayesNetGenerator< GUM_SCALAR, ICPTGenerator >
53
54namespace gum {
55 template < GUM_Numeric GUM_SCALAR >
57 gum::Size maxMod = 0;
58
59 for (auto node: bayesNet.nodes())
60 if (maxMod < bayesNet.variable(node).domainSize())
61 maxMod = bayesNet.variable(node).domainSize();
62
63 return maxMod;
64 }
65
66 // Default constructor.
67 // Use the SimpleCPTGenerator for generating the BNs CPT.
68 template < GUM_Numeric GUM_SCALAR,
69 template < typename > class ICPTGenerator,
70 template < typename > class ICPTDisturber >
76 Idx p,
78 if (p + q > 100)
80 "the sum of the probabilities p and q must be at most equal to 100");
81
83 p_ = p;
84 q_ = q;
85
86 GUM_CONSTRUCTOR(MCBayesNetGenerator);
87 }
88
89 template < GUM_Numeric GUM_SCALAR,
90 template < typename > class ICPTGenerator,
91 template < typename > class ICPTDisturber >
93 BayesNet< GUM_SCALAR > bayesNet,
95 Idx p,
96 Idx q) :
97 MCBayesNetGenerator(bayesNet.size(),
98 (Size)(bayesNet.sizeArcs() * 1.1),
99 getMaxModality(bayesNet)) {
101 p_ = p;
102 q_ = q;
103 }
104
105 // Destructor.
106 template < GUM_Numeric GUM_SCALAR,
107 template < typename > class ICPTGenerator,
108 template < typename > class ICPTDisturber >
112
113 template < GUM_Numeric GUM_SCALAR,
114 template < typename > class ICPTGenerator,
115 template < typename > class ICPTDisturber >
117 BayesNet< GUM_SCALAR >& bayesNet) {
119 Timer timer;
120 _createTree_(this->nbrNodes_);
121 _transformPoly_(this->nbrNodes_ / 2);
122 _PMMx_poly_();
123 this->fromDAG(bayesNet);
124
125 this->fromDAG(bayesNet);
126 this->fillCPT(bayesNet);
128 }
129
130 /*
131 template < GumScalar GUM_SCALAR,
132 template < typename >
133 class ICPTGenerator,
134 template < typename >
135 class ICPTDisturber >
136 void MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::disturbBN(
137 BayesNet< GUM_SCALAR >& bayesNetinit,
138 Size iteration) { // insert option for the variation
139 GUM_ERROR(NotImplementedYet,"Badly implemented method")
140
141 disturbing_ = true;
142 Size iter = iteration_;
143
144 if (iteration) iteration_ = iteration;
145
146 this->bayesNet_ = bayesNetinit;
147
148 if (_checkConditions_()) {
149 LazyPropagation< GUM_SCALAR > inf(&bayesNetinit);
150 inf.makeInference();
151
152 for (auto node: bayesNetinit.nodes()) {
153 auto pottemp = new Tensor< GUM_SCALAR >();
154 pottemp->copy(inf.posterior(node));
155 hashMarginal_.insert(node, pottemp);
156 }
157
158 bayesNettemp_ = this->bayesNet_;
159
160 if (_isPolytree_()) _PMMx_poly_();
161 else _PMMx_multi_();
162
163 bayesNetinit = (this->bayesNet_);
164
165 while (hashMarginal_.size()) {
166 delete (hashMarginal_.begin().val());
167 hashMarginal_.erase(hashMarginal_.beginSafe()); // safe iterator needed here.
168 }
169
170 } else {
171 std::cout << this->bayesNet_.toDot() << std::endl;
172 GUM_ERROR(OperationNotAllowed, "BN is not valid cause it does not respect constraint ")
173 }
174
175 iteration_ = iter;
176 disturbing_ = false;
177 }
178 */
179
180 template < GUM_Numeric GUM_SCALAR,
181 template < typename > class ICPTGenerator,
182 template < typename > class ICPTDisturber >
186
187 // main algorithme for moving between state of the IBayesNet according on the
188 // nature of the topology polytree or multi-connected
189
190 template < GUM_Numeric GUM_SCALAR,
191 template < typename > class ICPTGenerator,
192 template < typename > class ICPTDisturber >
194 while (true) {
195 if (!iteration_--) return;
196 DAG tmp_dag = this->dag_;
197 Idx per = randomValue(100);
198
199 if (per < p_) {
201
202 if (_checkConditions_()) {
203 tmp_dag = this->dag_;
204 _PMMx_multi_();
205 break;
206 } else {
207 this->dag_ = tmp_dag;
208 }
209 } else {
210 if (per < p_ + q_) {
212
213 if (!_checkConditions_()) {
214 this->dag_ = tmp_dag;
215 } else {
216 tmp_dag = this->dag_;
217 }
218 } else {
219 _jump_poly_();
220
221 if (_checkConditions_()) {
222 tmp_dag = this->dag_;
223 _PMMx_multi_();
224 break;
225 } else {
226 this->dag_ = tmp_dag;
227 }
228 }
229 }
230 }
231 }
232
233 template < GUM_Numeric GUM_SCALAR,
234 template < typename > class ICPTGenerator,
235 template < typename > class ICPTDisturber >
237 while (true) {
238 if (!iteration_--) return;
239 DAG tmp_dag = this->dag_;
240
241 Idx per = randomValue(100);
242
243 if (per < p_ + q_) {
245 if (_checkConditions_()) {
246 if (_isPolytree_()) {
247 if (per < p_) {
248 tmp_dag = this->dag_;
249 _PMMx_poly_();
250 break;
251 } else {
252 this->dag_ = tmp_dag;
253 }
254 } else {
255 tmp_dag = this->dag_;
256 }
257 } else {
258 this->dag_ = tmp_dag;
259 }
260 } else {
261 _jump_multi_();
262 if (_checkConditions_()) {
263 tmp_dag = this->dag_;
264 if (_isPolytree_()) {
265 _PMMx_poly_();
266 break;
267 }
268 } else {
269 this->dag_ = tmp_dag;
270 }
271 }
273 }
274
275 template < GUM_Numeric GUM_SCALAR,
276 template < typename > class ICPTGenerator,
277 template < typename > class ICPTDisturber >
279 NodeId i, j;
280 _chooseNodes_(i, j);
281 if (this->dag_.existsArc(i, j)) {
282 _eraseArc_(i, j);
283
284 return;
285 } else _insertArc_(i, j);
286 }
287
288 template < GUM_Numeric GUM_SCALAR,
289 template < typename > class ICPTGenerator,
290 template < typename > class ICPTDisturber >
292 NodeId i, j, head, tail;
294
295 if (this->dag_.existsArc(i, j) || this->dag_.existsArc(j, i)) {
296 return;
297 } else {
298 Idx per = randomValue(100);
299
300 if (per < 50) {
301 head = i;
302 tail = j;
303 } else {
304 head = j;
305 tail = i;
306 }
307
308 for (auto node: this->dag_.parents(j)) {
309 NodeSet excluded;
310 excluded.insert(j);
311
312 if (_is_connected_(node, i, excluded)) {
313 this->dag_.eraseArc(Arc(node, j));
314 this->dag_.addArc(head, tail);
315 return;
316 }
318
319 for (auto node: this->dag_.children(j)) {
320 NodeSet excluded;
321 excluded.insert(j);
322
323 if (_is_connected_(node, i, excluded)) {
324 this->dag_.eraseArc(Arc{j, node});
325 this->dag_.addArc(head, tail);
326 return;
327 }
328 }
329 }
331
332 template < GUM_Numeric GUM_SCALAR,
333 template < typename > class ICPTGenerator,
334 template < typename > class ICPTDisturber >
336 NodeId i, j;
337 _chooseNodes_(i, j);
339 if (!this->dag_.existsArc(i, j)) _insertArc_(i, j);
340 }
341
342 template < GUM_Numeric GUM_SCALAR,
343 template < typename > class ICPTGenerator,
344 template < typename > class ICPTDisturber >
346 NodeId i, j;
347 _chooseNodes_(i, j);
348
349 if (this->dag_.existsArc(i, j)) { _eraseArc_(i, j); }
351
352 template < GUM_Numeric GUM_SCALAR,
353 template < typename > class ICPTGenerator,
354 template < typename > class ICPTDisturber >
356 NodeId j) {
357 if (_directedPath_(j, i)) return;
358
359 /*if (disturbing_) {
360 auto potj = this->bayesNet_.cpt(j);
361 this->bayesNet_.addArc(i, j);
362
363 this->disturbAugmCPT(j, this->bayesNet_, potj, (GUM_SCALAR)0.5);
364 } else */
365 this->dag_.addArc(i, j);
366 }
367
368 template < GUM_Numeric GUM_SCALAR,
369 template < typename > class ICPTGenerator,
370 template < typename > class ICPTDisturber >
372 NodeId i,
373 NodeId j,
374 bool mustbeconnex) {
375 /*if (disturbing_) {
376 const BayesNet< GUM_SCALAR > bayesNet(this->bayesNet_);
377 Tensor< GUM_SCALAR > potj;
378 potj.copy(this->bayesNet_.cpt(j));
379 this->bayesNet_.eraseArc(i, j);
380
381 if (_connect_(i, j) || !mustbeconnex) {
382 auto marg = *hashMarginal_[i];
384 this->disturbReducCPT(j, this->bayesNet_, potj, marg);
385 } else this->bayesNet_.addArc(i, j);
386 } else */
387 {
388 this->dag_.eraseArc(Arc(i, j));
389
390 if (!_connect_(i, j) && mustbeconnex) { this->dag_.addArc(i, j); }
391 }
392 }
393
394 template < GUM_Numeric GUM_SCALAR,
395 template < typename > class ICPTGenerator,
396 template < typename > class ICPTDisturber >
398 NodeId& j) {
399 if (this->dag_.size() < 3) {
400 GUM_ERROR(ArgumentError, "This dag has only " << this->dag_.size() << " nodes.")
402 i = randomValue(this->dag_.size());
403 j = randomValue(this->dag_.size());
404
405 while (i == j)
406 j = randomValue(this->dag_.size());
408
409 template < GUM_Numeric GUM_SCALAR,
410 template < typename > class ICPTGenerator,
411 template < typename > class ICPTDisturber >
413 NodeId& i,
414 NodeId& j) {
415 NodeId temp = randomValue(this->dag_.size());
416 Size co = 0;
417
418 if (this->dag_.parents(temp).size()) {
419 j = temp;
420 auto it = this->dag_.parents(j).begin();
421 co = randomValue(this->dag_.parents(j).size());
422
423 while (co--) {
424 ++it;
425 }
426
427 i = *it;
428 } else if (this->dag_.children(temp).size()) {
429 i = temp;
430 auto it = this->dag_.children(i).begin();
431 co = randomValue(this->dag_.children(i).size());
432
433 while (co--) {
434 ++it;
435 }
436
437 j = *it;
438 } else {
439 GUM_ERROR(FatalError, "Sorry Misconstructed BN because of isolated node.")
440 }
441 }
442
443 template < GUM_Numeric GUM_SCALAR,
444 template < typename > class ICPTGenerator,
445 template < typename > class ICPTDisturber >
447 Idx n = 0;
448 NodeId root = this->dag_.addNode();
449 Size maxNodes = BNSize - 1;
450 Size SubG = 0;
451
452 while (maxNodes) {
453 SubG = randomValue(maxNodes) + 1;
454 maxNodes = maxNodes - SubG;
455 NodeId rootS = _createPartTree_(SubG, n);
456 this->dag_.addArc(root, rootS);
457 }
458 }
459
460 template < GUM_Numeric GUM_SCALAR,
461 template < typename > class ICPTGenerator,
462 template < typename > class ICPTDisturber >
463 NodeId
465 Idx& n) {
466 /*
467 Size nb_mod = 2 + randomValue(this->maxModality_ - 1);
468 std::stringstream strBuff;
469 strBuff << "n_" << n++;
470 NodeId root = this->bayesNet_.add(LabelizedVariable(strBuff.str(), "", nb_mod));
471 */
472 NodeId root = this->dag_.addNode();
473 Size maxNodes = BNSize - 1;
474 Size SubG = 0;
475
476 while (maxNodes) {
477 SubG = randomValue(maxNodes) + 1;
478 maxNodes = maxNodes - SubG;
479 NodeId rootS = _createPartTree_(SubG, n);
480 this->dag_.addArc(root, rootS);
481 }
482
483 return root;
484 }
485
486 // Allow to invert maximum nbiter arc to use from polytree only
487 template < GUM_Numeric GUM_SCALAR,
488 template < typename > class ICPTGenerator,
489 template < typename > class ICPTDisturber >
490 void
492 while (nbiter--) {
493 NodeId i, j;
494 _chooseCloseNodes_(i, j);
495 auto dag_tmp = this->dag_;
496 _eraseArc_(i, j, false);
497 this->dag_.addArc(j, i);
498
499 if (!_checkConditions_()) this->dag_ = dag_tmp;
500 }
501 }
502
503 template < GUM_Numeric GUM_SCALAR,
504 template < typename > class ICPTGenerator,
505 template < typename > class ICPTDisturber >
507 return this->dag_.size() - 1 == this->dag_.sizeArcs();
508 }
509
510 template < GUM_Numeric GUM_SCALAR,
511 template < typename > class ICPTGenerator,
512 template < typename > class ICPTDisturber >
514 const NodeId j) {
515 if (this->dag_.existsArc(i, j) || this->dag_.existsArc(j, i)) return true;
516 else {
517 NodeSet excluded;
518 excluded.insert(i);
519
520 for (auto par: this->dag_.parents(i)) {
521 if (!excluded.exists(par) && _is_connected_(par, j, excluded)) return true;
522 }
523
524 for (auto chi: this->dag_.children(i)) {
525 if (!excluded.exists(chi) && _is_connected_(chi, j, excluded)) return true;
526 }
527
528 return false;
529 }
530 }
531
532 template < GUM_Numeric GUM_SCALAR,
533 template < typename > class ICPTGenerator,
534 template < typename > class ICPTDisturber >
536 const NodeId i,
537 const NodeId j,
538 NodeSet& excluded) {
539 if (this->dag_.existsArc(i, j) || this->dag_.existsArc(j, i)) return true;
540 else {
541 excluded.insert(i);
542
543 for (auto par: this->dag_.parents(i)) {
544 if (!excluded.exists(par) && _is_connected_(par, j, excluded)) return true;
545 }
546
547 for (auto chi: this->dag_.children(i)) {
548 if (!excluded.exists(chi) && _is_connected_(chi, j, excluded)) return true;
549 }
550
551 return false;
552 }
553 }
554
555 template < GUM_Numeric GUM_SCALAR,
556 template < typename > class ICPTGenerator,
557 template < typename > class ICPTDisturber >
558 bool
560 NodeId head) {
561 if (this->dag_.existsArc(tail, head)) return true;
562 else {
563 NodeSet excluded;
564 excluded.insert(tail);
565
566 for (auto node: this->dag_.children(tail)) {
567 if (_directedPath_(node, head, excluded)) return true;
568 }
569
570 return false;
571 }
572 }
573
574 template < GUM_Numeric GUM_SCALAR,
575 template < typename > class ICPTGenerator,
576 template < typename > class ICPTDisturber >
578 NodeId tail,
579 NodeId head,
580 NodeSet& excluded) {
581 if (this->dag_.existsArc(tail, head)) return true;
582 else {
583 excluded.insert(tail);
584
585 for (auto node: this->dag_.children(tail)) {
586 if (!excluded.exists(node) && _directedPath_(node, head, excluded)) return true;
587 }
588
589 return false;
590 }
591 }
592
593 template < GUM_Numeric GUM_SCALAR,
594 template < typename > class ICPTGenerator,
595 template < typename > class ICPTDisturber >
599
600 template < GUM_Numeric GUM_SCALAR,
601 template < typename > class ICPTGenerator,
602 template < typename > class ICPTDisturber >
606
607 template < GUM_Numeric GUM_SCALAR,
608 template < typename > class ICPTGenerator,
609 template < typename > class ICPTDisturber >
613
614 template < GUM_Numeric GUM_SCALAR,
615 template < typename > class ICPTGenerator,
616 template < typename > class ICPTDisturber >
621
622 template < GUM_Numeric GUM_SCALAR,
623 template < typename > class ICPTGenerator,
624 template < typename > class ICPTDisturber >
626 p_ = p;
627
628 if (p + q_ > 100)
630 "the sum of the probabilities p and q must be at most equal to 100");
631 }
632
633 template < GUM_Numeric GUM_SCALAR,
634 template < typename > class ICPTGenerator,
635 template < typename > class ICPTDisturber >
637 q_ = q;
638
639 if (p_ + q > 100)
641 "the sum of the probabilities p and q must be at most equal to 100");
642 }
643
644} /* namespace gum */
Class for generating Bayesian networks.using MC algorithm cf.
The base class for all directed edges.
Exception base for argument error.
Class representing a Bayesian network.
Definition BayesNet.h:99
const DiscreteVariable & variable(std::string_view name) const
Returns a gum::DiscreteVariable given its name in the gum::BayesNet.
Base class for dag.
Definition DAG.h:121
const NodeGraphPart & nodes() const final
Returns a named copy of the internal DAG: each node id is assigned the name of the corresponding vari...
virtual Size domainSize() const =0
Idx p() const
Return a constant reference to the probabilité p imposed on the Markov Chain BayesNetGenerator.
void _createTree_(Size BNSize)
The function that randomly generate a simple tree.
NodeId _createPartTree_(Size BNSize, Idx &n)
The internal function used by createTree that randomly generate a simple tree.
void setQ(Idx q)
Modifies the value of the probability q imposed on the BayesNetGenerator.
void _transformPoly_(Idx nbiter)
The function that randomly change the simple tree into a polytree.
void _chooseCloseNodes_(NodeId &i, NodeId &j)
The function that randomly choose two neighbours nodes of the graph.
void _PMMx_poly_()
In the case that the graph is a polytree, the function will, according to the probability p and q,...
void generateBN(BayesNet< GUM_SCALAR > &bayesNet) override
Generates a random Bayesian network.
MCBayesNetGenerator(Size nbrNodes, Size maxArcs, Idx maxModality=2, Size iteration=NB_INIT_ITERATIONS, Idx p=30, Idx q=40)
Constructor.
void setP(Idx p)
Modifies the value of the probability p imposed on the BayesNetGenerator.
Idx q() const
Return a constant reference to the probabilité imposed on the Markov Chain BayesNetGenerator.
virtual bool _checkConditions_()
The boolean function that will assert the respect of the constraint.
Size iteration() const
Return a constant reference to the number of iteration imposed on the Markov Chain BayesNetGenerator.
void setIteration(Size iteration)
Modifies the value of the number of iterations impose on the BayesNetGenerator.
void _chooseNodes_(NodeId &i, NodeId &j)
The function that randomly choose two nodes of the graph.
void _eraseArc_(NodeId i, NodeId j, bool mustbeconnex=true)
The function that will remove the arc between node i and node j.
~MCBayesNetGenerator() override
Destructor.
Exception : operation not allowed.
bool exists(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:504
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:510
Class used to compute response times for benchmark purposes.
Definition timer.h:69
Complete concept for GUM_SCALAR template parameter.
Definition concepts.h:148
#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.
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
gum::Size getMaxModality(gum::BayesNet< GUM_SCALAR > &bayesNet)