aGrUM 3.1.1
a C++ library for (probabilistic) graphical models
jointTargetedInference_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-2026 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-2026 *
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
41#pragma once
42
43
52
53namespace gum {
54
55
56 // Default Constructor
57 template < GUM_Numeric GUM_SCALAR >
59 MarginalTargetedInference< GUM_SCALAR >(bn) {
60 // assign a BN if this has not been done before (due to virtual inheritance)
61 if (this->hasNoModel_()) {
63 }
64 GUM_CONSTRUCTOR(JointTargetedInference);
65 }
66
67 // Destructor
68 template < GUM_Numeric GUM_SCALAR >
72
73 // assigns a new BN to the inference engine
74 template < GUM_Numeric GUM_SCALAR >
80
81 // ##############################################################################
82 // Targets
83 // ##############################################################################
84
85 // return true if target is a nodeset target.
86 template < GUM_Numeric GUM_SCALAR >
88 if (this->hasNoModel_())
90 "No Bayes net has been assigned to the "
91 "inference algorithm");
92
93 const auto& dag = this->BN().internalDag();
94 for (const auto var: vars) {
95 if (!dag.exists(var)) { GUM_ERROR(UndefinedElement, var << " is not a NodeId in the bn") }
96 }
97
98 return _joint_targets_.contains(vars);
99 }
100
101 // Clear all previously defined single targets
102 template < GUM_Numeric GUM_SCALAR >
106
107 // Clear all previously defined targets (single targets and sets of targets)
108 template < GUM_Numeric GUM_SCALAR >
110 if (_joint_targets_.size() > 0) {
111 // we already are in target mode. So no this->setTargetedMode_(); is needed
113 _joint_targets_.clear();
115 }
116 }
117
118 // Clear all previously defined targets (single and joint targets)
119 template < GUM_Numeric GUM_SCALAR >
124
125 // Add a set of nodes as a new target
126 template < GUM_Numeric GUM_SCALAR >
128 // check if the nodes in the target belong to the Bayesian network
129 if (this->hasNoModel_())
131 "No Bayes net has been assigned to the "
132 "inference algorithm");
133
134 const auto& dag = this->BN().internalDag();
135 for (const auto node: joint_target) {
136 if (!dag.exists(node)) {
138 "at least one one in " << joint_target << " does not belong to the bn");
139 }
140 }
141
142 // check that the joint_target set does not contain the new target
143 if (_joint_targets_.contains(joint_target)) return;
144
145 // check if joint_target is a subset of an already existing target
146 for (const auto& target: _joint_targets_) {
147 if (target.isStrictSupersetOf(joint_target)) return;
148 }
149
150 // check if joint_target is not a superset of an already existing target
151 // in this case, we need to remove old existing target
152 for (auto iter = _joint_targets_.beginSafe(); iter != _joint_targets_.endSafe(); ++iter) {
153 if (iter->isStrictSubsetOf(joint_target)) eraseJointTarget(*iter);
154 }
155
156 this->setTargetedMode_(); // does nothing if already in targeted mode
157 _joint_targets_.insert(joint_target);
158 onJointTargetAdded_(joint_target);
160 }
161
162 // removes an existing set target
163 template < GUM_Numeric GUM_SCALAR >
165 // check if the nodes in the target belong to the Bayesian network
166 if (this->hasNoModel_())
168 "No Bayes net has been assigned to the "
169 "inference algorithm");
170
171 const auto& dag = this->BN().internalDag();
172 for (const auto node: joint_target) {
173 if (!dag.exists(node)) {
175 "at least one one in " << joint_target << " does not belong to the bn");
176 }
177 }
178
179 // check that the joint_target set does not contain the new target
180 if (_joint_targets_.contains(joint_target)) {
181 // note that we have to be in target mode when we are here
182 // so, no this->setTargetedMode_(); is necessary
183 onJointTargetErased_(joint_target);
184 _joint_targets_.erase(joint_target);
186 }
187 }
188
190 template < GUM_Numeric GUM_SCALAR >
194
196 template < GUM_Numeric GUM_SCALAR >
200
201 // ##############################################################################
202 // Inference
203 // ##############################################################################
204
205 // Compute the posterior of a nodeset.
206 template < GUM_Numeric GUM_SCALAR >
207 const Tensor< GUM_SCALAR >&
209 // try to get the smallest set of targets that contains "nodes"
210 NodeSet set;
211 bool found_exact_target = false;
212
213 if (_joint_targets_.contains(nodes)) {
214 set = nodes;
215 found_exact_target = true;
216 } else {
217 for (const auto& target: _joint_targets_) {
218 if (nodes.isStrictSubsetOf(target)) {
219 set = target;
220 break;
221 }
222 }
223 }
224
225 // if (set.empty()) {
226 // GUM_ERROR(UndefinedElement,
227 // " no joint target containing " << nodes << " could be found among "
228 // << _joint_targets_);
229 // }
230
231 if (!this->isInferenceDone()) { this->makeInference(); }
232
233 if (found_exact_target || set.empty()) return jointPosterior_(nodes);
234 else return jointPosterior_(nodes, set);
235 }
236
237 // Compute the posterior of a node
238 template < GUM_Numeric GUM_SCALAR >
239 const Tensor< GUM_SCALAR >& JointTargetedInference< GUM_SCALAR >::posterior(NodeId node) {
241 else return jointPosterior(NodeSet{node});
242 }
243
244 // Compute the posterior of a node
245 template < GUM_Numeric GUM_SCALAR >
246 const Tensor< GUM_SCALAR >&
248 return posterior(this->BN().idFromName(nodeName));
249 }
250
251 template < GUM_Numeric GUM_SCALAR >
252 Tensor< GUM_SCALAR >
254 const NodeSet& evs) {
255 if (!(evs * targets).empty()) {
257 "Targets (" << targets << ") can not intersect evs (" << evs << ").");
258 }
259 auto condset = this->BN().minimalCondSet(targets, evs);
260
261 this->eraseAllTargets();
262 this->eraseAllEvidence();
263
264 Instantiation iTarget;
265 Tensor< GUM_SCALAR > res;
266 for (const auto& target: targets) {
267 res.add(this->BN().variable(target));
268 iTarget.add(this->BN().variable(target));
269 }
270 this->addJointTarget(targets);
271
272 for (const auto& n: condset) {
273 res.add(this->BN().variable(n));
274 this->addEvidence(n, 0);
275 }
276
277 Instantiation inst(res);
278 for (inst.setFirstOut(iTarget); !inst.end(); inst.incOut(iTarget)) {
279 // inferring
280 for (const auto& n: condset)
281 this->chgEvidence(n, inst.val(this->BN().variable(n)));
282 this->makeInference();
283 // populate res
284 for (inst.setFirstIn(iTarget); !inst.end(); inst.incIn(iTarget)) {
285 res.set(inst, this->jointPosterior(targets)[inst]);
286 }
287 inst.setFirstIn(iTarget); // remove inst.end() flag
288 }
289
290 return res;
291 }
292
293 template < GUM_Numeric GUM_SCALAR >
295 const std::vector< std::string >& targets,
296 const std::vector< std::string >& evs) {
297 const auto& bn = this->BN();
298 return evidenceJointImpact(bn.nodeset(targets), bn.nodeset(evs));
299 }
300
301 template < GUM_Numeric GUM_SCALAR >
303 const auto& bn = this->BN();
304 const Size siz = targets.size();
305 if (siz <= 1) {
307 "jointMutualInformation needs at least 2 variables (targets=" << targets << ")");
308 }
309
310 this->eraseAllTargets();
311 this->eraseAllEvidence();
312 this->addJointTarget(targets);
313 this->makeInference();
314 const auto po = this->jointPosterior(targets);
315
316 gum::Instantiation caracteristic;
317 gum::Instantiation variables;
318 for (const auto nod: targets) {
319 const auto& var = bn.variable(nod);
320 auto pv = new gum::RangeVariable(var.name(), "", 0, 1);
321 caracteristic.add(*pv);
322 variables.add(var);
323 }
324
326
327 const GUM_SCALAR start = (siz % 2 == 0) ? GUM_SCALAR(-1.0) : GUM_SCALAR(1.0);
328 GUM_SCALAR sign;
329 GUM_SCALAR res = GUM_SCALAR(0.0);
330
331 caracteristic.setFirst();
332 for (caracteristic.inc(); !caracteristic.end(); caracteristic.inc()) {
333 sov.clear();
334 sign = start;
335 for (Idx i = 0; i < caracteristic.nbrDim(); i++) {
336 if (caracteristic.val(i) == 1) {
337 sign = -sign;
338 sov.insert(&variables.variable(i));
339 }
340 }
341 res += sign * po.sumIn(sov).entropy();
342 }
343
344 for (Idx i = 0; i < caracteristic.nbrDim(); i++) {
345 delete &caracteristic.variable(i);
346 }
347
348 return res;
349 }
350
351 template < GUM_Numeric GUM_SCALAR >
353 const std::vector< std::string >& targets) {
354 return jointMutualInformation(this->BN().ids(targets));
355 }
356
357} /* namespace gum */
void _setBayesNetDuringConstruction_(const IBayesNet< GUM_SCALAR > *bn)
assigns a BN during the inference engine construction
virtual const IBayesNet< GUM_SCALAR > & BN() const final
Returns a constant reference over the IBayesNet referenced by this class.
virtual void chgEvidence(NodeId id, const Idx val) final
change the value of an already existing hard evidence
virtual bool isInferenceDone() const noexcept final
returns whether the inference object is in a InferenceDone 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 eraseAllEvidence() final
removes all the evidence entered into the network
virtual void makeInference() final
perform the heavy computations needed to compute the targets' posteriors
virtual void addEvidence(NodeId id, const Idx val) final
adds a new hard evidence on node id
Virtual base class for probabilistic graphical models.
Class representing the minimal interface for Bayesian network with no numerical data.
Definition IBayesNet.h:75
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.
void incOut(const Instantiation &i)
Operator increment for the variables not in i.
void setFirstIn(const Instantiation &i)
Assign the first values in the Instantiation for the variables in i.
void add(const DiscreteVariable &v) final
Adds a new variable in the Instantiation.
void incIn(const Instantiation &i)
Operator increment for the variables in i.
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.
void setFirstOut(const Instantiation &i)
Assign the first values in the Instantiation for the variables not in i.
const DiscreteVariable & variable(Idx i) const final
Returns the variable at position i in the tuple.
Idx nbrDim() const final
Returns the number of variables in the Instantiation.
Exception: at least one argument passed to a function is not what was expected.
const Tensor< GUM_SCALAR > & posterior(NodeId node) final
Computes and returns the posterior of a node.
Tensor< GUM_SCALAR > evidenceJointImpact(const NodeSet &targets, const NodeSet &evs)
Create a gum::Tensor for P(joint targets|evs) (for all instantiation of targets and evs).
virtual Size nbrJointTargets() const noexcept final
returns the number of joint targets
virtual void onAllJointTargetsErased_()=0
fired before a all the joint targets are removed
Set< NodeSet > _joint_targets_
the set of joint targets
virtual void eraseAllMarginalTargets() final
Clear all the previously defined marginal targets.
virtual void onJointTargetErased_(const NodeSet &set)=0
fired before a joint target is removed
virtual bool isJointTarget(const NodeSet &vars) const final
return true if target is a joint target.
virtual void addJointTarget(const NodeSet &joint_target) final
Add a set of nodes as a new joint target. As a collateral effect, every node is added as a marginal t...
virtual void onJointTargetAdded_(const NodeSet &set)=0
fired after a new joint target is inserted
JointTargetedInference(const IBayesNet< GUM_SCALAR > *bn)
default constructor
virtual const Tensor< GUM_SCALAR > & jointPosterior_(const NodeSet &set)=0
asks derived classes for the joint posterior of a declared target set
virtual void eraseAllJointTargets() final
Clear all previously defined joint targets.
virtual const Set< NodeSet > & jointTargets() const noexcept final
returns the list of joint targets
void eraseAllTargets() override
Clear all previously defined targets (marginal and joint targets).
virtual const Tensor< GUM_SCALAR > & jointPosterior(const NodeSet &nodes) final
Compute the joint posterior of a set of nodes.
void onModelChanged_(const GraphicalModel *bn) override
fired after a new Bayes net has been assigned to the engine
virtual void eraseJointTarget(const NodeSet &joint_target) final
removes an existing joint target
GUM_SCALAR jointMutualInformation(const NodeSet &targets)
Mutual information between targets.
void onModelChanged_(const GraphicalModel *bn) override
fired after a new Bayes net has been assigned to the engine
virtual const Tensor< GUM_SCALAR > & posterior(NodeId node)
Computes and returns the posterior of a node.
virtual bool isTarget(NodeId node) const final
return true if variable is a (marginal) target
MarginalTargetedInference(const IBayesNet< GUM_SCALAR > *bn)
default constructor
virtual const NodeSet & targets() const noexcept final
returns the list of marginal targets
virtual void eraseAllTargets()
Clear all previously defined targets.
Exception : a pointer or a reference on a nullptr (0) object.
Defines a discrete random variable over an integer interval.
Representation of a set.
Definition set.h:129
void clear()
Removes all the elements, if any, from the set.
Definition set_tpl.h:315
bool empty() const noexcept
Indicates whether the set is the empty set.
Definition set_tpl.h:613
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:510
bool isStrictSubsetOf(const Set< Key > &s) const
Definition set_tpl.h:473
Exception : a looked-for element could not be found.
#define GUM_ERROR(type, msg)
Definition exceptions.h:76
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.
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
Class encapsulating computations of notions from Information Theory.
This file contains the abstract inference class definition for computing (incrementally) joint poster...
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
Set< const DiscreteVariable * > VariableSet
Header of gumRangeVariable.