aGrUM 2.3.2
a C++ library for (probabilistic) graphical models
influenceDiagram_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
50
51#include <algorithm>
52#include <cstdio>
53#include <iostream>
54
57
58namespace gum {
59 template < typename GUM_SCALAR >
61 std::string node,
62 const std::string& domain) {
63 bool isUtil = false;
64 bool isDeci = false;
65 bool isChanc = false;
66 std::string ds = domain;
67 switch (*(node.begin())) {
68 case '*' :
69 isDeci = true;
70 node.erase(0, 1);
71 break;
72 case '$' :
73 isUtil = true;
74 ds = "[1]";
75 node.erase(0, 1);
76 break;
77 default : isChanc = true;
78 }
79 auto v = fastVariable< GUM_SCALAR >(node, ds);
80
81 NodeId res;
82 try {
83 res = infdiag.idFromName(v->name());
84 } catch (gum::NotFound&) {
85 if (isChanc) res = infdiag.addChanceNode(*v);
86 else if (isDeci) res = infdiag.addDecisionNode(*v);
87 else if (isUtil) res = infdiag.addUtilityNode(*v);
88 else
90 "No type (chance, decision or utility) for the node '" << node << "'.")
91 }
92
93 return res;
94 }
95
96 template < typename GUM_SCALAR >
97 InfluenceDiagram< GUM_SCALAR >
98 InfluenceDiagram< GUM_SCALAR >::fastPrototype(const std::string& dotlike, Size domainSize) {
99 return fastPrototype(dotlike, "[" + std::to_string(domainSize) + "]");
100 }
101
102 template < typename GUM_SCALAR >
105 const std::string& domain) {
107
108 for (const auto& chaine: split(remove_newline(dotlike), ";")) {
109 NodeId lastId = 0;
110 bool notfirst = false;
111 for (const auto& souschaine: split(chaine, "->")) {
112 bool forward = true;
113 for (auto& node: split(souschaine, "<-")) {
114 auto idVar = build_node_for_ID(infdiag, node, domain);
115 if (notfirst) {
116 if (forward) {
117 infdiag.addArc(lastId, idVar);
118 forward = false;
119 } else {
120 infdiag.addArc(idVar, lastId);
121 }
122 } else {
123 notfirst = true;
124 forward = false;
125 }
126 lastId = idVar;
127 }
128 }
129 }
130
131 for (const auto n: infdiag.nodes()) {
132 if (infdiag.isChanceNode(n)) infdiag.cpt(n).randomCPT();
133 else if (infdiag.isUtilityNode(n)) { infdiag.utility(n).random().scale(50).translate(-10); }
134 }
135
136 infdiag.setProperty("name", "fastPrototype");
137 return infdiag;
138 }
139
140 // ===========================================================================
141 // Constructors / Destructors
142 // ===========================================================================
143
144 /*
145 * Default constructor.
146 */
147 template < typename GUM_SCALAR >
151
152 /*
153 * Destructor.
154 */
155 template < typename GUM_SCALAR >
160
161 /*
162 * Copy Constructor
163 */
164 template < typename GUM_SCALAR >
169
170 /*
171 * Copy Operator
172 */
173 template < typename GUM_SCALAR >
176 if (this != &source) {
177 clear();
178 // Copying tables and structure
180 }
181
182 return *this;
183 }
184
185 template < typename GUM_SCALAR >
187 // Removing previous tensors
188 removeTables_();
189 _variableMap_.clear();
190 dag_.clear();
191 _tensorMap_.clear();
192 _utilityMap_.clear();
193 }
194
195 /*
196 * Removing ancient table
197 */
198 template < typename GUM_SCALAR >
200 for (const auto node: dag_.nodes()) {
201 if (isChanceNode(node)) delete &cpt(node);
202 else if (isUtilityNode(node)) delete &utility(node);
203 }
204 }
205
206 /*
207 * Copying tables from another influence diagram
208 */
209 template < typename GUM_SCALAR >
211 const InfluenceDiagram< GUM_SCALAR >& IDsource) {
212 for (auto node: IDsource.nodes()) {
213 if (IDsource.isChanceNode(node)) addChanceNode(IDsource.variable(node), node);
214 else if (IDsource.isUtilityNode(node)) addUtilityNode(IDsource.variable(node), node);
215 else // decision node
216 addDecisionNode(IDsource.variable(node), node);
217 }
218 // we add arc in the same order of the tensors
219 for (auto node: IDsource.nodes()) {
220 const auto& s = IDsource.variable(node).name();
221 if (IDsource.isChanceNode(node)) {
222 for (Idx par = 1; par <= IDsource.parents(node).size(); par++)
223 addArc(IDsource.cpt(node).variable(par).name(), s);
224 } else if (IDsource.isUtilityNode(node)) {
225 for (Idx par = 1; par <= IDsource.parents(node).size(); par++)
226 addArc(IDsource.utility(node).variable(par).name(), s);
227 } else { // decision node
228 // here the order does not depend on a Tensor
229 for (NodeId par: IDsource.parents(node))
230 addArc(par, node);
231 }
232 }
233
234 // Copying tensors
235 for (auto node: IDsource.nodes()) {
236 const auto& s = IDsource.variable(node).name();
237 if (IDsource.isChanceNode(node)) {
238 cpt(node).fillWith(IDsource.cpt(s));
239 } else if (IDsource.isUtilityNode(node)) {
240 utility(node).fillWith(IDsource.utility(s));
241 }
242 }
243 }
244
245 template < typename GUM_SCALAR >
247 std::stringstream output;
248 std::stringstream decisionNode;
249 std::stringstream utilityNode;
250 std::stringstream chanceNode;
251 std::stringstream arcstream;
252 output << "digraph \"";
253
254 try {
255 output << this->property("name") << "\" {" << std::endl;
256 } catch (NotFound const&) { output << "no_name\" {" << std::endl; }
257
258 output << " node [bgcolor=\"#AAAAAA\", style=filled, height=0];" << std::endl;
259
260 decisionNode << "node [shape = box];" << std::endl;
261
262 utilityNode << "node [shape = hexagon, margin=0];" << std::endl;
263 chanceNode << "node [shape = ellipse];" << std::endl;
264 std::string tab = " ";
265
266 for (const auto node: dag_.nodes()) {
267 if (isChanceNode(node))
268 chanceNode << tab << "\"" << node << "-" << variable(node).name() << "\""
269 << ";";
270 else if (isUtilityNode(node))
271 utilityNode << tab << "\"" << node << "-" << variable(node).name() << "\""
272 << ";";
273 else
274 decisionNode << tab << "\"" << node << "-" << variable(node).name() << "\""
275 << ";";
276
277 if (dag_.children(node).size() > 0)
278 for (const auto chi: dag_.children(node)) {
279 arcstream << "\"" << node << "-" << variable(node).name() << "\""
280 << " -> "
281 << "\"" << chi << "-" << variable(chi).name() << "\"";
282 if (isDecisionNode(chi)) { arcstream << " [style=\"tapered, bold\"]"; }
283 arcstream << ";" << std::endl;
284 }
285 }
286
287 output << decisionNode.str() << std::endl
288 << utilityNode.str() << std::endl
289 << chanceNode.str() << std::endl
290 << std::endl
291 << arcstream.str() << std::endl
292 << "}" << std::endl;
293
294 return output.str();
295 }
296
297 template < typename GUM_SCALAR >
299 std::stringstream output;
300
301 output << "Influence Diagram{" << std::endl;
302 output << " chance: " << chanceNodeSize() << "," << std::endl;
303 output << " utility: " << utilityNodeSize() << "," << std::endl;
304 output << " decision: " << decisionNodeSize() << "," << std::endl;
305 output << " arcs: " << dag().sizeArcs() << "," << std::endl;
306
307
308 if (double dSize = log10DomainSize(); dSize > 6) output << " domainSize: 10^" << dSize;
309 else output << " domainSize: " << std::round(std::pow(10.0, dSize));
310
311 output << std::endl << "}";
312
313 return output.str();
314 }
315
316 // ===========================================================================
317 // Variable manipulation methods.
318 // ===========================================================================
319
320 /*
321 * Returns the CPT of a chance variable.
322 */
323 template < typename GUM_SCALAR >
324 INLINE const Tensor< GUM_SCALAR >& InfluenceDiagram< GUM_SCALAR >::cpt(NodeId varId) const {
325 return *(_tensorMap_[varId]);
326 }
327
328 /*
329 * Returns the utility table of a utility node.
330 */
331 template < typename GUM_SCALAR >
332 INLINE const Tensor< GUM_SCALAR >& InfluenceDiagram< GUM_SCALAR >::utility(NodeId varId) const {
333 return *(_utilityMap_[varId]);
334 }
335
336 /*
337 * Return true if node is a utility one
338 */
339 template < typename GUM_SCALAR >
341 return _utilityMap_.exists(varId);
342 }
343
344 /*
345 * Return true if node is a utility one
346 */
347 template < typename GUM_SCALAR >
349 bool ret = true;
350
351 if (isUtilityNode(varId) || isChanceNode(varId)) ret = false;
352
353 return ret;
354 }
355
356 /*
357 * Return true if node is a chance one
358 */
359 template < typename GUM_SCALAR >
361 return _tensorMap_.exists(varId);
362 }
363
364 /*
365 * Returns the number of utility nodes
366 */
367 template < typename GUM_SCALAR >
369 return _utilityMap_.size();
370 }
371
372 /*
373 * Returns the number of chance nodes
374 */
375 template < typename GUM_SCALAR >
377 return _tensorMap_.size();
378 }
379
380 /*
381 * Returns the number of decision nodes
382 */
383 template < typename GUM_SCALAR >
385 return (size() - _utilityMap_.size() - _tensorMap_.size());
386 }
387
388 /*
389 * Returns a constant reference to the VariableNodeMap of this Influence
390 * Diagram
391 */
392 template < typename GUM_SCALAR >
396
397 /*
398 * Returns a constant reference over a variable given it's node id.
399 */
400 template < typename GUM_SCALAR >
402 return _variableMap_[id];
403 }
404
405 /*
406 * Return id node from discrete var pointer.
407 */
408 template < typename GUM_SCALAR >
410 return _variableMap_.get(var);
411 }
412
413 // Getter by name
414 template < typename GUM_SCALAR >
415 INLINE NodeId InfluenceDiagram< GUM_SCALAR >::idFromName(const std::string& name) const {
416 return _variableMap_.idFromName(name);
417 }
418
419 // Getter by name
420 template < typename GUM_SCALAR >
421 INLINE const DiscreteVariable&
423 return _variableMap_.variableFromName(name);
424 }
425
426 /*
427 * Add a chance variable, it's associate node and it's CPT. The id of the new
428 * variable is automatically generated.
429 */
430 template < typename GUM_SCALAR >
432 return addChanceNode(var, varId);
433 }
434
435 /*
436 * Add a utility variable, it's associate node and it's UT. The id of the new
437 * variable is automatically generated.
438 * @Throws : Gum::InvalidArgument if var has more than one state
439 */
440 template < typename GUM_SCALAR >
442 auto newMultiDim = new MultiDimArray< GUM_SCALAR >();
443 NodeId res;
444
445 try {
446 res = addUtilityNode(var, newMultiDim, varId);
447 } catch (Exception const&) {
448 if (newMultiDim != nullptr) delete newMultiDim;
449 throw;
450 }
451
452 return res;
453 }
454
455 /*
456 * Add a decision variable. The id of the new
457 * variable is automatically generated.
458 */
459 template < typename GUM_SCALAR >
461 NodeId varId) {
462 return addNode_(var, varId);
463 }
464
465 /*
466 * Add a chance variable, it's associate node and it's CPT. The id of the new
467 * variable is automatically generated.
468 */
469 template < typename GUM_SCALAR >
471 auto newMultiDim = new MultiDimArray< GUM_SCALAR >();
472 NodeId res;
473
474 try {
475 res = addChanceNode(var, newMultiDim, varId);
476 } catch (Exception const&) {
477 delete newMultiDim;
478 throw;
479 }
480
481 return res;
482 }
483
484 /*
485 * Add a chance variable, it's associate node and it's CPT. The id of the new
486 * variable is automatically generated.
487 */
488 template < typename GUM_SCALAR >
489 NodeId
492 NodeId DesiredId) {
493 NodeId proposedId = addNode_(var, DesiredId);
494
495 auto varcpt = new Tensor< GUM_SCALAR >(aContent);
496 (*varcpt) << variable(proposedId);
497 _tensorMap_.insert(proposedId, varcpt);
498
499 return proposedId;
500 }
501
502 /*
503 * Add a utility variable, it's associate node and it's UT. The id of the new
504 * variable is automatically generated.
505 * @Throws : Gum::InvalidArgument if var has more than one state
506 */
507 template < typename GUM_SCALAR >
508 NodeId
511 NodeId DesiredId) {
512 if (var.domainSize() != 1) {
514 "Utility var have no state ( which implicates a "
515 "single label for data output reasons ).")
516 }
517
518 NodeId proposedId = addNode_(var, DesiredId);
519
520 auto varut = new Tensor< GUM_SCALAR >(aContent);
521
522 (*varut) << variable(proposedId);
523
524 _utilityMap_.insert(proposedId, varut);
525
526 return proposedId;
527 }
528
529 /*
530 * Add a node
531 */
532 template < typename GUM_SCALAR >
534 NodeId DesiredId) {
535 // None thread safe code!
536 NodeId proposedId;
537
538 if (DesiredId == 0) proposedId = dag_.nextNodeId();
539 else proposedId = DesiredId;
540
541 _variableMap_.insert(proposedId, variableType);
542
543 dag_.addNodeWithId(proposedId);
544
545 // end critical section
546 return proposedId;
547 }
548
549 /*
550 * Erase a Variable from the network and remove the variable from
551 * all children of id.
552 * If no variable matches the id, then nothing is done.
553 */
554 template < typename GUM_SCALAR >
556 if (_variableMap_.exists(varId)) {
557 // Reduce the variable child's CPT or Utility Table if necessary
558 for (const auto chi: dag_.children(varId))
559 if (isChanceNode(chi)) _tensorMap_[chi]->erase(variable(varId));
560 else if (isUtilityNode(chi)) _utilityMap_[chi]->erase(variable(varId));
561
562 if (isChanceNode(varId)) {
563 delete _tensorMap_[varId];
564 _tensorMap_.erase(varId);
565 } else if (isUtilityNode(varId)) {
566 delete _utilityMap_[varId];
567 _utilityMap_.erase(varId);
568 }
569
570 _variableMap_.erase(varId);
571 dag_.eraseNode(varId);
572 }
573 }
574
575 /*
576 * Erase a Variable from the network and remove the variable from
577 * all children of var.
578 * If no variable matches, then nothing is done.
579 */
580 template < typename GUM_SCALAR >
582 erase(_variableMap_.get(var));
583 }
584
585 /* we allow the user to change the name of a variable
586 */
587 template < typename GUM_SCALAR >
589 const std::string& new_name) {
590 _variableMap_.changeName(id, new_name);
591 }
592
593 // ===========================================================================
594 // @name Arc manipulation methods.
595 // ===========================================================================
596 /*
597 * Add an arc in the ID, and update diagram's chance nodes cpt if necessary.
598 */
599 template < typename GUM_SCALAR >
601 if (isUtilityNode(tail)) { GUM_ERROR(InvalidArc, "Tail cannot be a utility node") }
602
603 dag_.addArc(tail, head);
604
605 if (isChanceNode(head))
606 // Add parent in the child's CPT
607 (*(_tensorMap_[head])) << variable(tail);
608 else if (isUtilityNode(head)) {
609 // Add parent in the child's UT
610 (*(_utilityMap_[head])) << variable(tail);
611 }
612 }
613
614 /*
615 * Removes an arc in the ID, and update diagram chance nodes cpt if necessary.
616 *
617 * If (tail, head) doesn't exist, the nothing happens.
618 */
619 template < typename GUM_SCALAR >
621 if (dag_.existsArc(arc)) {
622 NodeId head = arc.head();
623 NodeId tail = arc.tail();
624 dag_.eraseArc(arc);
625
626 if (isChanceNode(head))
627 // Removes parent in the child's CPT
628 (*(_tensorMap_[head])) >> variable(tail);
629 else if (isUtilityNode(head))
630 // Removes parent in the child's UT
631 (*(_utilityMap_[head])) >> variable(tail);
632 }
633 }
634
635 /*
636 * Removes an arc in the ID, and update diagram chance nodes cpt if necessary.
637 *
638 * If (tail, head) doesn't exist, the nothing happens.
639 */
640 template < typename GUM_SCALAR >
642 eraseArc(Arc(tail, head));
643 }
644
645 // ===========================================================================
646 // Graphical methods
647 // ===========================================================================
648
649 /*
650 * The node's id are coherent with the variables and nodes of the topology.
651 */
652 template < typename GUM_SCALAR >
654 for (const auto node: dag_.nodes())
655 if (!isUtilityNode(node)) graph.addNodeWithId(node);
656
657 for (const auto node: dag_.nodes()) {
658 if (!isDecisionNode(node))
659 for (const auto par: dag_.parents(node)) {
660 if (isChanceNode(node)) graph.addEdge(node, par);
661
662 for (const auto par2: dag_.parents(node))
663 if (par != par2) graph.addEdge(par, par2);
664 }
665 }
666 }
667
668 /*
669 * True if a directed path exist with all decision nodes
670 */
671 template < typename GUM_SCALAR >
673 const Sequence< NodeId > order = topologicalOrder();
674
675 // Finding first decision node
676 Sequence< NodeId >::const_iterator orderIter = order.begin();
677
678 while ((orderIter != order.end()) && (!isDecisionNode(*orderIter)))
679 ++orderIter;
680
681 if (orderIter == order.end()) return true;
682
683 NodeId parentDecision = (*orderIter);
684 ++orderIter;
685
686 // Checking path between decisions nodes
687 while (orderIter != order.end()) {
688 if (isDecisionNode(*orderIter)) {
689 if (!existsPathBetween(parentDecision, *orderIter)) return false;
690
691 parentDecision = *orderIter;
692 }
693
694 ++orderIter;
695 }
696
697 return true;
698 }
699
700 /*
701 * Returns true if a path exists between source and destination
702 */
703 template < typename GUM_SCALAR >
705 List< NodeId > nodeFIFO;
706 // mark[node] contains 0 if not visited
707 // mark[node] = predecessor if visited
708 NodeProperty< int > mark = dag_.nodesPropertyFromVal(-1);
709 NodeId current;
710
711 mark[src] = (int)src;
712 nodeFIFO.pushBack(src);
713
714 while (!nodeFIFO.empty()) {
715 current = nodeFIFO.front();
716 nodeFIFO.popFront();
717
718 for (const auto new_one: dag_.children(current)) {
719 if (mark[new_one] != -1) continue; // if this node is already marked, continue
720
721 mark[new_one] = (int)current;
722
723 if (new_one == dest) break; // if we reach *orderIter, stop.
724
725 nodeFIFO.pushBack(new_one);
726 }
727 }
728
729 if (mark[dest] == -1) return false;
730
731 return true;
732 }
733
734 /*
735 * Returns the decision graph
736 */
737 template < typename GUM_SCALAR >
739 auto temporalGraph = new gum::DAG();
740
741 for (const auto node: dag_.nodes()) {
742 if (isDecisionNode(node)) {
743 if (!temporalGraph->existsNode(node)) temporalGraph->addNodeWithId(node);
744
745 for (const auto chi: getChildrenDecision_(node)) {
746 if (!temporalGraph->existsNode(chi)) temporalGraph->addNodeWithId(chi);
747
748 temporalGraph->addArc(node, chi);
749 }
750 }
751 }
752
753 return temporalGraph;
754 }
755
756 /*
757 * Returns the list of children decision for a given nodeId
758 */
759 template < typename GUM_SCALAR >
762 Sequence< NodeId > childrenSeq;
763
764 List< NodeId > nodeFIFO;
765 NodeId current;
766
767 // mark[node] contains false if not visited
768 // mark[node] contains true if visited
769 NodeProperty< bool > mark = dag_.nodesPropertyFromVal(false);
770
771 mark[parentDecision] = true;
772
773 nodeFIFO.pushBack(parentDecision);
774
775 while (!nodeFIFO.empty()) {
776 current = nodeFIFO.front();
777 nodeFIFO.popFront();
778
779 for (const auto new_one: dag_.children(current)) {
780 if (mark[new_one]) continue; // if this node is already marked, continue
781
782 mark[new_one] = true;
783
784 if (!isDecisionNode(new_one)) nodeFIFO.pushBack(new_one);
785 else childrenSeq.insert(new_one);
786 }
787 }
788
789 return childrenSeq;
790 }
791
792 /*
793 * Returns the sequence of decision nodes
794 * @throw NotFound if such a sequence does not exist
795 */
796 template < typename GUM_SCALAR >
797 std::vector< NodeId > InfluenceDiagram< GUM_SCALAR >::decisionOrder() const {
798 if (!decisionOrderExists()) { GUM_ERROR(NotFound, "No decision path exists") }
799
800 std::vector< NodeId > decisionSequence;
801
802 for (const auto elt: topologicalOrder())
803 if (isDecisionNode(elt)) decisionSequence.push_back(elt);
804
805 return decisionSequence;
806 }
807
808 /*
809 * Returns partial temporal ordering
810 * @throw NotFound if such a sequence does not exist
811 */
812 template < typename GUM_SCALAR >
814 if (clear) {
815 _temporalOrder_.clear();
816
817 std::vector< NodeId > order = decisionOrder();
818 NodeSet nodeList = dag_.asNodeSet();
819
820 for (auto i: order) {
821 NodeSet partialOrderedSet;
822
823 for (const auto par: dag_.parents(i)) {
824 if (nodeList.contains(par) && isChanceNode(par)) {
825 partialOrderedSet.insert(par);
826 nodeList.erase(par);
827 }
828 }
829
830 if (!partialOrderedSet.empty()) _temporalOrder_.pushFront(partialOrderedSet);
831
832 NodeSet decisionSet;
833
834 decisionSet.insert(i);
835
836 _temporalOrder_.pushFront(decisionSet);
837 }
838
839 NodeSet lastSet; //= new gum::NodeSet();
840
841 for (const auto node: nodeList)
842 if (isChanceNode(node)) lastSet.insert(node);
843
844 if (!lastSet.empty()) _temporalOrder_.pushFront(lastSet);
845 }
846
847 return _temporalOrder_;
848 }
849
850 template < typename GUM_SCALAR >
851 NodeId InfluenceDiagram< GUM_SCALAR >::addChanceNode(const std::string& fast_description,
852 unsigned int default_nbrmod) {
853 auto v = fastVariable< GUM_SCALAR >(fast_description, default_nbrmod);
854 if (v->domainSize() < 2) GUM_ERROR(OperationNotAllowed, v->name() << " has a domain size <2")
855 return addChanceNode(*v);
856 }
857
858 template < typename GUM_SCALAR >
859 NodeId InfluenceDiagram< GUM_SCALAR >::addUtilityNode(const std::string& fast_description) {
860 auto v = fastVariable< GUM_SCALAR >(fast_description, 1);
861 if (v->domainSize() >= 2)
863 v->name() << " has a domain size >= 2 which is impossible for a utility node")
864 return addUtilityNode(*v);
865 }
866
867 template < typename GUM_SCALAR >
868 NodeId InfluenceDiagram< GUM_SCALAR >::addDecisionNode(const std::string& fast_description,
869 unsigned int default_nbrmod) {
870 auto v = fastVariable< GUM_SCALAR >(fast_description, default_nbrmod);
871 if (v->domainSize() < 2) GUM_ERROR(OperationNotAllowed, v->name() << " has a domain size <2")
872 return addDecisionNode(*v);
873 }
874
875 template < typename GUM_SCALAR >
876 NodeId InfluenceDiagram< GUM_SCALAR >::add(const std::string& fast_description,
877 unsigned int default_nbrmod) {
878 std::string node = fast_description;
879 switch (*(node.begin())) {
880 case '*' : node.erase(0, 1); return addDecisionNode(node, default_nbrmod);
881 case '$' : node.erase(0, 1); return addUtilityNode(node);
882 default : return addChanceNode(fast_description, default_nbrmod);
883 }
884 }
885
887 template < typename GUM_SCALAR >
889 for (const auto node: nodes())
890 if (isChanceNode(node)) _tensorMap_[node]->beginMultipleChanges();
891 else if (this->isUtilityNode(node)) _utilityMap_[node]->beginMultipleChanges();
892 }
893
895 template < typename GUM_SCALAR >
897 for (const auto node: nodes())
898 if (isChanceNode(node)) _tensorMap_[node]->endMultipleChanges();
899 else if (isUtilityNode(node)) _utilityMap_[node]->endMultipleChanges();
900 }
901
902 template < typename GUM_SCALAR >
904 if (size() != from.size()) { return false; }
905
906 if (sizeArcs() != from.sizeArcs()) { return false; }
907
908 // alignment of variables between the 2 BNs
910
911 for (auto node: nodes()) {
912 try {
913 const auto& v1 = variable(node);
914 const auto& v2 = from.variableFromName(variable(node).name());
915 if (v1 != v2) { return false; }
916
917 if (isChanceNode(v1.name()) && !from.isChanceNode(v2.name())) { return false; }
918 if (isUtilityNode(v1.name()) && !from.isUtilityNode(v2.name())) { return false; }
919 if (isDecisionNode(v1.name()) && !from.isDecisionNode(v2.name())) { return false; }
920
921 alignment.insert(&variable(node), &from.variableFromName(variable(node).name()));
922 } catch (NotFound const&) {
923 // a name is not found in from
924 return false;
925 }
926 }
927
928 auto check_pot
929 = [&](const gum::Tensor< GUM_SCALAR >& p1, const gum::Tensor< GUM_SCALAR >& p2) -> bool {
930 if (p1.nbrDim() != p2.nbrDim()) { return false; }
931
932 if (p1.domainSize() != p2.domainSize()) { return false; }
933
934 Instantiation i(p1);
935 Instantiation j(p2);
936
937 for (i.setFirst(); !i.end(); i.inc()) {
938 for (Idx indice = 0; indice < p1.nbrDim(); ++indice) {
939 const DiscreteVariable* p = &(i.variable(indice));
940 j.chgVal(*(alignment.second(p)), i.val(*p));
941 }
942
943 if (std::pow(p1.get(i) - p2.get(j), (GUM_SCALAR)2) > (GUM_SCALAR)1e-6) { return false; }
944 }
945 return true;
946 };
947 for (auto node: nodes()) {
948 NodeId fromnode = from.idFromName(variable(node).name());
949 if (isChanceNode(node)) {
950 if (!check_pot(cpt(node), from.cpt(fromnode))) { return false; }
951 } else if (isUtilityNode(node)) {
952 if (!check_pot(utility(node), from.utility(fromnode))) { return false; }
953 }
954 }
955
956 return true;
957 }
958} // namespace gum
The base class for all directed edges.
GUM_NODISCARD NodeId head() const
returns the head of the arc
GUM_NODISCARD NodeId tail() const
returns the tail of the arc
void insert(const T1 &first, const T2 &second)
Inserts a new association in the gum::Bijection.
const T2 & second(const T1 &first) const
Returns the second value of a pair given its first value.
Set of pairs of elements with fast search for both elements.
Definition bijection.h:1594
Base class for dag.
Definition DAG.h:121
const DAG & dag() const
Returns a constant reference to the dag of this Bayes Net.
DAG dag_
The DAG of this Directed Graphical Model.
Definition DAGmodel.h:272
DAGmodel()
Default constructor.
Definition DAGmodel.cpp:49
virtual Size size() const final
Returns the number of variables in this Directed Graphical Model.
Size sizeArcs() const
Returns the number of arcs in this Directed Graphical Model.
Sequence< NodeId > topologicalOrder() const
The topological order stays the same as long as no variable or arcs are added or erased src the topol...
const NodeSet & parents(const NodeId id) const
returns the set of nodes with arc ingoing to a given node
const NodeGraphPart & nodes() const final
Returns a constant reference to the dag of this Bayes Net.
Base class for discrete random variable.
virtual Size domainSize() const =0
Base class for all aGrUM's exceptions.
Definition exceptions.h:118
Exception : fatal (unknown ?) error.
void setProperty(const std::string &name, const std::string &value)
Add or change a property of this GraphicalModel.
const std::string & property(const std::string &name) const
Return the value of the property name of this GraphicalModel.
double log10DomainSize() const
Class representing an Influence Diagram.
void beginTopologyTransformation()
When inserting/removing arcs, node CPTs/utilities change their dimension with a cost in time.
List< NodeSet > _temporalOrder_
The temporal order.
gum::DAG * getDecisionGraph() const
Returns the temporal Graph.
const List< NodeSet > & getPartialTemporalOrder(bool clear=true) const
Returns partial temporal ordering.
bool isDecisionNode(NodeId varId) const
Returns true if node is a decision one.
VariableNodeMap _variableMap_
Mapping between id and variable.
virtual const Tensor< GUM_SCALAR > & cpt(NodeId varId) const
Returns the CPT of a tensor variable.
NodeId addChanceNode(const DiscreteVariable &variable, NodeId id=0)
Add a chance variable, it's associate node and it's CPT.
Size chanceNodeSize() const
Returns the number of chance nodes.
void endTopologyTransformation()
terminates a sequence of insertions/deletions of arcs by adjusting all CPTs/utilities dimensions.
void removeTables_()
Removing ancient table.
NodeProperty< Tensor< GUM_SCALAR > * > _tensorMap_
Mapping between tensor variable's id and their CPT.
const VariableNodeMap & variableNodeMap() const final
Returns a constant reference to the VariableNodeMap of this Influence Diagram.
void addArc(NodeId tail, NodeId head)
Add an arc in the ID, and update diagram's tensor nodes cpt if necessary.
virtual void moralGraph_(UndiGraph &graph) const
Returns the moral graph of this InfluenceDiagram.
NodeId addNode_(const DiscreteVariable &variableType, NodeId DesiredId)
Add a node.
std::string toDot() const
bool decisionOrderExists() const
True if a directed path exist with all decision nodes.
Size utilityNodeSize() const
Returns the number of utility nodes.
InfluenceDiagram< GUM_SCALAR > & operator=(const InfluenceDiagram< GUM_SCALAR > &source)
Copy Operator.
void changeVariableName(NodeId id, const std::string &new_name)
we allow the user to change the name of a variable
NodeId add(const DiscreteVariable &variable, NodeId id=0)
Add a chance variable, it's associate node and it's CPT.
static InfluenceDiagram< GUM_SCALAR > fastPrototype(const std::string &dotlike, Size domainSize)
Create an Influence Diagram with a dot-like syntax which specifies:
NodeId addUtilityNode(const DiscreteVariable &variable, NodeId id=0)
Add a utility variable, it's associate node and it's UT.
std::string toString() const
bool isUtilityNode(NodeId varId) const
Returns true if node is a utility one.
void copyStructureAndTables_(const InfluenceDiagram< GUM_SCALAR > &IDsource)
Copying tables from another influence diagram.
NodeId addDecisionNode(const DiscreteVariable &variable, NodeId id=0)
Add a decision variable.
bool isChanceNode(NodeId varId) const
Returns true if node is a chance one.
InfluenceDiagram()
Default constructor.
const DiscreteVariable & variable(NodeId id) const final
Returns a constant reference over a variable given it's node id.
bool operator==(const InfluenceDiagram< GUM_SCALAR > &other) const
void eraseArc(const Arc &arc)
Removes an arc in the ID, and update diagram's tensor nodes cpt if necessary.
NodeProperty< Tensor< GUM_SCALAR > * > _utilityMap_
Mapping between utility variable's id and their utility table.
NodeId nodeId(const DiscreteVariable &var) const final
Return id node from discrete var pointer.
Size decisionNodeSize() const
Returns the number of decision nodes.
Sequence< NodeId > getChildrenDecision_(NodeId parentDecision) const
Returns the list of children decision for a given nodeId.
const DiscreteVariable & variableFromName(const std::string &name) const final
Getter by name.
void erase(NodeId id)
Erase a Variable from the network and remove the variable from all his children.
virtual const Tensor< GUM_SCALAR > & utility(NodeId varId) const
Returns the utility table of a utility node.
std::vector< NodeId > decisionOrder() const
Returns the sequence of decision nodes in the directed path.
NodeId idFromName(const std::string &name) const final
Getter by name.
bool existsPathBetween(NodeId src, NodeId dest) const
Returns true if a path exists between two nodes.
~InfluenceDiagram() override
Destructor.
Class for assigning/browsing values to tuples of discrete variables.
Instantiation & chgVal(const DiscreteVariable &v, Idx newval)
Assign newval to variable v in the Instantiation.
bool end() const
Returns true if the Instantiation reached the end.
void inc()
Operator increment.
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.
Exception : there is something wrong with an arc.
Exception: at least one argument passed to a function is not what was expected.
Generic doubly linked lists.
Definition list.h:379
Val & front() const
Returns a reference to first element of a list, if any.
Definition list_tpl.h:1703
bool empty() const noexcept
Returns a boolean indicating whether the chained list is empty.
Definition list_tpl.h:1831
void popFront()
Removes the first element of a List, if any.
Definition list_tpl.h:1825
Val & pushBack(const Val &val)
Inserts a new element (a copy) at the end of the chained list.
Definition list_tpl.h:1488
Multidimensional matrix stored as an array in memory.
virtual Idx nbrDim() const final
Returns the number of vars in the multidimensional container.
virtual GUM_SCALAR get(const Instantiation &i) const final
Default implementation of MultiDimContainer::get().
virtual Size domainSize() const final
Returns the product of the variables domain size.
<agrum/base/multidim/multiDimImplementation.h>
virtual void addNodeWithId(const NodeId id)
try to insert a node with the given id
Exception : the element we looked for cannot be found.
Exception : operation not allowed.
SequenceIterator< Key > const_iterator
Types for STL compliance.
Definition sequence.h:984
bool contains(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:497
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:539
bool empty() const noexcept
Indicates whether the set is the empty set.
Definition set_tpl.h:642
void erase(const Key &k)
Erases an element from the set.
Definition set_tpl.h:582
aGrUM's Tensor is a multi-dimensional array with tensor operators.
Definition tensor.h:85
Base class for undirected graphs.
Definition undiGraph.h:128
void addEdge(NodeId first, NodeId second) override
insert a new edge into the undirected graph
Container used to map discrete variables with nodes.
const std::string & name() const
returns the name of the variable
#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.
HashTable< NodeId, VAL > NodeProperty
Property on graph elements.
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
std::string remove_newline(const std::string &s)
remove all newlines in a string
std::vector< std::string > split(const std::string &str, const std::string &delim)
Split str using the delimiter.
Class representing Influence Diagrams.
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
std::unique_ptr< DiscreteVariable > fastVariable(std::string var_description, Size default_domain_size)
Create a pointer on a Discrete Variable from a "fast" syntax.
NodeId build_node_for_ID(gum::InfluenceDiagram< GUM_SCALAR > &infdiag, std::string node, const std::string &domain)