aGrUM  0.13.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  ***************************************************************************/
26 
27 namespace gum {
28 
29 
30  // Default Constructor
31  template < typename GUM_SCALAR >
33  const IBayesNet< GUM_SCALAR >* bn) :
34  MarginalTargetedInference< GUM_SCALAR >(bn) {
35  // assign a BN if this has not been done before (due to virtual inheritance)
36  if (this->__bn == nullptr) {
38  }
39  GUM_CONSTRUCTOR(JointTargetedInference);
40  }
41 
42 
43  // Destructor
44  template < typename GUM_SCALAR >
46  GUM_DESTRUCTOR(JointTargetedInference);
47  }
48 
49 
50  // assigns a new BN to the inference engine
51  template < typename GUM_SCALAR >
53  const IBayesNet< GUM_SCALAR >* bn) {
56  __joint_targets.clear();
57  }
58 
59 
60  // ##############################################################################
61  // Targets
62  // ##############################################################################
63 
64  // return true if target is a nodeset target.
65  template < typename GUM_SCALAR >
67  const NodeSet& vars) const {
68  if (this->__bn == nullptr)
70  "No Bayes net has been assigned to the "
71  "inference algorithm");
72 
73  const auto& dag = this->__bn->dag();
74  for (const auto var : vars) {
75  if (!dag.exists(var)) {
76  GUM_ERROR(UndefinedElement, var << " is not a NodeId in the bn");
77  }
78  }
79 
80  return __joint_targets.contains(vars);
81  }
82 
83 
84  // Clear all previously defined single targets
85  template < typename GUM_SCALAR >
88  }
89 
90 
91  // Clear all previously defined targets (single targets and sets of targets)
92  template < typename GUM_SCALAR >
94  if (__joint_targets.size() > 0) {
95  // we already are in target mode. So no this->_setTargetedMode(); is needed
97  __joint_targets.clear();
98  this->__setState(
100  }
101  }
102 
103 
104  // Clear all previously defined targets (single and joint targets)
105  template < typename GUM_SCALAR >
109  }
110 
111 
112  // Add a set of nodes as a new target
113  template < typename GUM_SCALAR >
115  const NodeSet& joint_target) {
116  // check if the nodes in the target belong to the Bayesian network
117  if (this->__bn == nullptr)
119  "No Bayes net has been assigned to the "
120  "inference algorithm");
121 
122  const auto& dag = this->__bn->dag();
123  for (const auto node : joint_target) {
124  if (!dag.exists(node)) {
126  "at least one one in " << joint_target
127  << " does not belong to the bn");
128  }
129  }
130 
131  // check that the joint_target set does not contain the new target
132  if (__joint_targets.contains(joint_target)) return;
133 
134  // check if joint_target is a subset of an already existing target
135  for (const auto& target : __joint_targets) {
136  if (target.isSupersetOf(joint_target)) return;
137  }
138 
139  // check if joint_target is not a superset of an already existing target
140  // in this case, we need to remove old existing target
141  for (auto iter = __joint_targets.beginSafe();
142  iter != __joint_targets.endSafe();
143  ++iter) {
144  if (iter->isSubsetOf(joint_target)) eraseJointTarget(*iter);
145  }
146 
147  this->_setTargetedMode(); // does nothing if already in targeted mode
148  __joint_targets.insert(joint_target);
149  _onJointTargetAdded(joint_target);
150  this->__setState(
152  }
153 
154 
155  // removes an existing set target
156  template < typename GUM_SCALAR >
158  const NodeSet& joint_target) {
159  // check if the nodes in the target belong to the Bayesian network
160  if (this->__bn == nullptr)
162  "No Bayes net has been assigned to the "
163  "inference algorithm");
164 
165  const auto& dag = this->__bn->dag();
166  for (const auto node : joint_target) {
167  if (!dag.exists(node)) {
169  "at least one one in " << joint_target
170  << " does not belong to the bn");
171  }
172  }
173 
174  // check that the joint_target set does not contain the new target
175  if (__joint_targets.contains(joint_target)) {
176  // note that we have to be in target mode when we are here
177  // so, no this->_setTargetedMode(); is necessary
178  _onJointTargetErased(joint_target);
179  __joint_targets.erase(joint_target);
180  this->__setState(
182  }
183  }
184 
185 
187  template < typename GUM_SCALAR >
188  INLINE const Set< NodeSet >&
190  return __joint_targets;
191  }
192 
194  template < typename GUM_SCALAR >
196  noexcept {
197  return __joint_targets.size();
198  }
199 
200 
201  // ##############################################################################
202  // Inference
203  // ##############################################################################
204 
205  // Compute the posterior of a nodeset.
206  template < typename 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.isSubsetOf(target)) {
219  set = target;
220  break;
221  }
222  }
223  }
224 
225  if (set.empty()) {
227  " no joint target containing " << nodes << "could be found");
228  }
229 
230  if (!this->isDone()) { this->makeInference(); }
231 
232  if (found_exact_target)
233  return _jointPosterior(nodes);
234  else
235  return _jointPosterior(nodes, set);
236  }
237 
238 
239  // Compute the posterior of a node
240  template < typename GUM_SCALAR >
243  if (this->isTarget(node))
245  else
246  return jointPosterior(NodeSet{node});
247  }
248 
249  // Compute the posterior of a node
250  template < typename GUM_SCALAR >
252  JointTargetedInference< GUM_SCALAR >::posterior(const std::string& nodeName) {
253  return posterior(this->BN().idFromName(nodeName));
254  }
255 
256  // ##############################################################################
257  // Entropy
258  // ##############################################################################
259 
260 
261  /* Mutual information between X and Y
262  * @see http://en.wikipedia.org/wiki/Mutual_information
263  *
264  * @warning Due to limitation of @joint, may not be able to compute this value
265  * @throw OperationNotAllowed in these cases
266  */
267  template < typename GUM_SCALAR >
269  Potential< GUM_SCALAR > pX, pY, *pXY = nullptr;
270  if (X == Y) {
271  GUM_ERROR(OperationNotAllowed, "Mutual Information I(X,Y) with X==Y");
272  }
273 
274  try {
275  // here use unnormalized joint posterior rather than just posterior
276  // to avoid saving the posterior in the cache of the inference engines
277  // like LazyPropagation or SahferShenoy.
278  pXY = this->_unnormalizedJointPosterior({X, Y});
279  pXY->normalize();
280  pX = pXY->margSumOut({&(this->BN().variable(Y))});
281  pY = pXY->margSumOut({&(this->BN().variable(X))});
282  } catch (...) {
283  if (pXY != nullptr) {
284  delete pXY;
285  }
286  throw;
287  }
288 
289  Instantiation i(*pXY);
290  auto res = (GUM_SCALAR)0;
291 
292  for (i.setFirst(); !i.end(); ++i) {
293  GUM_SCALAR vXY = (*pXY)[i];
294  GUM_SCALAR vX = pX[i];
295  GUM_SCALAR vY = pY[i];
296 
297  if (vXY > (GUM_SCALAR)0) {
298  if (vX == (GUM_SCALAR)0 || vY == (GUM_SCALAR)0) {
300  "Mutual Information (X,Y) with P(X)=0 or P(Y)=0 "
301  "and P(X,Y)>0");
302  }
303 
304  res += vXY * (log2(vXY) - log2(vX) - log2(vY));
305  }
306  }
307 
308  delete pXY;
309 
310  return res;
311  }
312 
313 
320  template < typename GUM_SCALAR >
322  return this->H(X) + this->H(Y) - 2 * I(X, Y);
323  }
324 
325 
326  template < typename GUM_SCALAR >
329  const std::vector< NodeId >& targets, const std::vector< NodeId >& evs) {
330  NodeSet sotargets(Size(targets.size()));
331  for (const auto& e : targets)
332  sotargets << e;
333 
334  NodeSet soevs(Size(evs.size()));
335  for (const auto& e : evs)
336  soevs << e;
337 
338  if (!(soevs * sotargets).empty()) {
340  "Targets (" << targets << ") can not intersect evs (" << evs
341  << ").");
342  }
343  auto condset = this->BN().minimalCondSet(sotargets, soevs);
344 
345  this->eraseAllTargets();
346  this->eraseAllEvidence();
347 
348  Instantiation iTarget;
350  for (const auto& target : sotargets) {
351  res.add(this->BN().variable(target));
352  iTarget.add(this->BN().variable(target));
353  }
354  this->addJointTarget(sotargets);
355 
356  for (const auto& n : condset) {
357  res.add(this->BN().variable(n));
358  this->addEvidence(n, 0);
359  }
360 
361  Instantiation inst(res);
362  for (inst.setFirstOut(iTarget); !inst.end(); inst.incOut(iTarget)) {
363  // inferring
364  for (const auto& n : condset)
365  this->chgEvidence(n, inst.val(this->BN().variable(n)));
366  this->makeInference();
367  // populate res
368  for (inst.setFirstIn(iTarget); !inst.end(); inst.incIn(iTarget)) {
369  res.set(inst, this->jointPosterior(sotargets)[inst]);
370  }
371  inst.setFirstIn(iTarget); // remove inst.end() flag
372  }
373 
374  return res;
375  }
376 
377  template < typename GUM_SCALAR >
380  const std::vector< std::string >& targets,
381  const std::vector< std::string >& evs) {
382  const auto& bn = this->BN();
383  auto transf = [&bn](const std::string& s) { return bn.idFromName(s); };
384 
385  std::vector< NodeId > targetsId;
386  targetsId.reserve(targets.size());
387  std::transform(std::begin(targets),
388  std::end(targets),
389  std::back_inserter(targetsId),
390  transf);
391 
392  std::vector< NodeId > evsId;
393  evsId.reserve(evs.size());
394  std::transform(
395  std::begin(evs), std::end(evs), std::back_inserter(evsId), transf);
396 
397  return evidenceJointImpact(targetsId, evsId);
398  }
399 
400 } /* 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
unsigned long Size
In aGrUM, hashed values are unsigned long int.
Definition: types.h:50
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
unsigned int NodeId
Type for node ids.
Definition: graphElements.h:97
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 IBayesNet< GUM_SCALAR > * __bn
the Bayes net on which we perform inferences
bool isSubsetOf(const Set< Key, OtherAlloc > &s) const
virtual bool isTarget(NodeId node) const final
return true if variable is a (marginal) target
Potential< GUM_SCALAR > margSumOut(const Set< const DiscreteVariable * > &del_vars) const
Projection using sum as operation (and implementation-optimized operations)
<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
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.
const Potential< GUM_SCALAR > & normalize() const
normalisation of this do nothing if sum is 0
<agrum/BN/inference/BayesNetInference.h>
void setFirstIn(const Instantiation &i)
Assign the first values in the Instantiation for the variables in 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 eraseAllJointTargets() final
Clear all previously defined joint targets.
<agrum/BN/inference/jointTargetedInference.h>
Potential< GUM_SCALAR > evidenceJointImpact(const std::vector< NodeId > &targets, const std::vector< NodeId > &evs)
Create a gum::Potential for P(joint targets|evs) (for all instanciation of targets and evs) ...
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.
virtual void _onBayesNetChanged(const IBayesNet< GUM_SCALAR > *bn)
fired after a new Bayes net has been assigned to the engine
bool end() const
Returns true if the Instantiation reached the end.
void incIn(const Instantiation &i)
Operator increment for the variables in i.
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 const NodeSet & targets() const noexceptfinal
returns the list of marginal targets
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.
virtual Size nbrJointTargets() const noexceptfinal
returns the number of joint targets
virtual void eraseAllTargets()
Clear all previously defined targets.
void add(const DiscreteVariable &v) final
Adds a new variable in the Instantiation.
GUM_SCALAR VI(NodeId X, NodeId Y)
Variation of information between X and Y.
virtual const Set< NodeSet > & jointTargets() const noexceptfinal
returns the list of joint targets
virtual void _onJointTargetErased(const NodeSet &set)=0
fired before a joint target is removed
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.
virtual bool isDone() const noexceptfinal
returns whether the inference object is in a done state
Idx val(Idx i) const
Returns the current value of the variable at position i.
#define GUM_ERROR(type, msg)
Definition: exceptions.h:66
void setFirstOut(const Instantiation &i)
Assign the first values in the Instantiation for the variables not in i.