aGrUM 3.1.1
a C++ library for (probabilistic) graphical models
tensor_tpl.h
Go to the documentation of this file.
1/****************************************************************************
2 * This file is part of the aGrUM/pyAgrum library. *
3 * *
4 * Copyright (c) 2005-2026 by *
5 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
6 * - Christophe GONZALES(_at_AMU) *
7 * *
8 * The aGrUM/pyAgrum library is free software; you can redistribute it *
9 * and/or modify it under the terms of either : *
10 * *
11 * - the GNU Lesser General Public License as published by *
12 * the Free Software Foundation, either version 3 of the License, *
13 * or (at your option) any later version, *
14 * - the MIT license (MIT), *
15 * - or both in dual license, as here. *
16 * *
17 * (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) *
18 * *
19 * This aGrUM/pyAgrum library is distributed in the hope that it will be *
20 * useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
21 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
25 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
26 * OTHER DEALINGS IN THE SOFTWARE. *
27 * *
28 * See LICENCES for more details. *
29 * *
30 * SPDX-FileCopyrightText: Copyright 2005-2026 *
31 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
32 * - Christophe GONZALES(_at_AMU) *
33 * SPDX-License-Identifier: LGPL-3.0-or-later OR MIT *
34 * *
35 * Contact : info_at_agrum_dot_org *
36 * homepage : http://agrum.gitlab.io *
37 * gitlab : https://gitlab.com/agrumery/agrum *
38 * *
39 ****************************************************************************/
40
41#pragma once
42
43
49
50#include <algorithm>
51
52#include <agrum/agrum.h>
53
55
57
58namespace gum {
59 // Default constructor: creates an empty null dimensional matrix
60 // choose a MultiDimArray<> as decorated implementation
61 template < GUM_Numeric GUM_SCALAR >
63 MultiDimDecorator< GUM_SCALAR >(new MultiDimArray< GUM_SCALAR >(), GUM_SCALAR(1)) {
64 GUM_CONSTRUCTOR(Tensor)
65 }
66
67 // Default constructor: creates an empty null dimensional matrix
68 // choose a MultiDimArray<> as decorated implementation
69 template < GUM_Numeric GUM_SCALAR >
70 Tensor< GUM_SCALAR >::Tensor(const std::vector< const DiscreteVariable* >& vars) : Tensor() {
71 for (const auto& var: vars) {
72 this->add(*var);
73 }
74 }
75
76 // constructor using aContent as content
77 template < GUM_Numeric GUM_SCALAR >
79 MultiDimDecorator< GUM_SCALAR >(aContent, GUM_SCALAR(1)) {
80 // for debugging purposes
81 GUM_CONSTRUCTOR(Tensor)
82 }
83
84 // copy constructor
85 template < GUM_Numeric GUM_SCALAR >
86 Tensor< GUM_SCALAR >::Tensor(const Tensor< GUM_SCALAR >& src) :
87 Tensor< GUM_SCALAR >(
88 static_cast< MultiDimImplementation< GUM_SCALAR >* >(src.content()->newFactory()),
89 *(src.content())) {
90 this->empty_value_ = src.empty_value_;
91 // GUM_CONS_CPY not here because in called Tensor
92 // GUM_CONS_CPY( Tensor );
93 }
94
96 template < GUM_Numeric GUM_SCALAR >
97 Tensor< GUM_SCALAR >::Tensor(Tensor< GUM_SCALAR >&& from) :
98 MultiDimDecorator< GUM_SCALAR >(std::forward< MultiDimDecorator< GUM_SCALAR > >(from)) {
99 GUM_CONS_MOV(Tensor)
100 }
101
102 // complex copy constructor : we choose the implementation
103 template < GUM_Numeric GUM_SCALAR >
106 MultiDimDecorator< GUM_SCALAR >(aContent) {
107 // for debugging purposes
108 GUM_CONSTRUCTOR(Tensor)
109
110 if (!src.empty()) {
111 this->beginMultipleChanges();
112
113 for (Idx i = 0; i < src.variablesSequence().size(); i++) {
114 this->add(*(src.variablesSequence()[i]));
115 }
116
117 this->endMultipleChanges();
118 this->content()->copyFrom(*src.content());
119 }
120 }
121
122 // operator= (copy)
123 template < GUM_Numeric GUM_SCALAR >
124 Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::operator=(const Tensor< GUM_SCALAR >& src) {
125 GUM_OP_CPY(Tensor)
126 if (&src == this) return *this;
128 return *this;
129 }
130
131 // operator= (move)
132 template < GUM_Numeric GUM_SCALAR >
133 Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::operator=(Tensor< GUM_SCALAR >&& src) {
134 GUM_OP_MOV(Tensor)
135 if (&src == this) return *this;
137 std::forward< MultiDimDecorator< GUM_SCALAR > >(src));
138 return *this;
139 }
140
141 // destructor
142
143 template < GUM_Numeric GUM_SCALAR >
145 // for debugging purposes
146 GUM_DESTRUCTOR(Tensor)
147 }
148
149 template < GUM_Numeric GUM_SCALAR >
150 Tensor< GUM_SCALAR >* Tensor< GUM_SCALAR >::newFactory() const {
151 return new Tensor< GUM_SCALAR >(
152 static_cast< MultiDimImplementation< GUM_SCALAR >* >(this->content()->newFactory()));
153 }
155 // sum of all elements in this
156 template < GUM_Numeric GUM_SCALAR >
157 GUM_SCALAR Tensor< GUM_SCALAR >::sum() const {
158 if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
159 return this->empty_value_;
160 }
161 return gum::projectSum(*this->content());
163
164 // product of all elements in this
165 template < GUM_Numeric GUM_SCALAR >
166 GUM_SCALAR Tensor< GUM_SCALAR >::product() const {
167 if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
168 return this->empty_value_;
169 }
170 return gum::projectProduct(*this->content());
171 }
172
173 // max of all elements in this
174 template < GUM_Numeric GUM_SCALAR >
175 GUM_SCALAR Tensor< GUM_SCALAR >::max() const {
176 if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
177 return this->empty_value_;
178 }
179 return gum::projectMax(*this->content());
181
182 // min of all elements in this
183 template < GUM_Numeric GUM_SCALAR >
184 GUM_SCALAR Tensor< GUM_SCALAR >::min() const {
185 if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
186 return this->empty_value_;
188 return gum::projectMin(*this->content());
189 }
190
191 // max of all non-one elements in this
192 // warning can return 1 if no other value than 1 ...
193 template < GUM_Numeric GUM_SCALAR >
195 GUM_SCALAR res;
196
197 if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
198 res = this->empty_value_;
199 } else {
200 res = this->reduce(
201 [](GUM_SCALAR z, GUM_SCALAR p) {
202 return (p == static_cast< GUM_SCALAR >(1)) ? z
203 : (z == static_cast< GUM_SCALAR >(1)) ? p
204 : (p > z ? p : z);
205 },
206 static_cast< GUM_SCALAR >(1));
207 }
209 return res;
210 }
211
212 // min of all non-zero elements in this
213 // warning can return 0 if no other value than 0 ...
214 template < GUM_Numeric GUM_SCALAR >
216 GUM_SCALAR res;
217
218 if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
219 res = this->empty_value_;
220 } else {
221 res = this->reduce(
222 [](GUM_SCALAR z, GUM_SCALAR p) {
223 return (p == static_cast< GUM_SCALAR >(0)) ? z
224 : (z == static_cast< GUM_SCALAR >(0)) ? p
225 : (p < z ? p : z);
226 },
227 static_cast< GUM_SCALAR >(0));
228 }
229 return res;
230 }
231
232 template < GUM_Numeric GUM_SCALAR >
234 std::function< GUM_SCALAR(const gum::Instantiation&) > f) const {
235 if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
236 return static_cast< GUM_SCALAR >(0);
238
239 GUM_SCALAR res = 0;
240 auto i = Instantiation(*this);
241 for (i.setFirst(); !i.end(); i.inc()) {
242 const GUM_SCALAR v_f = f(i);
243 if (v_f != GUM_SCALAR(0.0)) { res += this->get(i) * v_f; }
244 }
245 return res;
247
248 template < GUM_Numeric GUM_SCALAR >
249 GUM_SCALAR Tensor< GUM_SCALAR >::mean() const {
250 if (this->nbrDim() != 1) { GUM_ERROR(ArgumentError, "The tensor is not a marginal"); }
251 if (!gum::isCloseToOne(sum())) { GUM_ERROR(ArgumentError, "The tensor is not a distribution"); }
252 if (this->variable(0).isNumerical()) {
253 return expectedValue([this](const gum::Instantiation& i) -> GUM_SCALAR {
254 return GUM_SCALAR(this->variable(0).numerical(i.val(0)));
255 });
256 } else {
257 GUM_ERROR(ArgumentError, "The variable is not numerical");
259 }
260
261 template < GUM_Numeric GUM_SCALAR >
263 if (this->nbrDim() != 1) { GUM_ERROR(ArgumentError, "The tensor is not a marginal"); }
264 if (!gum::isCloseToOne(sum())) { GUM_ERROR(ArgumentError, "The tensor is not a distribution"); }
265 if (this->variable(0).isNumerical()) {
266 const auto mu = mean();
267 return expectedValue([this, mu](const gum::Instantiation& i) -> GUM_SCALAR {
268 const auto r = GUM_SCALAR(this->variable(0).numerical(i.val(0))) - mu;
269 return r * r;
270 });
271 } else {
272 GUM_ERROR(ArgumentError, "The variable is not numerical");
273 }
274 }
275
276 template < GUM_Numeric GUM_SCALAR >
277 GUM_SCALAR Tensor< GUM_SCALAR >::stdDev() const {
278 return std::sqrt(variance());
279 }
280
281 // entropy of this
282 template < GUM_Numeric GUM_SCALAR >
283 GUM_SCALAR Tensor< GUM_SCALAR >::entropy() const {
284 return -this->expectedValue([this](const gum::Instantiation& i) -> GUM_SCALAR {
285 return GUM_SCALAR(GUM_LOG2_OR_0(this->get(i)));
286 });
287 }
288
289 template < GUM_Numeric GUM_SCALAR >
290 const Tensor< GUM_SCALAR >&
291 Tensor< GUM_SCALAR >::fillWith(const std::vector< GUM_SCALAR >& data) const {
292 this->populate(data);
293 return *this;
295
296 template < GUM_Numeric GUM_SCALAR >
297 const Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::fillWith(const GUM_SCALAR& val) const {
298 this->fill(val);
299 return *this;
301
302 template < GUM_Numeric GUM_SCALAR >
303 const Tensor< GUM_SCALAR >&
304 Tensor< GUM_SCALAR >::fillWith(const Tensor< GUM_SCALAR >& src) const {
305 if (src.domainSize() != this->domainSize()) {
306 GUM_ERROR(InvalidArgument, "Tensor to copy has not the same domainSize.")
307 }
308 gum::Set< std::string > son; // set of names
309 for (const auto& v: src.variablesSequence()) {
310 son.insert(v->name());
312 for (const auto& v: this->variablesSequence()) {
313 if (!son.contains(v->name())) {
315 "Variable <" << v->name() << "> not present in src (" << son << ").")
317 // we check size, labels and order of labels in the same time
318 if (v->toString() != src.variable(v->name()).toString()) {
319 GUM_ERROR(InvalidArgument, "Variables <" << v->name() << "> are not identical.")
320 }
321 }
322
323 Instantiation Isrc(src);
324 Instantiation Idst(*this);
325
326 // pre-compute index mapping from src variable positions to dst positions (O(N))
327 // avoids repeated name lookups (O(N) each) inside the hot loop
328 const Idx ndim = src.nbrDim();
329 std::vector< Idx > src_to_dst(ndim);
330 for (Idx i = 0; i < ndim; i++) {
331 src_to_dst[i] = Idst.pos(this->variable(Isrc.variable(i).name()));
332 }
333
334 for (Isrc.setFirst(); !Isrc.end(); ++Isrc) {
335 for (Idx i = 0; i < ndim; i++) {
336 Idst.chgVal(src_to_dst[i], Isrc.val(i));
337 }
338 this->set(Idst, src.get(Isrc));
339 }
340
341 return *this;
342 }
343
344 template < GUM_Numeric GUM_SCALAR >
346 Tensor< GUM_SCALAR >::fillWith(const Tensor< GUM_SCALAR >& src,
347 const std::vector< std::string >& mapSrc) const {
348 if (src.nbrDim() != this->nbrDim()) {
349 GUM_ERROR(InvalidArgument, "Tensor to copy has not the same size.")
350 }
351 if (src.nbrDim() != mapSrc.size()) {
352 GUM_ERROR(InvalidArgument, "Tensor and vector have not the same size.")
353 }
354 Instantiation Isrc;
355 for (Idx i = 0; i < src.nbrDim(); i++) {
356 if (src.variable(mapSrc[i]).domainSize() != this->variable(i).domainSize()) {
358 "Variables " << mapSrc[i] << " (in the argument) and " << this->variable(i).name()
359 << " have not the same dimension.")
360 } else {
361 Isrc.add(src.variable(mapSrc[i]));
362 }
363 }
364 Instantiation Idst(*this);
365 for (Isrc.setFirst(); !Isrc.end(); ++Isrc, ++Idst) {
366 this->set(Idst, src.get(Isrc));
367 }
368
369 return *this;
370 }
371
372 template < GUM_Numeric GUM_SCALAR >
373 const Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::sq() const {
374 this->apply([](GUM_SCALAR x) { return x * x; });
375 return *this;
376 }
377
378 template < GUM_Numeric GUM_SCALAR >
379 const Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::log2() const {
380 this->apply([](GUM_SCALAR x) { return std::log2(x); });
381 return *this;
382 }
384 template < GUM_Numeric GUM_SCALAR >
385 const Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::sgn() const {
386 this->apply([](GUM_SCALAR x) { return (GUM_SCALAR(0) < x) - (x < GUM_SCALAR(0)); });
387 return *this;
388 }
389
390 template < GUM_Numeric GUM_SCALAR >
391 GUM_SCALAR Tensor< GUM_SCALAR >::KL(const Tensor< GUM_SCALAR >& p) const {
392 if (this->nbrDim() != p.nbrDim())
393 GUM_ERROR(InvalidArgument, "BNdistance between tensors with different numbers of dimensions")
394 for (const auto var: p.variablesSequence()) {
395 if (!this->contains(*var))
396 GUM_ERROR(InvalidArgument, "A variable in the argument does not belong to the tensor.")
397 }
398 for (const auto var: this->variablesSequence()) {
399 if (!p.contains(*var))
400 GUM_ERROR(InvalidArgument, "A variable does not belong to the argument.")
401 }
403 Instantiation inst(*this);
404 auto res = static_cast< GUM_SCALAR >(0);
405 for (inst.setFirst(); !inst.end(); inst.inc()) {
406 GUM_SCALAR x = this->get(inst);
407 GUM_SCALAR y = p.get(inst);
408 if (static_cast< GUM_SCALAR >(0) == x) // 0*log(0/y)=0
409 continue;
410
411 if (static_cast< GUM_SCALAR >(0) == y)
412 // we know that x!=0;
413 GUM_ERROR(FatalError, "The argument has a 0 at " << inst << " while the tensor has not.")
414
415 res += x * std::log2(x / y);
416 }
417 return res;
418 }
419
420 template < GUM_Numeric GUM_SCALAR >
421 const Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::abs() const {
422 this->apply([](GUM_SCALAR x) {
423 if (x >= 0) return x;
424 else return -x;
425 });
426 return *this;
427 }
428
429 // normalisation of this
430 // do nothing is sum is 0
431 template < GUM_Numeric GUM_SCALAR >
432 const Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::normalize() const {
433 if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
434 if (this->empty_value_ != static_cast< GUM_SCALAR >(0))
435 this->empty_value_ = static_cast< GUM_SCALAR >(1.0);
436 } else {
437 GUM_SCALAR s = sum();
438
439 if (s != (GUM_SCALAR)0) {
440 this->apply([s](GUM_SCALAR x) { return x / s; });
441 }
442 }
443 return *this;
444 }
446 template < GUM_Numeric GUM_SCALAR >
447 const Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::normalizeAsCPT(const Idx& varId) const {
448 if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
449 if (this->empty_value_ != static_cast< GUM_SCALAR >(0)) {
450 this->empty_value_ = static_cast< GUM_SCALAR >(1.0);
451 } else {
452 GUM_ERROR(FatalError, "Normalization for a tensor that sum to 0 in " << *this)
453 }
454 } else {
455 if (varId >= this->nbrDim()) {
456 GUM_ERROR(FatalError, varId << " is not a position for " << *this)
457 }
458 Instantiation inst(*this);
459 const auto& v = this->variable(varId);
461 for (inst.setFirst(); !inst.end(); inst.incNotVar(v)) {
462 auto s = (GUM_SCALAR)0.0;
463 for (inst.setFirstVar(v); !inst.end(); inst.incVar(v))
464 s += this->get(inst);
465 if (s == (GUM_SCALAR)0.0) {
466 GUM_ERROR(FatalError, "Normalization for a tensor that sum to 0 in " << *this)
467 }
468 if (s != (GUM_SCALAR)1.0) {
469 for (inst.setFirstVar(v); !inst.end(); inst.incVar(v))
470 this->set(inst, this->get(inst) / s);
471 }
472 inst.setFirstVar(v); // to remove inst.end()
473 }
475 return *this;
477
478 template < GUM_Numeric GUM_SCALAR >
479 const Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::scale(GUM_SCALAR v) const {
480 this->apply([v](GUM_SCALAR x) { return x * v; });
481 return *this;
483
484 template < GUM_Numeric GUM_SCALAR >
485 const Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::translate(GUM_SCALAR v) const {
486 this->apply([v](GUM_SCALAR x) { return x + v; });
487 return *this;
489
490 template < GUM_Numeric GUM_SCALAR >
491 const Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::inverse() const {
492 this->apply([](GUM_SCALAR x) { return 1 / x; });
493 return *this;
496 template < GUM_Numeric GUM_SCALAR >
497 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::sumOut(const gum::VariableSet& del_vars) const {
498 if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
499 return Tensor< GUM_SCALAR >().fillWith(this->empty_value_);
500 }
502 // if we remove all the variables, create an empty tensor
503 // TODO: remove this test when operations will be able to handle empty tensors
504 if (this->variablesSequence().size() <= del_vars.size()) {
505 bool equal = true;
506 for (const auto var: this->variablesSequence()) {
507 if (!del_vars.exists(var)) {
508 equal = false;
509 break;
510 }
511 }
512 if (equal) { return Tensor< GUM_SCALAR >().fillWith(this->sum()); }
514
515 return Tensor< GUM_SCALAR >(gum::projectSum(*this->content(), del_vars));
516 }
517
518 template < GUM_Numeric GUM_SCALAR >
519 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::prodOut(const gum::VariableSet& del_vars) const {
520 if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
521 return Tensor< GUM_SCALAR >().fillWith(this->empty_value_);
522 }
523
524 // if we remove all the variables, create an empty tensor
525 // TODO: remove this test when operations will be able to handle empty tensors
526 if (this->variablesSequence().size() <= del_vars.size()) {
527 bool equal = true;
528 for (const auto var: this->variablesSequence()) {
529 if (!del_vars.exists(var)) {
530 equal = false;
531 break;
532 }
533 }
534 if (equal) { return Tensor< GUM_SCALAR >().fillWith(this->product()); }
535 }
536
537 return Tensor< GUM_SCALAR >(gum::projectProduct(*this->content(), del_vars));
538 }
539
540 template < GUM_Numeric GUM_SCALAR >
541 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::minOut(const gum::VariableSet& del_vars) const {
542 if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
543 return Tensor< GUM_SCALAR >().fillWith(this->empty_value_);
544 }
545
546 // if we remove all the variables, create an empty tensor
547 // TODO: remove this test when operations will be able to handle empty tensors
548 if (this->variablesSequence().size() <= del_vars.size()) {
549 bool equal = true;
550 for (const auto var: this->variablesSequence()) {
551 if (!del_vars.exists(var)) {
552 equal = false;
553 break;
554 }
555 }
556 if (equal) { return Tensor< GUM_SCALAR >().fillWith(this->min()); }
557 }
558
559 return Tensor< GUM_SCALAR >(gum::projectMin(*this->content(), del_vars));
560 }
561
562 template < GUM_Numeric GUM_SCALAR >
563 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::maxOut(const gum::VariableSet& del_vars) const {
564 if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
565 return Tensor< GUM_SCALAR >().fillWith(this->empty_value_);
566 }
567
568 // if we remove all the variables, create an empty tensor
569 // TODO: remove this test when operations will be able to handle empty tensors
570 if (this->variablesSequence().size() <= del_vars.size()) {
571 bool equal = true;
572 for (const auto var: this->variablesSequence()) {
573 if (!del_vars.exists(var)) {
574 equal = false;
575 break;
576 }
577 }
578 if (equal) { return Tensor< GUM_SCALAR >().fillWith(this->max()); }
579 }
580
581 return Tensor< GUM_SCALAR >(gum::projectMax(*this->content(), del_vars));
582 }
583
584 template < GUM_Numeric GUM_SCALAR >
585 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::sumIn(const gum::VariableSet& kept_vars) const {
586 if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
587 return Tensor< GUM_SCALAR >().fillWith(this->empty_value_);
588 }
589
590 // if kept_var is empty, create an empty tensor
591 // TODO: remove this test when operations will be able to handle empty tensors
592 if (kept_vars.empty()) { return Tensor< GUM_SCALAR >().fillWith(this->sum()); }
593
594 return Tensor< GUM_SCALAR >(gum::projectSum(*this->content(), _complementVars_(kept_vars)));
595 }
596
597 template < GUM_Numeric GUM_SCALAR >
598 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::prodIn(const gum::VariableSet& kept_vars) const {
599 if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
600 return Tensor< GUM_SCALAR >().fillWith(this->empty_value_);
601 }
602
603 // if kept_var is empty, create an empty tensor
604 // TODO: remove this test when operations will be able to handle empty tensors
605 if (kept_vars.empty()) { return Tensor< GUM_SCALAR >().fillWith(this->product()); }
606
607 return Tensor< GUM_SCALAR >(gum::projectProduct(*this->content(), _complementVars_(kept_vars)));
608 }
609
610 template < GUM_Numeric GUM_SCALAR >
611 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::minIn(const gum::VariableSet& kept_vars) const {
612 if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
613 return Tensor< GUM_SCALAR >().fillWith(this->empty_value_);
614 }
615
616 // if kept_var is empty, create an empty tensor
617 // TODO: remove this test when operations will be able to handle empty tensors
618 if (kept_vars.empty()) { return Tensor< GUM_SCALAR >().fillWith(this->min()); }
619
620 return Tensor< GUM_SCALAR >(gum::projectMin(*this->content(), _complementVars_(kept_vars)));
621 }
622
623 template < GUM_Numeric GUM_SCALAR >
624 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::maxIn(const gum::VariableSet& kept_vars) const {
625 if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) {
626 return Tensor< GUM_SCALAR >().fillWith(this->empty_value_);
627 }
628
629 // if kept_var is empty, create an empty tensor
630 // TODO: remove this test when operations will be able to handle empty tensors
631 if (kept_vars.empty()) { return Tensor< GUM_SCALAR >().fillWith(this->max()); }
632
633 return Tensor< GUM_SCALAR >(gum::projectMax(*this->content(), _complementVars_(kept_vars)));
634 }
635
636 template < GUM_Numeric GUM_SCALAR >
637 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::isNonZeroMap() const {
638 auto p = Tensor< GUM_SCALAR >(*this);
639 p.apply([](GUM_SCALAR x) {
640 if (x != static_cast< GUM_SCALAR >(0)) return static_cast< GUM_SCALAR >(1);
641 else return static_cast< GUM_SCALAR >(0);
642 });
643 return p;
644 }
645
646 template < GUM_Numeric GUM_SCALAR >
648 gum::VariableSet cplt;
649
650 for (const auto x: this->variablesSequence())
651 if (!vars.contains(x)) cplt.insert(x);
652
653 return cplt;
654 }
655
656 template < GUM_Numeric GUM_SCALAR >
657 Tensor< GUM_SCALAR >
658 Tensor< GUM_SCALAR >::reorganize(const std::vector< const DiscreteVariable* >& vars) const {
659 if (vars.size() != this->nbrDim())
661 "The argument contains " << vars.size() << " variables instead of "
662 << this->nbrDim() << ".")
663 for (const auto var: vars) {
664 if (!this->contains(*var))
665 GUM_ERROR(InvalidArgument, "A variable in the argument does not belong to the tensor.")
666 }
667
668 Tensor< GUM_SCALAR > p;
669 p.beginMultipleChanges();
670 for (const auto var: vars)
671 p.add(*var);
672 p.endMultipleChanges();
673 p.copyFrom(*this, nullptr); // copy *this in p using the same order
674
675 return p;
676 }
677
678 template < GUM_Numeric GUM_SCALAR >
679 Tensor< GUM_SCALAR >
680 Tensor< GUM_SCALAR >::reorganize(const std::vector< std::string >& vars) const {
681 std::vector< const DiscreteVariable* > res;
682
684 for (gum::Idx i = 0; i < this->nbrDim(); i++)
685 namesToVars.insert(this->variable(i).name(), &(this->variable(i)));
686
687 for (const auto& name: vars) {
688 if (!namesToVars.exists(name)) {
689 GUM_ERROR(gum::InvalidArgument,
690 "'" << name << "' is a not a name of a variable in this tensor")
691 }
692 res.push_back(namesToVars[name]);
693 }
694 return reorganize(res);
695 }
696
697 template < GUM_Numeric GUM_SCALAR >
698 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::putFirst(const DiscreteVariable* var) const {
699 if (!this->contains(*var)) {
700 GUM_ERROR(InvalidArgument, "The variable to put first does not belong to the tensor")
701 }
702 if (&(this->variable(0)) == var) return Tensor< GUM_SCALAR >(*this);
703
704 std::vector< const DiscreteVariable* > vars;
705 vars.push_back(var);
706 for (Idx i = 0; i < this->nbrDim(); i++)
707 if (&(this->variable(i)) != var) vars.push_back(&(this->variable(i)));
708
709 return this->reorganize(vars);
710 }
711
712 template < GUM_Numeric GUM_SCALAR >
713 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::putFirst(std::string_view varname) const {
714 const DiscreteVariable* var = nullptr;
715
716 for (gum::Idx i = 0; i < this->nbrDim(); i++)
717 if (this->variable(i).name() == varname) {
718 var = &(this->variable(i));
719 break;
720 }
721 if (var == nullptr)
723 "The variable '" << varname << "' to put first does not belong to the tensor")
724 return this->putFirst(var);
725 }
726
727 template < GUM_Numeric GUM_SCALAR >
728 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::extract(const Instantiation& inst) const {
729 Tensor< GUM_SCALAR > p;
730 p.extractFrom(*this, inst);
731
732 return p;
733 }
734
735 template < GUM_Numeric GUM_SCALAR >
737 if (this->nbrDim() != 1) {
738 GUM_ERROR(FatalError, "To draw from a tensor, the dimension must be 1")
739 }
740
741 auto r = static_cast< GUM_SCALAR >(randomProba());
742 Instantiation Ip(*this);
743 for (Ip.setFirst(); !Ip.end(); Ip.inc()) {
744 r -= this->get(Ip);
745 if (r <= 0) return Ip.val(0);
746 }
747 return this->variable(0).domainSize() - 1;
748 }
749
750 template < GUM_Numeric GUM_SCALAR >
751 std::ostream& operator<<(std::ostream& out, const Tensor< GUM_SCALAR >& array) {
752 out << array.toString();
753 return out;
754 }
755
756 // argmax of all elements in this
757 template < GUM_Numeric GUM_SCALAR >
759 Instantiation I(*this);
761
762 // if (static_cast< MultiDimContainer< GUM_SCALAR >* >(this->content_)->empty()) { return
763 // res; }
764 for (I.setFirst(); !I.end(); ++I) {
765 if (this->get(I) == v) res.insert(Instantiation(I, false));
766 }
767 return res;
768 }
769
770 // argmax of all elements in this
771 template < GUM_Numeric GUM_SCALAR >
772 std::pair< Set< Instantiation >, GUM_SCALAR > Tensor< GUM_SCALAR >::argmax() const {
773 auto m = max();
774 return std::pair(findAll(m), m);
775 }
776
777 // argmin of all elements in this
778 template < GUM_Numeric GUM_SCALAR >
779 std::pair< Set< Instantiation >, GUM_SCALAR > Tensor< GUM_SCALAR >::argmin() const {
780 auto m = min();
781 return std::pair(findAll(m), m);
782 }
783
784 template < GUM_Numeric GUM_SCALAR >
785 const Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::random() const {
786 if (this->domainSize() == 0) return *this;
787
788 std::vector< GUM_SCALAR > v;
789 v.reserve(this->domainSize());
790 for (Size i = 0; i < this->domainSize(); ++i) {
791 auto r = (GUM_SCALAR)randomProba();
792 v.push_back(r);
793 }
794 this->fillWith(v);
795 return *this;
796 }
797
798 template < GUM_Numeric GUM_SCALAR >
799 const Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::randomDistribution() const {
800 if (this->domainSize() == 0) {
801 this->fillWith((GUM_SCALAR)1.0);
802 } else {
804 }
805
806 return *this;
807 }
808
809 template < GUM_Numeric GUM_SCALAR >
810 const Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::randomCPT() const {
811 if (this->domainSize() == 0) {
812 this->fillWith((GUM_SCALAR)1.0);
813 } else {
814 gum::Instantiation I(*this);
815 const auto& v = this->variable(0);
816 for (I.setFirstNotVar(v); !I.end(); I.incNotVar(v)) {
817 const auto& distrib = gum::randomDistribution< GUM_SCALAR >(v.domainSize());
818 for (I.setFirstVar(v); !I.end(); I.incVar(v)) {
819 this->set(I, distrib[I.val(0)]);
820 }
821 I.unsetEnd();
822 }
823 }
824 return *this;
825 }
826
827 template < GUM_Numeric GUM_SCALAR >
828 const Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::noising(GUM_SCALAR alpha) const {
829 if ((alpha < GUM_SCALAR(0.0)) || (alpha > GUM_SCALAR(1.0))) {
830 GUM_ERROR(InvalidArgument, "alpha must be in [0,1]")
831 }
832 Tensor< GUM_SCALAR > noise(*this);
833 return fillWith(scale(1 - alpha) + noise.randomCPT().scale(alpha)).normalizeAsCPT();
834 }
835
836 template < GUM_Numeric GUM_SCALAR >
837 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::new_abs() const {
838 return Tensor< GUM_SCALAR >(*this).abs();
839 }
840
841 template < GUM_Numeric GUM_SCALAR >
842 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::new_sq() const {
843 return Tensor< GUM_SCALAR >(*this).sq();
844 }
845
846 template < GUM_Numeric GUM_SCALAR >
847 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::new_log2() const {
848 return Tensor< GUM_SCALAR >(*this).log2();
849 }
850
851 template < GUM_Numeric GUM_SCALAR >
852 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::new_sgn() const {
853 return Tensor< GUM_SCALAR >(*this).sgn();
854 }
855
856 template < GUM_Numeric GUM_SCALAR >
857 Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::operator<<(const DiscreteVariable& v) {
858 this->add(v);
859 return *this;
860 }
861
863 template < GUM_Numeric GUM_SCALAR >
864 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::operator+(const Tensor< GUM_SCALAR >& p2) const {
865 if (p2.empty()) return Tensor< GUM_SCALAR >(*this).translate(p2.empty_value_);
866 if (this->empty()) return Tensor< GUM_SCALAR >(p2).translate(this->empty_value_);
867
868 return Tensor< GUM_SCALAR >(*this->content() + *p2.content());
869 }
870
872 template < GUM_Numeric GUM_SCALAR >
873 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::operator+(const GUM_SCALAR& v) const {
874 return Tensor< GUM_SCALAR >(*this).translate(v);
875 }
876
878 template < GUM_Numeric GUM_SCALAR >
880 if (this->nbrDim() != 1) return false;
881 if (this->sum() <= 0.0) return false;
882 return (this->min() >= 0.0) && (this->max() <= 1.0);
883 }
884
885 // max function between two evidence
886 template < GUM_Numeric GUM_SCALAR >
887 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::operator|(const Tensor< GUM_SCALAR >& p2) const {
888 if (!isEvidence() || !p2.isEvidence())
889 GUM_ERROR(InvalidArgument, "The tensors are not evidences.")
890 if (this->variable(0) != p2.variable(0))
891 GUM_ERROR(InvalidArgument, "The evidence are not on the same variable.")
892 Tensor< GUM_SCALAR > res(*this);
893 gum::Instantiation I(res);
894 for (I.setFirst(); !I.end(); ++I) {
895 res.set(I, std::max(res.get(I), p2.get(I)));
896 }
897 return res;
898 }
899
900 // min function between two evidence
901 template < GUM_Numeric GUM_SCALAR >
902 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::operator&(const Tensor< GUM_SCALAR >& p2) const {
903 if (!isEvidence() || !p2.isEvidence())
904 GUM_ERROR(InvalidArgument, "The tensors are not evidences.")
905 if (this->variable(0) != p2.variable(0))
906 GUM_ERROR(InvalidArgument, "The evidence are not on the same variable.")
907 Tensor< GUM_SCALAR > res(*this);
908 gum::Instantiation I(res);
909 for (I.setFirst(); !I.end(); ++I) {
910 res.set(I, std::min(res.get(I), p2.get(I)));
911 }
912 return res;
913 }
914
915 // complement function between two evidence
916 template < GUM_Numeric GUM_SCALAR >
917 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::operator~() const {
918 if (!isEvidence()) GUM_ERROR(InvalidArgument, "The tensor is not an evidence.")
919
920 Tensor< GUM_SCALAR > res(*this);
921 gum::Instantiation I(res);
922 for (I.setFirst(); !I.end(); ++I) {
923 res.set(I, 1 - res.get(I));
924 }
925 return res;
926 }
927
929 template < GUM_Numeric GUM_SCALAR >
930 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::operator-(const Tensor< GUM_SCALAR >& p2) const {
931 if (p2.empty()) return Tensor< GUM_SCALAR >(*this).translate(-p2.empty_value_);
932 if (this->empty()) {
933 auto p = Tensor< GUM_SCALAR >(p2);
934 p.apply([this](GUM_SCALAR x) { return this->empty_value_ - x; });
935 return p;
936 }
937 return Tensor< GUM_SCALAR >(*this->content() - *p2.content());
938 }
939
941 template < GUM_Numeric GUM_SCALAR >
942 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::operator-(const GUM_SCALAR& v) const {
943 return Tensor< GUM_SCALAR >(*this).translate(-v);
944 }
945
947 template < GUM_Numeric GUM_SCALAR >
948 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::operator*(const Tensor< GUM_SCALAR >& p2) const {
949 if (p2.empty()) return Tensor< GUM_SCALAR >(*this).scale(p2.empty_value_);
950 if (this->empty()) return Tensor< GUM_SCALAR >(p2).scale(this->empty_value_);
951
952 return Tensor< GUM_SCALAR >(*this->content() * *p2.content());
953 }
954
956 template < GUM_Numeric GUM_SCALAR >
957 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::operator*(const GUM_SCALAR& v) const {
958 return Tensor< GUM_SCALAR >(*this).scale(v);
959 }
960
962 template < GUM_Numeric GUM_SCALAR >
963 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::operator/(const Tensor< GUM_SCALAR >& p2) const {
964 if (p2.empty()) return Tensor< GUM_SCALAR >(*this).scale(1 / p2.empty_value_);
965 if (this->empty()) {
966 auto p = Tensor< GUM_SCALAR >(p2);
967 p.apply([this](GUM_SCALAR x) { return this->empty_value_ / x; });
968 return p;
969 }
970 return Tensor< GUM_SCALAR >(*this->content() / *p2.content());
971 }
972
974 template < GUM_Numeric GUM_SCALAR >
975 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::operator/(const GUM_SCALAR& v) const {
976 return Tensor< GUM_SCALAR >(*this).scale(1 / v);
977 }
978
979 template < GUM_Numeric GUM_SCALAR >
980 Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::operator+=(const Tensor< GUM_SCALAR >& r) {
981 *this = *this + r;
982 return *this;
983 }
984
985 template < GUM_Numeric GUM_SCALAR >
986 Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::operator+=(const GUM_SCALAR& v) {
987 this->translate(v);
988 return *this;
989 }
990
991 template < GUM_Numeric GUM_SCALAR >
992 Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::operator*=(const Tensor< GUM_SCALAR >& r) {
993 *this = *this * r;
994 return *this;
995 }
996
997 template < GUM_Numeric GUM_SCALAR >
998 Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::operator*=(const GUM_SCALAR& v) {
999 this->scale(v);
1000 return *this;
1001 }
1002
1003 template < GUM_Numeric GUM_SCALAR >
1004 Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::operator-=(const Tensor< GUM_SCALAR >& r) {
1005 *this = *this - r;
1006 return *this;
1007 }
1008
1009 template < GUM_Numeric GUM_SCALAR >
1010 Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::operator-=(const GUM_SCALAR& v) {
1011 this->translate(-v);
1012 return *this;
1013 }
1014
1015 template < GUM_Numeric GUM_SCALAR >
1016 Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::operator/=(const Tensor< GUM_SCALAR >& r) {
1017 *this = *this / r;
1018 return *this;
1019 }
1020
1021 template < GUM_Numeric GUM_SCALAR >
1022 Tensor< GUM_SCALAR >& Tensor< GUM_SCALAR >::operator/=(const GUM_SCALAR& v) {
1023 this->scale(1 / v);
1024 return *this;
1025 }
1026
1027 template < GUM_Numeric GUM_SCALAR >
1028 bool Tensor< GUM_SCALAR >::operator==(const Tensor< GUM_SCALAR >& r) const {
1029 if (this->empty()) {
1030 if (r.empty()) return this->empty_value_ == r.empty_value_;
1031 else return false;
1032 } else {
1033 if (r.empty()) return false;
1034 else return (*this->content_) == (*r.content_);
1035 }
1036 }
1037
1038 template < GUM_Numeric GUM_SCALAR >
1040 auto table = this->content();
1041 std::stringstream ss;
1042
1043 if (table->nbrDim() == 0) {
1044 Instantiation I(this);
1045 ss << "[" << this->get(I) << "]";
1046 return ss.str();
1047 }
1048 const Size colwidth = 6;
1049 const Size numberwidth = 9;
1050 const Size nbrLigMax = 6;
1051
1052 ss << std::left << std::fixed << std::endl;
1053 ss.precision(numberwidth - 5);
1054
1055 const auto& var = table->variable(0);
1056
1057 const Size nbparents = table->nbrDim() - 1;
1058 const Size nbcol = var.domainSize();
1059
1060 // box-drawing characters
1061 constexpr const char* vbar = "│"; // U+2502 single vertical
1062 constexpr const char* dvbar = "â•‘"; // U+2551 double vertical
1063 constexpr const char* hbar = "─"; // U+2500 single horizontal
1064
1065 const auto repeat_str = [](const char* s, Size n) {
1066 std::string r;
1067 const std::string unit(s);
1068 r.reserve(unit.size() * n);
1069 for (Size i = 0; i < n; i++)
1070 r += unit;
1071 return r;
1072 };
1073 const std::string maskparent = repeat_str(hbar, colwidth);
1074 const std::string masknumber = repeat_str(hbar, numberwidth);
1075
1076 if (nbparents > 0) ss << std::setw(nbparents * (colwidth + 1) - 1) << " " << dvbar;
1077 ss << " " << std::setw(nbcol * (numberwidth + 1) - 3)
1078 << var.name().substr(0, nbcol * (numberwidth + 1) - 3) << vbar;
1079 ss << std::endl;
1080
1081 if (nbparents > 0) {
1082 for (Idx i = 1; i <= nbparents; i++)
1083 ss << std::setw(colwidth) << table->variable(i).name().substr(0, colwidth)
1084 << (i < (Idx)nbparents ? vbar : dvbar);
1085 }
1086 for (Idx i = 0; i < nbcol; i++)
1087 ss << std::setw(numberwidth) << var.label(i).substr(0, numberwidth) << vbar;
1088 ss << std::endl;
1089
1090
1091 if (nbparents > 0) {
1092 for (Idx i = 1; i <= nbparents; i++)
1093 ss << maskparent << (i < (Idx)nbparents ? vbar : dvbar);
1094 }
1095 for (Idx i = 0; i < nbcol; i++)
1096 ss << masknumber << vbar;
1097 ss << std::endl;
1098 Instantiation I(*table);
1099
1100 const auto drawligne = [&]() {
1101 if (nbparents > 0) {
1102 for (Idx i = 1; i <= nbparents; i++)
1103 ss << std::setw(colwidth) << table->variable(i).label(I.val(i)).substr(0, colwidth)
1104 << (i < (Idx)nbparents ? vbar : dvbar);
1105 }
1106 for (I.setFirstVar(var); !I.end(); I.incVar(var))
1107 ss << " " << std::setw(numberwidth - 1) << table->get(I) << vbar;
1108 I.setFirstVar(var);
1109 ss << std::endl;
1110 };
1111
1112 if (const Size nbrLig = table->domainSize() / var.domainSize(); nbrLig < nbrLigMax * 2 + 1) {
1113 for (I.setFirst(); !I.end(); I.incNotVar(var))
1114 drawligne();
1115 } else {
1116 Size cpt = 0;
1117 for (I.setFirst(); !I.end(); I.incNotVar(var)) {
1118 cpt++;
1119 if (cpt > nbrLigMax) break;
1120 drawligne();
1121 }
1122 ss << "[..." << nbrLig - nbrLigMax * 2 << " more line(s) ...]" << std::endl;
1123 I.setLast();
1124 for (Idx revi = 1; revi < nbrLigMax; revi++)
1125 I.decNotVar(var);
1126 for (I.setFirstVar(var); !I.end(); I.incNotVar(var)) {
1127 drawligne();
1128 }
1129 }
1130
1131 return ss.str();
1132 }
1133
1134 template < GUM_Numeric GUM_SCALAR >
1135 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::evEq(const DiscreteVariable& v, double val) {
1136 const auto i = v.closestIndex(val);
1137
1139 p.add(v);
1140 p.fillWith(0);
1141 Instantiation I(p);
1142 I.chgVal(0, i);
1143 p.set(I, 1);
1144 return p;
1145 }
1146
1147 template < GUM_Numeric GUM_SCALAR >
1148 Tensor< GUM_SCALAR >
1149 Tensor< GUM_SCALAR >::evIn(const DiscreteVariable& v, double val1, double val2) {
1150 if (val2 < val1) {
1152 "val2 (" << val2 << ") must be greater than val1 (" << val1 << ").")
1153 }
1154 const auto i1 = v.closestIndex(val1);
1155 const auto i2 = v.closestIndex(val2);
1156
1158 p.add(v);
1159 p.fillWith(0);
1160 Instantiation I(p);
1161 for (Idx i = i1; i <= i2; i++) {
1162 I.chgVal(0, i);
1163 p.set(I, 1);
1164 }
1165 return p;
1166 }
1167
1168 template < GUM_Numeric GUM_SCALAR >
1169 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::evGt(const DiscreteVariable& v, double val) {
1170 const auto i = v.closestIndex(val);
1171 if (i == v.domainSize() - 1) { return evEq(v, val); }
1172
1174 p.add(v);
1175 p.fillWith(0);
1176 Instantiation I(p);
1177 for (Idx i2 = i + 1; i2 < v.domainSize(); i2++) {
1178 I.chgVal(0, i2);
1179 p.set(I, 1);
1180 }
1181 return p;
1182 }
1183
1184 template < GUM_Numeric GUM_SCALAR >
1185 Tensor< GUM_SCALAR > Tensor< GUM_SCALAR >::evLt(const DiscreteVariable& v, double val) {
1186 const auto i = v.closestIndex(val);
1187 if (i == 0) { return evEq(v, val); }
1188
1190 p.add(v);
1191 p.fillWith(0);
1192 Instantiation I(p);
1193 for (Idx i2 = 0; i2 < i; i2++) {
1194 I.chgVal(0, i2);
1195 p.set(I, 1);
1196 }
1197 return p;
1198 }
1199
1200 template < GUM_Numeric GUM_SCALAR >
1202 return this->content()->realSize() * sizeof(GUM_SCALAR);
1203 }
1204
1205 template < GUM_Numeric GUM_SCALAR >
1207 Idx value) {
1208 Tensor< GUM_SCALAR > pot;
1209
1210 pot.beginMultipleChanges();
1211 pot << var;
1212 pot.endMultipleChanges(GUM_SCALAR(0.0));
1213
1214 Instantiation I(pot);
1215 I.chgVal(var, value);
1216 pot.set(I, GUM_SCALAR(1.0));
1217
1218 return pot;
1219 }
1220
1221 template < GUM_Numeric GUM_SCALAR >
1223 std::string_view value) {
1224 return Tensor< GUM_SCALAR >::deterministicTensor(var, var.index(value));
1225 }
1226
1227 template < GUM_Numeric GUM_SCALAR >
1229 Tensor< GUM_SCALAR > pot;
1230
1231 pot.beginMultipleChanges();
1232 pot << var;
1233 pot.endMultipleChanges(GUM_SCALAR(1.0));
1234
1235 pot.normalize();
1236
1237 return pot;
1238 }
1239} /* namespace gum */
Exception base for argument error.
Base class for discrete random variable.
virtual Idx closestIndex(double val) const =0
for numerical variables, returns the closest index for the value
virtual Idx index(std::string_view label) const =0
virtual Size domainSize() const =0
Exception : fatal (unknown ?) error.
The class for generic Hash Tables.
Definition hashTable.h:640
value_type & insert(const Key &key, const Val &val)
Adds a new element (actually a copy of this element) into the hash table.
bool exists(const Key &key) const
Checks whether there exists an element with a given key in the hashtable.
Class for assigning/browsing values to tuples of discrete variables.
Instantiation & chgVal(const DiscreteVariable &v, Idx newval)
Assign newval to variable v in the Instantiation.
bool end() const
Returns true if the Instantiation reached the end.
void inc()
Operator increment.
void incVar(const DiscreteVariable &v)
Operator increment for variable v only.
void setFirstNotVar(const DiscreteVariable &v)
Assign the first values to variables different of v.
void add(const DiscreteVariable &v) final
Adds a new variable in the Instantiation.
void setFirstVar(const DiscreteVariable &v)
Assign the first value in the Instantiation for var v.
void incNotVar(const DiscreteVariable &v)
Operator increment for vars which are not v.
Idx val(Idx i) const
Returns the current value of the variable at position i.
void setFirst()
Assign the first values to the tuple of the Instantiation.
void unsetEnd()
Alias for unsetOverflow().
void decNotVar(const DiscreteVariable &v)
Operator decrement for vars which are not v.
void setLast()
Assign the last values in the Instantiation.
Exception: at least one argument passed to a function is not what was expected.
Multidimensional matrix stored as an array in memory.
virtual void copyFrom(const MultiDimContainer< GUM_ELEMENT > &src) const
Basic copy of a MultiDimContainer.
virtual const MultiDimImplementation< GUM_ELEMENT > * content() const =0
Returns the implementation for this object (may be *this).
void populate(const std::vector< GUM_SCALAR > &v) const final
const MultiDimImplementation< GUM_SCALAR > * content() const final
void fill(const GUM_SCALAR &d) const final
const Sequence< const DiscreteVariable * > & variablesSequence() const final
MultiDimDecorator(MultiDimImplementation< GUM_SCALAR > *aContent=nullptr, GUM_SCALAR empty_value=(GUM_SCALAR) 0)
const DiscreteVariable & variable(Idx) const final
MultiDimDecorator< GUM_ELEMENT > & operator=(const MultiDimDecorator &from) noexcept
copy operator
GUM_SCALAR reduce(std::function< GUM_SCALAR(GUM_SCALAR, GUM_SCALAR) > f, GUM_SCALAR base) const final
void set(const Instantiation &i, const GUM_SCALAR &value) const final
MultiDimImplementation< GUM_SCALAR > * content_
void add(const DiscreteVariable &v) final
void apply(std::function< GUM_SCALAR(GUM_SCALAR) > f) const final
GUM_SCALAR get(const Instantiation &i) const final
virtual const Sequence< const DiscreteVariable * > & variablesSequence() const =0
Returns a const ref to the sequence of DiscreteVariable*.
virtual bool empty() const =0
Returns true if no var is in *this.
bool contains(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:468
bool exists(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:504
bool empty() const noexcept
Indicates whether the set is the empty set.
Definition set_tpl.h:613
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:510
Size size() const noexcept
Returns the number of elements in the set.
Definition set_tpl.h:607
aGrUM's Tensor is a multi-dimensional array with tensor operators.
Definition tensor.h:85
GUM_SCALAR mean() const
compute the mean of a numerical discrete random variable @raise
Definition tensor_tpl.h:249
Tensor< GUM_SCALAR > & operator-=(const Tensor< GUM_SCALAR > &r)
the function to be used to add two Tensors
const Tensor< GUM_SCALAR > & translate(GUM_SCALAR v) const
add v to (each value of) *this
Definition tensor_tpl.h:485
Tensor< GUM_SCALAR > & operator/=(const Tensor< GUM_SCALAR > &r)
the function to be used to add two Tensors
Tensor< GUM_SCALAR > sumOut(const gum::VariableSet &del_vars) const
Projection using sum as operation (and implementation-optimized operations).
Definition tensor_tpl.h:497
GUM_SCALAR variance() const
compute the variance of a numerical discrete random variable @raise
Definition tensor_tpl.h:262
Tensor()
Default constructor.
Definition tensor_tpl.h:62
Idx draw() const
get a value at random from a 1-D distribution
Definition tensor_tpl.h:736
GUM_SCALAR entropy() const
entropy of the Tensor
Definition tensor_tpl.h:283
Tensor< GUM_SCALAR > operator|(const Tensor< GUM_SCALAR > &p2) const
the function to be used to add two Tensors
Definition tensor_tpl.h:887
const Tensor< GUM_SCALAR > & log2() const
apply $log_2(x)$ on every element of the container
Definition tensor_tpl.h:379
Tensor< GUM_SCALAR > extract(const Instantiation &inst) const
create a new Tensor extracted from *this given a partial instantiation
Definition tensor_tpl.h:728
const Tensor< GUM_SCALAR > & random() const
generate a random Tensor with each parameter in [0,1]
Definition tensor_tpl.h:785
Tensor< GUM_SCALAR > new_sq() const
Create a new tensor and apply $x^2$ on every element of the container.
Definition tensor_tpl.h:842
Tensor< GUM_SCALAR > isNonZeroMap() const
create a boolean-like tensor using the predicate isNonZero
Definition tensor_tpl.h:637
Tensor< GUM_SCALAR > minOut(const gum::VariableSet &del_vars) const
Projection using min as operation (and implementation-optimized operations).
Definition tensor_tpl.h:541
GUM_SCALAR stdDev() const
compute the stdDev of a numerical discrete random variable
Definition tensor_tpl.h:277
Tensor< GUM_SCALAR > putFirst(const DiscreteVariable *var) const
create a new Tensor with a certain variable in first
Definition tensor_tpl.h:698
Tensor< GUM_SCALAR > maxIn(const gum::VariableSet &kept_vars) const
Projection using max as operation (and implementation-optimized operations).
Definition tensor_tpl.h:624
static Tensor< GUM_SCALAR > evEq(const DiscreteVariable &v, double val)
numerical evidence generator
Tensor< GUM_SCALAR > sumIn(const gum::VariableSet &kept_vars) const
Projection using sum as operation (and implementation-optimized operations).
Definition tensor_tpl.h:585
Tensor< GUM_SCALAR > reorganize(const std::vector< const DiscreteVariable * > &vars) const
create a new Tensor with another order
Definition tensor_tpl.h:658
std::pair< Set< Instantiation >, GUM_SCALAR > argmax() const
Pair of the set of instantiation corresponding to the max and this max in the Tensor.
Definition tensor_tpl.h:772
Tensor< GUM_SCALAR > operator&(const Tensor< GUM_SCALAR > &p2) const
the function to be used to add two Tensors
Definition tensor_tpl.h:902
GUM_SCALAR max() const
max of all elements in the Tensor
Definition tensor_tpl.h:175
Tensor< GUM_SCALAR > * newFactory() const final
Definition tensor_tpl.h:150
const Tensor< GUM_SCALAR > & sgn() const
apply sgn(x)$ on every element of the container
Definition tensor_tpl.h:385
Size memoryFootprint() const
compute the (approximated) footprint in memory of the tensor
Tensor< GUM_SCALAR > new_sgn() const
Create a new tensor and apply sgn(x)$ on every element of the container.
Definition tensor_tpl.h:852
GUM_SCALAR maxNonOne() const
max of all non one elements in the Tensor
Definition tensor_tpl.h:194
Tensor< GUM_SCALAR > prodIn(const gum::VariableSet &kept_vars) const
Projection using multiplication as operation (and implementation-optimized operations).
Definition tensor_tpl.h:598
bool operator==(const Tensor< GUM_SCALAR > &r) const
the function to be used to add two Tensors
static Tensor< GUM_SCALAR > deterministicTensor(const DiscreteVariable &var, Idx value)
Tensor< GUM_SCALAR > new_log2() const
Create a new tensor and apply $log_2(x)$ on every element of the container.
Definition tensor_tpl.h:847
static Tensor< GUM_SCALAR > uniformTensor(const DiscreteVariable &var)
static Tensor< GUM_SCALAR > evGt(const DiscreteVariable &v, double val)
numerical evidence generator
GUM_SCALAR minNonZero() const
min of all non zero elements in the Tensor
Definition tensor_tpl.h:215
~Tensor() final
Destructor.
Definition tensor_tpl.h:144
GUM_SCALAR KL(const Tensor< GUM_SCALAR > &p) const
compute KL divergence between this and p Checks the compatibility and then compute KL divergence
Definition tensor_tpl.h:391
Set< Instantiation > findAll(GUM_SCALAR v) const
set of instantiation corresponding to the parameter v in the Tensor
Definition tensor_tpl.h:758
static Tensor< GUM_SCALAR > evIn(const DiscreteVariable &v, double val1, double val2)
numerical evidence generator
Tensor< GUM_SCALAR > prodOut(const gum::VariableSet &del_vars) const
Projection using multiplication as operation (and implementation-optimized operations).
Definition tensor_tpl.h:519
const Tensor< GUM_SCALAR > & fillWith(const Tensor< GUM_SCALAR > &src) const
copy a Tensor data using name of variables and labels (not necessarily the same variables in the same...
Definition tensor_tpl.h:304
Tensor< GUM_SCALAR > maxOut(const gum::VariableSet &del_vars) const
Projection using max as operation (and implementation-optimized operations).
Definition tensor_tpl.h:563
const Tensor< GUM_SCALAR > & noising(GUM_SCALAR alpha) const
add a noise in a CPT by mixing (1-alpha)this+alpha.randomCPT()
Definition tensor_tpl.h:828
bool isEvidence() const
is an evidence ? (marginal-like but has not to sum to 1)
Definition tensor_tpl.h:879
Tensor< GUM_SCALAR > & operator=(const Tensor< GUM_SCALAR > &src)
Default constructor.
Definition tensor_tpl.h:124
gum::VariableSet _complementVars_(const gum::VariableSet &del_vars) const
Definition tensor_tpl.h:647
Tensor< GUM_SCALAR > operator/(const Tensor< GUM_SCALAR > &p2) const
the function to be used to divide two Tensors
Definition tensor_tpl.h:963
const Tensor< GUM_SCALAR > & normalizeAsCPT(const Idx &varId=0) const
normalisation of this as a CPT for the variable varId
Definition tensor_tpl.h:447
const Tensor< GUM_SCALAR > & normalize() const
normalisation of this do nothing if sum is 0
Definition tensor_tpl.h:432
GUM_SCALAR expectedValue(std::function< GUM_SCALAR(const gum::Instantiation &) >) const
ExpectedValue computes the expectation of f over *this.
Definition tensor_tpl.h:233
Tensor< GUM_SCALAR > new_abs() const
Create a new tensor and apply abs on every element of the container.
Definition tensor_tpl.h:837
const Tensor< GUM_SCALAR > & inverse() const
the function to inverse (each value of) *this
Definition tensor_tpl.h:491
Tensor< GUM_SCALAR > & operator<<(const DiscreteVariable &v)
the function to be used to add two Tensors
Definition tensor_tpl.h:857
Tensor< GUM_SCALAR > minIn(const gum::VariableSet &kept_vars) const
Projection using min as operation (and implementation-optimized operations).
Definition tensor_tpl.h:611
Tensor< GUM_SCALAR > operator*(const Tensor< GUM_SCALAR > &p2) const
the function to be used to multiply two Tensors
Definition tensor_tpl.h:948
Tensor< GUM_SCALAR > operator-(const Tensor< GUM_SCALAR > &p2) const
the function to be used to subtract two Tensors
Definition tensor_tpl.h:930
const Tensor< GUM_SCALAR > & scale(GUM_SCALAR v) const
multiply (each value of) *this by v
Definition tensor_tpl.h:479
GUM_SCALAR min() const
min of all elements in the Tensor
Definition tensor_tpl.h:184
Tensor< GUM_SCALAR > & operator*=(const Tensor< GUM_SCALAR > &r)
the function to be used to add two Tensors
Definition tensor_tpl.h:992
GUM_SCALAR product() const
product of all elements in the Tensor
Definition tensor_tpl.h:166
std::pair< Set< Instantiation >, GUM_SCALAR > argmin() const
Pair of the set of instantiation corresponding to the min and this min in the Tensor.
Definition tensor_tpl.h:779
const Tensor< GUM_SCALAR > & sq() const
apply $x^2$ on every element of the container
Definition tensor_tpl.h:373
std::string toString() const final
the function to be used to add two Tensors
const Tensor< GUM_SCALAR > & randomCPT() const
generate a random CPT in the Tensor
Definition tensor_tpl.h:810
GUM_SCALAR sum() const
sum of all elements in the Tensor
Definition tensor_tpl.h:157
static Tensor< GUM_SCALAR > evLt(const DiscreteVariable &v, double val)
numerical evidence generator
const Tensor< GUM_SCALAR > & abs() const
Apply abs on every element of the container.
Definition tensor_tpl.h:421
Tensor< GUM_SCALAR > & operator+=(const Tensor< GUM_SCALAR > &r)
the function to be used to add two Tensors
Definition tensor_tpl.h:980
Tensor< GUM_SCALAR > operator+(const Tensor< GUM_SCALAR > &p2) const
the function to be used to add two Tensors
Definition tensor_tpl.h:864
Tensor< GUM_SCALAR > operator~() const
the function to be used to add two Tensors
Definition tensor_tpl.h:917
const Tensor< GUM_SCALAR > & randomDistribution() const
generate a random Distribution in the Tensor
Definition tensor_tpl.h:799
Complete concept for GUM_SCALAR template parameter.
Definition concepts.h:148
#define GUM_ERROR(type, msg)
Definition exceptions.h:76
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition types.h:74
Size Idx
Type for indexes.
Definition types.h:79
bool contains(std::string_view s, std::string_view needle)
true if needle in s
bool isNumerical(std::string_view val)
return true is a string contains a numerical (double) value
double randomProba()
Returns a random double between 0 and 1 included (i.e.
std::vector< GUM_SCALAR > randomDistribution(Size n)
Return a random discrete distribution.
Useful macros for maths.
#define GUM_LOG2_OR_0(x)
Definition math_utils.h:70
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
Set< const DiscreteVariable * > VariableSet
GUM_ELEMENT projectProduct(const MultiDimImplementation< GUM_ELEMENT > &table, Instantiation *instantiation=0)
the function to be used to project a MultiDimImplementation using a Product
std::ostream & operator<<(std::ostream &stream, const AVLTree< Val, Cmp > &tree)
display the content of a tree
GUM_ELEMENT projectSum(const MultiDimImplementation< GUM_ELEMENT > &table, Instantiation *instantiation=0)
the function to be used to project a MultiDimImplementation using a sum
GUM_ELEMENT projectMin(const MultiDimImplementation< GUM_ELEMENT > &table, Instantiation *instantiation=0)
the function to be used to project a MultiDimImplementation using a Min
bool isCloseToOne(T x, T tol=T(1e-9))
GUM_ELEMENT projectMax(const MultiDimImplementation< GUM_ELEMENT > &table, Instantiation *instantiation=0)
the function to be used to project a MultiDimImplementation using a Max
STL namespace.
Header of the Tensor class.