1 /**
2  *
3  *   Copyright (c) 2005-2021 by Pierre-Henri WUILLEMIN(_at_LIP6) et Christophe GONZALES(_at_AMU)
4  * (_at_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(_at_LIP6) & Christophe GONZALES(_at_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 >
Potential()37   INLINE Potential< GUM_SCALAR >::Potential() :
38       MultiDimDecorator< GUM_SCALAR >(new MultiDimArray< GUM_SCALAR >(), GUM_SCALAR(1)) {
39     GUM_CONSTRUCTOR(Potential);
40   }
41 
42   // constructor using aContent as content
43   template < typename GUM_SCALAR >
Potential(MultiDimImplementation<GUM_SCALAR> * aContent)44   INLINE Potential< GUM_SCALAR >::Potential(MultiDimImplementation< GUM_SCALAR >* aContent) :
45       MultiDimDecorator< GUM_SCALAR >(aContent, GUM_SCALAR(1)) {
46     // for debugging purposes
47     GUM_CONSTRUCTOR(Potential);
48   }
49   // copy constructor
50   template < typename GUM_SCALAR >
Potential(const Potential<GUM_SCALAR> & src)51   INLINE Potential< GUM_SCALAR >::Potential(const Potential< GUM_SCALAR >& src) :
52       Potential< GUM_SCALAR >(
53          static_cast< MultiDimImplementation< GUM_SCALAR >* >(src.content()->newFactory()),
54          *(src.content())) {
55     this->empty_value_ = src.empty_value_;
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 >
Potential(Potential<GUM_SCALAR> && from)62   INLINE Potential< GUM_SCALAR >::Potential(Potential< GUM_SCALAR >&& from) :
63       MultiDimDecorator< GUM_SCALAR >(std::forward< MultiDimDecorator< GUM_SCALAR > >(from)) {
64     GUM_CONS_MOV(Potential);
65   }
66 
67   // complex copy constructor : we choose the implementation
68   template < typename GUM_SCALAR >
Potential(MultiDimImplementation<GUM_SCALAR> * aContent,const MultiDimContainer<GUM_SCALAR> & src)69   Potential< GUM_SCALAR >::Potential(MultiDimImplementation< GUM_SCALAR >*  aContent,
70                                      const MultiDimContainer< GUM_SCALAR >& src) :
71       MultiDimDecorator< GUM_SCALAR >(aContent) {
72     // for debugging purposes
73     GUM_CONSTRUCTOR(Potential);
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 >
89   Potential< GUM_SCALAR >& Potential< GUM_SCALAR >::operator=(const Potential< GUM_SCALAR >& src) {
90     GUM_OP_CPY(Potential);
91     if (&src == this) return *this;
92     MultiDimDecorator< GUM_SCALAR >::operator=(src);
93     return *this;
94   }
95 
96   // operator = move
97   template < typename GUM_SCALAR >
98   Potential< GUM_SCALAR >& Potential< GUM_SCALAR >::operator=(Potential< GUM_SCALAR >&& src) {
99     GUM_OP_MOV(Potential);
100     if (&src == this) return *this;
101     MultiDimDecorator< GUM_SCALAR >::operator=(
102        std::forward< MultiDimDecorator< GUM_SCALAR > >(src));
103     return *this;
104   }
105 
106   // destructor
107 
108   template < typename GUM_SCALAR >
~Potential()109   Potential< GUM_SCALAR >::~Potential() {
110     // for debugging purposes
111     GUM_DESTRUCTOR(Potential);
112   }
113 
114   template < typename GUM_SCALAR >
newFactory()115   INLINE Potential< GUM_SCALAR >* Potential< GUM_SCALAR >::newFactory() const {
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 >
sum()122   INLINE GUM_SCALAR Potential< GUM_SCALAR >::sum() const {
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 >
product()130   INLINE GUM_SCALAR Potential< GUM_SCALAR >::product() const {
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 >
max()138   INLINE GUM_SCALAR Potential< GUM_SCALAR >::max() const {
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 >
min()146   INLINE GUM_SCALAR Potential< GUM_SCALAR >::min() const {
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 >
maxNonOne()156   GUM_SCALAR Potential< GUM_SCALAR >::maxNonOne() const {
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 >
minNonZero()177   INLINE GUM_SCALAR Potential< GUM_SCALAR >::minNonZero() const {
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 >
entropy()196   INLINE GUM_SCALAR Potential< GUM_SCALAR >::entropy() const {
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 >&
fillWith(const std::vector<GUM_SCALAR> & data)208                Potential< GUM_SCALAR >::fillWith(const std::vector< GUM_SCALAR >& data) const {
209     this->populate(data);
210     return *this;
211   }
212 
213   template < typename GUM_SCALAR >
214   INLINE const Potential< GUM_SCALAR >&
fillWith(const GUM_SCALAR & val)215                Potential< GUM_SCALAR >::fillWith(const GUM_SCALAR& val) const {
216     this->fill(val);
217     return *this;
218   }
219 
220   template < typename GUM_SCALAR >
221   INLINE const Potential< GUM_SCALAR >&
fillWith(const Potential<GUM_SCALAR> & src)222                Potential< GUM_SCALAR >::fillWith(const Potential< GUM_SCALAR >& src) const {
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())) {
232         GUM_ERROR(InvalidArgument,
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 
241     Instantiation Isrc(src);
242     Instantiation Idst(*this);
243     for (Isrc.setFirst(); !Isrc.end(); ++Isrc) {
244       for (Idx i = 0; i < this->nbrDim(); i++) {
245         Idst.chgVal(Isrc.variable(i).name(), Isrc.val(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 >&
fillWith(const Potential<GUM_SCALAR> & src,const std::vector<std::string> & mapSrc)255                Potential< GUM_SCALAR >::fillWith(const Potential< GUM_SCALAR >&    src,
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     }
263     Instantiation Isrc;
264     for (Idx i = 0; i < src.nbrDim(); i++) {
265       if (src.variable(mapSrc[i]).domainSize() != this->variable(i).domainSize()) {
266         GUM_ERROR(InvalidArgument,
267                   "Variables " << mapSrc[i] << " (in the argument) and " << this->variable(i).name()
268                                << " have not the same dimension.");
269       } else {
270         Isrc.add(src.variable(mapSrc[i]));
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 >
sq()282   INLINE const Potential< GUM_SCALAR >& Potential< GUM_SCALAR >::sq() const {
283     this->apply([](GUM_SCALAR x) { return x * x; });
284     return *this;
285   }
286 
287   template < typename GUM_SCALAR >
log2()288   INLINE const Potential< GUM_SCALAR >& Potential< GUM_SCALAR >::log2() const {
289     this->apply([](GUM_SCALAR x) { return std::log2(x); });
290     return *this;
291   }
292 
293   template < typename GUM_SCALAR >
KL(const Potential<GUM_SCALAR> & p)294   INLINE GUM_SCALAR Potential< GUM_SCALAR >::KL(const Potential< GUM_SCALAR >& p) const {
295     if (this->nbrDim() != p.nbrDim())
296       GUM_ERROR(InvalidArgument,
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 >
abs()325   INLINE const Potential< GUM_SCALAR >& Potential< GUM_SCALAR >::abs() const {
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 >
normalize()338   INLINE const Potential< GUM_SCALAR >& Potential< GUM_SCALAR >::normalize() const {
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 >&
normalizeAsCPT(const Idx & varId)354                Potential< GUM_SCALAR >::normalizeAsCPT(const Idx& varId) const {
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 >
scale(GUM_SCALAR v)386   INLINE const Potential< GUM_SCALAR >& Potential< GUM_SCALAR >::scale(GUM_SCALAR v) const {
387     this->apply([v](GUM_SCALAR x) { return x * v; });
388     return *this;
389   }
390 
391   template < typename GUM_SCALAR >
translate(GUM_SCALAR v)392   INLINE const Potential< GUM_SCALAR >& Potential< GUM_SCALAR >::translate(GUM_SCALAR v) const {
393     this->apply([v](GUM_SCALAR x) { return x + v; });
394     return *this;
395   }
396 
397   template < typename GUM_SCALAR >
inverse()398   INLINE const Potential< GUM_SCALAR >& Potential< GUM_SCALAR >::inverse() const {
399     this->apply([](GUM_SCALAR x) { return 1 / x; });
400     return *this;
401   }
402 
403   template < typename GUM_SCALAR >
404   INLINE Potential< GUM_SCALAR >
margSumOut(const Set<const DiscreteVariable * > & del_vars)405          Potential< GUM_SCALAR >::margSumOut(const Set< const DiscreteVariable* >& del_vars) const {
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 >
413   INLINE Potential< GUM_SCALAR >
margProdOut(const Set<const DiscreteVariable * > & del_vars)414      Potential< GUM_SCALAR >::margProdOut(const Set< const DiscreteVariable* >& del_vars) const {
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 >
422   INLINE Potential< GUM_SCALAR >
margMinOut(const Set<const DiscreteVariable * > & del_vars)423          Potential< GUM_SCALAR >::margMinOut(const Set< const DiscreteVariable* >& del_vars) const {
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 >
431   INLINE Potential< GUM_SCALAR >
margMaxOut(const Set<const DiscreteVariable * > & del_vars)432          Potential< GUM_SCALAR >::margMaxOut(const Set< const DiscreteVariable* >& del_vars) const {
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 >
439   INLINE Potential< GUM_SCALAR >
margSumIn(const Set<const DiscreteVariable * > & kept_vars)440          Potential< GUM_SCALAR >::margSumIn(const Set< const DiscreteVariable* >& kept_vars) const {
441     if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
442       return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
443     }
444     return Potential< GUM_SCALAR >(gum::projectSum(*this->content(), complementVars_(kept_vars)));
445   }
446 
447   template < typename GUM_SCALAR >
448   INLINE Potential< GUM_SCALAR >
margProdIn(const Set<const DiscreteVariable * > & kept_vars)449      Potential< GUM_SCALAR >::margProdIn(const Set< const DiscreteVariable* >& kept_vars) const {
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 >(
454        gum::projectProduct(*this->content(), complementVars_(kept_vars)));
455   }
456 
457   template < typename GUM_SCALAR >
458   INLINE Potential< GUM_SCALAR >
margMinIn(const Set<const DiscreteVariable * > & kept_vars)459          Potential< GUM_SCALAR >::margMinIn(const Set< const DiscreteVariable* >& kept_vars) const {
460     if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
461       return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
462     }
463     return Potential< GUM_SCALAR >(gum::projectMin(*this->content(), complementVars_(kept_vars)));
464   }
465 
466   template < typename GUM_SCALAR >
467   INLINE Potential< GUM_SCALAR >
margMaxIn(const Set<const DiscreteVariable * > & kept_vars)468          Potential< GUM_SCALAR >::margMaxIn(const Set< const DiscreteVariable* >& kept_vars) const {
469     if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
470       return Potential< GUM_SCALAR >().fillWith(this->empty_value_);
471     }
472     return Potential< GUM_SCALAR >(gum::projectMax(*this->content(), complementVars_(kept_vars)));
473   }
474 
475   template < typename GUM_SCALAR >
isNonZeroMap()476   INLINE Potential< GUM_SCALAR > Potential< GUM_SCALAR >::isNonZeroMap() const {
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* >
complementVars_(const Set<const DiscreteVariable * > & vars)489      Potential< GUM_SCALAR >::complementVars_(const Set< const DiscreteVariable* >& vars) const {
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 >
499   Potential< GUM_SCALAR >
reorganize(const std::vector<const DiscreteVariable * > & vars)500      Potential< GUM_SCALAR >::reorganize(const std::vector< const DiscreteVariable* >& vars) const {
501     if (vars.size() != this->nbrDim())
502       GUM_ERROR(InvalidArgument,
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 
510     Potential< GUM_SCALAR > p;
511     p.beginMultipleChanges();
512     for (const auto var: vars)
513       p.add(*var);
514     p.endMultipleChanges();
515     p.copyFrom(*this, nullptr);   // copy *this in p using the same order
516 
517     return p;
518   }
519 
520   template < typename GUM_SCALAR >
521   Potential< GUM_SCALAR >
reorganize(const std::vector<std::string> & vars)522      Potential< GUM_SCALAR >::reorganize(const std::vector< std::string >& vars) const {
523     std::vector< const DiscreteVariable* > res;
524 
525     gum::HashTable< std::string, const gum::DiscreteVariable* > namesToVars;
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)) {
531         GUM_ERROR(gum::InvalidArgument,
532                   "'" << name << "' is a not a name of a variable in this potential");
533       }
534       res.push_back(namesToVars[name]);
535     }
536     return reorganize(res);
537   }
538 
539   template < typename GUM_SCALAR >
putFirst(const DiscreteVariable * var)540   Potential< GUM_SCALAR > Potential< GUM_SCALAR >::putFirst(const DiscreteVariable* var) const {
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 >
putFirst(const std::string & varname)555   Potential< GUM_SCALAR > Potential< GUM_SCALAR >::putFirst(const std::string& varname) const {
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)
564       GUM_ERROR(InvalidArgument,
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 >
extract(const Instantiation & inst)570   Potential< GUM_SCALAR > Potential< GUM_SCALAR >::extract(const Instantiation& inst) const {
571     Potential< GUM_SCALAR > p;
572     p.extractFrom(*this, inst);
573 
574     return p;
575   }
576 
577   template < typename GUM_SCALAR >
draw()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 >
593   std::ostream& operator<<(std::ostream& out, const Potential< GUM_SCALAR >& array) {
594     out << array.toString();
595     return out;
596   }
597 
598   // argmax of all elements in this
599   template < typename GUM_SCALAR >
findAll(GUM_SCALAR v)600   Set< Instantiation > Potential< GUM_SCALAR >::findAll(GUM_SCALAR v) const {
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 >
argmax()612   INLINE Set< Instantiation > Potential< GUM_SCALAR >::argmax() const {
613     return findAll(max());
614   }
615   // argmin of all elements in this
616   template < typename GUM_SCALAR >
argmin()617   INLINE Set< Instantiation > Potential< GUM_SCALAR >::argmin() const {
618     return findAll(min());
619   }
620 
621   template < typename GUM_SCALAR >
random()622   const Potential< GUM_SCALAR >& Potential< GUM_SCALAR >::random() const {
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 >
randomDistribution()641   INLINE const Potential< GUM_SCALAR >& Potential< GUM_SCALAR >::randomDistribution() const {
642     return this->random().normalize();
643   }
644 
645   template < typename GUM_SCALAR >
randomCPT()646   INLINE const Potential< GUM_SCALAR >& Potential< GUM_SCALAR >::randomCPT() const {
647     return this->random().normalizeAsCPT();
648   }
649 
650   template < typename GUM_SCALAR >
noising(GUM_SCALAR alpha)651   const Potential< GUM_SCALAR >& Potential< GUM_SCALAR >::noising(GUM_SCALAR alpha) const {
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);
656     return fillWith(scale(1 - alpha) + noise.randomCPT().scale(alpha)).normalizeAsCPT();
657   }
658 
659   template < typename GUM_SCALAR >
new_abs()660   const Potential< GUM_SCALAR > Potential< GUM_SCALAR >::new_abs() const {
661     return Potential< GUM_SCALAR >(*this).abs();
662   }
663 
664   template < typename GUM_SCALAR >
new_sq()665   const Potential< GUM_SCALAR > Potential< GUM_SCALAR >::new_sq() const {
666     return Potential< GUM_SCALAR >(*this).sq();
667   }
668 
669   template < typename GUM_SCALAR >
new_log2()670   const Potential< GUM_SCALAR > Potential< GUM_SCALAR >::new_log2() const {
671     return Potential< GUM_SCALAR >(*this).log2();
672   }
673 
674 } /* namespace gum */
675