aGrUM  0.20.3
a C++ library for (probabilistic) graphical models
graphicalModelInference_tpl.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright (c) 2005-2021 by 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(const GraphicalModel* model) :
36  _model_(model) {
37  _computeDomainSizes_();
38 
39  GUM_CONSTRUCTOR(GraphicalModelInference);
40  }
41 
42 
43  // Default Constructor
44  template < typename GUM_SCALAR >
47  }
48 
49 
50  // Destructor
51  template < typename GUM_SCALAR >
53  // clear all evidence.
54  // Warning: Do not use method eraseAllEvidence () because it contains a call
55  // to pure virtual method onAllEvidenceErased_ which belongs to an inherited
56  // instance and, therefore, does not exist anymore when
57  // ~GraphicalModelInference () is called
58  for (const auto& pair: _evidence_) {
59  if (pair.second != nullptr) { delete (pair.second); }
60  }
61 
63  }
64 
65 
66  // returns whether the inference object is in a ready state
67  template < typename GUM_SCALAR >
70  }
71  // returns whether the inference object is in a OutdatedStructure state
72  template < typename GUM_SCALAR >
75  }
76  // returns whether the inference object is in a OutdatedPotential state
77  template < typename GUM_SCALAR >
78  INLINE bool
81  }
82  // returns whether the inference object is in a InferenceDone state
83  template < typename GUM_SCALAR >
85  return (_state_ == StateOfInference::Done);
86  }
87 
88 
89  // returns the state of the inference engine
90  template < typename GUM_SCALAR >
92  GraphicalModelInference< GUM_SCALAR >::state() const noexcept {
93  return _state_;
94  }
95 
96  // set the state of the inference
97  template < typename GUM_SCALAR >
99  if (_state_ != state) {
100  _state_ = state;
101  onStateChanged_();
102  }
103  }
104 
105  // Returns a constant reference over the IBayesNet referenced by this class
106  template < typename GUM_SCALAR >
108  if (_model_ == nullptr)
110  "No Bayes net has been assigned to "
111  "the inference algorithm.");
112  return *_model_;
113  }
114 
115 
116  // assigns a new BN to the inference engine
117  template < typename GUM_SCALAR >
119  clear();
120  _model_ = model;
124  }
125 
126 
127  // assigns a BN to a newly constructed inference engine
128  template < typename GUM_SCALAR >
130  const GraphicalModel* model) {
131  _model_ = model;
134  }
135 
136 
137  // clears all the data structures allocated for the last inference
138  template < typename GUM_SCALAR >
142  }
143 
144 
145  /// computes the domain sizes of the random variables
146  template < typename GUM_SCALAR >
149  if (!hasNoModel_()) {
150  for (auto node: _model_->nodes()) {
152  }
153  }
154  }
155 
156 
157  // get the domain sizes of the random variables of the BN
158  template < typename GUM_SCALAR >
160  return _domain_sizes_;
161  }
162 
163 
164  // ##############################################################################
165  // Evidence
166  // ##############################################################################
167 
168  // create the internal structure for a hard evidence
169  template < typename GUM_SCALAR >
172  // check that it is possible to create the evidence
173  if (_model_ == nullptr)
175  "No Bayes net has been assigned to the "
176  "inference algorithm");
177 
178  if (!_model_->exists(id)) { GUM_ERROR(UndefinedElement, id << " is not a NodeId in the model") }
179 
180  if (_model_->variable(id).domainSize() <= val) {
182  "node " << _model_->variable(id) << " has fewer possible values than " << val);
183  }
184 
185  // create the deterministic potential
188  pot << _model_->variable(id);
189  pot.endMultipleChanges(0.0);
190 
193  pot.set(I, 1.0);
194 
195  return pot;
196  }
197 
198 
199  // checks wether a potential corresponds to a hard evidence
200  template < typename GUM_SCALAR >
202  Idx& val) const {
203  // checking if pot is determininstic
204  bool notZero = false;
206 
207  for (I.setFirst(); !I.end(); I.inc()) {
208  if (pot[I] != 0.0) {
209  if (notZero) { // we already met a non-zero value
210  return false;
211  } else {
212  val = I.val(0);
213  notZero = true; // this is the first met non-zero value
214  }
215  }
216  }
217 
218  if (!notZero) { // we met no non-zero value
219  GUM_ERROR(FatalError, "Evidence of impossibility (vector of 0s)")
220  }
221 
222  return true; // pot is deterministic
223  }
224 
225 
226  // adds a new hard evidence on node id
227  template < typename GUM_SCALAR >
230  }
231 
232  // adds a new hard evidence on node id
233  template < typename GUM_SCALAR >
235  const Idx val) {
237  }
238 
239  // adds a new hard evidence on node id
240  template < typename GUM_SCALAR >
242  const std::string& label) {
243  addEvidence(id, this->model().variable(id)[label]);
244  }
245 
246  // adds a new hard evidence on node id
247  template < typename GUM_SCALAR >
249  const std::string& label) {
250  NodeId id = this->model().idFromName(nodeName);
251  addEvidence(id, this->model().variable(id)[label]);
252  }
253 
254  // adds a new evidence on node id (might be soft or hard)
255  template < typename GUM_SCALAR >
257  const std::vector< GUM_SCALAR >& vals) {
258  // checks that the evidence is meaningful
259  if (_model_ == nullptr)
261  "No Bayes net has been assigned to the "
262  "inference algorithm");
263 
264  if (!_model_->exists(id)) { GUM_ERROR(UndefinedElement, id << " is not a NodeId in the model") }
265 
266  if (_model_->variable(id).domainSize() != vals.size()) {
268  "node " << _model_->variable(id)
269  << " and its evidence vector have different sizes.");
270  }
271 
274  pot.fillWith(vals);
275  addEvidence(std::move(pot));
276  }
277 
278  // adds a new evidence on node id (might be soft or hard)
279  template < typename GUM_SCALAR >
281  const std::vector< GUM_SCALAR >& vals) {
283  }
284 
285  // adds a new evidence on node id (might be soft or hard)
286  template < typename GUM_SCALAR >
288  // check if the potential corresponds to an evidence
289  if (pot.nbrDim() != 1) { GUM_ERROR(InvalidArgument, pot << " is not mono-dimensional.") }
290  if (_model_ == nullptr)
292  "No Bayes net has been assigned to the "
293  "inference algorithm");
294 
296 
297  if (hasEvidence(id)) {
299  " node " << id << " already has an evidence. Please use chgEvidence().");
300  }
301 
302  // check whether we have a hard evidence (and also check whether the
303  // potential only contains 0 (in this case, this will automatically raise
304  // an exception) )
305  Idx val;
307 
308  // insert the evidence
311  if (is_hard_evidence) { // pot is deterministic
314  } else {
316  }
319  }
320 
321 
322  // adds a new evidence on node id (might be soft or hard)
323  template < typename GUM_SCALAR >
324  INLINE void
328  }
329 
330 
331  /// adds a new list of evidence
332  template < typename GUM_SCALAR >
334  const List< const Potential< GUM_SCALAR >* >& potlist) {
335  for (const auto pot: potlist)
336  addEvidence(*pot);
337  }
338 
339 
340  /// adds a new set of evidence
341  template < typename GUM_SCALAR >
343  const Set< const Potential< GUM_SCALAR >* >& potset) {
344  for (const auto pot: potset)
345  addEvidence(*pot);
346  }
347 
348 
349  // indicates whether some node(s) have received evidence
350  template < typename GUM_SCALAR >
352  return !_evidence_.empty();
353  }
354 
355 
356  // indicates whether node id has received an evidence
357  template < typename GUM_SCALAR >
359  return _evidence_.exists(id);
360  }
361 
362 
363  // indicates whether node id has received a hard evidence
364  template < typename GUM_SCALAR >
367  }
368 
369 
370  // indicates whether node id has received a soft evidence
371  template < typename GUM_SCALAR >
374  }
375 
376 
377  // indicates whether node id has received an evidence
378  template < typename GUM_SCALAR >
379  INLINE bool
381  return hasEvidence(this->model().idFromName(nodeName));
382  }
383 
384 
385  // indicates whether node id has received a hard evidence
386  template < typename GUM_SCALAR >
387  INLINE bool
389  return hasHardEvidence(this->model().idFromName(nodeName));
390  }
391 
392 
393  // indicates whether node id has received a soft evidence
394  template < typename GUM_SCALAR >
395  INLINE bool
397  return hasSoftEvidence(this->model().idFromName(nodeName));
398  }
399 
400  // change the value of an already existing hard evidence
401  template < typename GUM_SCALAR >
404  }
405 
406  // change the value of an already existing hard evidence
407  template < typename GUM_SCALAR >
409  const Idx val) {
411  }
412 
413  // change the value of an already existing hard evidence
414  template < typename GUM_SCALAR >
416  const std::string& label) {
417  chgEvidence(id, this->model().variable(id)[label]);
418  }
419 
420  // change the value of an already existing hard evidence
421  template < typename GUM_SCALAR >
423  const std::string& label) {
424  NodeId id = this->model().idFromName(nodeName);
425  chgEvidence(id, this->model().variable(id)[label]);
426  }
427 
428  // change the value of an already existing evidence (might be soft or hard)
429  template < typename GUM_SCALAR >
430  INLINE void
432  const std::vector< GUM_SCALAR >& vals) {
433  // check whether this corresponds to an evidence
434  if (_model_ == nullptr)
436  "No Bayes net has been assigned to the "
437  "inference algorithm");
438 
439  if (!_model_->exists(id)) { GUM_ERROR(UndefinedElement, id << " is not a NodeId in the model") }
440 
441  if (_model_->variable(id).domainSize() != vals.size()) {
443  "node " << _model_->variable(id) << " and its evidence have different sizes.");
444  }
445 
446  // create the potential corresponding to vals
449  pot.fillWith(vals);
450  chgEvidence(pot);
451  }
452 
453  // change the value of an already existing evidence (might be soft or hard)
454  template < typename GUM_SCALAR >
455  INLINE void
457  const std::vector< GUM_SCALAR >& vals) {
459  }
460 
461 
462  // change the value of an already existing evidence (might be soft or hard)
463  template < typename GUM_SCALAR >
465  // check if the potential corresponds to an evidence
466  if (pot.nbrDim() != 1) {
467  GUM_ERROR(InvalidArgument, pot << " is not a mono-dimensional potential.")
468  }
469  if (_model_ == nullptr)
471  "No Bayes net has been assigned to the "
472  "inference algorithm");
473 
475 
476  if (!hasEvidence(id)) {
477  GUM_ERROR(InvalidArgument, id << " has no evidence. Please use addEvidence().")
478  }
479 
480  // check whether we have a hard evidence (and also check whether the
481  // potential only contains 0 (in this case, this will automatically raise
482  // an exception) )
483  Idx val;
485 
486  // modify the evidence already stored
489  for (I.setFirst(); !I.end(); I.inc()) {
490  localPot->set(I, pot[I]);
491  }
492 
493  // the inference state will be different
494  // whether evidence change from Hard to Soft or not.
495  bool hasChangedSoftHard = false;
496 
497  if (is_hard_evidence) {
498  if (!hasHardEvidence(id)) {
499  hasChangedSoftHard = true;
503  } else {
505  }
506  } else {
507  if (hasHardEvidence(id)) { // evidence was hard
511  hasChangedSoftHard = true;
512  }
513  }
514 
515  if (hasChangedSoftHard) {
517  } else {
519  }
520 
522  }
523 
524 
525  // removed the evidence, if any, corresponding to node id
526  template < typename GUM_SCALAR >
528  if (hasEvidence(id)) {
529  if (hasHardEvidence(id)) {
530  onEvidenceErased_(id, true);
534  } else {
535  onEvidenceErased_(id, false);
538  }
539 
540  delete (_evidence_[id]);
542  }
543  }
544  // removed the evidence, if any, corresponding to node of name nodeName
545  template < typename GUM_SCALAR >
548  }
549 
550 
551  // removes all the evidence entered into the network
552  template < typename GUM_SCALAR >
556 
557  for (const auto& pair: _evidence_) {
558  if (pair.second != nullptr) { delete (pair.second); }
559  }
560 
561  _evidence_.clear();
565 
566  if (has_hard_evidence) {
568  } else {
570  }
571  }
572 
573 
574  // returns the number of evidence entered into the Bayesian network
575  template < typename GUM_SCALAR >
577  return _evidence_.size();
578  }
579 
580 
581  // returns the number of hard evidence entered into the Bayesian network
582  template < typename GUM_SCALAR >
584  return _hard_evidence_nodes_.size();
585  }
586 
587 
588  // returns the number of soft evidence entered into the Bayesian network
589  template < typename GUM_SCALAR >
591  return _soft_evidence_nodes_.size();
592  }
593 
594 
595  // indicate for each node with hard evidence which value it took
596  template < typename GUM_SCALAR >
598  return _hard_evidence_;
599  }
600 
601 
602  // the set of evidence entered into the network
603  template < typename GUM_SCALAR >
604  INLINE const NodeProperty< const Potential< GUM_SCALAR >* >&
606  return _evidence_;
607  }
608 
609 
610  /// the set of nodes that received soft evidence
611  template < typename GUM_SCALAR >
613  return _soft_evidence_nodes_;
614  }
615 
616 
617  /// the set of nodes that received hard evidence
618  template < typename GUM_SCALAR >
620  return _hard_evidence_nodes_;
621  }
622 
623 
624  // ##############################################################################
625  // Inference
626  // ##############################################################################
627 
628  // put the inference into an unprepared state
629  template < typename GUM_SCALAR >
632  }
633 
634 
635  /** puts the inference into an OutdatedPotentials state if it is not
636  * already in an OutdatedStructure state */
637  template < typename GUM_SCALAR >
640  }
641 
642 
643  // prepare the internal inference structures for the next inference
644  template < typename GUM_SCALAR >
646  if (isInferenceReady() || isInferenceDone()) { return; }
647 
648  if (_model_ == nullptr)
650  "No model been assigned to the "
651  "inference algorithm");
652 
655  else
657 
659  }
660 
661 
662  // perform the heavy computations needed to compute the targets' posteriors
663  template < typename GUM_SCALAR >
665  if (isInferenceDone()) { return; }
666 
667  if (!isInferenceReady()) { prepareInference(); }
668 
669  makeInference_();
670 
672  }
673 
674 
675 } /* namespace gum */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:643