aGrUM  0.20.3
a C++ library for (probabilistic) graphical models
potential_tpl.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright (c) 2005-2021 by Pierre-Henri WUILLEMIN(@LIP6) et Christophe GONZALES(@AMU)
4  * (@AMU) 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 Potential class.
25  * @author Pierre-Henri WUILLEMIN(@LIP6) & Christophe GONZALES(@AMU)
26  */
27 
28 #include <agrum/agrum.h>
29 #include <agrum/tools/core/math/math_utils.h>
30 #include <agrum/tools/multidim/potential.h>
31 
32 namespace gum {
33 
34  // Default constructor: creates an empty null dimensional matrix
35  // choose a MultiDimArray<> as decorated implementation
36  template < typename GUM_SCALAR >
37  INLINE Potential< GUM_SCALAR >::Potential() :
40  }
41 
42  // constructor using aContent as content
43  template < typename GUM_SCALAR >
46  // for debugging purposes
48  }
49  // copy constructor
50  template < typename GUM_SCALAR >
53  static_cast< MultiDimImplementation< GUM_SCALAR >* >(src.content()->newFactory()),
54  *(src.content())) {
56  // GUM_CONS_CPY not here because in called Potential
57  // GUM_CONS_CPY( Potential );
58  }
59 
60  /// move constructor
61  template < typename GUM_SCALAR >
65  }
66 
67  // complex copy constructor : we choose the implementation
68  template < typename GUM_SCALAR >
70  const MultiDimContainer< GUM_SCALAR >& src) :
72  // for debugging purposes
74 
75  if (!src.empty()) {
76  this->beginMultipleChanges();
77 
78  for (Idx i = 0; i < src.variablesSequence().size(); i++) {
79  this->add(*(src.variablesSequence()[i]));
80  }
81 
82  this->endMultipleChanges();
83  this->content()->copyFrom(*src.content());
84  }
85  }
86 
87  // operator = copy
88  template < typename GUM_SCALAR >
91  if (&src == this) return *this;
93  return *this;
94  }
95 
96  // operator = move
97  template < typename GUM_SCALAR >
100  if (&src == this) return *this;
103  return *this;
104  }
105 
106  // destructor
107 
108  template < typename GUM_SCALAR >
110  // for debugging purposes
112  }
113 
114  template < typename GUM_SCALAR >
116  return new Potential< GUM_SCALAR >(
117  static_cast< MultiDimImplementation< GUM_SCALAR >* >(this->content()->newFactory()));
118  }
119 
120  // sum of all elements in this
121  template < typename GUM_SCALAR >
123  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
124  return this->empty_value_;
125  }
126  return gum::projectSum(*this->content());
127  }
128  // product of all elements in this
129  template < typename GUM_SCALAR >
131  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
132  return this->empty_value_;
133  }
134  return gum::projectProduct(*this->content());
135  }
136  // max of all elements in this
137  template < typename GUM_SCALAR >
139  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
140  return this->empty_value_;
141  }
142  return gum::projectMax(*this->content());
143  }
144  // min of all elements in this
145  template < typename GUM_SCALAR >
147  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
148  return this->empty_value_;
149  }
150  return gum::projectMin(*this->content());
151  }
152 
153  // max of all non one elements in this
154  // warning can return 1 if no other value than 1 ...
155  template < typename GUM_SCALAR >
157  GUM_SCALAR res;
158 
159  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
160  res = this->empty_value_;
161  } else {
162  res = this->reduce(
163  [](GUM_SCALAR z, GUM_SCALAR p) {
164  return (p == static_cast< GUM_SCALAR >(1)) ? z
165  : (z == static_cast< GUM_SCALAR >(1)) ? p
166  : (p > z ? p : z);
167  },
168  static_cast< GUM_SCALAR >(1));
169  }
170 
171  return res;
172  }
173 
174  // min of all non zero elements in this
175  // warning can return 0 if no other value than 0 ...
176  template < typename GUM_SCALAR >
178  GUM_SCALAR res;
179 
180  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
181  res = this->empty_value_;
182  } else {
183  res = this->reduce(
184  [](GUM_SCALAR z, GUM_SCALAR p) {
185  return (p == static_cast< GUM_SCALAR >(0)) ? z
186  : (z == static_cast< GUM_SCALAR >(0)) ? p
187  : (p < z ? p : z);
188  },
189  static_cast< GUM_SCALAR >(0));
190  }
191  return res;
192  }
193 
194  // entropy of this
195  template < typename GUM_SCALAR >
197  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
198  return static_cast< GUM_SCALAR >(0);
199  }
200 
201  return this->reduce(
202  [](GUM_SCALAR z, GUM_SCALAR p) { return (p == 0.0) ? z : (z - p * std::log2(p)); },
203  0.0);
204  }
205 
206  template < typename GUM_SCALAR >
207  INLINE const Potential< GUM_SCALAR >&
209  this->populate(data);
210  return *this;
211  }
212 
213  template < typename GUM_SCALAR >
214  INLINE const Potential< GUM_SCALAR >&
216  this->fill(val);
217  return *this;
218  }
219 
220  template < typename GUM_SCALAR >
221  INLINE const Potential< GUM_SCALAR >&
223  if (src.domainSize() != this->domainSize()) {
224  GUM_ERROR(InvalidArgument, "Potential to copy has not the same dimension.")
225  }
226  gum::Set< std::string > son; // set of names
227  for (const auto& v: src.variablesSequence()) {
228  son.insert(v->name());
229  }
230  for (const auto& v: this->variablesSequence()) {
231  if (!son.contains(v->name())) {
233  "Variable <" << v->name() << "> not present in src (" << son << ").");
234  }
235  // we check size, labels and order of labels in the same time
236  if (v->toString() != src.variable(v->name()).toString()) {
237  GUM_ERROR(InvalidArgument, "Variables <" << v->name() << "> are not identical.")
238  }
239  }
240 
242  Instantiation Idst(*this);
243  for (Isrc.setFirst(); !Isrc.end(); ++Isrc) {
244  for (Idx i = 0; i < this->nbrDim(); i++) {
246  }
247  this->set(Idst, src.get(Isrc));
248  }
249 
250  return *this;
251  }
252 
253  template < typename GUM_SCALAR >
254  INLINE const Potential< GUM_SCALAR >&
256  const std::vector< std::string >& mapSrc) const {
257  if (src.nbrDim() != this->nbrDim()) {
258  GUM_ERROR(InvalidArgument, "Potential to copy has not the same dimension.")
259  }
260  if (src.nbrDim() != mapSrc.size()) {
261  GUM_ERROR(InvalidArgument, "Potential and vector have not the same dimension.")
262  }
264  for (Idx i = 0; i < src.nbrDim(); i++) {
265  if (src.variable(mapSrc[i]).domainSize() != this->variable(i).domainSize()) {
267  "Variables " << mapSrc[i] << " (in the argument) and " << this->variable(i).name()
268  << " have not the same dimension.");
269  } else {
271  }
272  }
273  Instantiation Idst(*this);
274  for (Isrc.setFirst(); !Isrc.end(); ++Isrc, ++Idst) {
275  this->set(Idst, src.get(Isrc));
276  }
277 
278  return *this;
279  }
280 
281  template < typename GUM_SCALAR >
283  this->apply([](GUM_SCALAR x) { return x * x; });
284  return *this;
285  }
286 
287  template < typename GUM_SCALAR >
289  this->apply([](GUM_SCALAR x) { return std::log2(x); });
290  return *this;
291  }
292 
293  template < typename GUM_SCALAR >
295  if (this->nbrDim() != p.nbrDim())
297  "BNdistance between potentials with different numbers of dimensions");
298  for (const auto var: p.variablesSequence()) {
299  if (!this->contains(*var))
300  GUM_ERROR(InvalidArgument, "A variable in the argument does not belong to the potential.")
301  }
302  for (const auto var: this->variablesSequence()) {
303  if (!p.contains(*var))
304  GUM_ERROR(InvalidArgument, "A variable does not belong to the argument.")
305  }
306 
307  Instantiation inst(*this);
308  GUM_SCALAR res = static_cast< GUM_SCALAR >(0);
309  for (inst.setFirst(); !inst.end(); inst.inc()) {
310  GUM_SCALAR x = this->get(inst);
311  GUM_SCALAR y = p.get(inst);
312  if (static_cast< GUM_SCALAR >(0) == x) // 0*log(0/y)=0
313  continue;
314 
315  if (static_cast< GUM_SCALAR >(0) == y)
316  // we know that x!=0;
317  GUM_ERROR(FatalError, "The argument has a 0 at " << inst << " while the potential has not.")
318 
319  res += x * std::log2(x / y);
320  }
321  return res;
322  }
323 
324  template < typename GUM_SCALAR >
326  this->apply([](GUM_SCALAR x) {
327  if (x >= 0)
328  return x;
329  else
330  return -x;
331  });
332  return *this;
333  }
334 
335  // normalisation of this
336  // do nothing is sum is 0
337  template < typename GUM_SCALAR >
339  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
340  if (this->empty_value_ != static_cast< GUM_SCALAR >(0))
341  this->empty_value_ = static_cast< GUM_SCALAR >(1.0);
342  } else {
343  GUM_SCALAR s = sum();
344 
345  if (s != (GUM_SCALAR)0) {
346  this->apply([s](GUM_SCALAR x) { return x / s; });
347  }
348  }
349  return *this;
350  }
351 
352  template < typename GUM_SCALAR >
353  INLINE const Potential< GUM_SCALAR >&
355  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
356  if (this->empty_value_ != static_cast< GUM_SCALAR >(0)) {
357  this->empty_value_ = static_cast< GUM_SCALAR >(1.0);
358  } else {
359  GUM_ERROR(FatalError, "Normalization for a potential that sum to 0 in " << *this)
360  }
361  } else {
362  if (varId >= this->nbrDim()) {
363  GUM_ERROR(FatalError, varId << " is not a position for " << *this)
364  }
365  Instantiation inst(*this);
366  const auto& v = this->variable(varId);
367 
368  for (inst.setFirst(); !inst.end(); inst.incNotVar(v)) {
369  GUM_SCALAR s = (GUM_SCALAR)0.0;
370  for (inst.setFirstVar(v); !inst.end(); inst.incVar(v))
371  s += this->get(inst);
372  if (s == (GUM_SCALAR)0.0) {
373  GUM_ERROR(FatalError, "Normalization for a potential that sum to 0 in " << *this)
374  }
375  if (s != (GUM_SCALAR)1.0) {
376  for (inst.setFirstVar(v); !inst.end(); inst.incVar(v))
377  this->set(inst, this->get(inst) / s);
378  }
379  inst.setFirstVar(v); // to remove inst.end()
380  }
381  }
382  return *this;
383  }
384 
385  template < typename GUM_SCALAR >
387  this->apply([v](GUM_SCALAR x) { return x * v; });
388  return *this;
389  }
390 
391  template < typename GUM_SCALAR >
393  this->apply([v](GUM_SCALAR x) { return x + v; });
394  return *this;
395  }
396 
397  template < typename GUM_SCALAR >
399  this->apply([](GUM_SCALAR x) { return 1 / x; });
400  return *this;
401  }
402 
403  template < typename GUM_SCALAR >
406  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
407  return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
408  }
409  return Potential< GUM_SCALAR >(gum::projectSum(*this->content(), del_vars));
410  }
411 
412  template < typename GUM_SCALAR >
415  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
416  return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
417  }
418  return Potential< GUM_SCALAR >(gum::projectProduct(*this->content(), del_vars));
419  }
420 
421  template < typename GUM_SCALAR >
424  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
425  return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
426  }
427  return Potential< GUM_SCALAR >(gum::projectMin(*this->content(), del_vars));
428  }
429 
430  template < typename GUM_SCALAR >
433  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
434  return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
435  }
436  return Potential< GUM_SCALAR >(gum::projectMax(*this->content(), del_vars));
437  }
438  template < typename GUM_SCALAR >
441  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
442  return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
443  }
445  }
446 
447  template < typename GUM_SCALAR >
450  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
451  return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
452  }
453  return Potential< GUM_SCALAR >(
455  }
456 
457  template < typename GUM_SCALAR >
460  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
461  return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
462  }
464  }
465 
466  template < typename GUM_SCALAR >
469  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
470  return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
471  }
473  }
474 
475  template < typename GUM_SCALAR >
477  auto p = Potential< GUM_SCALAR >(*this);
478  p.apply([](GUM_SCALAR x) {
479  if (x != static_cast< GUM_SCALAR >(0))
480  return static_cast< GUM_SCALAR >(1);
481  else
482  return static_cast< GUM_SCALAR >(0);
483  });
484  return p;
485  }
486 
487  template < typename GUM_SCALAR >
488  Set< const DiscreteVariable* >
490  Set< const DiscreteVariable* > cplt;
491 
492  for (const auto x: this->variablesSequence())
493  if (!vars.contains(x)) cplt.insert(x);
494 
495  return cplt;
496  }
497 
498  template < typename GUM_SCALAR >
501  if (vars.size() != this->nbrDim())
503  "The vector contains " << vars.size() << " variables instead of " << this->nbrDim()
504  << ".");
505  for (const auto var: vars) {
506  if (!this->contains(*var))
507  GUM_ERROR(InvalidArgument, "A variable in the vector does not belong to the potential.")
508  }
509 
512  for (const auto var: vars)
513  p.add(*var);
515  p.copyFrom(*this, nullptr); // copy *this in p using the same order
516 
517  return p;
518  }
519 
520  template < typename GUM_SCALAR >
523  std::vector< const DiscreteVariable* > res;
524 
526  for (gum::Idx i = 0; i < this->nbrDim(); i++)
527  namesToVars.insert(this->variable(i).name(), &(this->variable(i)));
528 
529  for (const auto& name: vars) {
530  if (!namesToVars.exists(name)) {
532  "'" << name << "' is a not a name of a variable in this potential");
533  }
535  }
536  return reorganize(res);
537  }
538 
539  template < typename GUM_SCALAR >
541  if (!this->contains(*var)) {
542  GUM_ERROR(InvalidArgument, "The variable to put first does not belong to the potential")
543  }
544  if (&(this->variable(0)) == var) return Potential< GUM_SCALAR >(*this);
545 
546  std::vector< const DiscreteVariable* > vars;
547  vars.push_back(var);
548  for (Idx i = 0; i < this->nbrDim(); i++)
549  if (&(this->variable(i)) != var) vars.push_back(&(this->variable(i)));
550 
551  return this->reorganize(vars);
552  }
553 
554  template < typename GUM_SCALAR >
556  const DiscreteVariable* var = nullptr;
557 
558  for (gum::Idx i = 0; i < this->nbrDim(); i++)
559  if (this->variable(i).name() == varname) {
560  var = &(this->variable(i));
561  break;
562  }
563  if (var == nullptr)
565  "The variable '" << varname << "' to put first does not belong to the potential");
566  return this->putFirst(var);
567  }
568 
569  template < typename GUM_SCALAR >
572  p.extractFrom(*this, inst);
573 
574  return p;
575  }
576 
577  template < typename GUM_SCALAR >
578  Idx Potential< GUM_SCALAR >::draw() const {
579  if (this->nbrDim() != 1) {
580  GUM_ERROR(FatalError, "To draw from a potential, the dimension must be 1")
581  }
582 
583  GUM_SCALAR r = static_cast< GUM_SCALAR >(randomProba());
584  Instantiation Ip(*this);
585  for (Ip.setFirst(); !Ip.end(); Ip.inc()) {
586  r -= this->get(Ip);
587  if (r <= 0) return Ip.val(0);
588  }
589  return this->variable(0).domainSize() - 1;
590  }
591 
592  template < typename GUM_SCALAR >
594  out << array.toString();
595  return out;
596  }
597 
598  // argmax of all elements in this
599  template < typename GUM_SCALAR >
601  Instantiation I(*this);
602  Set< Instantiation > res;
603 
604  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) { return res; }
605  for (I.setFirst(); !I.end(); ++I) {
606  if (this->get(I) == v) res.insert(I);
607  }
608  return res;
609  }
610  // argmax of all elements in this
611  template < typename GUM_SCALAR >
613  return findAll(max());
614  }
615  // argmin of all elements in this
616  template < typename GUM_SCALAR >
618  return findAll(min());
619  }
620 
621  template < typename GUM_SCALAR >
623  if (this->domainSize() == 0) return *this;
624 
625  std::vector< GUM_SCALAR > v;
626  GUM_SCALAR sum;
627  v.reserve(this->domainSize());
628  sum = 0.0;
629  for (Size i = 0; i < this->domainSize(); ++i) {
630  auto r = (GUM_SCALAR)randomProba();
631  v.push_back(r);
632  sum += r;
633  }
634  if (sum == 0.0) v[gum::randomValue(this->domainSize())] = 1.0; // a 1 somewhere
635 
636  this->fillWith(v);
637  return *this;
638  }
639 
640  template < typename GUM_SCALAR >
642  return this->random().normalize();
643  }
644 
645  template < typename GUM_SCALAR >
647  return this->random().normalizeAsCPT();
648  }
649 
650  template < typename GUM_SCALAR >
652  if ((alpha < GUM_SCALAR(0.0)) || (alpha > GUM_SCALAR(1.0))) {
653  GUM_ERROR(InvalidArgument, "alpha must be in [0,1]")
654  }
655  Potential< GUM_SCALAR > noise(*this);
657  }
658 
659  template < typename GUM_SCALAR >
661  return Potential< GUM_SCALAR >(*this).abs();
662  }
663 
664  template < typename GUM_SCALAR >
666  return Potential< GUM_SCALAR >(*this).sq();
667  }
668 
669  template < typename GUM_SCALAR >
671  return Potential< GUM_SCALAR >(*this).log2();
672  }
673 
674 } /* namespace gum */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:643