aGrUM 2.3.2
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-2025 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-2025 *
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#pragma once
41
42
49
51#define IBNG IBayesNetGenerator< GUM_SCALAR, ICPTGenerator >
52
53namespace gum {
54 template < typename GUM_SCALAR >
56 gum::Size maxMod = 0;
57
58 for (auto node: bayesNet.nodes())
59 if (maxMod < bayesNet.variable(node).domainSize())
60 maxMod = bayesNet.variable(node).domainSize();
61
62 return maxMod;
63 }
64
65 // Default constructor.
66 // Use the SimpleCPTGenerator for generating the BNs CPT.
67 template < typename GUM_SCALAR,
68 template < typename > class ICPTGenerator,
69 template < typename > class ICPTDisturber >
75 Idx p,
77 if (p + q > 100)
79 "the sum of the probabilities p and q must be at most equal to 100");
80
82 p_ = p;
83 q_ = q;
84
85 GUM_CONSTRUCTOR(MCBayesNetGenerator);
86 }
87
88 template < typename GUM_SCALAR,
89 template < typename > class ICPTGenerator,
90 template < typename > class ICPTDisturber >
92 BayesNet< GUM_SCALAR > bayesNet,
94 Idx p,
95 Idx q) :
96 MCBayesNetGenerator(bayesNet.size(),
97 (Size)(bayesNet.sizeArcs() * 1.1),
98 getMaxModality(bayesNet)) {
100 p_ = p;
101 q_ = q;
102 }
103
104 // Destructor.
105 template < typename GUM_SCALAR,
106 template < typename > class ICPTGenerator,
107 template < typename > class ICPTDisturber >
111
112 template < typename GUM_SCALAR,
113 template < typename > class ICPTGenerator,
114 template < typename > class ICPTDisturber >
116 BayesNet< GUM_SCALAR >& bayesNet) {
118 Timer timer;
119 _createTree_(this->nbrNodes_);
120 _transformPoly_(this->nbrNodes_ / 2);
121 _PMMx_poly_();
122 this->fromDAG(bayesNet);
123
124 this->fromDAG(bayesNet);
125 this->fillCPT(bayesNet);
127 }
128
129 /*
130 template < typename GUM_SCALAR,
131 template < typename >
132 class ICPTGenerator,
133 template < typename >
134 class ICPTDisturber >
135 void MCBayesNetGenerator< GUM_SCALAR, ICPTGenerator, ICPTDisturber >::disturbBN(
136 BayesNet< GUM_SCALAR >& bayesNetinit,
137 Size iteration) { // insert option for the variation
138 GUM_ERROR(NotImplementedYet,"Badly implemented method")
139
140 disturbing_ = true;
141 Size iter = iteration_;
142
143 if (iteration) iteration_ = iteration;
144
145 this->bayesNet_ = bayesNetinit;
146
147 if (_checkConditions_()) {
148 LazyPropagation< GUM_SCALAR > inf(&bayesNetinit);
149 inf.makeInference();
150
151 for (auto node: bayesNetinit.nodes()) {
152 auto pottemp = new Tensor< GUM_SCALAR >();
153 pottemp->copy(inf.posterior(node));
154 hashMarginal_.insert(node, pottemp);
155 }
156
157 bayesNettemp_ = this->bayesNet_;
158
159 if (_isPolytree_()) _PMMx_poly_();
160 else _PMMx_multi_();
161
162 bayesNetinit = (this->bayesNet_);
163
164 while (hashMarginal_.size()) {
165 delete (hashMarginal_.begin().val());
166 hashMarginal_.erase(hashMarginal_.beginSafe()); // safe iterator needed here.
167 }
168
169 } else {
170 std::cout << this->bayesNet_.toDot() << std::endl;
171 GUM_ERROR(OperationNotAllowed, "BN is not valid cause it does not respect constraint ")
172 }
173
174 iteration_ = iter;
175 disturbing_ = false;
176 }
177 */
178
179 template < typename GUM_SCALAR,
180 template < typename > class ICPTGenerator,
181 template < typename > class ICPTDisturber >
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 > class ICPTGenerator,
191 template < typename > class ICPTDisturber >
193 while (true) {
194 if (!iteration_--) return;
195 DAG tmp_dag = this->dag_;
196 Idx per = randomValue(100);
197
198 if (per < p_) {
200
201 if (_checkConditions_()) {
202 tmp_dag = this->dag_;
203 _PMMx_multi_();
204 break;
205 } else {
206 this->dag_ = tmp_dag;
207 }
208 } else {
209 if (per < p_ + q_) {
210 _Add_and_Remove_();
211
212 if (!_checkConditions_()) {
213 this->dag_ = tmp_dag;
214 } else {
215 tmp_dag = this->dag_;
216 }
217 } else {
218 _jump_poly_();
219
220 if (_checkConditions_()) {
221 tmp_dag = this->dag_;
222 _PMMx_multi_();
223 break;
224 } else {
225 this->dag_ = tmp_dag;
226 }
227 }
228 }
229 }
230 }
231
232 template < typename GUM_SCALAR,
233 template < typename > class ICPTGenerator,
234 template < typename > class ICPTDisturber >
236 while (true) {
237 if (!iteration_--) return;
238 DAG tmp_dag = this->dag_;
239
240 Idx per = randomValue(100);
241
242 if (per < p_ + q_) {
244 if (_checkConditions_()) {
245 if (_isPolytree_()) {
246 if (per < p_) {
247 tmp_dag = this->dag_;
248 _PMMx_poly_();
249 break;
250 } else {
251 this->dag_ = tmp_dag;
252 }
253 } else {
254 tmp_dag = this->dag_;
255 }
256 } else {
257 this->dag_ = tmp_dag;
258 }
259 } else {
260 _jump_multi_();
261 if (_checkConditions_()) {
262 tmp_dag = this->dag_;
263 if (_isPolytree_()) {
264 _PMMx_poly_();
265 break;
267 } else {
268 this->dag_ = tmp_dag;
269 }
270 }
271 }
273
274 template < typename GUM_SCALAR,
275 template < typename > class ICPTGenerator,
276 template < typename > class ICPTDisturber >
278 NodeId i, j;
279 _chooseNodes_(i, j);
280 if (this->dag_.existsArc(i, j)) {
281 _eraseArc_(i, j);
282
283 return;
284 } else _insertArc_(i, j);
286
287 template < typename GUM_SCALAR,
288 template < typename > class ICPTGenerator,
289 template < typename > class ICPTDisturber >
291 NodeId i, j, head, tail;
292 _chooseNodes_(i, j);
294 if (this->dag_.existsArc(i, j) || this->dag_.existsArc(j, i)) {
295 return;
296 } else {
297 Idx per = randomValue(100);
298
299 if (per < 50) {
300 head = i;
301 tail = j;
302 } else {
303 head = j;
304 tail = i;
305 }
306
307 for (auto node: this->dag_.parents(j)) {
308 NodeSet excluded;
309 excluded.insert(j);
310
311 if (_is_connected_(node, i, excluded)) {
312 this->dag_.eraseArc(Arc(node, j));
313 this->dag_.addArc(head, tail);
314 return;
315 }
316 }
318 for (auto node: this->dag_.children(j)) {
319 NodeSet excluded;
320 excluded.insert(j);
321
322 if (_is_connected_(node, i, excluded)) {
323 this->dag_.eraseArc(Arc{j, node});
324 this->dag_.addArc(head, tail);
325 return;
326 }
327 }
328 }
329 }
331 template < typename GUM_SCALAR,
332 template < typename > class ICPTGenerator,
333 template < typename > class ICPTDisturber >
335 NodeId i, j;
336 _chooseNodes_(i, j);
337
338 if (!this->dag_.existsArc(i, j)) _insertArc_(i, j);
339 }
340
341 template < typename GUM_SCALAR,
342 template < typename > class ICPTGenerator,
343 template < typename > class ICPTDisturber >
345 NodeId i, j;
346 _chooseNodes_(i, j);
347
348 if (this->dag_.existsArc(i, j)) { _eraseArc_(i, j); }
349 }
351 template < typename GUM_SCALAR,
352 template < typename > class ICPTGenerator,
353 template < typename > class ICPTDisturber >
354 INLINE void
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 < typename 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 < typename GUM_SCALAR,
395 template < typename > class ICPTGenerator,
396 template < typename > class ICPTDisturber >
397 INLINE void
399 NodeId& j) {
400 if (this->dag_.size() < 3) {
401 GUM_ERROR(ArgumentError, "This dag has only " << this->dag_.size() << " nodes.")
402 }
403 i = randomValue(this->dag_.size());
404 j = randomValue(this->dag_.size());
405
406 while (i == j)
407 j = randomValue(this->dag_.size());
408 }
409
410 template < typename GUM_SCALAR,
411 template < typename > class ICPTGenerator,
412 template < typename > class ICPTDisturber >
414 NodeId& i,
415 NodeId& j) {
416 NodeId temp = randomValue(this->dag_.size());
417 Size co = 0;
418
419 if (this->dag_.parents(temp).size()) {
420 j = temp;
421 auto it = this->dag_.parents(j).begin();
422 co = randomValue(this->dag_.parents(j).size());
423
424 while (co--) {
425 ++it;
426 }
427
428 i = *it;
429 } else if (this->dag_.children(temp).size()) {
430 i = temp;
431 auto it = this->dag_.children(i).begin();
432 co = randomValue(this->dag_.children(i).size());
433
434 while (co--) {
435 ++it;
436 }
437
438 j = *it;
439 } else {
440 GUM_ERROR(FatalError, "Sorry Misconstructed BN because of isolated node.")
441 }
442 }
443
444 template < typename GUM_SCALAR,
445 template < typename > class ICPTGenerator,
446 template < typename > class ICPTDisturber >
448 Idx n = 0;
449 NodeId root = this->dag_.addNode();
450 Size maxNodes = BNSize - 1;
451 Size SubG = 0;
452
453 while (maxNodes) {
454 SubG = randomValue(maxNodes) + 1;
455 maxNodes = maxNodes - SubG;
456 NodeId rootS = _createPartTree_(SubG, n);
457 this->dag_.addArc(root, rootS);
458 }
459 }
460
461 template < typename GUM_SCALAR,
462 template < typename > class ICPTGenerator,
463 template < typename > class ICPTDisturber >
464 NodeId
466 Idx& n) {
467 /*
468 Size nb_mod = 2 + randomValue(this->maxModality_ - 1);
469 std::stringstream strBuff;
470 strBuff << "n_" << n++;
471 NodeId root = this->bayesNet_.add(LabelizedVariable(strBuff.str(), "", nb_mod));
472 */
473 NodeId root = this->dag_.addNode();
474 Size maxNodes = BNSize - 1;
475 Size SubG = 0;
476
477 while (maxNodes) {
478 SubG = randomValue(maxNodes) + 1;
479 maxNodes = maxNodes - SubG;
480 NodeId rootS = _createPartTree_(SubG, n);
481 this->dag_.addArc(root, rootS);
482 }
483
484 return root;
485 }
486
487 // Allow to invert maximum nbiter arc to use from polytree only
488 template < typename GUM_SCALAR,
489 template < typename > class ICPTGenerator,
490 template < typename > class ICPTDisturber >
491 void
493 while (nbiter--) {
494 NodeId i, j;
495 _chooseCloseNodes_(i, j);
496 auto dag_tmp = this->dag_;
497 _eraseArc_(i, j, false);
498 this->dag_.addArc(j, i);
499
500 if (!_checkConditions_()) this->dag_ = dag_tmp;
501 }
502 }
503
504 template < typename GUM_SCALAR,
505 template < typename > class ICPTGenerator,
506 template < typename > class ICPTDisturber >
508 return this->dag_.size() - 1 == this->dag_.sizeArcs();
509 }
510
511 template < typename GUM_SCALAR,
512 template < typename > class ICPTGenerator,
513 template < typename > class ICPTDisturber >
515 const NodeId j) {
516 if (this->dag_.existsArc(i, j) || this->dag_.existsArc(j, i)) return true;
517 else {
518 NodeSet excluded;
519 excluded.insert(i);
520
521 for (auto par: this->dag_.parents(i)) {
522 if (!excluded.exists(par) && _is_connected_(par, j, excluded)) return true;
523 }
524
525 for (auto chi: this->dag_.children(i)) {
526 if (!excluded.exists(chi) && _is_connected_(chi, j, excluded)) return true;
527 }
528
529 return false;
530 }
531 }
532
533 template < typename GUM_SCALAR,
534 template < typename > class ICPTGenerator,
535 template < typename > class ICPTDisturber >
537 const NodeId i,
538 const NodeId j,
539 NodeSet& excluded) {
540 if (this->dag_.existsArc(i, j) || this->dag_.existsArc(j, i)) return true;
541 else {
542 excluded.insert(i);
543
544 for (auto par: this->dag_.parents(i)) {
545 if (!excluded.exists(par) && _is_connected_(par, j, excluded)) return true;
546 }
547
548 for (auto chi: this->dag_.children(i)) {
549 if (!excluded.exists(chi) && _is_connected_(chi, j, excluded)) return true;
550 }
551
552 return false;
553 }
554 }
555
556 template < typename GUM_SCALAR,
557 template < typename > class ICPTGenerator,
558 template < typename > class ICPTDisturber >
559 bool
561 NodeId head) {
562 if (this->dag_.existsArc(tail, head)) return true;
563 else {
564 NodeSet excluded;
565 excluded.insert(tail);
566
567 for (auto node: this->dag_.children(tail)) {
568 if (_directedPath_(node, head, excluded)) return true;
569 }
570
571 return false;
572 }
573 }
574
575 template < typename GUM_SCALAR,
576 template < typename > class ICPTGenerator,
577 template < typename > class ICPTDisturber >
579 NodeId tail,
580 NodeId head,
581 NodeSet& excluded) {
582 if (this->dag_.existsArc(tail, head)) return true;
583 else {
584 excluded.insert(tail);
585
586 for (auto node: this->dag_.children(tail)) {
587 if (!excluded.exists(node) && _directedPath_(node, head, excluded)) return true;
588 }
589
590 return false;
591 }
592 }
593
594 template < typename GUM_SCALAR,
595 template < typename > class ICPTGenerator,
596 template < typename > class ICPTDisturber >
600
601 template < typename GUM_SCALAR,
602 template < typename > class ICPTGenerator,
603 template < typename > class ICPTDisturber >
607
608 template < typename GUM_SCALAR,
609 template < typename > class ICPTGenerator,
610 template < typename > class ICPTDisturber >
614
615 template < typename GUM_SCALAR,
616 template < typename > class ICPTGenerator,
617 template < typename > class ICPTDisturber >
622
623 template < typename GUM_SCALAR,
624 template < typename > class ICPTGenerator,
625 template < typename > class ICPTDisturber >
627 p_ = p;
628
629 if (p + q_ > 100)
631 "the sum of the probabilities p and q must be at most equal to 100");
632 }
633
634 template < typename GUM_SCALAR,
635 template < typename > class ICPTGenerator,
636 template < typename > class ICPTDisturber >
638 q_ = q;
639
640 if (p_ + q > 100)
642 "the sum of the probabilities p and q must be at most equal to 100");
643 }
644
645} /* 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:93
const DiscreteVariable & variable(NodeId id) const final
Returns a gum::DiscreteVariable given its gum::NodeId in the gum::BayesNet.
Base class for dag.
Definition DAG.h:121
const NodeGraphPart & nodes() const final
Returns a constant reference to the dag of this Bayes Net.
virtual Size domainSize() const =0
void _jump_multi_()
In the case that the graph is a multiconnect graph, the function will choose randomly two nodes and w...
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 _jump_poly_()
In the case that the graph is a polytree, the function will add a random arc by the use of the functi...
void _chooseCloseNodes_(NodeId &i, NodeId &j)
The function that randomly choose two neighbours nodes of the graph.
void _insertArc_(NodeId i, NodeId j)
The function that will insert an arc between node i to node j, but only if there isn't any cycle crea...
void _PMMx_poly_()
In the case that the graph is a polytree, the function will, according to the probability p and q,...
void _eraseArc_(NodeId i, NodeId j, bool mustbeconnex=true)
The function that will remove the arc between node i and node j.
void setIteration(Size iteration)
Modifies the value of the number of iterations impose on the BayesNetGenerator.
Idx q() const
Return a constant reference to the probabilité imposed on the Markov Chain BayesNetGenerator.
void generateBN(BayesNet< GUM_SCALAR > &bayesNet) override
Generates a random Bayesian network.
virtual bool _checkConditions_()
The boolean function that will assert the respect of the constraint.
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.
Size iteration() const
Return a constant reference to the number of iteration imposed on the Markov Chain BayesNetGenerator.
void _Add_and_Remove_()
The function will remove and add a random arc changing the topology of the graph but asserting its co...
void _chooseNodes_(NodeId &i, NodeId &j)
The function that randomly choose two nodes of the graph.
Idx p() const
Return a constant reference to the probabilité p imposed on the Markov Chain BayesNetGenerator.
~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:533
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:539
Class used to compute response times for benchmark purposes.
Definition timer.h:69
#define GUM_ERROR(type, msg)
Definition exceptions.h:72
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)