aGrUM  0.20.2
a C++ library for (probabilistic) graphical models
potential_tpl.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright 2005-2020 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() :
39  GUM_SCALAR(1)) {
41  }
42 
43  // constructor using aContent as content
44  template < typename GUM_SCALAR >
48  // for debugging purposes
50  }
51  // copy constructor
52  template < typename GUM_SCALAR >
55  src.content()->newFactory()),
56  *(src.content())) {
58  // GUM_CONS_CPY not here because in called Potential
59  // GUM_CONS_CPY( Potential );
60  }
61 
62  /// move constructor
63  template < typename GUM_SCALAR >
68  }
69 
70  // complex copy constructor : we choose the implementation
71  template < typename GUM_SCALAR >
74  const MultiDimContainer< GUM_SCALAR >& src) :
76  // for debugging purposes
78 
79  if (!src.empty()) {
80  this->beginMultipleChanges();
81 
82  for (Idx i = 0; i < src.variablesSequence().size(); i++) {
83  this->add(*(src.variablesSequence()[i]));
84  }
85 
86  this->endMultipleChanges();
87  this->content()->copyFrom(*src.content());
88  }
89  }
90 
91  // operator = copy
92  template < typename GUM_SCALAR >
96  if (&src == this) return *this;
98  return *this;
99  }
100 
101  // operator = move
102  template < typename GUM_SCALAR >
106  if (&src == this) return *this;
109  return *this;
110  }
111 
112  // destructor
113 
114  template < typename GUM_SCALAR >
116  // for debugging purposes
118  }
119 
120  template < typename GUM_SCALAR >
122  return new Potential< GUM_SCALAR >(
123  static_cast< MultiDimImplementation< GUM_SCALAR >* >(
124  this->content()->newFactory()));
125  }
126 
127  // sum of all elements in this
128  template < typename GUM_SCALAR >
130  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
131  return this->empty_value_;
132  }
133  return gum::projectSum(*this->content());
134  }
135  // product of all elements in this
136  template < typename GUM_SCALAR >
138  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
139  return this->empty_value_;
140  }
141  return gum::projectProduct(*this->content());
142  }
143  // max of all elements in this
144  template < typename GUM_SCALAR >
146  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
147  return this->empty_value_;
148  }
149  return gum::projectMax(*this->content());
150  }
151  // min of all elements in this
152  template < typename GUM_SCALAR >
154  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
155  return this->empty_value_;
156  }
157  return gum::projectMin(*this->content());
158  }
159 
160  // max of all non one elements in this
161  // warning can return 1 if no other value than 1 ...
162  template < typename GUM_SCALAR >
164  GUM_SCALAR res;
165 
166  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
167  res = this->empty_value_;
168  } else {
169  res = this->reduce(
170  [](GUM_SCALAR z, GUM_SCALAR p) {
171  return (p == static_cast< GUM_SCALAR >(1)) ? z
172  : (z == static_cast< GUM_SCALAR >(1)) ? p
173  : (p > z ? p : z);
174  },
175  static_cast< GUM_SCALAR >(1));
176  }
177 
178  return res;
179  }
180 
181  // min of all non zero elements in this
182  // warning can return 0 if no other value than 0 ...
183  template < typename GUM_SCALAR >
185  GUM_SCALAR res;
186 
187  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
188  res = this->empty_value_;
189  } else {
190  res = this->reduce(
191  [](GUM_SCALAR z, GUM_SCALAR p) {
192  return (p == static_cast< GUM_SCALAR >(0)) ? z
193  : (z == static_cast< GUM_SCALAR >(0)) ? p
194  : (p < z ? p : z);
195  },
196  static_cast< GUM_SCALAR >(0));
197  }
198  return res;
199  }
200 
201  // entropy of this
202  template < typename GUM_SCALAR >
204  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
205  return static_cast< GUM_SCALAR >(0);
206  }
207 
208  return this->reduce(
209  [](GUM_SCALAR z, GUM_SCALAR p) {
210  return (p == 0.0) ? z : (z - p * std::log2(p));
211  },
212  0.0);
213  }
214 
215  template < typename GUM_SCALAR >
217  const std::vector< GUM_SCALAR >& data) const {
218  this->populate(data);
219  return *this;
220  }
221 
222  template < typename GUM_SCALAR >
223  INLINE const Potential< GUM_SCALAR >&
225  this->fill(val);
226  return *this;
227  }
228 
229  template < typename GUM_SCALAR >
230  INLINE const Potential< GUM_SCALAR >&
232  if (src.domainSize() != this->domainSize()) {
233  GUM_ERROR(InvalidArgument, "Potential to copy has not the same dimension.");
234  }
235  gum::Set< std::string > son; // set of names
236  for (const auto& v: src.variablesSequence()) {
237  son.insert(v->name());
238  }
239  for (const auto& v: this->variablesSequence()) {
240  if (!son.contains(v->name())) {
242  "Variable <" << v->name() << "> not present in src (" << son
243  << ").");
244  }
245  // we check size, labels and order of labels in the same time
246  if (v->toString() != src.variable(v->name()).toString()) {
248  "Variables <" << v->name() << "> are not identical.");
249  }
250  }
251 
253  Instantiation Idst(*this);
254  for (Isrc.setFirst(); !Isrc.end(); ++Isrc) {
255  for (Idx i = 0; i < this->nbrDim(); i++) {
257  }
258  this->set(Idst, src.get(Isrc));
259  }
260 
261  return *this;
262  }
263 
264  template < typename GUM_SCALAR >
266  const Potential< GUM_SCALAR >& src,
267  const std::vector< std::string >& mapSrc) const {
268  if (src.nbrDim() != this->nbrDim()) {
269  GUM_ERROR(InvalidArgument, "Potential to copy has not the same dimension.");
270  }
271  if (src.nbrDim() != mapSrc.size()) {
273  "Potential and vector have not the same dimension.");
274  }
276  for (Idx i = 0; i < src.nbrDim(); i++) {
277  if (src.variable(mapSrc[i]).domainSize() != this->variable(i).domainSize()) {
279  "Variables " << mapSrc[i] << " (in the argument) and "
280  << this->variable(i).name()
281  << " have not the same dimension.");
282  } else {
284  }
285  }
286  Instantiation Idst(*this);
287  for (Isrc.setFirst(); !Isrc.end(); ++Isrc, ++Idst) {
288  this->set(Idst, src.get(Isrc));
289  }
290 
291  return *this;
292  }
293 
294  template < typename GUM_SCALAR >
296  this->apply([](GUM_SCALAR x) { return x * x; });
297  return *this;
298  }
299 
300  template < typename GUM_SCALAR >
302  this->apply([](GUM_SCALAR x) { return std::log2(x); });
303  return *this;
304  }
305 
306  template < typename GUM_SCALAR >
308  Potential< GUM_SCALAR >::KL(const Potential< GUM_SCALAR >& p) const {
309  if (this->nbrDim() != p.nbrDim())
310  GUM_ERROR(
312  "BNdistance between potentials with different numbers of dimensions");
313  for (const auto var: p.variablesSequence()) {
314  if (!this->contains(*var))
316  "A variable in the argument does not belong to the potential.");
317  }
318  for (const auto var: this->variablesSequence()) {
319  if (!p.contains(*var))
320  GUM_ERROR(InvalidArgument, "A variable does not belong to the argument.");
321  }
322 
323  Instantiation inst(*this);
324  GUM_SCALAR res = static_cast< GUM_SCALAR >(0);
325  for (inst.setFirst(); !inst.end(); inst.inc()) {
326  GUM_SCALAR x = this->get(inst);
327  GUM_SCALAR y = p.get(inst);
328  if (static_cast< GUM_SCALAR >(0) == x) // 0*log(0/y)=0
329  continue;
330 
331  if (static_cast< GUM_SCALAR >(0) == y)
332  // we know that x!=0;
334  "The argument has a 0 at " << inst
335  << " while the potential has not.")
336 
337  res += x * std::log2(x / y);
338  }
339  return res;
340  }
341 
342  template < typename GUM_SCALAR >
344  this->apply([](GUM_SCALAR x) {
345  if (x >= 0)
346  return x;
347  else
348  return -x;
349  });
350  return *this;
351  }
352 
353  // normalisation of this
354  // do nothing is sum is 0
355  template < typename GUM_SCALAR >
356  INLINE const Potential< GUM_SCALAR >&
358  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
359  if (this->empty_value_ != static_cast< GUM_SCALAR >(0))
360  this->empty_value_ = static_cast< GUM_SCALAR >(1.0);
361  } else {
362  GUM_SCALAR s = sum();
363 
364  if (s != (GUM_SCALAR)0) {
365  this->apply([s](GUM_SCALAR x) { return x / s; });
366  }
367  }
368  return *this;
369  }
370 
371  template < typename GUM_SCALAR >
372  INLINE const Potential< GUM_SCALAR >&
374  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
375  if (this->empty_value_ != static_cast< GUM_SCALAR >(0)) {
376  this->empty_value_ = static_cast< GUM_SCALAR >(1.0);
377  } else {
379  "Normalization for a potential that sum to 0 in " << *this);
380  }
381  } else {
382  if (varId >= this->nbrDim()) {
383  GUM_ERROR(FatalError, varId << " is not a position for " << *this);
384  }
385  Instantiation inst(*this);
386  const auto& v = this->variable(varId);
387 
388  for (inst.setFirst(); !inst.end(); inst.incNotVar(v)) {
389  GUM_SCALAR s = (GUM_SCALAR)0.0;
390  for (inst.setFirstVar(v); !inst.end(); inst.incVar(v))
391  s += this->get(inst);
392  if (s == (GUM_SCALAR)0.0) {
394  "Normalization for a potential that sum to 0 in " << *this);
395  }
396  if (s != (GUM_SCALAR)1.0) {
397  for (inst.setFirstVar(v); !inst.end(); inst.incVar(v))
398  this->set(inst, this->get(inst) / s);
399  }
400  inst.setFirstVar(v); // to remove inst.end()
401  }
402  }
403  return *this;
404  }
405 
406  template < typename GUM_SCALAR >
407  INLINE const Potential< GUM_SCALAR >&
409  this->apply([v](GUM_SCALAR x) { return x * v; });
410  return *this;
411  }
412 
413  template < typename GUM_SCALAR >
414  INLINE const Potential< GUM_SCALAR >&
416  this->apply([v](GUM_SCALAR x) { return x + v; });
417  return *this;
418  }
419 
420  template < typename GUM_SCALAR >
422  this->apply([](GUM_SCALAR x) { return 1 / x; });
423  return *this;
424  }
425 
426  template < typename GUM_SCALAR >
428  const Set< const DiscreteVariable* >& del_vars) const {
429  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
430  return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
431  }
432  return Potential< GUM_SCALAR >(gum::projectSum(*this->content(), del_vars));
433  }
434 
435  template < typename GUM_SCALAR >
437  const Set< const DiscreteVariable* >& del_vars) const {
438  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
439  return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
440  }
441  return Potential< GUM_SCALAR >(
442  gum::projectProduct(*this->content(), del_vars));
443  }
444 
445  template < typename GUM_SCALAR >
447  const Set< const DiscreteVariable* >& del_vars) const {
448  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
449  return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
450  }
451  return Potential< GUM_SCALAR >(gum::projectMin(*this->content(), del_vars));
452  }
453 
454  template < typename GUM_SCALAR >
456  const Set< const DiscreteVariable* >& del_vars) const {
457  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
458  return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
459  }
460  return Potential< GUM_SCALAR >(gum::projectMax(*this->content(), del_vars));
461  }
462  template < typename GUM_SCALAR >
464  const Set< const DiscreteVariable* >& kept_vars) const {
465  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
466  return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
467  }
468  return Potential< GUM_SCALAR >(
470  }
471 
472  template < typename GUM_SCALAR >
474  const Set< const DiscreteVariable* >& kept_vars) const {
475  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
476  return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
477  }
478  return Potential< GUM_SCALAR >(
480  }
481 
482  template < typename GUM_SCALAR >
484  const Set< const DiscreteVariable* >& kept_vars) const {
485  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
486  return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
487  }
488  return Potential< GUM_SCALAR >(
490  }
491 
492  template < typename GUM_SCALAR >
494  const Set< const DiscreteVariable* >& kept_vars) const {
495  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
496  return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
497  }
498  return Potential< GUM_SCALAR >(
500  }
501 
502  template < typename GUM_SCALAR >
504  auto p = Potential< GUM_SCALAR >(*this);
505  p.apply([](GUM_SCALAR x) {
506  if (x != static_cast< GUM_SCALAR >(0))
507  return static_cast< GUM_SCALAR >(1);
508  else
509  return static_cast< GUM_SCALAR >(0);
510  });
511  return p;
512  }
513 
514  template < typename GUM_SCALAR >
516  const Set< const DiscreteVariable* >& vars) const {
517  Set< const DiscreteVariable* > cplt;
518 
519  for (const auto x: this->variablesSequence())
520  if (!vars.contains(x)) cplt.insert(x);
521 
522  return cplt;
523  }
524 
525  template < typename GUM_SCALAR >
527  const std::vector< const DiscreteVariable* >& vars) const {
528  if (vars.size() != this->nbrDim())
530  "The vector contains " << vars.size() << " variables instead of "
531  << this->nbrDim() << ".");
532  for (const auto var: vars) {
533  if (!this->contains(*var))
535  "A variable in the vector does not belong to the potential.");
536  }
537 
540  for (const auto var: vars)
541  p.add(*var);
543  p.copyFrom(*this, nullptr); // copy *this in p using the same order
544 
545  return p;
546  }
547 
548  template < typename GUM_SCALAR >
550  const std::vector< std::string >& vars) const {
551  std::vector< const DiscreteVariable* > res;
552 
554  for (gum::Idx i = 0; i < this->nbrDim(); i++)
555  namesToVars.insert(this->variable(i).name(), &(this->variable(i)));
556 
557  for (const auto& name: vars) {
558  if (!namesToVars.exists(name)) {
560  "'" << name
561  << "' is a not a name of a variable in this potential");
562  }
564  }
565  return reorganize(res);
566  }
567 
568  template < typename GUM_SCALAR >
571  if (!this->contains(*var)) {
573  "The variable to put first does not belong to the potential");
574  }
575  if (&(this->variable(0)) == var) return Potential< GUM_SCALAR >(*this);
576 
577  std::vector< const DiscreteVariable* > vars;
578  vars.push_back(var);
579  for (Idx i = 0; i < this->nbrDim(); i++)
580  if (&(this->variable(i)) != var) vars.push_back(&(this->variable(i)));
581 
582  return this->reorganize(vars);
583  }
584 
585  template < typename GUM_SCALAR >
588  const DiscreteVariable* var = nullptr;
589 
590  for (gum::Idx i = 0; i < this->nbrDim(); i++)
591  if (this->variable(i).name() == varname) {
592  var = &(this->variable(i));
593  break;
594  }
595  if (var == nullptr)
597  "The variable '"
598  << varname
599  << "' to put first does not belong to the potential");
600  return this->putFirst(var);
601  }
602 
603  template < typename GUM_SCALAR >
607  p.extractFrom(*this, inst);
608 
609  return p;
610  }
611 
612  template < typename GUM_SCALAR >
613  Idx Potential< GUM_SCALAR >::draw() const {
614  if (this->nbrDim() != 1) {
615  GUM_ERROR(FatalError, "To draw from a potential, the dimension must be 1")
616  }
617 
618  GUM_SCALAR r = static_cast< GUM_SCALAR >(randomProba());
619  Instantiation Ip(*this);
620  for (Ip.setFirst(); !Ip.end(); Ip.inc()) {
621  r -= this->get(Ip);
622  if (r <= 0) return Ip.val(0);
623  }
624  return this->variable(0).domainSize() - 1;
625  }
626 
627  template < typename GUM_SCALAR >
629  const Potential< GUM_SCALAR >& array) {
630  out << array.toString();
631  return out;
632  }
633 
634  // argmax of all elements in this
635  template < typename GUM_SCALAR >
637  Instantiation I(*this);
638  Set< Instantiation > res;
639 
640  if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
641  return res;
642  }
643  for (I.setFirst(); !I.end(); ++I) {
644  if (this->get(I) == v) res.insert(I);
645  }
646  return res;
647  }
648  // argmax of all elements in this
649  template < typename GUM_SCALAR >
651  return findAll(max());
652  }
653  // argmin of all elements in this
654  template < typename GUM_SCALAR >
656  return findAll(min());
657  }
658 
659  template < typename GUM_SCALAR >
661  if (this->domainSize() == 0) return *this;
662 
663  std::vector< GUM_SCALAR > v;
664  GUM_SCALAR sum;
665  v.reserve(this->domainSize());
666  sum = 0.0;
667  for (Size i = 0; i < this->domainSize(); ++i) {
668  auto r = (GUM_SCALAR)randomProba();
669  v.push_back(r);
670  sum += r;
671  }
672  if (sum == 0.0)
673  v[gum::randomValue(this->domainSize())] = 1.0; // a 1 somewhere
674 
675  this->fillWith(v);
676  return *this;
677  }
678 
679  template < typename GUM_SCALAR >
680  INLINE const Potential< GUM_SCALAR >&
682  return this->random().normalize();
683  }
684 
685  template < typename GUM_SCALAR >
686  INLINE const Potential< GUM_SCALAR >&
688  return this->random().normalizeAsCPT();
689  }
690 
691  template < typename GUM_SCALAR >
692  const Potential< GUM_SCALAR >&
694  if ((alpha < GUM_SCALAR(0.0)) || (alpha > GUM_SCALAR(1.0))) {
695  GUM_ERROR(InvalidArgument, "alpha must be in [0,1]");
696  }
697  Potential< GUM_SCALAR > noise(*this);
698  return fillWith(scale(1 - alpha) + noise.randomCPT().scale(alpha))
699  .normalizeAsCPT();
700  }
701 
702  template < typename GUM_SCALAR >
704  return Potential< GUM_SCALAR >(*this).abs();
705  }
706 
707  template < typename GUM_SCALAR >
709  return Potential< GUM_SCALAR >(*this).sq();
710  }
711 
712  template < typename GUM_SCALAR >
714  return Potential< GUM_SCALAR >(*this).log2();
715  }
716 
717 } /* namespace gum */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:669