aGrUM  0.20.2
a C++ library for (probabilistic) graphical models
jointTargetedMNInference_tpl.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright 2005-2020 Pierre-Henri WUILLEMIN(@LIP6) & Christophe GONZALES(@AMU)
4  * info_at_agrum_dot_org
5  *
6  * This library is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library. If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 
22 /**
23  * @file
24  * @brief Implementation of the non pure virtual methods of class
25  * JointTargetedMNInference.
26  */
27 #include <agrum/MN/inference/tools/jointTargetedMNInference.h>
28 #include <agrum/tools/variables/rangeVariable.h>
29 
30 namespace gum {
31 
32 
33  // Default Constructor
34  template < typename GUM_SCALAR >
35  JointTargetedMNInference< GUM_SCALAR >::JointTargetedMNInference(
36  const IMarkovNet< GUM_SCALAR >* mn) :
37  MarginalTargetedMNInference< GUM_SCALAR >(mn) {
38  // assign a MN if this has not been done before (due to virtual inheritance)
39  if (this->hasNoModel_()) {
40  MarkovNetInference< GUM_SCALAR >::setMarkovNetDuringConstruction__(mn);
41  }
42  GUM_CONSTRUCTOR(JointTargetedMNInference);
43  }
44 
45 
46  // Destructor
47  template < typename GUM_SCALAR >
50  }
51 
52 
53  // assigns a new MN to the inference engine
54  template < typename GUM_SCALAR >
56  const GraphicalModel* mn) {
60  }
61 
62 
63  // ##############################################################################
64  // Targets
65  // ##############################################################################
66 
67  // return true if target is a nodeset target.
68  template < typename GUM_SCALAR >
70  const NodeSet& vars) const {
71  if (this->hasNoModel_())
73  "No Markov net has been assigned to the "
74  "inference algorithm");
75 
76  const auto& gra = this->MN().graph();
77  for (const auto var: vars) {
78  if (!gra.exists(var)) {
80  var << " is not a NodeId in the Markov network");
81  }
82  }
83 
85  }
86 
87 
88  // Clear all previously defined single targets
89  template < typename GUM_SCALAR >
92  }
93 
94 
95  // Clear all previously defined targets (single targets and sets of targets)
96  template < typename GUM_SCALAR >
98  if (joint_targets__.size() > 0) {
99  // we already are in target mode. So no this->setTargetedMode_(); is needed
102  this->setState_(
104  }
105  }
106 
107 
108  // Clear all previously defined targets (single and joint targets)
109  template < typename GUM_SCALAR >
113  }
114 
115 
116  // Add a set of nodes as a new target
117  template < typename GUM_SCALAR >
119  const NodeSet& joint_target) {
120  // check if the nodes in the target belong to the Markov network
121  if (this->hasNoModel_())
123  "No Markov net has been assigned to the "
124  "inference algorithm");
125 
126  const auto& dag = this->MN().graph();
127  for (const auto node: joint_target) {
128  if (!dag.exists(node)) {
130  "at least one one in " << joint_target
131  << " does not belong to the mn");
132  }
133  }
134 
137 
138  // check if joint_target is a subset of an already existing target
139  for (const auto& target: joint_targets__) {
141  }
142 
143  // check if joint_target is not a superset of an already existing target
144  // in this case, we need to remove old existing target
145  for (auto iter = joint_targets__.beginSafe();
147  ++iter) {
149  }
150 
151  this->setTargetedMode_(); // does nothing if already in targeted mode
154  this->setState_(
156  }
157 
158 
159  // removes an existing set target
160  template < typename GUM_SCALAR >
162  const NodeSet& joint_target) {
163  // check if the nodes in the target belong to the Markov network
164  if (this->hasNoModel_())
166  "No Markov net has been assigned to the "
167  "inference algorithm");
168 
169  const auto& dag = this->MN().graph();
170  for (const auto node: joint_target) {
171  if (!dag.exists(node)) {
173  "at least one one in " << joint_target
174  << " does not belong to the mn");
175  }
176  }
177 
178  // check that the joint_target set does not contain the new target
180  // note that we have to be in target mode when we are here
181  // so, no this->setTargetedMode_(); is necessary
184  this->setState_(
186  }
187  }
188 
189 
190  /// returns the list of target sets
191  template < typename GUM_SCALAR >
192  INLINE const Set< NodeSet >&
194  return joint_targets__;
195  }
196 
197  /// returns the number of target sets
198  template < typename GUM_SCALAR >
199  INLINE Size
201  return joint_targets__.size();
202  }
203 
204 
205  // ##############################################################################
206  // Inference
207  // ##############################################################################
208 
209  // Compute the posterior of a nodeset.
210  template < typename GUM_SCALAR >
211  const Potential< GUM_SCALAR >&
213  // try to get the smallest set of targets that contains "nodes"
214  bool found_exact_target = false;
216 
218  found_exact_target = true;
219  } else {
221  if (super_target.empty()) {
223  "No joint target containing "
224  << nodes << " could be found among " << joint_targets__);
225  }
226  }
227 
228  if (!this->isInferenceDone()) { this->makeInference(); }
229 
230  if (found_exact_target)
231  return jointPosterior_(nodes);
232  else
234  }
235 
236 
237  // Compute the posterior of a node
238  template < typename GUM_SCALAR >
239  const Potential< GUM_SCALAR >&
241  if (this->isTarget(node))
243  else
244  return jointPosterior(NodeSet{node});
245  }
246 
247  // Compute the posterior of a node
248  template < typename GUM_SCALAR >
250  const std::string& nodeName) {
251  return posterior(this->MN().idFromName(nodeName));
252  }
253 
254  // ##############################################################################
255  // Entropy
256  // ##############################################################################
257  template < typename GUM_SCALAR >
259  const std::string& Yname) {
260  return I(this->MN().idFromName(Xname), this->MN().idFromName(Yname));
261  }
262 
263  template < typename GUM_SCALAR >
265  const std::string& Yname) {
266  return VI(this->MN().idFromName(Xname), this->MN().idFromName(Yname));
267  }
268 
269 
270  /* Mutual information between X and Y
271  * @see http://en.wikipedia.org/wiki/Mutual_information
272  *
273  * @warning Due to limitation of @joint, may not be able to compute this value
274  * @throw OperationNotAllowed in these cases
275  */
276  template < typename GUM_SCALAR >
278  Potential< GUM_SCALAR > pX, pY, *pXY = nullptr;
279  if (X == Y) {
280  GUM_ERROR(OperationNotAllowed, "Mutual Information I(X,Y) with X==Y");
281  }
282 
283  try {
284  // here use unnormalized joint posterior rather than just posterior
285  // to avoid saving the posterior in the cache of the inference engines
286  // like LazyPropagation or Shafer-Shenoy.
287  pXY = this->unnormalizedJointPosterior_({X, Y});
288  pXY->normalize();
289  pX = pXY->margSumOut({&(this->MN().variable(Y))});
290  pY = pXY->margSumOut({&(this->MN().variable(X))});
291  } catch (...) {
292  if (pXY != nullptr) { delete pXY; }
293  throw;
294  }
295 
296  Instantiation i(*pXY);
297  auto res = (GUM_SCALAR)0;
298 
299  for (i.setFirst(); !i.end(); ++i) {
300  GUM_SCALAR vXY = (*pXY)[i];
301  GUM_SCALAR vX = pX[i];
302  GUM_SCALAR vY = pY[i];
303 
304  if (vXY > (GUM_SCALAR)0) {
305  if (vX == (GUM_SCALAR)0 || vY == (GUM_SCALAR)0) {
307  "Mutual Information (X,Y) with P(X)=0 or P(Y)=0 "
308  "and P(X,Y)>0");
309  }
310 
311  res += vXY * (std::log2(vXY) - std::log2(vX) - std::log2(vY));
312  }
313  }
314 
315  delete pXY;
316 
317  return res;
318  }
319 
320 
321  /** Variation of information between X and Y
322  * @see http://en.wikipedia.org/wiki/Variation_of_information
323  *
324  * @warning Due to limitation of @joint, may not be able to compute this value
325  * @throw OperationNotAllowed in these cases
326  */
327  template < typename GUM_SCALAR >
329  NodeId Y) {
330  return this->H(X) + this->H(Y) - 2 * I(X, Y);
331  }
332 
333 
334  template < typename GUM_SCALAR >
337  const NodeSet& targets,
338  const NodeSet& evs) {
339  if (!(evs * targets).empty()) {
341  "Targets (" << targets << ") can not intersect evs (" << evs
342  << ").");
343  }
344  auto condset = this->MN().minimalCondSet(targets, evs);
345 
346  this->eraseAllTargets();
347  this->eraseAllEvidence();
348 
351  for (const auto& target: targets) {
352  res.add(this->MN().variable(target));
353  iTarget.add(this->MN().variable(target));
354  }
355  this->addJointTarget(targets);
356 
357  for (const auto& n: condset) {
358  res.add(this->MN().variable(n));
359  this->addEvidence(n, 0);
360  }
361 
364  // inferring
365  for (const auto& n: condset)
366  this->chgEvidence(n, inst.val(this->MN().variable(n)));
367  this->makeInference();
368  // populate res
371  }
372  inst.setFirstIn(iTarget); // remove inst.end() flag
373  }
374 
375  return res;
376  }
377 
378  template < typename GUM_SCALAR >
381  const std::vector< std::string >& targets,
382  const std::vector< std::string >& evs) {
383  const auto& mn = this->MN();
385  }
386 
387 
388  template < typename GUM_SCALAR >
390  const NodeSet& targets) {
391  const auto& mn = this->MN();
392  const Size siz = targets.size();
393  if (siz <= 1) {
395  "jointMutualInformation needs at least 2 variables (targets="
396  << targets << ")");
397  }
398 
399  this->eraseAllTargets();
400  this->eraseAllEvidence();
401  this->addJointTarget(targets);
402  this->makeInference();
403  const auto po = this->jointPosterior(targets);
404 
407  for (const auto nod: targets) {
408  const auto& var = mn.variable(nod);
409  auto pv = new gum::RangeVariable(var.name(), "", 0, 1);
410  caracteristic.add(*pv);
411  variables.add(var);
412  }
413 
414  Set< const DiscreteVariable* > sov;
415 
416  const GUM_SCALAR start = (siz % 2 == 0) ? GUM_SCALAR(-1.0) : GUM_SCALAR(1.0);
418  GUM_SCALAR res = GUM_SCALAR(0.0);
419 
422  sov.clear();
423  sign = start;
424  for (Idx i = 0; i < caracteristic.nbrDim(); i++) {
425  if (caracteristic.val(i) == 1) {
426  sign = -sign;
428  }
429  }
430  res += sign * po.margSumIn(sov).entropy();
431  }
432 
433  for (Idx i = 0; i < caracteristic.nbrDim(); i++) {
434  delete &caracteristic.variable(i);
435  }
436 
437  return res;
438  }
439 
440  template < typename GUM_SCALAR >
442  const std::vector< std::string >& targets) {
443  return jointMutualInformation(this->MN().nodeset(targets));
444  }
445 
446  template < typename GUM_SCALAR >
448  const NodeSet& vars) {
449  if (joint_targets__.contains(vars)) return true;
450 
451  return false;
452  }
453 
454  template < typename GUM_SCALAR >
456  const NodeSet& vars) {
457  for (const auto& target: joint_targets__)
458  if (vars.isProperSubsetOf(target)) return target;
459 
460  for (const auto& factor: this->MN().factors())
462 
463  return NodeSet();
464  }
465 
466 } /* namespace gum */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:669