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 InfluenceDiagramGenerator
24  *
25  * @author Pierre-Henri WUILLEMIN(_at_LIP6) and Jean-Christophe MAGNAN and Christophe
26  * GONZALES(_at_AMU)
27  *
28  */
29 #include <agrum/ID/generator/influenceDiagramGenerator.h>
30 
31 namespace gum {
32 
33   // Default constructor.
34   // Use the SimpleCPTGenerator for generating the IDs CPT.
35   template < typename GUM_SCALAR >
InfluenceDiagramGenerator()36   InfluenceDiagramGenerator< GUM_SCALAR >::InfluenceDiagramGenerator() {
37     GUM_CONSTRUCTOR(InfluenceDiagramGenerator);
38     _cptGenerator_ = new SimpleCPTGenerator< GUM_SCALAR >();
39     _utGenerator_  = new SimpleUTGenerator();
40   }
41 
42   // Use this constructor if you want to use a different policy for generating
43   // CPT than the default one.
44   // The cptGenerator will be erased when the destructor is called.
45   // @param cptGenerator The policy used to generate CPT.
46   template < typename GUM_SCALAR >
InfluenceDiagramGenerator(ICPTGenerator<GUM_SCALAR> * cptGenerator)47   InfluenceDiagramGenerator< GUM_SCALAR >::InfluenceDiagramGenerator(
48      ICPTGenerator< GUM_SCALAR >* cptGenerator) {
49     GUM_CONSTRUCTOR(InfluenceDiagramGenerator);
50     _cptGenerator_ = cptGenerator;
51     _utGenerator_  = new SimpleUTGenerator();
52   }
53 
54   // Use this constructor if you want to use a different policy for generating
55   // UT than the default one.
56   // The utGenerator will be erased when the destructor is called.
57   // @param utGenerator The policy used to generate UT.
58   template < typename GUM_SCALAR >
InfluenceDiagramGenerator(UTGenerator * utGenerator)59   InfluenceDiagramGenerator< GUM_SCALAR >::InfluenceDiagramGenerator(UTGenerator* utGenerator) {
60     GUM_CONSTRUCTOR(InfluenceDiagramGenerator);
61     _cptGenerator_ = new SimpleCPTGenerator< GUM_SCALAR >();
62     _utGenerator_  = utGenerator;
63   }
64 
65   // Use this constructor if you want to use a different policy for generating
66   // both CPT & UT than the defaults ones.
67   // The cptGenerator and utGenerator will be erased when the destructor is
68   // called.
69   // @param cptGenerator The policy used to generate CPT.
70   // @param utGenerator The policy used to generate UT.
71   template < typename GUM_SCALAR >
InfluenceDiagramGenerator(ICPTGenerator<GUM_SCALAR> * cptGenerator,UTGenerator * utGenerator)72   InfluenceDiagramGenerator< GUM_SCALAR >::InfluenceDiagramGenerator(
73      ICPTGenerator< GUM_SCALAR >* cptGenerator,
74      UTGenerator*                 utGenerator) {
75     GUM_CONSTRUCTOR(InfluenceDiagramGenerator);
76     _cptGenerator_ = cptGenerator;
77     _utGenerator_  = utGenerator;
78   }
79 
80   // Destructor.
81   template < typename GUM_SCALAR >
~InfluenceDiagramGenerator()82   InfluenceDiagramGenerator< GUM_SCALAR >::~InfluenceDiagramGenerator() {
83     GUM_DESTRUCTOR(InfluenceDiagramGenerator);
84     delete _cptGenerator_;
85     delete _utGenerator_;
86   }
87 
88   // Generates an influence diagram using floats.
89   // @param nbrNodes The number of nodes in the generated ID.
90   // @param arcdensity The probability of adding an arc between two nodes.
91   // @param chanceNodeDensity The proportion of chance node
92   // @param utilityNodeDensity The proportion of utility node
93   // @param max_modality Each DRV has from 2 to max_modality modalities
94   // @return A IDs randomly generated.
95   template < typename GUM_SCALAR >
96   InfluenceDiagram< GUM_SCALAR >*
generateID(Size nbrNodes,GUM_SCALAR arcDensity,GUM_SCALAR chanceNodeDensity,GUM_SCALAR utilityNodeDensity,Size max_modality)97      InfluenceDiagramGenerator< GUM_SCALAR >::generateID(Size       nbrNodes,
98                                                          GUM_SCALAR arcDensity,
99                                                          GUM_SCALAR chanceNodeDensity,
100                                                          GUM_SCALAR utilityNodeDensity,
101                                                          Size       max_modality) {
102     InfluenceDiagram< GUM_SCALAR >* influenceDiagram = new InfluenceDiagram< GUM_SCALAR >();
103     // First we add nodes
104     HashTable< Size, NodeId > map;
105     std::stringstream         strBuff;
106     Size                      nb_mod;
107 
108     for (Idx i = 0; i < nbrNodes; ++i) {
109       strBuff << i;
110       nb_mod = (max_modality == 2) ? 2 : 2 + rand() % (max_modality - 1);
111 
112       GUM_SCALAR cnd = chanceNodeDensity * (GUM_SCALAR)RAND_MAX;
113       GUM_SCALAR und = utilityNodeDensity * (GUM_SCALAR)RAND_MAX;
114 
115       GUM_SCALAR d = (GUM_SCALAR)rand();
116 
117       if (d < cnd)
118         map.insert(i,
119                    influenceDiagram->addChanceNode(LabelizedVariable(strBuff.str(), "", nb_mod)));
120       else if (d < (cnd + und))
121         map.insert(i, influenceDiagram->addUtilityNode(LabelizedVariable(strBuff.str(), "", 1)));
122       else
123         map.insert(i,
124                    influenceDiagram->addDecisionNode(LabelizedVariable(strBuff.str(), "", nb_mod)));
125 
126       strBuff.str("");
127     }
128 
129     // We add arcs
130     GUM_SCALAR p = arcDensity * (GUM_SCALAR)RAND_MAX;
131 
132     for (Size i = 0; i < nbrNodes; ++i)
133       if (!influenceDiagram->isUtilityNode(map[i]))
134         for (Size j = i + 1; j < nbrNodes; ++j)
135           if (((GUM_SCALAR)rand()) < p) { influenceDiagram->addArc(map[i], map[j]); }
136 
137     // And fill the CPTs and UTs
138     for (Size i = 0; i < nbrNodes; ++i)
139       if (influenceDiagram->isChanceNode(map[i]))
140         _cptGenerator_->generateCPT(
141            influenceDiagram->cpt(map[i]).pos(influenceDiagram->variable(map[i])),
142            influenceDiagram->cpt(map[i]));
143       else if (influenceDiagram->isUtilityNode(map[i]))
144         _utGenerator_->generateUT(
145            influenceDiagram->utility(map[i]).pos(influenceDiagram->variable(map[i])),
146            influenceDiagram->utility(map[i]));
147 
148     _checkTemporalOrder_(influenceDiagram);
149 
150     return influenceDiagram;
151   }
152 
153   template < typename GUM_SCALAR >
_checkTemporalOrder_(InfluenceDiagram<GUM_SCALAR> * infdiag)154   void InfluenceDiagramGenerator< GUM_SCALAR >::_checkTemporalOrder_(
155      InfluenceDiagram< GUM_SCALAR >* infdiag) {
156     if (!infdiag->decisionOrderExists()) {
157       Sequence< NodeId > order = infdiag->topologicalOrder(true);
158 
159       auto orderIter = order.begin();
160 
161       while ((orderIter != order.end()) && (!infdiag->isDecisionNode(*orderIter)))
162         ++orderIter;
163 
164       if (orderIter == order.end()) return;
165 
166       NodeId parentDecision = (*orderIter);
167 
168       ++orderIter;
169 
170       for (; orderIter != order.end(); ++orderIter)
171         if (infdiag->isDecisionNode(*orderIter)) {
172           infdiag->addArc(parentDecision, (*orderIter));
173           parentDecision = (*orderIter);
174         }
175     }
176   }
177 
178 } /* namespace gum */
179