aGrUM  0.20.2
a C++ library for (probabilistic) graphical models
graphicalModelInference_tpl.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright 2005-2020 Pierre-Henri WUILLEMIN(@LIP6) & Christophe GONZALES(@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 /**
23  * @file
24  * @brief Implementation of the non pure virtual methods of class
25  * GraphicalModelInference.
26  */
27 
28 #include <agrum/tools/graphicalModels/inference/graphicalModelInference.h>
29 
30 namespace gum {
31 
32 
33  // Default Constructor
34  template < typename GUM_SCALAR >
35  GraphicalModelInference< GUM_SCALAR >::GraphicalModelInference(
36  const GraphicalModel* model) :
37  model__(model) {
38  computeDomainSizes__();
39 
40  GUM_CONSTRUCTOR(GraphicalModelInference);
41  }
42 
43 
44  // Default Constructor
45  template < typename GUM_SCALAR >
48  }
49 
50 
51  // Destructor
52  template < typename GUM_SCALAR >
54  // clear all evidence.
55  // Warning: Do not use method eraseAllEvidence () because it contains a call
56  // to pure virtual method onAllEvidenceErased_ which belongs to an inherited
57  // instance and, therefore, does not exist anymore when
58  // ~GraphicalModelInference () is called
59  for (const auto& pair: evidence__) {
60  if (pair.second != nullptr) { delete (pair.second); }
61  }
62 
64  }
65 
66 
67  // returns whether the inference object is in a ready state
68  template < typename GUM_SCALAR >
69  INLINE bool
72  }
73  // returns whether the inference object is in a OutdatedStructure state
74  template < typename GUM_SCALAR >
76  const noexcept {
78  }
79  // returns whether the inference object is in a OutdatedPotential state
80  template < typename GUM_SCALAR >
81  INLINE bool
83  const noexcept {
85  }
86  // returns whether the inference object is in a InferenceDone state
87  template < typename GUM_SCALAR >
88  INLINE bool
90  return (state__ == StateOfInference::Done);
91  }
92 
93 
94  // returns the state of the inference engine
95  template < typename GUM_SCALAR >
97  GraphicalModelInference< GUM_SCALAR >::state() const noexcept {
98  return state__;
99  }
100 
101  // set the state of the inference
102  template < typename GUM_SCALAR >
104  const StateOfInference state) {
105  if (state__ != state) {
106  state__ = state;
107  onStateChanged_();
108  }
109  }
110 
111  // Returns a constant reference over the IBayesNet referenced by this class
112  template < typename GUM_SCALAR >
113  INLINE const GraphicalModel&
115  if (model__ == nullptr)
117  "No Bayes net has been assigned to "
118  "the inference algorithm.");
119  return *model__;
120  }
121 
122 
123  // assigns a new BN to the inference engine
124  template < typename GUM_SCALAR >
126  const GraphicalModel* model) {
127  clear();
128  model__ = model;
132  }
133 
134 
135  // assigns a BN to a newly constructed inference engine
136  template < typename GUM_SCALAR >
138  const GraphicalModel* model) {
139  model__ = model;
142  }
143 
144 
145  // clears all the data structures allocated for the last inference
146  template < typename GUM_SCALAR >
150  }
151 
152 
153  /// computes the domain sizes of the random variables
154  template < typename GUM_SCALAR >
157  if (!hasNoModel_()) {
158  for (auto node: model__->nodes()) {
160  }
161  }
162  }
163 
164 
165  // get the domain sizes of the random variables of the BN
166  template < typename GUM_SCALAR >
167  INLINE const NodeProperty< Size >&
169  return domain_sizes__;
170  }
171 
172 
173  // ##############################################################################
174  // Evidence
175  // ##############################################################################
176 
177  // create the internal structure for a hard evidence
178  template < typename GUM_SCALAR >
181  NodeId id,
182  const Idx val) const {
183  // check that it is possible to create the evidence
184  if (model__ == nullptr)
186  "No Bayes net has been assigned to the "
187  "inference algorithm");
188 
189  if (!model__->exists(id)) {
190  GUM_ERROR(UndefinedElement, id << " is not a NodeId in the model");
191  }
192 
193  if (model__->variable(id).domainSize() <= val) {
195  "node " << model__->variable(id)
196  << " has fewer possible values than " << val);
197  }
198 
199  // create the deterministic potential
202  pot << model__->variable(id);
203  pot.endMultipleChanges(0.0);
204 
207  pot.set(I, 1.0);
208 
209  return pot;
210  }
211 
212 
213  // checks wether a potential corresponds to a hard evidence
214  template < typename GUM_SCALAR >
216  const Potential< GUM_SCALAR >& pot,
217  Idx& val) const {
218  // checking if pot is determininstic
219  bool notZero = false;
221 
222  for (I.setFirst(); !I.end(); I.inc()) {
223  if (pot[I] != 0.0) {
224  if (notZero) { // we already met a non-zero value
225  return false;
226  } else {
227  val = I.val(0);
228  notZero = true; // this is the first met non-zero value
229  }
230  }
231  }
232 
233  if (!notZero) { // we met no non-zero value
234  GUM_ERROR(FatalError, "Evidence of impossibility (vector of 0s)");
235  }
236 
237  return true; // pot is deterministic
238  }
239 
240 
241  // adds a new hard evidence on node id
242  template < typename GUM_SCALAR >
244  const Idx val) {
246  }
247 
248  // adds a new hard evidence on node id
249  template < typename GUM_SCALAR >
251  const std::string& nodeName,
252  const Idx val) {
254  }
255 
256  // adds a new hard evidence on node id
257  template < typename GUM_SCALAR >
258  INLINE void
260  const std::string& label) {
261  addEvidence(id, this->model().variable(id)[label]);
262  }
263 
264  // adds a new hard evidence on node id
265  template < typename GUM_SCALAR >
267  const std::string& nodeName,
268  const std::string& label) {
269  NodeId id = this->model().idFromName(nodeName);
270  addEvidence(id, this->model().variable(id)[label]);
271  }
272 
273  // adds a new evidence on node id (might be soft or hard)
274  template < typename GUM_SCALAR >
276  NodeId id,
277  const std::vector< GUM_SCALAR >& vals) {
278  // checks that the evidence is meaningful
279  if (model__ == nullptr)
281  "No Bayes net has been assigned to the "
282  "inference algorithm");
283 
284  if (!model__->exists(id)) {
285  GUM_ERROR(UndefinedElement, id << " is not a NodeId in the model");
286  }
287 
288  if (model__->variable(id).domainSize() != vals.size()) {
290  "node " << model__->variable(id)
291  << " and its evidence vector have different sizes.");
292  }
293 
296  pot.fillWith(vals);
297  addEvidence(std::move(pot));
298  }
299 
300  // adds a new evidence on node id (might be soft or hard)
301  template < typename GUM_SCALAR >
303  const std::string& nodeName,
304  const std::vector< GUM_SCALAR >& vals) {
306  }
307 
308  // adds a new evidence on node id (might be soft or hard)
309  template < typename GUM_SCALAR >
311  Potential< GUM_SCALAR >&& pot) {
312  // check if the potential corresponds to an evidence
313  if (pot.nbrDim() != 1) {
314  GUM_ERROR(InvalidArgument, pot << " is not mono-dimensional.");
315  }
316  if (model__ == nullptr)
318  "No Bayes net has been assigned to the "
319  "inference algorithm");
320 
322 
323  if (hasEvidence(id)) {
325  " node " << id
326  << " already has an evidence. Please use chgEvidence().");
327  }
328 
329  // check whether we have a hard evidence (and also check whether the
330  // potential only contains 0 (in this case, this will automatically raise
331  // an exception) )
332  Idx val;
334 
335  // insert the evidence
337  id,
339  if (is_hard_evidence) { // pot is deterministic
342  } else {
344  }
347  }
348 
349 
350  // adds a new evidence on node id (might be soft or hard)
351  template < typename GUM_SCALAR >
353  const Potential< GUM_SCALAR >& pot) {
356  }
357 
358 
359  /// adds a new list of evidence
360  template < typename GUM_SCALAR >
362  const List< const Potential< GUM_SCALAR >* >& potlist) {
363  for (const auto pot: potlist)
364  addEvidence(*pot);
365  }
366 
367 
368  /// adds a new set of evidence
369  template < typename GUM_SCALAR >
371  const Set< const Potential< GUM_SCALAR >* >& potset) {
372  for (const auto pot: potset)
373  addEvidence(*pot);
374  }
375 
376 
377  // indicates whether some node(s) have received evidence
378  template < typename GUM_SCALAR >
380  return !evidence__.empty();
381  }
382 
383 
384  // indicates whether node id has received an evidence
385  template < typename GUM_SCALAR >
387  return evidence__.exists(id);
388  }
389 
390 
391  // indicates whether node id has received a hard evidence
392  template < typename GUM_SCALAR >
393  INLINE bool
396  }
397 
398 
399  // indicates whether node id has received a soft evidence
400  template < typename GUM_SCALAR >
401  INLINE bool
404  }
405 
406 
407  // indicates whether node id has received an evidence
408  template < typename GUM_SCALAR >
410  const std::string& nodeName) const {
411  return hasEvidence(this->model().idFromName(nodeName));
412  }
413 
414 
415  // indicates whether node id has received a hard evidence
416  template < typename GUM_SCALAR >
418  const std::string& nodeName) const {
419  return hasHardEvidence(this->model().idFromName(nodeName));
420  }
421 
422 
423  // indicates whether node id has received a soft evidence
424  template < typename GUM_SCALAR >
426  const std::string& nodeName) const {
427  return hasSoftEvidence(this->model().idFromName(nodeName));
428  }
429 
430  // change the value of an already existing hard evidence
431  template < typename GUM_SCALAR >
433  const Idx val) {
435  }
436 
437  // change the value of an already existing hard evidence
438  template < typename GUM_SCALAR >
440  const std::string& nodeName,
441  const Idx val) {
443  }
444 
445  // change the value of an already existing hard evidence
446  template < typename GUM_SCALAR >
447  INLINE void
449  const std::string& label) {
450  chgEvidence(id, this->model().variable(id)[label]);
451  }
452 
453  // change the value of an already existing hard evidence
454  template < typename GUM_SCALAR >
456  const std::string& nodeName,
457  const std::string& label) {
458  NodeId id = this->model().idFromName(nodeName);
459  chgEvidence(id, this->model().variable(id)[label]);
460  }
461 
462  // change the value of an already existing evidence (might be soft or hard)
463  template < typename GUM_SCALAR >
465  NodeId id,
466  const std::vector< GUM_SCALAR >& vals) {
467  // check whether this corresponds to an evidence
468  if (model__ == nullptr)
470  "No Bayes net has been assigned to the "
471  "inference algorithm");
472 
473  if (!model__->exists(id)) {
474  GUM_ERROR(UndefinedElement, id << " is not a NodeId in the model");
475  }
476 
477  if (model__->variable(id).domainSize() != vals.size()) {
479  "node " << model__->variable(id)
480  << " and its evidence have different sizes.");
481  }
482 
483  // create the potential corresponding to vals
486  pot.fillWith(vals);
487  chgEvidence(pot);
488  }
489 
490  // change the value of an already existing evidence (might be soft or hard)
491  template < typename GUM_SCALAR >
493  const std::string& nodeName,
494  const std::vector< GUM_SCALAR >& vals) {
496  }
497 
498 
499  // change the value of an already existing evidence (might be soft or hard)
500  template < typename GUM_SCALAR >
502  const Potential< GUM_SCALAR >& pot) {
503  // check if the potential corresponds to an evidence
504  if (pot.nbrDim() != 1) {
505  GUM_ERROR(InvalidArgument, pot << " is not a mono-dimensional potential.");
506  }
507  if (model__ == nullptr)
509  "No Bayes net has been assigned to the "
510  "inference algorithm");
511 
513 
514  if (!hasEvidence(id)) {
516  id << " has no evidence. Please use addEvidence().");
517  }
518 
519  // check whether we have a hard evidence (and also check whether the
520  // potential only contains 0 (in this case, this will automatically raise
521  // an exception) )
522  Idx val;
524 
525  // modify the evidence already stored
528  for (I.setFirst(); !I.end(); I.inc()) {
529  localPot->set(I, pot[I]);
530  }
531 
532  // the inference state will be different
533  // whether evidence change from Hard to Soft or not.
534  bool hasChangedSoftHard = false;
535 
536  if (is_hard_evidence) {
537  if (!hasHardEvidence(id)) {
538  hasChangedSoftHard = true;
542  } else {
544  }
545  } else {
546  if (hasHardEvidence(id)) { // evidence was hard
550  hasChangedSoftHard = true;
551  }
552  }
553 
554  if (hasChangedSoftHard) {
556  } else {
559  }
560  }
561 
563  }
564 
565 
566  // removed the evidence, if any, corresponding to node id
567  template < typename GUM_SCALAR >
569  if (hasEvidence(id)) {
570  if (hasHardEvidence(id)) {
571  onEvidenceErased_(id, true);
575  } else {
576  onEvidenceErased_(id, false);
580  }
581  }
582 
583  delete (evidence__[id]);
585  }
586  }
587  // removed the evidence, if any, corresponding to node of name nodeName
588  template < typename GUM_SCALAR >
590  const std::string& nodeName) {
592  }
593 
594 
595  // removes all the evidence entered into the network
596  template < typename GUM_SCALAR >
600 
601  for (const auto& pair: evidence__) {
602  if (pair.second != nullptr) { delete (pair.second); }
603  }
604 
605  evidence__.clear();
609 
610  if (has_hard_evidence) {
612  } else {
615  }
616  }
617  }
618 
619 
620  // returns the number of evidence entered into the Bayesian network
621  template < typename GUM_SCALAR >
623  return evidence__.size();
624  }
625 
626 
627  // returns the number of hard evidence entered into the Bayesian network
628  template < typename GUM_SCALAR >
630  return hard_evidence_nodes__.size();
631  }
632 
633 
634  // returns the number of soft evidence entered into the Bayesian network
635  template < typename GUM_SCALAR >
637  return soft_evidence_nodes__.size();
638  }
639 
640 
641  // indicate for each node with hard evidence which value it took
642  template < typename GUM_SCALAR >
643  INLINE const NodeProperty< Idx >&
645  return hard_evidence__;
646  }
647 
648 
649  // the set of evidence entered into the network
650  template < typename GUM_SCALAR >
651  INLINE const NodeProperty< const Potential< GUM_SCALAR >* >&
653  return evidence__;
654  }
655 
656 
657  /// the set of nodes that received soft evidence
658  template < typename GUM_SCALAR >
659  INLINE const NodeSet&
661  return soft_evidence_nodes__;
662  }
663 
664 
665  /// the set of nodes that received hard evidence
666  template < typename GUM_SCALAR >
667  INLINE const NodeSet&
669  return hard_evidence_nodes__;
670  }
671 
672 
673  // ##############################################################################
674  // Inference
675  // ##############################################################################
676 
677  // put the inference into an unprepared state
678  template < typename GUM_SCALAR >
681  }
682 
683 
684  /** puts the inference into an OutdatedPotentials state if it is not
685  * already in an OutdatedStructure state */
686  template < typename GUM_SCALAR >
687  INLINE void
690  }
691 
692 
693  // prepare the internal inference structures for the next inference
694  template < typename GUM_SCALAR >
696  if (isInferenceReady() || isInferenceDone()) { return; }
697 
698  if (model__ == nullptr)
700  "No model been assigned to the "
701  "inference algorithm");
702 
705  else
707 
709  }
710 
711 
712  // perform the heavy computations needed to compute the targets' posteriors
713  template < typename GUM_SCALAR >
715  if (isInferenceDone()) { return; }
716 
717  if (!isInferenceReady()) { prepareInference(); }
718 
719  makeInference_();
720 
722  }
723 
724 
725 } /* namespace gum */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:669