aGrUM  0.14.2
jointTargetedInference_tpl.h
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2005 by Pierre-Henri WUILLEMIN et Christophe GONZALES *
3  * {prenom.nom}_at_lip6.fr *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the *
17  * Free Software Foundation, Inc., *
18  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19  ***************************************************************************/
27 
28 namespace gum {
29 
30 
31  // Default Constructor
32  template < typename GUM_SCALAR >
34  const IBayesNet< GUM_SCALAR >* bn) :
35  MarginalTargetedInference< GUM_SCALAR >(bn) {
36  // assign a BN if this has not been done before (due to virtual inheritance)
37  if (this->__bn == nullptr) {
39  }
40  GUM_CONSTRUCTOR(JointTargetedInference);
41  }
42 
43 
44  // Destructor
45  template < typename GUM_SCALAR >
47  GUM_DESTRUCTOR(JointTargetedInference);
48  }
49 
50 
51  // assigns a new BN to the inference engine
52  template < typename GUM_SCALAR >
54  const IBayesNet< GUM_SCALAR >* bn) {
57  __joint_targets.clear();
58  }
59 
60 
61  // ##############################################################################
62  // Targets
63  // ##############################################################################
64 
65  // return true if target is a nodeset target.
66  template < typename GUM_SCALAR >
68  const NodeSet& vars) const {
69  if (this->__bn == nullptr)
71  "No Bayes net has been assigned to the "
72  "inference algorithm");
73 
74  const auto& dag = this->__bn->dag();
75  for (const auto var : vars) {
76  if (!dag.exists(var)) {
77  GUM_ERROR(UndefinedElement, var << " is not a NodeId in the bn");
78  }
79  }
80 
81  return __joint_targets.contains(vars);
82  }
83 
84 
85  // Clear all previously defined single targets
86  template < typename GUM_SCALAR >
89  }
90 
91 
92  // Clear all previously defined targets (single targets and sets of targets)
93  template < typename GUM_SCALAR >
95  if (__joint_targets.size() > 0) {
96  // we already are in target mode. So no this->_setTargetedMode(); is needed
98  __joint_targets.clear();
99  this->__setState(
101  }
102  }
103 
104 
105  // Clear all previously defined targets (single and joint targets)
106  template < typename GUM_SCALAR >
110  }
111 
112 
113  // Add a set of nodes as a new target
114  template < typename GUM_SCALAR >
116  const NodeSet& joint_target) {
117  // check if the nodes in the target belong to the Bayesian network
118  if (this->__bn == nullptr)
120  "No Bayes net has been assigned to the "
121  "inference algorithm");
122 
123  const auto& dag = this->__bn->dag();
124  for (const auto node : joint_target) {
125  if (!dag.exists(node)) {
127  "at least one one in " << joint_target
128  << " does not belong to the bn");
129  }
130  }
131 
132  // check that the joint_target set does not contain the new target
133  if (__joint_targets.contains(joint_target)) return;
134 
135  // check if joint_target is a subset of an already existing target
136  for (const auto& target : __joint_targets) {
137  if (target.isSupersetOf(joint_target)) return;
138  }
139 
140  // check if joint_target is not a superset of an already existing target
141  // in this case, we need to remove old existing target
142  for (auto iter = __joint_targets.beginSafe();
143  iter != __joint_targets.endSafe();
144  ++iter) {
145  if (iter->isSubsetOf(joint_target)) eraseJointTarget(*iter);
146  }
147 
148  this->_setTargetedMode(); // does nothing if already in targeted mode
149  __joint_targets.insert(joint_target);
150  _onJointTargetAdded(joint_target);
151  this->__setState(
153  }
154 
155 
156  // removes an existing set target
157  template < typename GUM_SCALAR >
159  const NodeSet& joint_target) {
160  // check if the nodes in the target belong to the Bayesian network
161  if (this->__bn == nullptr)
163  "No Bayes net has been assigned to the "
164  "inference algorithm");
165 
166  const auto& dag = this->__bn->dag();
167  for (const auto node : joint_target) {
168  if (!dag.exists(node)) {
170  "at least one one in " << joint_target
171  << " does not belong to the bn");
172  }
173  }
174 
175  // check that the joint_target set does not contain the new target
176  if (__joint_targets.contains(joint_target)) {
177  // note that we have to be in target mode when we are here
178  // so, no this->_setTargetedMode(); is necessary
179  _onJointTargetErased(joint_target);
180  __joint_targets.erase(joint_target);
181  this->__setState(
183  }
184  }
185 
186 
188  template < typename GUM_SCALAR >
189  INLINE const Set< NodeSet >&
191  return __joint_targets;
192  }
193 
195  template < typename GUM_SCALAR >
197  noexcept {
198  return __joint_targets.size();
199  }
200 
201 
202  // ##############################################################################
203  // Inference
204  // ##############################################################################
205 
206  // Compute the posterior of a nodeset.
207  template < typename GUM_SCALAR >
210  // try to get the smallest set of targets that contains "nodes"
211  NodeSet set;
212  bool found_exact_target = false;
213 
214  if (__joint_targets.contains(nodes)) {
215  set = nodes;
216  found_exact_target = true;
217  } else {
218  for (const auto& target : __joint_targets) {
219  if (nodes.isSubsetOf(target)) {
220  set = target;
221  break;
222  }
223  }
224  }
225 
226  if (set.empty()) {
228  " no joint target containing " << nodes << "could be found");
229  }
230 
231  if (!this->isDone()) { this->makeInference(); }
232 
233  if (found_exact_target)
234  return _jointPosterior(nodes);
235  else
236  return _jointPosterior(nodes, set);
237  }
238 
239 
240  // Compute the posterior of a node
241  template < typename GUM_SCALAR >
244  if (this->isTarget(node))
246  else
247  return jointPosterior(NodeSet{node});
248  }
249 
250  // Compute the posterior of a node
251  template < typename GUM_SCALAR >
253  JointTargetedInference< GUM_SCALAR >::posterior(const std::string& nodeName) {
254  return posterior(this->BN().idFromName(nodeName));
255  }
256 
257  // ##############################################################################
258  // Entropy
259  // ##############################################################################
260 
261 
262  /* Mutual information between X and Y
263  * @see http://en.wikipedia.org/wiki/Mutual_information
264  *
265  * @warning Due to limitation of @joint, may not be able to compute this value
266  * @throw OperationNotAllowed in these cases
267  */
268  template < typename GUM_SCALAR >
270  Potential< GUM_SCALAR > pX, pY, *pXY = nullptr;
271  if (X == Y) {
272  GUM_ERROR(OperationNotAllowed, "Mutual Information I(X,Y) with X==Y");
273  }
274 
275  try {
276  // here use unnormalized joint posterior rather than just posterior
277  // to avoid saving the posterior in the cache of the inference engines
278  // like LazyPropagation or SahferShenoy.
279  pXY = this->_unnormalizedJointPosterior({X, Y});
280  pXY->normalize();
281  pX = pXY->margSumOut({&(this->BN().variable(Y))});
282  pY = pXY->margSumOut({&(this->BN().variable(X))});
283  } catch (...) {
284  if (pXY != nullptr) { delete pXY; }
285  throw;
286  }
287 
288  Instantiation i(*pXY);
289  auto res = (GUM_SCALAR)0;
290 
291  for (i.setFirst(); !i.end(); ++i) {
292  GUM_SCALAR vXY = (*pXY)[i];
293  GUM_SCALAR vX = pX[i];
294  GUM_SCALAR vY = pY[i];
295 
296  if (vXY > (GUM_SCALAR)0) {
297  if (vX == (GUM_SCALAR)0 || vY == (GUM_SCALAR)0) {
299  "Mutual Information (X,Y) with P(X)=0 or P(Y)=0 "
300  "and P(X,Y)>0");
301  }
302 
303  res += vXY * (log2(vXY) - log2(vX) - log2(vY));
304  }
305  }
306 
307  delete pXY;
308 
309  return res;
310  }
311 
312 
319  template < typename GUM_SCALAR >
321  return this->H(X) + this->H(Y) - 2 * I(X, Y);
322  }
323 
324 
325  template < typename GUM_SCALAR >
328  const NodeSet& targets, const NodeSet& evs) {
329  if (!(evs * targets).empty()) {
331  "Targets (" << targets << ") can not intersect evs (" << evs
332  << ").");
333  }
334  auto condset = this->BN().minimalCondSet(targets, evs);
335 
336  this->eraseAllTargets();
337  this->eraseAllEvidence();
338 
339  Instantiation iTarget;
341  for (const auto& target : targets) {
342  res.add(this->BN().variable(target));
343  iTarget.add(this->BN().variable(target));
344  }
345  this->addJointTarget(targets);
346 
347  for (const auto& n : condset) {
348  res.add(this->BN().variable(n));
349  this->addEvidence(n, 0);
350  }
351 
352  Instantiation inst(res);
353  for (inst.setFirstOut(iTarget); !inst.end(); inst.incOut(iTarget)) {
354  // inferring
355  for (const auto& n : condset)
356  this->chgEvidence(n, inst.val(this->BN().variable(n)));
357  this->makeInference();
358  // populate res
359  for (inst.setFirstIn(iTarget); !inst.end(); inst.incIn(iTarget)) {
360  res.set(inst, this->jointPosterior(targets)[inst]);
361  }
362  inst.setFirstIn(iTarget); // remove inst.end() flag
363  }
364 
365  return res;
366  }
367 
368  template < typename GUM_SCALAR >
371  const std::vector< std::string >& targets,
372  const std::vector< std::string >& evs) {
373  const auto& bn = this->BN();
374 
375  gum::NodeSet targetsId;
376  for (const auto& targetname : targets) {
377  targetsId.insert(bn.idFromName(targetname));
378  }
379 
380  gum::NodeSet evsId;
381  for (const auto& evname : evs) {
382  evsId.insert(bn.idFromName(evname));
383  }
384 
385  return evidenceJointImpact(targetsId, evsId);
386  }
387 
388 
389  template < typename GUM_SCALAR >
391  const NodeSet& targets) {
392  const auto& bn = this->BN();
393  const Size siz = targets.size();
394  if (siz <= 1) {
396  "jointMutualInformation needs at least 2 variables (targets="
397  << targets << ")");
398  }
399 
400  this->eraseAllTargets();
401  this->eraseAllEvidence();
402  this->addJointTarget(targets);
403  this->makeInference();
404  const auto po = this->jointPosterior(targets);
405 
406  gum::Instantiation caracteristic;
407  gum::Instantiation variables;
408  for (const auto nod : targets) {
409  const auto& var = bn.variable(nod);
410  auto pv = new gum::RangeVariable(var.name(), "", 0, 1);
411  caracteristic.add(*pv);
412  variables.add(var);
413  }
414 
416 
417  const GUM_SCALAR start = (siz % 2 == 0) ? GUM_SCALAR(-1.0) : GUM_SCALAR(1.0);
418  GUM_SCALAR sign;
419  GUM_SCALAR res = GUM_SCALAR(0.0);
420 
421  caracteristic.setFirst();
422  for (caracteristic.inc(); !caracteristic.end(); caracteristic.inc()) {
423  sov.clear();
424  sign = start;
425  for (Idx i = 0; i < caracteristic.nbrDim(); i++) {
426  if (caracteristic.val(i) == 1) {
427  sign = -sign;
428  sov.insert(&variables.variable(i));
429  }
430  }
431  res += sign * po.margSumIn(sov).entropy();
432  }
433 
434  for (Idx i = 0; i < caracteristic.nbrDim(); i++) {
435  delete &caracteristic.variable(i);
436  }
437 
438  return res;
439  }
440 
441  template < typename GUM_SCALAR >
443  const std::vector< std::string >& targets) {
444  const auto& bn = this->BN();
445 
446  gum::NodeSet targetsId;
447  for (const auto& targetname : targets) {
448  targetsId.insert(bn.idFromName(targetname));
449  }
450 
451  return jointMutualInformation(targetsId);
452  }
453 
454 } /* namespace gum */
void __setBayesNetDuringConstruction(const IBayesNet< GUM_SCALAR > *bn)
assigns a BN during the inference engine construction
virtual void eraseAllMarginalTargets() final
Clear all the previously defined marginal targets.
virtual void eraseAllTargets()
Clear all previously defined targets (marginal and joint targets)
aGrUM&#39;s Potential is a multi-dimensional array with tensor operators.
Definition: potential.h:57
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 __setState(const StateOfInference state) final
set the state of the inference engine and call the notification _onStateChanged when necessary (i...
virtual ~JointTargetedInference()
destructor
virtual void _onBayesNetChanged(const IBayesNet< GUM_SCALAR > *bn)
fired after a new Bayes net has been assigned to the engine
JointTargetedInference(const IBayesNet< GUM_SCALAR > *bn)
default constructor
GUM_SCALAR I(NodeId X, NodeId Y)
Mutual information between X and Y.
virtual GUM_SCALAR H(NodeId X) final
Entropy Compute Shanon&#39;s entropy of a node given the observation.
virtual void _onAllJointTargetsErased()=0
fired before a all the joint targets are removed
Idx nbrDim() const final
Returns the number of variables in the Instantiation.
This file contains the abstract inference class definition for computing (incrementally) joint poster...
virtual bool isJointTarget(const NodeSet &vars) const final
return true if target is a joint target.
const Potential< GUM_SCALAR > & normalize() const
normalisation of this do nothing if sum is 0
bool isSubsetOf(const Set< Key, OtherAlloc > &s) const
const IBayesNet< GUM_SCALAR > * __bn
the Bayes net on which we perform inferences
virtual bool isTarget(NodeId node) const final
return true if variable is a (marginal) target
<agrum/BN/inference/marginalTargetedInference.h>
virtual Potential< GUM_SCALAR > * _unnormalizedJointPosterior(const NodeSet &set)=0
returns a fresh unnormalized joint posterior of a given set of variables
Class representing the minimal interface for Bayesian Network.
Definition: IBayesNet.h:59
gum is the global namespace for all aGrUM entities
Definition: agrum.h:25
virtual void _onJointTargetAdded(const NodeSet &set)=0
fired after a new joint target is inserted
virtual void eraseJointTarget(const NodeSet &joint_target) final
removes an existing joint target
Idx val(Idx i) const
Returns the current value of the variable at position i.
Representation of a setA Set is a structure that contains arbitrary elements.
Definition: set.h:162
virtual void makeInference() final
perform the heavy computations needed to compute the targets&#39; posteriors
void incOut(const Instantiation &i)
Operator increment for the variables not in i.
<agrum/BN/inference/BayesNetInference.h>
void setFirstIn(const Instantiation &i)
Assign the first values in the Instantiation for the variables in i.
void inc()
Operator increment.
virtual void chgEvidence(NodeId id, const Idx val) final
change the value of an already existing hard evidence
virtual bool isDone() const noexcept final
returns whether the inference object is in a done state
virtual void addEvidence(NodeId id, const Idx val) final
adds a new hard evidence on node id
virtual void eraseAllJointTargets() final
Clear all previously defined joint targets.
<agrum/BN/inference/jointTargetedInference.h>
virtual const Set< NodeSet > & jointTargets() const noexcept final
returns the list of joint targets
virtual void eraseAllEvidence() final
removes all the evidence entered into the network
virtual const Potential< GUM_SCALAR > & jointPosterior(const NodeSet &nodes) final
Compute the joint posterior of a set of nodes.
virtual const Potential< GUM_SCALAR > & posterior(NodeId node)
Computes and returns the posterior of a node.
Potential< GUM_SCALAR > evidenceJointImpact(const NodeSet &targets, const NodeSet &evs)
Create a gum::Potential for P(joint targets|evs) (for all instanciation of targets and evs) ...
virtual void _onBayesNetChanged(const IBayesNet< GUM_SCALAR > *bn)
fired after a new Bayes net has been assigned to the engine
Defines a discrete random variable over an integer interval.
Definition: rangeVariable.h:51
void incIn(const Instantiation &i)
Operator increment for the variables in i.
Potential< GUM_SCALAR > margSumOut(const Set< const DiscreteVariable * > &del_vars) const
Projection using sum as operation (and implementation-optimized operations)
Class for assigning/browsing values to tuples of discrete variables.
Definition: instantiation.h:80
virtual const Potential< GUM_SCALAR > & _jointPosterior(const NodeSet &set)=0
asks derived classes for the joint posterior of a declared target set
virtual void add(const DiscreteVariable &v) final
Adds a new var to the variables of the multidimensional matrix.
virtual const Potential< GUM_SCALAR > & posterior(NodeId node) final
Computes and returns the posterior of a node.
virtual void set(const Instantiation &i, const GUM_SCALAR &value) const final
Default implementation of MultiDimContainer::set().
void setFirst()
Assign the first values to the tuple of the Instantiation.
Header of gumRangeVariable.
virtual Size nbrJointTargets() const noexcept final
returns the number of joint targets
Size Idx
Type for indexes.
Definition: types.h:50
virtual const NodeSet & targets() const noexcept final
returns the list of marginal targets
GUM_SCALAR jointMutualInformation(const NodeSet &targets)
Mutual information between targets.
virtual void eraseAllTargets()
Clear all previously defined targets.
void add(const DiscreteVariable &v) final
Adds a new variable in the Instantiation.
void clear()
Removes all the elements, if any, from the set.
Definition: set_tpl.h:372
GUM_SCALAR VI(NodeId X, NodeId Y)
Variation of information between X and Y.
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition: types.h:45
Size size() const noexcept
Returns the number of elements in the set.
Definition: set_tpl.h:698
virtual void _onJointTargetErased(const NodeSet &set)=0
fired before a joint target is removed
const DiscreteVariable & variable(Idx i) const final
Returns the variable at position i in the tuple.
Set< NodeSet > __joint_targets
the set of joint targets
virtual const IBayesNet< GUM_SCALAR > & BN() const final
Returns a constant reference over the IBayesNet referenced by this class.
Size NodeId
Type for node ids.
Definition: graphElements.h:97
void insert(const Key &k)
Inserts a new element into the set.
Definition: set_tpl.h:610
#define GUM_ERROR(type, msg)
Definition: exceptions.h:52
bool end() const
Returns true if the Instantiation reached the end.
void setFirstOut(const Instantiation &i)
Assign the first values in the Instantiation for the variables not in i.