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