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