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