aGrUM  0.14.2
influenceDiagramInference_tpl.h
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2005 by Pierre-Henri WUILLEMIN et Christophe GONZALES *
3  * {prenom.nom}_at_lip6.fr *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the *
17  * Free Software Foundation, Inc., *
18  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19  ***************************************************************************/
34 #ifndef DOXYGEN_SHOULD_SKIP_THIS
35 
36 // to ease parsing by IDE
38 
39 namespace gum {
40  // Default constructor.
41  // @param inDiag The influence diagram on which we perform inferences
42  template < typename GUM_SCALAR >
44  const InfluenceDiagram< GUM_SCALAR >& infDiag) :
45  IInfluenceDiagramInference< GUM_SCALAR >(infDiag),
46  __triangulation(0), __inferencePotential(0), __inferenceUtility(0),
47  __inferenceMade(false) {
48  GUM_CONSTRUCTOR(InfluenceDiagramInference);
49 
50  // Make modalities map
51  NodeProperty< Size > __modalitiesMap;
52 
53  // the moral graph does not include utility nodes
54  UndiGraph partialMoralGraph(this->influenceDiagram().moralGraph());
55 
56  for (const auto node : this->influenceDiagram().nodes()) {
57  if (this->influenceDiagram().isUtilityNode(node)) {
58  partialMoralGraph.eraseNode(node);
59  } else {
60  __modalitiesMap.insert(
61  node, this->influenceDiagram().variable(node).domainSize());
62  }
63  }
64 
65  const List< NodeSet >* partialTemporalOrder =
66  &(this->influenceDiagram().getPartialTemporalOrder());
67 
68  // Make Junction Tree
69  __triangulation = new PartialOrderedTriangulation(
70  &partialMoralGraph, &__modalitiesMap, partialTemporalOrder);
74  }
75 
76  // Default destructor.
77 
78  template < typename GUM_SCALAR >
80  GUM_DESTRUCTOR(InfluenceDiagramInference);
81 
82  delete __triangulation;
83 
84  __cleanUp();
85 
86  for (const auto& prop : __cliquePropertiesMap)
87  delete prop.second;
88 
89  for (const auto dummyPot : __potentialDummies)
90  delete dummyPot;
91 
92  for (const auto dummyUtility : __utilityDummies)
93  delete dummyUtility;
94  }
95 
96  template < typename GUM_SCALAR >
98  __cleanUp();
99  NodeId rootClique = __cliqueEliminationMap[0];
100 
101  for (const auto chil : __triangulation->junctionTree().neighbours(rootClique))
102  __collectChild(rootClique, chil);
103 
104  NodeSet separator;
105  __reduceClique(__cliquePropertiesMap[rootClique],
106  separator,
109  __inferenceMade = true;
110  }
111 
112  // getMEU : Returns the maximum expected utility for this inference on this
113  // diagram
114 
115  template < typename GUM_SCALAR >
117  if (!__inferenceMade)
118  GUM_ERROR(OperationNotAllowed, "No inference have yet been made");
119 
120  Instantiation res(__inferenceUtility);
121  return __inferenceUtility->get(res);
122  }
123 
124  // getBestDecisionChoice : Returns for given decision node the best decision
125  // to
126  // take under this infernece
127  template < typename GUM_SCALAR >
129  NodeId decisionId) {
130  if (!__inferenceMade)
131  GUM_ERROR(OperationNotAllowed, "No inference have yet been made");
132 
133  if (!this->influenceDiagram().isDecisionNode(decisionId))
134  GUM_ERROR(InvalidNode, "Node is not a decision node");
135 
136  return __utakenDecisionMap[decisionId];
137  }
138 
139  // displayResult : displays results obtained from inference
140  template < typename GUM_SCALAR >
142  std::stringstream stream;
143 
144  if (!__inferenceMade)
145  GUM_ERROR(OperationNotAllowed, "No inference have yet been made");
146 
147  stream << "max EU : " << std::endl;
148  stream << *__inferenceUtility << std::endl;
149 
150  stream << "Best choices : " << std::endl;
151 
152  for (const auto& ut : __utakenDecisionMap)
153  stream << " - Decision " << this->influenceDiagram().variable(ut.first)
154  << " : "
155  << this->influenceDiagram().variable(ut.first).label(ut.second)
156  << std::endl;
157 
158  return stream.str();
159  }
160 
161  // insertEvidence : inserts new evidence in the graph
162  template < typename GUM_SCALAR >
164  const List< const Potential< GUM_SCALAR >* >& evidenceList) {
165  for (const auto ev : evidenceList)
166  __cliquePropertiesMap[__nodeToCliqueMap[this->influenceDiagram().nodeId(
167  (ev)->variable(0))]]
168  ->addEvidence(*ev);
169  }
170 
171  // eraseEvidence : removes a given evidence from the graph
172 
173  template < typename GUM_SCALAR >
175  const Potential< GUM_SCALAR >* evidence) {
176  if (!(evidence->variablesSequence().size() != 1))
177  __cliquePropertiesMap[__nodeToCliqueMap[this->influenceDiagram().nodeId(
178  evidence->variable(0))]]
179  ->removeEvidence(evidence->variable(0));
180  }
181 
182  // eraseAllEvidence : removes all evidence from the graph
183 
184  template < typename GUM_SCALAR >
186  for (const auto& elt : __cliquePropertiesMap)
187  elt.second->removeAllEvidence();
188  }
189 
190  /* ****************************************************************************************************
191  * **/
192  /* ** **/
193  /* ** Getters
194  * **/
195  /* ** **/
196  /* ****************************************************************************************************
197  * **/
198 
199  // getTriangulation : returns the triangulation obtained for this influence
200  // diagram
201 
202  template < typename GUM_SCALAR >
203  INLINE Triangulation&
205  return *__triangulation;
206  }
207 
208  // __getSeparator :: returns the set of node contains in clique1 inter clique2
209 
210  template < typename GUM_SCALAR >
211  INLINE const NodeSet&
213  NodeId clique_2) {
214  return __triangulation->junctionTree().separator(clique_1, clique_2);
215  }
216 
217  // __getClique : for a given node in diagram, returns the clique id of the
218  // first
219  // variable to disappear between node variable and its parents variables
220 
221  template < typename GUM_SCALAR >
223  const std::vector< NodeId >& eliminationOrder, NodeId id) {
224  //***********************************************************************************************************************************
225  // First, we create a node set with node id and parents id
226  NodeSet idSet;
227  idSet.insert(id);
228 
229  for (const auto par : this->influenceDiagram().parents(id))
230  idSet.insert(par);
231 
232  //***********************************************************************************************************************************
233 
234  //***********************************************************************************************************************************
235  // Then, we search for the first one to be eliminated in elimination order
236  for (size_t i = 0; i < eliminationOrder.size(); ++i)
237  if (idSet.contains(eliminationOrder[i]))
238  return __triangulation->createdJunctionTreeClique(eliminationOrder[i]);
239 
240  GUM_ERROR(FatalError, "No clique found for node " << id);
241  //***********************************************************************************************************************************
242  }
243 
244  // __makeCliquePropertiesMap : Builds up clique data structure for the
245  // inference
246 
247  template < typename GUM_SCALAR >
249  const std::vector< NodeId > elim = __triangulation->eliminationOrder();
250 
251  // Those two sets will contains cliques id for cliques which doesn't have a
252  // potential or a utility table at all
253  NodeSet potentialsCliquesSet, utilitiesCliqueSet;
254 
255  // First pass to create the clique's table
256  for (const auto cli : __triangulation->junctionTree().nodes()) {
257  __cliquePropertiesMap.insert(cli, new CliqueProperties< GUM_SCALAR >());
258 
259  potentialsCliquesSet.insert(cli);
260  utilitiesCliqueSet.insert(cli);
261 
262  // Insertion in clique properties of the variables contains in the clique
263  for (const auto node : __triangulation->junctionTree().clique(cli))
264  __cliquePropertiesMap[cli]->addVariable(
265  this->influenceDiagram().variable(node));
266 
267  // Creation of clique own elimination order (based on the general one)
268  __cliquePropertiesMap[cli]->makeEliminationOrder(elim,
269  this->influenceDiagram());
270  }
271 
272  // Second pass to add the potentials into good cliques
273  for (size_t i = 0; i < elim.size(); i++) {
274  // Récupération de la bonne clique
275  NodeId cliqueId = __getClique(elim, elim[i]);
276  __nodeToCliqueMap.insert(elim[i], cliqueId);
277 
278  // Ajout de la cpt si le noeud est un noeud chance
279  if (this->influenceDiagram().isChanceNode(elim[i])) {
280  __cliquePropertiesMap[cliqueId]->addPotential(
281  this->influenceDiagram().cpt(elim[i]));
282  potentialsCliquesSet.erase(cliqueId);
283  }
284  }
285 
286  // Third pass to fill empty cliques with "one" matrices for potentials and
287  // "zero"
288  // matrices for utilities.
289  for (const auto cliq : potentialsCliquesSet)
290  __cliquePropertiesMap[cliq]->addPotential(*__makeDummyPotential(cliq));
291 
292  // Fourth pass to adress utility table to the good clique
293  // We go trought all diagram's nodes in search of utility nodes since they
294  // do not
295  // appear in elimination order
296  for (const auto node : this->influenceDiagram().nodes())
297  if (this->influenceDiagram().isUtilityNode(node)) {
298  // Récupération de la bonne clique
299  NodeId cliqueId = __getClique(elim, node);
300  __cliquePropertiesMap[cliqueId]->addUtility(
301  this->influenceDiagram().utility(node));
302  utilitiesCliqueSet.erase(cliqueId);
303  }
304 
305  // Fifth pass to fill empty cliques with "zero" matrices for utilities.
306  for (const auto cliq : utilitiesCliqueSet)
307  __cliquePropertiesMap[cliq]->addUtility(*__makeDummyUtility(cliq));
308  }
309 
310  template < typename GUM_SCALAR >
312  // Pour chaque clique
313  for (NodeId cli : __triangulation->junctionTree().nodes()) {
314  Sequence< NodeId > eliminationOrder =
315  __cliquePropertiesMap[cli]->cliqueEliminationOrder();
316  SequenceIterator< NodeId > cliqueNodesIter = eliminationOrder.begin();
317  bool validIndex = false;
318 
319  // On parcours chaque noeud de la clique par ordre d'élimination, ...
320  while (cliqueNodesIter != eliminationOrder.end() && !validIndex) {
321  SequenceIterator< NodeId > cliqueRemainingNodesIter = cliqueNodesIter;
322  ++cliqueRemainingNodesIter;
323 
324  if (cliqueRemainingNodesIter != eliminationOrder.end()) {
326  *cliqueRemainingNodesIter));
327 
328  while (cliqueRemainingNodesIter != eliminationOrder.end()
329  && !suspectedNodes.empty()) {
330  NodeSet possiblesNodes(suspectedNodes);
331 
332  for (const auto possibleNode : possiblesNodes)
334  .neighbours(*cliqueRemainingNodesIter)
335  .exists(possibleNode))
336  suspectedNodes.erase(possibleNode);
337 
338  ++cliqueRemainingNodesIter;
339  }
340 
341  if (!suspectedNodes.empty())
342  for (const auto suspectedNode : suspectedNodes)
343  if (!eliminationOrder.exists(suspectedNode)
344  && __IsEliminatedAfter(suspectedNode, *cliqueNodesIter)) {
345  validIndex = true;
346  break;
347  }
348  }
349 
350  if (!validIndex) ++cliqueNodesIter;
351  }
352 
353  if (validIndex) {
354  Idx index = 1;
355 
356  for (std::vector< NodeId >::const_iterator eliminationOrderIter =
358  eliminationOrderIter != __triangulation->eliminationOrder().end()
359  && *eliminationOrderIter != *cliqueNodesIter;
360  ++eliminationOrderIter, ++index)
361  ;
362 
364  Size(__triangulation->eliminationOrder().size() - index), cli);
365  } else
366  try {
368  } catch (Exception& e) { throw(e); }
369  }
370  }
371 
372  // __IsEliminatedAfter : checks if observed node is eliminated after
373  // current
374  // node.
375 
376  template < typename GUM_SCALAR >
378  NodeId observedNode, NodeId currentNode) {
379  for (std::vector< NodeId >::const_iterator eliminationOrderIter =
381  eliminationOrderIter != __triangulation->eliminationOrder().end();
382  ++eliminationOrderIter) {
383  if (*eliminationOrderIter == currentNode) return true;
384 
385  if (*eliminationOrderIter == observedNode) return false;
386  }
387 
388  return false;
389  }
390 
391  // displayStrongJunctionTree : displays the junction tree obtained for this
392  // influence diagram
393  template < typename GUM_SCALAR >
395  std::ostream& stream) {
396  stream << std::endl << "Strong junction tree map : " << std::endl;
397 
398  for (const auto& elt : __cliqueEliminationMap)
399  stream << "Clique : " << __triangulation->junctionTree().clique(elt.second)
400  << " - Index : " << elt.first << std::endl;
401  }
402 
403  template < typename GUM_SCALAR >
405  if (__inferencePotential != nullptr) {
406  delete __inferencePotential;
407  __inferencePotential = nullptr;
408  }
409 
410  if (__inferenceUtility != nullptr) {
411  delete __inferenceUtility;
412  __inferenceUtility = nullptr;
413  }
414 
415  for (const auto& prop : __cliquePropertiesMap)
416  prop.second->cleanFromInference();
417 
418  __utakenDecisionMap.clear();
419  __inferenceMade = false;
420  }
421 
422  // __collectChild : or when the parents collects message from its child
423  template < typename GUM_SCALAR >
425  NodeId child) {
426  for (const auto nei : __triangulation->junctionTree().neighbours(child))
427  if (nei != parent) __collectChild(child, nei);
428 
429  __absorbClique(child, parent);
430  }
431 
432  // __absorbClique : Performs a clique absorption by another one
433  template < typename GUM_SCALAR >
435  NodeId absorbedCliqueId, NodeId absorbingCliqueId) {
436  // Recuperation of absorbed clique properties
437  CliqueProperties< GUM_SCALAR >* absorbedClique =
438  __cliquePropertiesMap[absorbedCliqueId];
439 
440  // Get the nodes making the separtion between the absorbed clique and the
441  // absorbing one
442  NodeSet separator = __getSeparator(absorbedCliqueId, absorbingCliqueId);
443 
444  Potential< GUM_SCALAR >* potentialMarginal = 0;
445  Potential< GUM_SCALAR >* utilityMarginal = 0;
446 
447  // First we reduce absorbed clique by eleminating clique variables which
448  // aren't
449  // in the separator.
450  // The aim of this operation is to obtained by marginalization a potential
451  // and a
452  // utility over the variable presents
453  // in the separator (and only those one ). Those tables represent the
454  // "messages"
455  // sent by child
456  // clique to its parent.
457  __reduceClique(absorbedClique, separator, potentialMarginal, utilityMarginal);
458 
459  // Then those tables are add in parents clique property.
460  // For the potential, we just add it
461  __cliquePropertiesMap[absorbingCliqueId]->addPotential(*potentialMarginal,
462  true);
463 
464  // For the utility table, we need first to divide it by the potential
465  Instantiation utilityMarginalInst(utilityMarginal);
466 
467  for (utilityMarginalInst.setFirst(); !utilityMarginalInst.end();
468  utilityMarginalInst.inc()) {
469  GUM_SCALAR uVal = (GUM_SCALAR)0;
470 
471  if (potentialMarginal->get(utilityMarginalInst) != (GUM_SCALAR)0)
472  uVal = utilityMarginal->get(utilityMarginalInst)
473  / potentialMarginal->get(utilityMarginalInst);
474 
475  utilityMarginal->set(utilityMarginalInst, uVal);
476  }
477 
478  // And then we can add the utility table.
479  __cliquePropertiesMap[absorbingCliqueId]->addUtility(*utilityMarginal, true);
480  }
481 
482  // __reduceClique : Performs a clique reduction
483 
484  // This operations consists in eliminating each variable of the clique wich
485  // are not
486  // in the separtor between this clique and its parent clique.
487  // Variable elimination is done by performing the correct marging operation on
488  // clique potential. That operation is determined by the nature
489  // of currently eleminated variable : if its a chance variable, we sum over
490  // its
491  // modalities, if its a decision node we maximise over its modalities.
492  template < typename GUM_SCALAR >
494  CliqueProperties< GUM_SCALAR >* absorbedClique,
495  NodeSet& separator,
496  Potential< GUM_SCALAR >*& potentialMarginal,
497  Potential< GUM_SCALAR >*& utilityMarginal) {
498  Instantiation cliqueInstance(absorbedClique->cliqueInstantiation());
499  Sequence< const DiscreteVariable* > cliqueRemainVarList(
500  cliqueInstance.variablesSequence());
501 
502  // So for each variable of that clique ...
503  for (const auto node : absorbedClique->cliqueEliminationOrder()) {
504  // if it's not on separtor with its parent
505  if (!separator.contains(node)) {
506  // Initialisation Operations
507 
508  // First we create the tables that will result from variable elimination
509  Potential< GUM_SCALAR >* newPotential = new Potential< GUM_SCALAR >();
510  Potential< GUM_SCALAR >* newUtility = new Potential< GUM_SCALAR >();
511 
512  // Then we need to add all not yet eliminated variables of the clique in
513  // ours
514  // new table
515  cliqueRemainVarList.erase(&(this->influenceDiagram().variable(node)));
516 
517  for (const auto remain : cliqueRemainVarList) {
518  newPotential->add(*remain);
519  newUtility->add(*remain);
520  }
521 
522  // And finally, before doing marginalizing operations,
523  // we add an instanciation to those tables to ease marginalizing
524  // operations.
525  // Note that we only need one tablel instance because the other one 's
526  // got
527  // exactly sames variables
528  Instantiation newInstantiation(newPotential);
529 
530  //=====================================================================================
531  // Marginalization
532 
533  // Then we fill the new potentials over all their values by
534  // marginalizing the
535  // previous one
536  for (newInstantiation.setFirst(); !newInstantiation.end();
537  newInstantiation.inc()) {
538  // Various initialization
539  GUM_SCALAR potentialValue = (GUM_SCALAR)0;
540  GUM_SCALAR utilityValue = (GUM_SCALAR)0;
541 
542  if (this->influenceDiagram().isDecisionNode(node))
543  utilityValue = -1 * (std::numeric_limits< GUM_SCALAR >::max());
544 
545  // Then we compute value for current newInstanciation
546  cliqueInstance.setVals(newInstantiation);
547 
548  for (cliqueInstance.setFirstOut(newInstantiation); !cliqueInstance.end();
549  cliqueInstance.incOut(newInstantiation)) {
550  // Récupération de la valeur de la cpt de la clique pour cette
551  // instance
552  GUM_SCALAR currentPotential = (GUM_SCALAR)1;
553 
554  if (potentialMarginal == 0) {
555  // If there's no ancient potential it means that we haven't yet
556  // compute
557  // him
558  for (const auto& pot : absorbedClique->potentialBucket()) {
559  pot.second->setVals(cliqueInstance);
560  currentPotential *= pot.first->get(*pot.second);
561  }
562  } else {
563  Instantiation potentialMarginalInst(potentialMarginal);
564  potentialMarginalInst.setVals(cliqueInstance);
565  currentPotential = potentialMarginal->get(potentialMarginalInst);
566  }
567 
568  // Récupération de la valeur d'utilité de la clique pour cette
569  // instance
570  GUM_SCALAR currentUtility = (GUM_SCALAR)0;
571 
572  if (utilityMarginal == 0) {
573  for (const auto& uti : absorbedClique->utilityBucket()) {
574  uti.second->setVals(cliqueInstance);
575  currentUtility += uti.first->get(*uti.second);
576  }
577 
578  currentUtility *= currentPotential;
579  } else {
580  Instantiation utilityMarginalInst(utilityMarginal);
581  utilityMarginalInst.setVals(cliqueInstance);
582  currentUtility = utilityMarginal->get(utilityMarginalInst);
583  }
584 
585  // Marginalization
586  if (this->influenceDiagram().isDecisionNode(node)) {
587  if (potentialValue < currentPotential) {
588  potentialValue = currentPotential;
589  }
590 
591  if (utilityValue < currentUtility) {
592  utilityValue = currentUtility;
593 
594  if (__utakenDecisionMap.exists(node))
595  __utakenDecisionMap.erase(node);
596 
597  __utakenDecisionMap.insert(
598  node,
599  cliqueInstance.val(this->influenceDiagram().variable(node)));
600  }
601  } else {
602  potentialValue += currentPotential;
603  utilityValue += currentUtility;
604  }
605  }
606 
607  // And finally we update the potentials with computed value for
608  // newInstanciation
609  newPotential->set(newInstantiation, potentialValue);
610  newUtility->set(newInstantiation, utilityValue);
611  }
612 
613  //=====================================================================================
614  // Updates of tables
615  if (potentialMarginal != 0) delete potentialMarginal;
616 
617  potentialMarginal = newPotential;
618 
619  if (utilityMarginal != 0) delete utilityMarginal;
620 
621  utilityMarginal = newUtility;
622 
623  //=====================================================================================
624  // Then we removed variable from clique list of variable ...
625  cliqueInstance.erase(this->influenceDiagram().variable(node));
626  }
627  }
628  }
629 
630  // __makeDummyPotential : creates a generic potential
631 
632  template < typename GUM_SCALAR >
633  INLINE Potential< GUM_SCALAR >*
635  NodeId cliqueId) {
636  Potential< GUM_SCALAR >* pot = new Potential< GUM_SCALAR >(
637  new MultiDimSparse< GUM_SCALAR >((GUM_SCALAR)1));
638  __potentialDummies.insert(pot);
639 
640  for (const auto cliqueNode : __triangulation->junctionTree().clique(cliqueId))
641  pot->add(this->influenceDiagram().variable(cliqueNode));
642 
643  pot->normalize();
644  return pot;
645  }
646 
647  // __makeDummyUtility : creates a generic utility
648 
649  template < typename GUM_SCALAR >
650  INLINE Potential< GUM_SCALAR >*
652  Potential< GUM_SCALAR >* ut = new Potential< GUM_SCALAR >(
653  new MultiDimSparse< GUM_SCALAR >((GUM_SCALAR)0));
655 
656  for (const auto cliqueNode : __triangulation->junctionTree().clique(cliqueId))
657  ut->add(this->influenceDiagram().variable(cliqueNode));
658 
659  return ut;
660  }
661 
662  // Default constructor
663  template < typename GUM_SCALAR >
664  CliqueProperties< GUM_SCALAR >::CliqueProperties() {
665  GUM_CONSTRUCTOR(CliqueProperties);
666  }
667 
668  // Default destructor
669  template < typename GUM_SCALAR >
670  CliqueProperties< GUM_SCALAR >::~CliqueProperties() {
671  GUM_DESTRUCTOR(CliqueProperties);
672 
673  cleanFromInference();
674  removeAllEvidence();
675 
676  for (const auto& pot : __potentialBucket)
677  delete pot.second;
678 
679  for (const auto& uti : __utilityBucket)
680  delete uti.second;
681  }
682 
683  // addVariable : adds a variable to the clique
684  template < typename GUM_SCALAR >
685  void CliqueProperties< GUM_SCALAR >::addVariable(const DiscreteVariable& v) {
686  try {
687  __allVarsInst.add(v);
688  } catch (DuplicateElement&) {
689  // Nothing to do then!
690  }
691  }
692 
693  // cliqueVariables : returns List containing all variables contained in this
694  // clique
695  // @return returns List containing all variables contained in this clique
696  template < typename GUM_SCALAR >
697  INLINE const Sequence< const DiscreteVariable* >&
698  CliqueProperties< GUM_SCALAR >::cliqueVariables() {
699  return __allVarsInst.variablesSequence();
700  }
701 
702  // cliqueInstantiation : returns instanciation on variable within this clique
703  // @return returns instanciation on variable within this clique
704  template < typename GUM_SCALAR >
705  INLINE Instantiation& CliqueProperties< GUM_SCALAR >::cliqueInstantiation() {
706  return __allVarsInst;
707  }
708 
709  // addPotential : adds a potential to this clique
710  // The removable boolean inidcates if this potential can be cleaned off after
711  // an
712  // inference or not
713  template < typename GUM_SCALAR >
714  void CliqueProperties< GUM_SCALAR >::addPotential(
715  const Potential< GUM_SCALAR >& cpt, bool removable) {
716  __potentialBucket.insert(&cpt, new Instantiation(cpt));
717 
718  if (removable) __removablePotentialList.insert(&cpt);
719 
720  const auto& vars = cpt.variablesSequence();
721  for (auto iter = vars.beginSafe(); iter != vars.end(); ++iter) {
722  if (removable && !__allVarsInst.contains(**iter)) try {
723  __removableVarList.insert(*iter);
724  } catch (DuplicateElement&) {
725  // Nothing to do then!
726  }
727 
728  addVariable(**iter);
729  }
730  }
731 
732  // potentialBucket : Returns the potential bucket of this Clique
733  template < typename GUM_SCALAR >
734  INLINE const HashTable< const Potential< GUM_SCALAR >*, Instantiation* >&
735  CliqueProperties< GUM_SCALAR >::potentialBucket() {
736  return __potentialBucket;
737  }
738 
739  // addUtility : adds a utility table to this clique
740  // The removable boolean inidcates if this utilityTable can be cleaned off
741  // after an
742  // inference or not
743  template < typename GUM_SCALAR >
744  void
745  CliqueProperties< GUM_SCALAR >::addUtility(const Potential< GUM_SCALAR >& ut,
746  bool removable) {
747  __utilityBucket.insert(&ut, new Instantiation(ut));
748 
749  if (removable) __removableUtilityList.insert(&ut);
750 
751  for (auto var : ut.variablesSequence()) {
752  if (removable && !__allVarsInst.contains(*var)) try {
753  __removableVarList.insert(var);
754  } catch (DuplicateElement&) {
755  // Nothing to do then!
756  }
757 
758  addVariable(*var);
759  }
760  }
761 
762  // utilityBucket : Returns the utiluty table bucket of this Clique
763  template < typename GUM_SCALAR >
764  INLINE const HashTable< const Potential< GUM_SCALAR >*, Instantiation* >&
765  CliqueProperties< GUM_SCALAR >::utilityBucket() {
766  return __utilityBucket;
767  }
768 
769  // cleanFromInference : performs a cleaning of the clique after an inference
770  // This is done by removing the "message" given by a child clique to its
771  // parents,
772  // meaning the potentials and utility tables obtained by
773  // variable elemination in the clique.
774  template < typename GUM_SCALAR >
775  void CliqueProperties< GUM_SCALAR >::cleanFromInference() {
776  // Removed added variables during inference (normally, the
777  // __removableVarList is
778  // empty, but we never know )
779  for (const auto removableVar : __removableVarList)
780  __allVarsInst.erase(*removableVar);
781 
782  // Removed added potentials during inference
783  for (const auto removablePot : __removablePotentialList) {
784  delete __potentialBucket[removablePot];
785  __potentialBucket.erase(removablePot);
786  delete removablePot;
787  }
788 
789  // Removed added utility tables during inference
790  for (const auto removabledUt : __removableUtilityList) {
791  delete __utilityBucket[removabledUt];
792  __utilityBucket.erase(removabledUt);
793  delete removabledUt;
794  }
795 
796  __removableVarList.clear();
797  __removablePotentialList.clear();
798  __removableUtilityList.clear();
799  }
800 
801  // makeEliminationOrder : creates an elimination order in the clique
802  // compatible
803  // with global elimination order
804  // The global elimination order is given by elim.
805  template < typename GUM_SCALAR >
806  void CliqueProperties< GUM_SCALAR >::makeEliminationOrder(
807  const std::vector< NodeId >& elim,
808  const InfluenceDiagram< GUM_SCALAR >& infDiag) {
809  for (size_t i = 0; i < elim.size(); i++) {
810  if (__allVarsInst.contains(infDiag.variable(elim[i])))
811  __eliminationOrder.insert(elim[i]);
812  }
813  }
814 
815  // cliqueEliminationOrder : returns the elimination sequence for this clique
816  template < typename GUM_SCALAR >
817  INLINE const Sequence< NodeId >&
818  CliqueProperties< GUM_SCALAR >::cliqueEliminationOrder() {
819  return __eliminationOrder;
820  }
821 
822  // addEvidence : add evidence over one variable present in the clique
823  // @throw OperationNotAllowed if evidence has more than one variable.
824  // @throw NotFound Raised if the evidence is on a variable not present in this
825  // clique.
826  // @throw DuplicateElement, if another evidence over this variable exists for
827  // this
828  // clique
829  template < typename GUM_SCALAR >
830  void CliqueProperties< GUM_SCALAR >::addEvidence(
831  const Potential< GUM_SCALAR >& evidence) {
832  // To avoid interference
833  cleanFromInference();
834 
835  // First, we check if evidence is over only one variable.
836  if (evidence.variablesSequence().size() != 1) {
837  GUM_ERROR(OperationNotAllowed,
838  "expected evidence on 1 variable, found on "
839  << evidence.variablesSequence().size());
840  }
841 
842  // Then we assure us that that variable is in the clique
843  if (!__allVarsInst.contains(evidence.variablesSequence().atPos(0))) {
844  GUM_ERROR(NotFound,
845  evidence.variablesSequence().atPos(0)->name()
846  << " not found in clique ");
847  }
848 
849  __evidences.insert(evidence.variablesSequence().atPos(0), &evidence);
850 
851  __potentialBucket.insert(&evidence, new Instantiation(evidence));
852  }
853 
854  // evidences : Returns the mapping of evidences on variables in this clique
855  template < typename GUM_SCALAR >
856  INLINE const
857  HashTable< const DiscreteVariable*, const Potential< GUM_SCALAR >* >&
858  CliqueProperties< GUM_SCALAR >::evidences() const {
859  return __evidences;
860  }
861 
862  // removeEvidence : Removes the evidence over v
863  template < typename GUM_SCALAR >
864  INLINE void
865  CliqueProperties< GUM_SCALAR >::removeEvidence(const DiscreteVariable& v) {
866  const Potential< GUM_SCALAR >* evi = __evidences[&v];
867 
868  delete __potentialBucket[evi];
869  __potentialBucket.erase(evi);
870  __evidences.erase(&v);
871  }
872 
873  // removeAllEvidence : Removes all the evidences
874  template < typename GUM_SCALAR >
875  INLINE void CliqueProperties< GUM_SCALAR >::removeAllEvidence() {
876  for (const auto& elt : __evidences)
877  removeEvidence(*elt.first);
878 
879  __evidences.clear();
880  }
881 
882 } /* namespace gum */
883 
884 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
const InfluenceDiagram< GUM_SCALAR > & influenceDiagram() const
Returns a constant reference over the InfluenceDiagram on which this class work.
Potential< GUM_SCALAR > * __makeDummyPotential(NodeId cliqueId)
Returns a pointer over a "dummy" potential, which is a CPT filled with one MultiDimSparse filled with...
Set< Potential< GUM_SCALAR > *> __utilityDummies
The set of dummies sparse utilities matrix created.
Set< Potential< GUM_SCALAR > *> __potentialDummies
The set of dummies sparse potential matrix created.
iterator begin() const
Returns an unsafe begin iterator.
Definition: sequence_tpl.h:654
bool __IsEliminatedAfter(NodeId observedNode, NodeId currentNode)
Returns true if observed node is eliminated after current node.
Idx getBestDecisionChoice(NodeId decisionId)
const NodeSet & separator(const Edge &edge) const
returns the separator included in a given edge
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
NodeProperty< NodeId > __nodeToCliqueMap
Mapping of the nodes with the clique used to put their CPT.
Triangulation & getTriangulation()
Returns the Triangulation used by this class.
NodeId __getClique(const std::vector< NodeId > &eliminationOrder, NodeId id)
Potential< GUM_SCALAR > * __inferenceUtility
The resulting utility from inference.
gum is the global namespace for all aGrUM entities
Definition: agrum.h:25
Triangulation * __triangulation
The triangulation algorithm.
const NodeSet & neighbours(const NodeId id) const
returns the set of edges adjacent to a given node
void __cleanUp()
Cleans Up remaining stuff due to inference.
bool exists(const Key &k) const
Indicates whether a given elements belong to the set.
Definition: set_tpl.h:604
std::string displayResult()
displays the result of an inference
void __reduceClique(CliqueProperties< GUM_SCALAR > *absorbedClique, NodeSet &separator, Potential< GUM_SCALAR > *&potentialMarginal, Potential< GUM_SCALAR > *&utilityMarginal)
Reduces a clique down to her separator from another clique elements.
virtual NodeId createdJunctionTreeClique(const NodeId id)=0
returns the Id of the clique created by the elimination of a given node during the triangulation proc...
Implementation of an influence diagram inference algorithm based upon Shaffer-Shenoy&#39;s one for bayes ...
void __makeCliquePropertiesMap()
Builds the cliques tables Uses __getCliquesTable to initialize the cliques table, and multiply the ta...
const NodeGraphPart & nodes() const
return *this as a NodeGraphPart
bool exists(const Key &k) const
Check the existence of k in the sequence.
Definition: sequence_tpl.h:399
HashTable< Size, NodeId > __cliqueEliminationMap
Mapping of the nodes with the clique used to put their CPT.
void __collectChild(NodeId parent, NodeId child)
collect child clique for inferences
void __absorbClique(NodeId absorbedCliqueId, NodeId absorbingCliqueId)
Performs the operation of absorption of a clique by another.
InfluenceDiagramInference(const InfluenceDiagram< GUM_SCALAR > &infDiag)
Default constructor.
const NodeSet & clique(const NodeId idClique) const
returns the set of nodes included into a given clique
Potential< GUM_SCALAR > * __inferencePotential
The resulting potential from inference.
virtual ~InfluenceDiagramInference()
Destructor.
NodeProperty< CliqueProperties< GUM_SCALAR > *> __cliquePropertiesMap
Mapping of the nodes with the clique used to put their CPT.
bool __inferenceMade
Mapping of the nodes with the clique used to put their CPT.
virtual const UndiGraph & triangulatedGraph()=0
returns the triangulated graph
void displayStrongJunctionTree(std::ostream &stream=std::cout)
Displays on terminal the result of strong junction tree computation for test purpose only...
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition: types.h:45
virtual void insertEvidence(const List< const Potential< GUM_SCALAR > * > &evidenceList)
virtual const CliqueGraph & junctionTree()=0
returns a compatible junction tree
value_type & insert(const Key &key, const Val &val)
Adds a new element (actually a copy of this element) into the hash table.
HashTable< NodeId, Idx > __utakenDecisionMap
Mapping of the nodes with the clique used to put their CPT.
const iterator & end() const noexcept
Returns the unsafe end iterator.
Definition: sequence_tpl.h:661
const NodeSet & __getSeparator(NodeId clique_1, NodeId clique_2)
virtual void eraseEvidence(const Potential< GUM_SCALAR > *evidence)
Potential< GUM_SCALAR > * __makeDummyUtility(NodeId cliqueId)
Returns a pointer over a "dummy" utility, which is a utility table filled with one MultiDimSparse fil...
virtual const std::vector< NodeId > & eliminationOrder()=0
returns an elimination ordering compatible with the triangulated graph
Size NodeId
Type for node ids.
Definition: graphElements.h:97
void insert(const Key &k)
Inserts a new element into the set.
Definition: set_tpl.h:610
#define GUM_ERROR(type, msg)
Definition: exceptions.h:52
void __makeStrongJunctionTree()
Makes a strong junction tree that make easier elimination.