aGrUM 2.3.2
a C++ library for (probabilistic) graphical models
graphicalModelInference_tpl.h
Go to the documentation of this file.
1/****************************************************************************
2 * This file is part of the aGrUM/pyAgrum library. *
3 * *
4 * Copyright (c) 2005-2025 by *
5 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
6 * - Christophe GONZALES(_at_AMU) *
7 * *
8 * The aGrUM/pyAgrum library is free software; you can redistribute it *
9 * and/or modify it under the terms of either : *
10 * *
11 * - the GNU Lesser General Public License as published by *
12 * the Free Software Foundation, either version 3 of the License, *
13 * or (at your option) any later version, *
14 * - the MIT license (MIT), *
15 * - or both in dual license, as here. *
16 * *
17 * (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) *
18 * *
19 * This aGrUM/pyAgrum library is distributed in the hope that it will be *
20 * useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
21 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
25 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
26 * OTHER DEALINGS IN THE SOFTWARE. *
27 * *
28 * See LICENCES for more details. *
29 * *
30 * SPDX-FileCopyrightText: Copyright 2005-2025 *
31 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
32 * - Christophe GONZALES(_at_AMU) *
33 * SPDX-License-Identifier: LGPL-3.0-or-later OR MIT *
34 * *
35 * Contact : info_at_agrum_dot_org *
36 * homepage : http://agrum.gitlab.io *
37 * gitlab : https://gitlab.com/agrumery/agrum *
38 * *
39 ****************************************************************************/
40#pragma once
41
42
48
50
51namespace gum {
52
53
54 // Default Constructor
55 template < typename GUM_SCALAR >
62
63 // Default Constructor
64 template < typename GUM_SCALAR >
68
69 // Destructor
70 template < typename GUM_SCALAR >
72 // clear all evidence.
73 // Warning: Do not use method eraseAllEvidence () because it contains a call
74 // to pure virtual method onAllEvidenceErased_ which belongs to an inherited
75 // instance and, therefore, does not exist anymore when
76 // ~GraphicalModelInference () is called
77 for (const auto& pair: _evidence_) {
78 if (pair.second != nullptr) { delete (pair.second); }
79 }
80
81 GUM_DESTRUCTOR(GraphicalModelInference);
82 }
83
84 // returns whether the inference object is in a ready state
85 template < typename GUM_SCALAR >
89
90 // returns whether the inference object is in a OutdatedStructure state
91 template < typename GUM_SCALAR >
95
96 // returns whether the inference object is in a OutdatedTensor state
97 template < typename GUM_SCALAR >
101
102 // returns whether the inference object is in a InferenceDone state
103 template < typename GUM_SCALAR >
106 }
107
108 // returns the state of the inference engine
109 template < typename GUM_SCALAR >
112 return _state_;
113 }
114
115 // set the state of the inference
116 template < typename GUM_SCALAR >
123
124 // Returns a constant reference over the IBayesNet referenced by this class
125 template < typename GUM_SCALAR >
127 if (_model_ == nullptr)
129 "No Bayes net has been assigned to "
130 "the inference algorithm.");
131 return *_model_;
132 }
133
134 // assigns a new BN to the inference engine
135 template < typename GUM_SCALAR >
143
144 // assigns a BN to a newly constructed inference engine
145 template < typename GUM_SCALAR >
152
153 // clears all the data structures allocated for the last inference
154 template < typename GUM_SCALAR >
159
161 template < typename GUM_SCALAR >
163 _domain_sizes_.clear();
164 if (!hasNoModel_()) {
165 for (auto node: _model_->nodes()) {
166 _domain_sizes_.insert(node, _model_->variable(node).domainSize());
167 }
168 }
169 }
170
171 // get the domain sizes of the random variables of the BN
172 template < typename GUM_SCALAR >
176
177 // ##############################################################################
178 // Evidence
179 // ##############################################################################
180
181 // create the internal structure for a hard evidence
182 template < typename GUM_SCALAR >
183 Tensor< GUM_SCALAR >
185 // check that it is possible to create the evidence
186 if (_model_ == nullptr)
188 "No Bayes net has been assigned to the "
189 "inference algorithm");
190
191 if (!_model_->exists(id)) { GUM_ERROR(UndefinedElement, id << " is not a NodeId in the model") }
192
193 if (_model_->variable(id).domainSize() <= val) {
195 "node " << _model_->variable(id) << " has fewer possible values than " << val);
196 }
197
198 return Tensor< GUM_SCALAR >::deterministicTensor(_model_->variable(id), val);
199 }
200
201 // checks wether a tensor corresponds to a hard evidence
202 template < typename GUM_SCALAR >
204 Idx& val) const {
205 // checking if pot is determininstic
206 bool notZero = false;
207 Instantiation I(pot);
208
209 for (I.setFirst(); !I.end(); I.inc()) {
210 if (pot[I] != 0.0) {
211 if (notZero) { // we already met a non-zero value
212 return false;
213 } else {
214 val = I.val(0);
215 notZero = true; // this is the first met non-zero value
216 }
217 }
218 }
219
220 if (!notZero) { // we met no non-zero value
221 GUM_ERROR(FatalError, "Evidence of impossibility (vector of 0s)")
222 }
223
224 return true; // pot is deterministic
225 }
226
227 // adds a new hard evidence on node id
228 template < typename GUM_SCALAR >
232
233 // adds a new hard evidence on node id
234 template < typename GUM_SCALAR >
235 INLINE void GraphicalModelInference< GUM_SCALAR >::addEvidence(const std::string& nodeName,
236 const Idx val) {
237 addEvidence(this->model().idFromName(nodeName), val);
238 }
239
240 // adds a new hard evidence on node id
241 template < typename GUM_SCALAR >
243 const std::string& label) {
244 addEvidence(id, this->model().variable(id)[label]);
245 }
246
247 // adds a new hard evidence on node id
248 template < typename GUM_SCALAR >
249 INLINE void GraphicalModelInference< GUM_SCALAR >::addEvidence(const std::string& nodeName,
250 const std::string& label) {
251 const NodeId id = this->model().idFromName(nodeName);
252 addEvidence(id, this->model().variable(id)[label]);
253 }
254
255 // adds a new evidence on node id (might be soft or hard)
256 template < typename GUM_SCALAR >
258 const std::vector< GUM_SCALAR >& vals) {
259 // checks that the evidence is meaningful
260 if (_model_ == nullptr)
262 "No Bayes net has been assigned to the "
263 "inference algorithm");
264
265 if (!_model_->exists(id)) { GUM_ERROR(UndefinedElement, id << " is not a NodeId in the model") }
266
267 if (_model_->variable(id).domainSize() != vals.size()) {
269 "node " << _model_->variable(id)
270 << " and its evidence vector have different sizes.");
271 }
272
273 Tensor< GUM_SCALAR > pot;
274 pot.add(_model_->variable(id));
275 pot.fillWith(vals);
276 addEvidence(std::move(pot));
277 }
278
279 // adds a new evidence on node id (might be soft or hard)
280 template < typename GUM_SCALAR >
282 const std::vector< GUM_SCALAR >& vals) {
283 addEvidence(this->model().idFromName(nodeName), vals);
284 }
285
286 // adds a new evidence on node id (might be soft or hard)
287 template < typename GUM_SCALAR >
288 void GraphicalModelInference< GUM_SCALAR >::addEvidence(Tensor< GUM_SCALAR >&& pot) {
289 // check if the tensor corresponds to an evidence
290 if (pot.nbrDim() != 1) { GUM_ERROR(InvalidArgument, pot << " is not mono-dimensional.") }
291 if (_model_ == nullptr)
293 "No Bayes net has been assigned to the "
294 "inference algorithm");
295
296 NodeId id = _model_->nodeId(pot.variable(0));
297
298 if (hasEvidence(id)) {
300 " node " << id << " already has an evidence. Please use chgEvidence().");
301 }
302
303 // check whether we have a hard evidence (and also check whether the
304 // tensor only contains 0 (in this case, this will automatically raise
305 // an exception) )
306 Idx val = 0;
307 bool is_hard_evidence = _isHardEvidence_(pot, val);
308
309 // insert the evidence
310 _evidence_.insert(id, new Tensor< GUM_SCALAR >(std::forward< Tensor< GUM_SCALAR > >(pot)));
311 if (is_hard_evidence) { // pot is deterministic
312 _hard_evidence_.insert(id, val);
313 _hard_evidence_nodes_.insert(id);
314 } else {
315 _soft_evidence_nodes_.insert(id);
316 }
318 onEvidenceAdded_(id, is_hard_evidence);
319 }
320
321 // adds a new evidence on node id (might be soft or hard)
322 template < typename GUM_SCALAR >
323 INLINE void GraphicalModelInference< GUM_SCALAR >::addEvidence(const Tensor< GUM_SCALAR >& pot) {
324 Tensor< GUM_SCALAR > new_pot(pot);
325 addEvidence(std::move(new_pot));
326 }
327
329 template < typename GUM_SCALAR >
331 const List< const Tensor< GUM_SCALAR >* >& potlist) {
332 for (const auto pot: potlist)
333 addEvidence(*pot);
334 }
335
337 template < typename GUM_SCALAR >
339 const Set< const Tensor< GUM_SCALAR >* >& potset) {
340 for (const auto pot: potset)
341 addEvidence(*pot);
342 }
343
344 // indicates whether some node(s) have received evidence
345 template < typename GUM_SCALAR >
347 return !_evidence_.empty();
348 }
349
350 // indicates whether node id has received an evidence
351 template < typename GUM_SCALAR >
353 return _evidence_.exists(id);
354 }
355
356 // indicates whether node id has received a hard evidence
357 template < typename GUM_SCALAR >
359 return _hard_evidence_nodes_.exists(id);
360 }
361
362 // indicates whether node id has received a soft evidence
363 template < typename GUM_SCALAR >
365 return _soft_evidence_nodes_.exists(id);
366 }
367
368 // indicates whether node id has received an evidence
369 template < typename GUM_SCALAR >
370 INLINE bool
371 GraphicalModelInference< GUM_SCALAR >::hasEvidence(const std::string& nodeName) const {
372 return hasEvidence(this->model().idFromName(nodeName));
373 }
374
375 // indicates whether node id has received a hard evidence
376 template < typename GUM_SCALAR >
377 INLINE bool
379 return hasHardEvidence(this->model().idFromName(nodeName));
380 }
381
382 // indicates whether node id has received a soft evidence
383 template < typename GUM_SCALAR >
384 INLINE bool
386 return hasSoftEvidence(this->model().idFromName(nodeName));
387 }
388
389 // change the value of an already existing hard evidence
390 template < typename GUM_SCALAR >
394
395 // change the value of an already existing hard evidence
396 template < typename GUM_SCALAR >
397 INLINE void GraphicalModelInference< GUM_SCALAR >::chgEvidence(const std::string& nodeName,
398 const Idx val) {
399 chgEvidence(this->model().idFromName(nodeName), val);
400 }
401
402 // change the value of an already existing hard evidence
403 template < typename GUM_SCALAR >
405 const std::string& label) {
406 chgEvidence(id, this->model().variable(id)[label]);
407 }
408
409 // change the value of an already existing hard evidence
410 template < typename GUM_SCALAR >
411 INLINE void GraphicalModelInference< GUM_SCALAR >::chgEvidence(const std::string& nodeName,
412 const std::string& label) {
413 NodeId id = this->model().idFromName(nodeName);
414 chgEvidence(id, this->model().variable(id)[label]);
415 }
416
417 // change the value of an already existing evidence (might be soft or hard)
418 template < typename GUM_SCALAR >
419 INLINE void
421 const std::vector< GUM_SCALAR >& vals) {
422 // check whether this corresponds to an evidence
423 if (_model_ == nullptr)
425 "No Bayes net has been assigned to the "
426 "inference algorithm");
427
428 if (!_model_->exists(id)) { GUM_ERROR(UndefinedElement, id << " is not a NodeId in the model") }
429
430 if (_model_->variable(id).domainSize() != vals.size()) {
432 "node " << _model_->variable(id) << " and its evidence have different sizes.");
433 }
434
435 // create the tensor corresponding to vals
436 Tensor< GUM_SCALAR > pot;
437 pot.add(_model_->variable(id));
438 pot.fillWith(vals);
439 chgEvidence(pot);
440 }
441
442 // change the value of an already existing evidence (might be soft or hard)
443 template < typename GUM_SCALAR >
444 INLINE void
446 const std::vector< GUM_SCALAR >& vals) {
447 chgEvidence(this->model().idFromName(nodeName), vals);
448 }
449
450 // change the value of an already existing evidence (might be soft or hard)
451 template < typename GUM_SCALAR >
452 void GraphicalModelInference< GUM_SCALAR >::chgEvidence(const Tensor< GUM_SCALAR >& pot) {
453 // check if the tensor corresponds to an evidence
454 if (pot.nbrDim() != 1) {
455 GUM_ERROR(InvalidArgument, pot << " is not a mono-dimensional tensor.")
456 }
457 if (_model_ == nullptr)
459 "No Bayes net has been assigned to the "
460 "inference algorithm");
461
462 NodeId id = _model_->nodeId(pot.variable(0));
463
464 if (!hasEvidence(id)) {
465 GUM_ERROR(InvalidArgument, id << " has no evidence. Please use addEvidence().")
466 }
467
468 // check whether we have a hard evidence (and also check whether the
469 // tensor only contains 0 (in this case, this will automatically raise
470 // an exception) )
471 Idx val;
472 bool is_hard_evidence = _isHardEvidence_(pot, val);
473
474 // modify the evidence already stored
475 const Tensor< GUM_SCALAR >* localPot = _evidence_[id];
476 Instantiation I(pot);
477 for (I.setFirst(); !I.end(); I.inc()) {
478 localPot->set(I, pot[I]);
479 }
480
481 // the inference state will be different
482 // whether evidence change from Hard to Soft or not.
483 bool hasChangedSoftHard = false;
484
485 if (is_hard_evidence) {
486 if (!hasHardEvidence(id)) {
487 hasChangedSoftHard = true;
488 _hard_evidence_.insert(id, val);
489 _hard_evidence_nodes_.insert(id);
490 _soft_evidence_nodes_.erase(id);
491 } else {
492 _hard_evidence_[id] = val;
493 }
494 } else {
495 if (hasHardEvidence(id)) { // evidence was hard
496 _hard_evidence_.erase(id);
497 _hard_evidence_nodes_.erase(id);
498 _soft_evidence_nodes_.insert(id);
499 hasChangedSoftHard = true;
500 }
501 }
502
503 if (hasChangedSoftHard) {
505 } else {
507 }
508
509 onEvidenceChanged_(id, hasChangedSoftHard);
510 }
511
512 // removed the evidence, if any, corresponding to node id
513 template < typename GUM_SCALAR >
515 if (hasEvidence(id)) {
516 if (hasHardEvidence(id)) {
517 onEvidenceErased_(id, true);
518 _hard_evidence_.erase(id);
519 _hard_evidence_nodes_.erase(id);
521 } else {
522 onEvidenceErased_(id, false);
523 _soft_evidence_nodes_.erase(id);
525 }
526
527 delete (_evidence_[id]);
528 _evidence_.erase(id);
529 }
530 }
531
532 // removed the evidence, if any, corresponding to node of name nodeName
533 template < typename GUM_SCALAR >
534 INLINE void GraphicalModelInference< GUM_SCALAR >::eraseEvidence(const std::string& nodeName) {
535 eraseEvidence(this->model().idFromName(nodeName));
536 }
537
538 // removes all the evidence entered into the network
539 template < typename GUM_SCALAR >
541 bool has_hard_evidence = !_hard_evidence_.empty();
542 this->onAllEvidenceErased_(has_hard_evidence);
543
544 for (const auto& pair: _evidence_) {
545 if (pair.second != nullptr) { delete (pair.second); }
546 }
547
548 _evidence_.clear();
549 _hard_evidence_.clear();
550 _hard_evidence_nodes_.clear();
551 _soft_evidence_nodes_.clear();
552
553 if (has_hard_evidence) {
555 } else {
557 }
558 }
559
560 // returns the number of evidence entered into the Bayesian network
561 template < typename GUM_SCALAR >
563 return _evidence_.size();
564 }
565
566 // returns the number of hard evidence entered into the Bayesian network
567 template < typename GUM_SCALAR >
571
572 // returns the number of soft evidence entered into the Bayesian network
573 template < typename GUM_SCALAR >
577
578 // indicate for each node with hard evidence which value it took
579 template < typename GUM_SCALAR >
583
584 // the set of evidence entered into the network
585 template < typename GUM_SCALAR >
590
592 template < typename GUM_SCALAR >
596
598 template < typename GUM_SCALAR >
602
603 // ##############################################################################
604 // Inference
605 // ##############################################################################
606
607 // put the inference into an unprepared state
608 template < typename GUM_SCALAR >
612
615 template < typename GUM_SCALAR >
619
620 // prepare the internal inference structures for the next inference
621 template < typename GUM_SCALAR >
623 if (isInferenceReady() || isInferenceDone()) { return; }
624
625 if (_model_ == nullptr)
627 "No model been assigned to the "
628 "inference algorithm");
629
632
634 }
635
636 // perform the heavy computations needed to compute the targets' posteriors
637 template < typename GUM_SCALAR >
647
648
649} /* namespace gum */
Exception : fatal (unknown ?) error.
GraphicalModelInference()
default constructor with a null model (useful for virtual inheritance)
const NodeSet & softEvidenceNodes() const
returns the set of nodes with soft evidence
virtual bool hasHardEvidence(NodeId id) const final
indicates whether node id has received a hard evidence
virtual void prepareInference() final
prepare the internal inference structures for the next inference
void _computeDomainSizes_()
computes the domain sizes of the random variables
virtual bool isInferenceOutdatedStructure() const noexcept final
returns whether the inference object is in a OutdatedStructure state
virtual void setState_(const StateOfInference state) final
set the state of the inference engine and call the notification onStateChanged_ when necessary (i....
virtual void onAllEvidenceErased_(bool contains_hard_evidence)=0
fired before all the evidence are erased
virtual void onStateChanged_()=0
fired when the stage is changed
virtual void onEvidenceChanged_(const NodeId id, bool hasChangedSoftHard)=0
fired after an evidence is changed, in particular when its status (soft/hard) changes
GraphicalModelInference(const GraphicalModel *model)
default constructor
virtual void chgEvidence(NodeId id, const Idx val) final
change the value of an already existing hard evidence
virtual void addListOfEvidence(const List< const Tensor< GUM_SCALAR > * > &potlist) final
adds a new list of evidence
NodeProperty< const Tensor< GUM_SCALAR > * > _evidence_
the set of evidence entered into the network
virtual const NodeProperty< Size > & domainSizes() const final
get the domain sizes of the random variables of the model
virtual void onModelChanged_(const GraphicalModel *model)=0
fired after a new Bayes net has been assigned to the engine
void setModel_(const GraphicalModel *model)
bool _isHardEvidence_(const Tensor< GUM_SCALAR > &pot, Idx &val) const
checks whether a tensor corresponds to a hard evidence or not
NodeSet _soft_evidence_nodes_
the set of nodes that received soft evidence
virtual void addEvidence(NodeId id, const Idx val) final
adds a new hard evidence on node id
virtual Size nbrSoftEvidence() const final
returns the number of soft evidence entered into the Bayesian network
virtual StateOfInference state() const noexcept final
returns the state of the inference engine
virtual void eraseAllEvidence() final
removes all the evidence entered into the network
StateOfInference _state_
the current state of the inference (outdated/ready/done)
const NodeSet & hardEvidenceNodes() const
returns the set of nodes with hard evidence
NodeProperty< Size > _domain_sizes_
the domain sizes of the random variables
const NodeProperty< const Tensor< GUM_SCALAR > * > & evidence() const
returns the set of evidence
void setModelDuringConstruction_(const GraphicalModel *model)
assigns a model during the inference engine construction
virtual bool isInferenceReady() const noexcept final
returns whether the inference object is in a ready state
virtual void makeInference() final
perform the heavy computations needed to compute the targets' posteriors
virtual Size nbrEvidence() const final
returns the number of evidence entered into the Bayesian network
virtual bool hasEvidence() const final
indicates whether some node(s) have received evidence
virtual void updateOutdatedTensors_()=0
prepares inference when the latter is in OutdatedTensors state
virtual void onEvidenceAdded_(const NodeId id, bool isHardEvidence)=0
fired after a new evidence is inserted
const NodeProperty< Idx > & hardEvidence() const
indicate for each node with hard evidence which value it took
virtual void makeInference_()=0
called when the inference has to be performed effectively
virtual void clear()
clears all the data structures allocated for the last inference
void setOutdatedTensorsState_()
puts the inference into an OutdatedTensors state if it is not already in an OutdatedStructure state
virtual void addSetOfEvidence(const Set< const Tensor< GUM_SCALAR > * > &potset) final
adds a new set of evidence
virtual Size nbrHardEvidence() const final
returns the number of hard evidence entered into the Bayesian network
virtual void updateOutdatedStructure_()=0
prepares inference when the latter is in OutdatedStructure state
virtual void eraseEvidence(NodeId id) final
removed the evidence, if any, corresponding to node id
virtual bool hasSoftEvidence(NodeId id) const final
indicates whether node id has received a soft evidence
StateOfInference
current state of the inference
Tensor< GUM_SCALAR > _createHardEvidence_(NodeId id, Idx val) const
create the internal structure for a hard evidence
virtual bool isInferenceDone() const noexcept final
returns whether the inference object is in a InferenceDone state
virtual const GraphicalModel & model() const final
Returns a constant reference over the IBayesNet referenced by this class.
NodeSet _hard_evidence_nodes_
the set of nodes that received hard evidence
virtual bool isInferenceOutdatedTensors() const noexcept final
returns whether the inference object is in a OutdatedTensor state
const GraphicalModel * _model_
the Bayes net on which we perform inferences
NodeProperty< Idx > _hard_evidence_
assign to each node with a hard evidence the index of its observed value
void setOutdatedStructureState_()
put the inference into an outdated model structure state
virtual void onEvidenceErased_(const NodeId id, bool isHardEvidence)=0
fired before an evidence is removed
Virtual base class for probabilistic graphical models.
Class for assigning/browsing values to tuples of discrete variables.
bool end() const
Returns true if the Instantiation reached the end.
void inc()
Operator increment.
Idx val(Idx i) const
Returns the current value of the variable at position i.
void setFirst()
Assign the first values to the tuple of the Instantiation.
Exception: at least one argument passed to a function is not what was expected.
Generic doubly linked lists.
Definition list.h:379
Exception : a pointer or a reference on a nullptr (0) object.
Representation of a set.
Definition set.h:131
static Tensor< GUM_SCALAR > deterministicTensor(const DiscreteVariable &var, Idx value)
Exception : a looked-for element could not be found.
#define GUM_ERROR(type, msg)
Definition exceptions.h:72
This file contains abstract class definitions for graphical models inference classes.
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition types.h:74
Size Idx
Type for indexes.
Definition types.h:79
Size NodeId
Type for node ids.
HashTable< NodeId, VAL > NodeProperty
Property on graph elements.
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
gum is the global namespace for all aGrUM entities
Definition agrum.h:46