aGrUM 2.3.2
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-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
51
52namespace gum {
53
54
55 // Default Constructor
56 template < typename GUM_SCALAR >
58 MarginalTargetedInference< GUM_SCALAR >(bn) {
59 // assign a BN if this has not been done before (due to virtual inheritance)
60 if (this->hasNoModel_()) {
62 }
63 GUM_CONSTRUCTOR(JointTargetedInference);
64 }
65
66 // Destructor
67 template < typename GUM_SCALAR >
71
72 // assigns a new BN to the inference engine
73 template < typename GUM_SCALAR >
79
80 // ##############################################################################
81 // Targets
82 // ##############################################################################
83
84 // return true if target is a nodeset target.
85 template < typename GUM_SCALAR >
87 if (this->hasNoModel_())
89 "No Bayes net has been assigned to the "
90 "inference algorithm");
91
92 const auto& dag = this->BN().dag();
93 for (const auto var: vars) {
94 if (!dag.exists(var)) { GUM_ERROR(UndefinedElement, var << " is not a NodeId in the bn") }
95 }
96
97 return _joint_targets_.contains(vars);
98 }
99
100 // Clear all previously defined single targets
101 template < typename GUM_SCALAR >
105
106 // Clear all previously defined targets (single targets and sets of targets)
107 template < typename GUM_SCALAR >
109 if (_joint_targets_.size() > 0) {
110 // we already are in target mode. So no this->setTargetedMode_(); is needed
112 _joint_targets_.clear();
114 }
115 }
116
117 // Clear all previously defined targets (single and joint targets)
118 template < typename GUM_SCALAR >
123
124 // Add a set of nodes as a new target
125 template < typename GUM_SCALAR >
127 // check if the nodes in the target belong to the Bayesian network
128 if (this->hasNoModel_())
130 "No Bayes net has been assigned to the "
131 "inference algorithm");
132
133 const auto& dag = this->BN().dag();
134 for (const auto node: joint_target) {
135 if (!dag.exists(node)) {
137 "at least one one in " << joint_target << " does not belong to the bn");
138 }
139 }
140
141 // check that the joint_target set does not contain the new target
142 if (_joint_targets_.contains(joint_target)) return;
143
144 // check if joint_target is a subset of an already existing target
145 for (const auto& target: _joint_targets_) {
146 if (target.isStrictSupersetOf(joint_target)) return;
147 }
148
149 // check if joint_target is not a superset of an already existing target
150 // in this case, we need to remove old existing target
151 for (auto iter = _joint_targets_.beginSafe(); iter != _joint_targets_.endSafe(); ++iter) {
152 if (iter->isStrictSubsetOf(joint_target)) eraseJointTarget(*iter);
153 }
154
155 this->setTargetedMode_(); // does nothing if already in targeted mode
156 _joint_targets_.insert(joint_target);
157 onJointTargetAdded_(joint_target);
159 }
160
161 // removes an existing set target
162 template < typename GUM_SCALAR >
164 // check if the nodes in the target belong to the Bayesian network
165 if (this->hasNoModel_())
167 "No Bayes net has been assigned to the "
168 "inference algorithm");
169
170 const auto& dag = this->BN().dag();
171 for (const auto node: joint_target) {
172 if (!dag.exists(node)) {
174 "at least one one in " << joint_target << " does not belong to the bn");
175 }
176 }
177
178 // check that the joint_target set does not contain the new target
179 if (_joint_targets_.contains(joint_target)) {
180 // note that we have to be in target mode when we are here
181 // so, no this->setTargetedMode_(); is necessary
182 onJointTargetErased_(joint_target);
183 _joint_targets_.erase(joint_target);
185 }
186 }
187
189 template < typename GUM_SCALAR >
193
195 template < typename GUM_SCALAR >
197 return _joint_targets_.size();
198 }
199
200 // ##############################################################################
201 // Inference
202 // ##############################################################################
203
204 // Compute the posterior of a nodeset.
205 template < typename GUM_SCALAR >
206 const Tensor< GUM_SCALAR >&
208 // try to get the smallest set of targets that contains "nodes"
209 NodeSet set;
210 bool found_exact_target = false;
211
212 if (_joint_targets_.contains(nodes)) {
213 set = nodes;
214 found_exact_target = true;
215 } else {
216 for (const auto& target: _joint_targets_) {
217 if (nodes.isStrictSubsetOf(target)) {
218 set = target;
219 break;
220 }
221 }
222 }
223
224 // if (set.empty()) {
225 // GUM_ERROR(UndefinedElement,
226 // " no joint target containing " << nodes << " could be found among "
227 // << _joint_targets_);
228 // }
229
230 if (!this->isInferenceDone()) { this->makeInference(); }
231
232 if (found_exact_target || set.empty()) return jointPosterior_(nodes);
233 else return jointPosterior_(nodes, set);
234 }
235
236 // Compute the posterior of a node
237 template < typename GUM_SCALAR >
238 const Tensor< GUM_SCALAR >& JointTargetedInference< GUM_SCALAR >::posterior(NodeId node) {
240 else return jointPosterior(NodeSet{node});
241 }
242
243 // Compute the posterior of a node
244 template < typename GUM_SCALAR >
245 const Tensor< GUM_SCALAR >&
247 return posterior(this->BN().idFromName(nodeName));
248 }
249
250 template < typename GUM_SCALAR >
251 Tensor< GUM_SCALAR >
253 const NodeSet& evs) {
254 if (!(evs * targets).empty()) {
256 "Targets (" << targets << ") can not intersect evs (" << evs << ").");
257 }
258 auto condset = this->BN().minimalCondSet(targets, evs);
259
260 this->eraseAllTargets();
261 this->eraseAllEvidence();
262
263 Instantiation iTarget;
264 Tensor< GUM_SCALAR > res;
265 for (const auto& target: targets) {
266 res.add(this->BN().variable(target));
267 iTarget.add(this->BN().variable(target));
268 }
269 this->addJointTarget(targets);
270
271 for (const auto& n: condset) {
272 res.add(this->BN().variable(n));
273 this->addEvidence(n, 0);
274 }
275
276 Instantiation inst(res);
277 for (inst.setFirstOut(iTarget); !inst.end(); inst.incOut(iTarget)) {
278 // inferring
279 for (const auto& n: condset)
280 this->chgEvidence(n, inst.val(this->BN().variable(n)));
281 this->makeInference();
282 // populate res
283 for (inst.setFirstIn(iTarget); !inst.end(); inst.incIn(iTarget)) {
284 res.set(inst, this->jointPosterior(targets)[inst]);
285 }
286 inst.setFirstIn(iTarget); // remove inst.end() flag
287 }
288
289 return res;
290 }
291
292 template < typename GUM_SCALAR >
294 const std::vector< std::string >& targets,
295 const std::vector< std::string >& evs) {
296 const auto& bn = this->BN();
297 return evidenceJointImpact(bn.nodeset(targets), bn.nodeset(evs));
298 }
299
300 template < typename GUM_SCALAR >
302 const auto& bn = this->BN();
303 const Size siz = targets.size();
304 if (siz <= 1) {
306 "jointMutualInformation needs at least 2 variables (targets=" << targets << ")");
307 }
308
309 this->eraseAllTargets();
310 this->eraseAllEvidence();
311 this->addJointTarget(targets);
312 this->makeInference();
313 const auto po = this->jointPosterior(targets);
314
315 gum::Instantiation caracteristic;
316 gum::Instantiation variables;
317 for (const auto nod: targets) {
318 const auto& var = bn.variable(nod);
319 auto pv = new gum::RangeVariable(var.name(), "", 0, 1);
320 caracteristic.add(*pv);
321 variables.add(var);
322 }
323
325
326 const GUM_SCALAR start = (siz % 2 == 0) ? GUM_SCALAR(-1.0) : GUM_SCALAR(1.0);
327 GUM_SCALAR sign;
328 GUM_SCALAR res = GUM_SCALAR(0.0);
329
330 caracteristic.setFirst();
331 for (caracteristic.inc(); !caracteristic.end(); caracteristic.inc()) {
332 sov.clear();
333 sign = start;
334 for (Idx i = 0; i < caracteristic.nbrDim(); i++) {
335 if (caracteristic.val(i) == 1) {
336 sign = -sign;
337 sov.insert(&variables.variable(i));
338 }
339 }
340 res += sign * po.sumIn(sov).entropy();
341 }
342
343 for (Idx i = 0; i < caracteristic.nbrDim(); i++) {
344 delete &caracteristic.variable(i);
345 }
346
347 return res;
348 }
349
350 template < typename GUM_SCALAR >
352 const std::vector< std::string >& targets) {
353 return jointMutualInformation(this->BN().ids(targets));
354 }
355
356} /* 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 setState_(const StateOfInference state) final
set the state of the inference engine and call the notification onStateChanged_ when necessary (i....
virtual void chgEvidence(NodeId id, const Idx val) final
change the value of an already existing hard evidence
virtual void addEvidence(NodeId id, const Idx val) final
adds a new hard evidence on node id
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 bool isInferenceDone() const noexcept final
returns whether the inference object is in a InferenceDone state
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.
void incIn(const Instantiation &i)
Operator increment for the variables in i.
bool end() const
Returns true if the Instantiation reached the end.
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 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.
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.
virtual 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 instanciation of targets and evs).
virtual void eraseAllMarginalTargets() final
Clear all the previously defined marginal targets.
virtual void onModelChanged_(const GraphicalModel *bn)
fired after a new Bayes net has been assigned to the engine
virtual void onAllJointTargetsErased_()=0
fired before a all the joint targets are removed
Set< NodeSet > _joint_targets_
the set of joint targets
virtual bool isJointTarget(const NodeSet &vars) const final
return true if target is a joint target.
virtual const Set< NodeSet > & jointTargets() const noexcept final
returns the list of joint targets
virtual void eraseAllTargets()
Clear all previously defined targets (marginal and joint targets).
virtual void onJointTargetErased_(const NodeSet &set)=0
fired before a joint target is removed
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 Tensor< GUM_SCALAR > & jointPosterior(const NodeSet &nodes) final
Compute the joint posterior of a set of nodes.
virtual Size nbrJointTargets() const noexcept final
returns the number of joint targets
virtual void eraseJointTarget(const NodeSet &joint_target) final
removes an existing joint target
GUM_SCALAR jointMutualInformation(const NodeSet &targets)
Mutual information between targets.
virtual const Tensor< GUM_SCALAR > & posterior(NodeId node)
Computes and returns the posterior of a node.
virtual void eraseAllTargets()
Clear all previously defined targets.
virtual const NodeSet & targets() const noexcept final
returns the list of marginal targets
MarginalTargetedInference(const IBayesNet< GUM_SCALAR > *bn)
default constructor
virtual bool isTarget(NodeId node) const final
return true if variable is a (marginal) target
virtual void onModelChanged_(const GraphicalModel *bn)
fired after a new Bayes net has been assigned to the engine
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:131
bool isStrictSubsetOf(const Set< Key > &s) const
Definition set_tpl.h:502
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:539
bool empty() const noexcept
Indicates whether the set is the empty set.
Definition set_tpl.h:642
void clear()
Removes all the elements, if any, from the set.
Definition set_tpl.h:338
Exception : a looked-for element could not be found.
#define GUM_ERROR(type, msg)
Definition exceptions.h:72
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.