aGrUM  0.20.2
a C++ library for (probabilistic) graphical models
marginalTargetedMNInference_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 generic class for the computation of
25  * (possibly incrementally) marginal posteriors
26  */
27 #include <iterator>
28 
29 namespace gum {
30 
31 
32  // Default Constructor
33  template < typename GUM_SCALAR >
34  MarginalTargetedMNInference< GUM_SCALAR >::MarginalTargetedMNInference(
35  const IMarkovNet< GUM_SCALAR >* mn) :
36  MarkovNetInference< GUM_SCALAR >(mn) {
37  // assign a MN if this has not been done before (due to virtual inheritance)
38  if (this->hasNoModel_()) {
39  MarkovNetInference< GUM_SCALAR >::setMarkovNetDuringConstruction__(mn);
40  }
41 
42  // sets all the nodes as targets
43  if (mn != nullptr) {
44  targeted_mode__ = false;
45  targets__ = mn->graph().asNodeSet();
46  }
47 
48  GUM_CONSTRUCTOR(MarginalTargetedMNInference);
49  }
50 
51 
52  // Destructor
53  template < typename GUM_SCALAR >
54  MarginalTargetedMNInference< GUM_SCALAR >::~MarginalTargetedMNInference() {
55  GUM_DESTRUCTOR(MarginalTargetedMNInference);
56  }
57 
58 
59  // fired when a new MN is assigned to the inference engine
60  template < typename GUM_SCALAR >
61  void MarginalTargetedMNInference< GUM_SCALAR >::onModelChanged_(
62  const GraphicalModel* mn) {
63  targeted_mode__ = true;
64  setAllMarginalTargets__();
65  }
66 
67 
68  // ##############################################################################
69  // Targets
70  // ##############################################################################
71 
72  // return true if variable is a target
73  template < typename GUM_SCALAR >
74  INLINE bool
75  MarginalTargetedMNInference< GUM_SCALAR >::isTarget(NodeId node) const {
76  // check that the variable belongs to the mn
77  if (this->hasNoModel_())
78  GUM_ERROR(NullElement,
79  "No Markov net has been assigned to the "
80  "inference algorithm");
81  if (!this->MN().graph().exists(node)) {
82  GUM_ERROR(UndefinedElement,
83  node << " is not a NodeId in the Markov network");
84  }
85 
86  return targets__.contains(node);
87  }
88 
89  // Add a single target to the list of targets
90  template < typename GUM_SCALAR >
91  INLINE bool MarginalTargetedMNInference< GUM_SCALAR >::isTarget(
92  const std::string& nodeName) const {
93  return isTarget(this->MN().idFromName(nodeName));
94  }
95 
96 
97  // Clear all previously defined targets (single targets and sets of targets)
98  template < typename GUM_SCALAR >
99  INLINE void MarginalTargetedMNInference< GUM_SCALAR >::eraseAllTargets() {
100  onAllMarginalTargetsErased_();
101 
102  targets__.clear();
103  setTargetedMode_(); // does nothing if already in targeted mode
104 
105  this->setState_(
106  MarkovNetInference< GUM_SCALAR >::StateOfInference::OutdatedStructure);
107  }
108 
109 
110  // Add a single target to the list of targets
111  template < typename GUM_SCALAR >
112  void MarginalTargetedMNInference< GUM_SCALAR >::addTarget(NodeId target) {
113  // check if the node belongs to the Markov network
114  if (this->hasNoModel_())
115  GUM_ERROR(NullElement,
116  "No Markov net has been assigned to the "
117  "inference algorithm");
118 
119  if (!this->MN().graph().exists(target)) {
120  GUM_ERROR(UndefinedElement,
121  target << " is not a NodeId in the Markov network");
122  }
123 
124  setTargetedMode_(); // does nothing if already in targeted mode
125  // add the new target
126  if (!targets__.contains(target)) {
127  targets__.insert(target);
128  onMarginalTargetAdded_(target);
129  this->setState_(
130  MarkovNetInference< GUM_SCALAR >::StateOfInference::OutdatedStructure);
131  }
132  }
133 
134 
135  // Add all nodes as targets
136  template < typename GUM_SCALAR >
137  void MarginalTargetedMNInference< GUM_SCALAR >::addAllTargets() {
138  // check if the node belongs to the Markov network
139  if (this->hasNoModel_())
140  GUM_ERROR(NullElement,
141  "No Markov net has been assigned to the "
142  "inference algorithm");
143 
144 
145  setTargetedMode_(); // does nothing if already in targeted mode
146  for (const auto target: this->MN().graph()) {
147  if (!targets__.contains(target)) {
148  targets__.insert(target);
149  onMarginalTargetAdded_(target);
150  this->setState_(
151  MarkovNetInference< GUM_SCALAR >::StateOfInference::OutdatedStructure);
152  }
153  }
154  }
155 
156 
157  // Add a single target to the list of targets
158  template < typename GUM_SCALAR >
159  void MarginalTargetedMNInference< GUM_SCALAR >::addTarget(
160  const std::string& nodeName) {
161  // check if the node belongs to the Markov network
162  if (this->hasNoModel_())
163  GUM_ERROR(NullElement,
164  "No Markov net has been assigned to the "
165  "inference algorithm");
166 
167  addTarget(this->MN().idFromName(nodeName));
168  }
169 
170 
171  // removes an existing target
172  template < typename GUM_SCALAR >
173  void MarginalTargetedMNInference< GUM_SCALAR >::eraseTarget(NodeId target) {
174  // check if the node belongs to the Markov network
175  if (this->hasNoModel_())
176  GUM_ERROR(NullElement,
177  "No Markov net has been assigned to the "
178  "inference algorithm");
179 
180  if (!this->MN().graph().exists(target)) {
181  GUM_ERROR(UndefinedElement,
182  target << " is not a NodeId in the Markov network");
183  }
184 
185 
186  if (targets__.contains(target)) {
187  targeted_mode__ = true; // we do not use setTargetedMode_ because we do not
188  // want to clear the targets
189  onMarginalTargetErased_(target);
190  targets__.erase(target);
191  this->setState_(
192  MarkovNetInference< GUM_SCALAR >::StateOfInference::OutdatedStructure);
193  }
194  }
195 
196 
197  // Add a single target to the list of targets
198  template < typename GUM_SCALAR >
199  void MarginalTargetedMNInference< GUM_SCALAR >::eraseTarget(
200  const std::string& nodeName) {
201  // check if the node belongs to the Markov network
202  if (this->hasNoModel_())
203  GUM_ERROR(NullElement,
204  "No Markov net has been assigned to the "
205  "inference algorithm");
206 
207  eraseTarget(this->MN().idFromName(nodeName));
208  }
209 
210 
211  // returns the list of single targets
212  template < typename GUM_SCALAR >
213  INLINE const NodeSet&
214  MarginalTargetedMNInference< GUM_SCALAR >::targets() const noexcept {
215  return targets__;
216  }
217 
218  // returns the list of single targets
219  template < typename GUM_SCALAR >
220  INLINE const Size
221  MarginalTargetedMNInference< GUM_SCALAR >::nbrTargets() const noexcept {
222  return targets__.size();
223  }
224 
225 
226  /// sets all the nodes of the Markov net as targets
227  template < typename GUM_SCALAR >
228  void MarginalTargetedMNInference< GUM_SCALAR >::setAllMarginalTargets__() {
229  targets__.clear();
230  if (!this->hasNoModel_()) {
231  targets__ = this->MN().graph().asNodeSet();
232  onAllMarginalTargetsAdded_();
233  }
234  }
235 
236 
237  // ##############################################################################
238  // Inference
239  // ##############################################################################
240 
241  // Compute the posterior of a node.
242  template < typename GUM_SCALAR >
243  const Potential< GUM_SCALAR >&
244  MarginalTargetedMNInference< GUM_SCALAR >::posterior(NodeId node) {
245  if (this->hardEvidenceNodes().contains(node)) {
246  return *(this->evidence()[node]);
247  }
248 
249  if (!isTarget(node)) {
250  // throws UndefinedElement if var is not a target
251  GUM_ERROR(UndefinedElement, node << " is not a target node");
252  }
253 
254  if (!this->isInferenceDone()) { this->makeInference(); }
255 
256  return posterior_(node);
257  }
258 
259  // Compute the posterior of a node.
260  template < typename GUM_SCALAR >
261  const Potential< GUM_SCALAR >&
262  MarginalTargetedMNInference< GUM_SCALAR >::posterior(
263  const std::string& nodeName) {
264  return posterior(this->MN().idFromName(nodeName));
265  }
266 
267  /* Entropy
268  * Compute Shanon's entropy of a node given the observation
269  */
270  template < typename GUM_SCALAR >
271  INLINE GUM_SCALAR MarginalTargetedMNInference< GUM_SCALAR >::H(NodeId X) {
272  return posterior(X).entropy();
273  }
274 
275  /* Entropy
276  * Compute Shanon's entropy of a node given the observation
277  */
278  template < typename GUM_SCALAR >
279  INLINE GUM_SCALAR
280  MarginalTargetedMNInference< GUM_SCALAR >::H(const std::string& nodeName) {
281  return H(this->MN().idFromName(nodeName));
282  }
283 
284 
285  template < typename GUM_SCALAR >
286  Potential< GUM_SCALAR >
287  MarginalTargetedMNInference< GUM_SCALAR >::evidenceImpact(
288  NodeId target,
289  const NodeSet& evs) {
290  const auto& vtarget = this->MN().variable(target);
291 
292  if (evs.contains(target)) {
293  GUM_ERROR(InvalidArgument,
294  "Target <" << vtarget.name() << "> (" << target
295  << ") can not be in evs (" << evs << ").");
296  }
297  auto condset = this->MN().minimalCondSet(target, evs);
298 
299  Potential< GUM_SCALAR > res;
300  this->eraseAllTargets();
301  this->eraseAllEvidence();
302  res.add(this->MN().variable(target));
303  this->addTarget(target);
304  for (const auto& n: condset) {
305  res.add(this->MN().variable(n));
306  this->addEvidence(n, 0);
307  }
308 
309  Instantiation inst(res);
310  for (inst.setFirst(); !inst.end(); inst.incNotVar(vtarget)) {
311  // inferring
312  for (const auto& n: condset)
313  this->chgEvidence(n, inst.val(this->MN().variable(n)));
314  this->makeInference();
315  // populate res
316  for (inst.setFirstVar(vtarget); !inst.end(); inst.incVar(vtarget)) {
317  res.set(inst, this->posterior(target)[inst]);
318  }
319  inst.setFirstVar(vtarget); // remove inst.end() flag
320  }
321 
322  return res;
323  }
324 
325 
326  template < typename GUM_SCALAR >
327  Potential< GUM_SCALAR >
328  MarginalTargetedMNInference< GUM_SCALAR >::evidenceImpact(
329  const std::string& target,
330  const std::vector< std::string >& evs) {
331  const auto& mn = this->MN();
332  return evidenceImpact(mn.idFromName(target), mn.nodeset(evs));
333  }
334 
335 
336  template < typename GUM_SCALAR >
337  INLINE bool MarginalTargetedMNInference< GUM_SCALAR >::isTargetedMode_() const {
338  return targeted_mode__;
339  }
340  template < typename GUM_SCALAR >
341  INLINE void MarginalTargetedMNInference< GUM_SCALAR >::setTargetedMode_() {
342  if (!targeted_mode__) {
343  targets__.clear();
344  targeted_mode__ = true;
345  }
346  }
347 } /* namespace gum */