tesseract++ 0.0.1
N-dimensional tensor library for embedded systems
Loading...
Searching...
No Matches
tensor.h
Go to the documentation of this file.
1#ifndef TENSORND_H
2#define TENSORND_H
3
4#include <algorithm> // for std::fill_n and std::copy
5#include <utility> // for std::move
6
7#include "memory/mem_utils.h"
8
9#include "config.h"
10#include "helper_traits.h"
11
12// Base class: TensorND
13template <typename T, my_size_t... Dims>
15{
16public:
17 // Default constructor
19 {
20 initTransposeOrder();
21 }
22
23 // Constructor to initialize all elements to a specific value
24 TensorND(T initValue)
25 {
26 initTransposeOrder();
27 std::fill_n(data_, totalSize, initValue);
28 }
29
30 // Copy constructor
31 TensorND(const TensorND &other)
32 {
33 std::copy(other.transposeOrder_, other.transposeOrder_ + getNumDims(), transposeOrder_);
34 transposeOrderSet_ = true;
35 initTransposeOrder();
36
37 std::copy(other.data_, other.data_ + totalSize, data_);
38 }
39
40 // Move constructor
41 TensorND(TensorND &&other) noexcept
42 {
43 std::copy(other.transposeOrder_, other.transposeOrder_ + getNumDims(), transposeOrder_);
44 transposeOrderSet_ = true;
45 initTransposeOrder();
46
47 std::move(other.data_, other.data_ + totalSize, data_);
48 }
49
50 // Variadic access operator for accessing tensor elements with separate indices
51 template <typename... Indices>
52 T &operator()(Indices... indices)
53 {
54 #ifdef COMPILETIME_CHECK_DIMENSIONS_COUNT_MISMATCH
55 static_assert(sizeof...(indices) == sizeof...(Dims), "Incorrect number of indices");
56 #endif
57
58 my_size_t idxArray[] = {static_cast<my_size_t>(indices)...}; // Convert indices to an array
59 return data_[computeIndex(idxArray)];
60 }
61
62 // Const version of the access operator
63 template <typename... Indices>
64 const T &operator()(Indices... indices) const
65 {
66 #ifdef COMPILETIME_CHECK_DIMENSIONS_COUNT_MISMATCH
67 static_assert(sizeof...(indices) == sizeof...(Dims), "Incorrect number of indices");
68 #endif
69
70 my_size_t idxArray[] = {static_cast<my_size_t>(indices)...};
71 return data_[computeIndex(idxArray)];
72 }
73
74 // version of passing a array of indices eg _tensor1(indices1), indices1 is an array of known size use template
75 template<my_size_t length>
76 T& operator()(my_size_t (&indices)[length])
77 {
78 #ifdef COMPILETIME_CHECK_DIMENSIONS_COUNT_MISMATCH
79 static_assert(length == sizeof...(Dims), "Incorrect number of indicessss");
80 #endif
81
82 return data_[computeIndex(indices)];
83 }
84
85 template<my_size_t length>
86 const T& operator()(my_size_t (&indices)[length]) const
87 {
88 #ifdef COMPILETIME_CHECK_DIMENSIONS_COUNT_MISMATCH
89 static_assert(length == sizeof...(Dims), "Incorrect number of indicessss");
90 #endif
91
92 return data_[computeIndex(indices)];
93 }
94
95 // overload == operator to compare two tensors, introduce a tolerance for floating point numbers
96 template <my_size_t... Dims1>
97 bool operator==(const TensorND<T, Dims1...> &other) const
98 {
99 // check for dimensions mismatch, we don't check if they are square
100 checkDimensionsMismatch(other);
101
102 my_size_t indices[sizeof...(Dims)] = {0};
103 for (my_size_t i = 0; i < totalSize; ++i)
104 {
105 // increment the indices using for loop
106 for (my_size_t j = 0; j < sizeof...(Dims); ++j)
107 {
108 if (indices[j] < getDim(j) - 1)
109 {
110 indices[j]++;
111 break;
112 }
113 else
114 {
115 indices[j] = 0;
116 }
117 }
118
119 // use the () operator to access the elements
120 if (std::abs((*this)(indices) - other(indices)) > T(PRECISION_TOLERANCE))
121 {
122 return false;
123 }
124 }
125 return true;
126 }
127
128 // overload != operator to compare two tensors
129 template <my_size_t... Dims1>
130 bool operator!=(const TensorND<T, Dims1...> &other) const
131 {
132 return !(*this == other);
133 }
134
135 // overload = operator to assign a tensor to the tensor
137 {
138 // std::cout << "Operator = called" << std::endl;
139 if (this == &other)
140 {
141 return *this;
142 }
143
144 // copy the transpose order
145 std::copy(other.transposeOrder_, other.transposeOrder_ + getNumDims(), transposeOrder_);
146 transposeOrderSet_ = true;
147
148 // copy the data
149 std::copy(other.data_, other.data_ + totalSize, data_);
150 return *this;
151 }
152
153 // overload + operator to add a scalar to the tensor
154 TensorND operator+(const T scalar) const
155 {
156 TensorND outp = *this;
157 for (my_size_t i = 0; i < totalSize; ++i)
158 {
159 outp.data_[i] += scalar;
160 }
161 return outp;
162 }
163
164 // overload + operator to add the tensor to a scalar
165 friend TensorND operator+(const T scalar, const TensorND& tensor)
166 {
167 return tensor + scalar;
168 }
169
170 // overload + operator to add a tensor to the tensor elementwise
171 template <my_size_t... Dims1>
173 {
174 // check for dimensions mismatch
175 checkDimensionsMismatch(other);
176
177 TensorND outp = *this;
178 my_size_t indices[sizeof...(Dims)] = {0};
179 for (my_size_t i = 0; i < totalSize; ++i)
180 {
181 // increment the indices using for loop
182 for (my_size_t j = 0; j < sizeof...(Dims); ++j)
183 {
184 if (indices[j] < getDim(j) - 1)
185 {
186 indices[j]++;
187 break;
188 }
189 else
190 {
191 indices[j] = 0;
192 }
193 }
194
195 // use the () operator to access the elements
196 outp(indices) += other(indices);
197 }
198 return outp;
199 }
200
201 // overload - operator to subtract a scalar from the tensor
202 TensorND operator-(const T scalar) const
203 {
204 TensorND outp = *this;
205 for (my_size_t i = 0; i < totalSize; ++i)
206 {
207 outp.data_[i] -= scalar;
208 }
209 return outp;
210 }
211
212 // overload - operator to subtract a scalar from the tensor
213 friend TensorND operator-(const T scalar, const TensorND& tensor)
214 {
215 // return tensor - scalar;
216 TensorND outp = tensor;
217 for (my_size_t i = 0; i < totalSize; ++i)
218 {
219 outp.data_[i] = scalar - outp.data_[i];
220 }
221 return outp;
222 }
223
224 // overload - operator to get the negative of the tensor
225 TensorND operator-(void) const
226 {
227 TensorND outp = *this;
228 for (my_size_t i = 0; i < totalSize; ++i)
229 {
230 outp.data_[i] = -outp.data_[i];
231 }
232 return outp;
233 }
234
235 // overload - operator to subtract a tensor from the tensor elementwise
236 template <my_size_t... Dims1>
238 {
239 // check for dimensions mismatch
240 checkDimensionsMismatch(other);
241
242 TensorND outp = *this;
243 my_size_t indices[sizeof...(Dims)] = {0};
244 for (my_size_t i = 0; i < totalSize; ++i)
245 {
246 // increment the indices using for loop
247 for (my_size_t j = 0; j < sizeof...(Dims); ++j)
248 {
249 if (indices[j] < getDim(j) - 1)
250 {
251 indices[j]++;
252 break;
253 }
254 else
255 {
256 indices[j] = 0;
257 }
258 }
259
260 // use the () operator to access the elements
261 outp(indices) -= other(indices);
262 }
263 return outp;
264 }
265
266 // overload * operator to multiply a scalar with the tensor
267 TensorND operator*(const T scalar) const
268 {
269 TensorND outp = *this;
270 for (my_size_t i = 0; i < totalSize; ++i)
271 {
272 outp.data_[i] *= scalar;
273 }
274 return outp;
275 }
276
277 // overload * operator to multiply a tensor with a scalar
278 friend TensorND operator*(const T scalar, const TensorND& tensor)
279 {
280 return tensor * scalar;
281 }
282
283 // overload an operator to multiply a tensor with a tensor elementwise
284 template <my_size_t... Dims1>
286 {
287 // check for dimensions mismatch
288 checkDimensionsMismatch(other);
289
290 TensorND outp = *this;
291 my_size_t indices[sizeof...(Dims)] = {0};
292 for (my_size_t i = 0; i < totalSize; ++i)
293 {
294 // increment the indices using for loop
295 for (my_size_t j = 0; j < sizeof...(Dims); ++j)
296 {
297 if (indices[j] < getDim(j) - 1)
298 {
299 indices[j]++;
300 break;
301 }
302 else
303 {
304 indices[j] = 0;
305 }
306 }
307
308 // use the () operator to access the elements
309 outp(indices) *= other(indices);
310 }
311 return outp;
312 }
313
314 // overload / operator to divide the tensor by a scalar, check for division by zero, account floats as well
315 TensorND operator/(const T scalar) const
316 {
317 if (scalar == 0)
318 {
319 MyErrorHandler::error("Division by zero");
320 }
321
322 return *this * (1 / scalar);
323 }
324
325 // overload / operator to divide a scalar by the tensor
326 friend TensorND operator/(const T scalar, const TensorND& tensor)
327 {
328 TensorND outp = tensor;
329 for (my_size_t i = 0; i < tensor.totalSize; ++i)
330 {
331 if (tensor.data_[i] == 0)
332 {
333 MyErrorHandler::error("Division by zero");
334 }
335 outp.data_[i] = scalar / tensor.data_[i];
336 }
337 return outp;
338 }
339
340 // overload / operator to divide the tensor by a tensor elementwise, check for division by zero
341 template <my_size_t... Dims1>
343 {
344 // check for dimensions mismatch
345 checkDimensionsMismatch(other);
346
347 TensorND outp = *this;
348 my_size_t indices[sizeof...(Dims)] = {0};
349 for (my_size_t i = 0; i < totalSize; ++i)
350 {
351 // increment the indices using for loop
352 for (my_size_t j = 0; j < sizeof...(Dims); ++j)
353 {
354 if (indices[j] < getDim(j) - 1)
355 {
356 indices[j]++;
357 break;
358 }
359 else
360 {
361 indices[j] = 0;
362 }
363 }
364
365 // use the () operator to access the elements
366 if (other(indices) == 0)
367 {
368 MyErrorHandler::error("Division by zero");
369 }
370 outp(indices) /= other(indices);
371 }
372 return outp;
373 }
374
375 // check if all dimensions are the same at compile time
376 constexpr bool areDimsEqual() const
377 {
378 for (my_size_t i = 0; i < getNumDims(); ++i)
379 {
380 if (dims[i] != dims[0])
381 {
382 return false;
383 }
384 }
385 return true;
386 }
387
388 bool isIdentity() const
389 {
390 // Check if the tensor is "square" (hypercube). If the tensor
391 // is not square, it cannot be identity -> return false
392 if (!areDimsEqual())
393 {
394 return false;
395 }
396
397 // Calculate all indices combinations for all dimensions
398 constexpr my_size_t total_combinations = (1 * ... * Dims); // fold expression to calculate the total number of combinations
399 my_size_t combinations[total_combinations][sizeof...(Dims)]; // 2D array to store all combinations
400 my_size_t max_vals[sizeof...(Dims)] = {Dims...}; // array to store the maximum values for each dimension
401 generate_combinations(max_vals, combinations); // generate all combinations
402
403 for (my_size_t i = 0; i < total_combinations; ++i)
404 {
405 // itterate over all dimensions
406 // if all indices are the same, then it's a diagonal element
407 bool isElementDiagonal = true;
408 for (my_size_t j = 0; j < getNumDims(); ++j)
409 {
410 if (combinations[i][j] != combinations[i][0])
411 {
412 isElementDiagonal = false;
413 break;
414 }
415 }
416
417 if (isElementDiagonal)
418 {
419 // if the element is diagonal, check if it is equal to 1.
420 // element - 1 must be greater than the precision tolerance
421 if (std::abs((*this)(combinations[i]) - 1) > PRECISION_TOLERANCE)
422 {
423 return false;
424 }
425 }
426 else
427 {
428 // if the element is not diagonal, check if it is equal to 0.
429 // element must be less than the precision tolerance
430 if (!(std::abs((*this)(combinations[i])) < PRECISION_TOLERANCE))
431 {
432 return false;
433 }
434 }
435 }
436 return true;
437 }
438
439 // non-inplace transpose function
440 TensorND transposed(const my_size_t order[sizeof...(Dims)]) const
441 {
442 TensorND outp = *this;
443 for (my_size_t i = 0; i < getNumDims(); ++i)
444 {
445 outp.transposeOrder_[i] = order[i];
446 }
447 return outp;
448 }
449
450 // inplace transpose function
451 void inplace_transpose(const my_size_t order[sizeof...(Dims)])
452 {
453 for (my_size_t i = 0; i < getNumDims(); ++i)
454 {
455 this->transposeOrder_[i] = order[i];
456 }
457 }
458
459 // non-inplace transpose function
461 {
462 // check if the tensor is 2D
463 static_assert(sizeof...(Dims) == 2, "Transpose is only supported for 2D tensors");
464
465 TensorND outp = *this;
466 // reverse the transpose order
467 if (outp.transposeOrder_[0] == 0)
468 {
469 outp.transposeOrder_[0] = 1;
470 outp.transposeOrder_[1] = 0;
471 }
472 else
473 {
474 outp.transposeOrder_[0] = 0;
475 outp.transposeOrder_[1] = 1;
476 }
477 return outp;
478 }
479
480 // inplace transpose function
482 {
483 // reverse the transpose order
484 if (this->transposeOrder_[0] == 0)
485 {
486 this->transposeOrder_[0] = 1;
487 this->transposeOrder_[1] = 0;
488 }
489 else
490 {
491 this->transposeOrder_[0] = 0;
492 this->transposeOrder_[1] = 1;
493 }
494 }
495
496 // Utility function to retrieve total number of elements
497 constexpr my_size_t getTotalSize() const
498 {
499 return totalSize;
500 }
501
502 // Utility function to retrieve the number of dimensions
503 constexpr my_size_t getNumDims() const
504 {
505 return sizeof...(Dims);
506 }
507
508 // Utility function to retrieve the shape of the tensor as (1,5,6) for a 3D tensor use the getNumDims
509 std::string getShape() const
510 // account for the trnaspose order as well
511 {
512 std::string shape = "(";
513 for (my_size_t i = 0; i < getNumDims(); ++i)
514 {
515 shape += std::to_string(getDim(i));
516 if (i < getNumDims() - 1)
517 shape += ",";
518 }
519 shape += ")";
520 return shape;
521 }
522
524 {
525 std::fill_n(data_, totalSize, 0);
526 return *this;
527 }
528
530 {
531 std::fill_n(data_, totalSize, _val);
532 return *this;
533 }
534
536 {
537 for (my_size_t i = 0; i < totalSize; ++i)
538 {
539 // TODO: seed the random number generator
540 data_[i] = static_cast<T>((rand() % (_maxRand - _minRand + 1)) + _minRand);
541 }
542 return *this;
543 }
544
545 // for all dimensions
547 {
548 static_assert(sizeof...(Dims) >= 2, "setDiagonal requires at least 2 dimensions.");
549
550 // set the entire matrix to zeros
551 setToZero();
552
553 // Calculate the minimum dimension
554 my_size_t minDim = std::min({Dims...}); // Using initializer list to find the minimum
555 my_size_t indices[numDims] = {0}; // Initialize all indices to zero
556
557 for (my_size_t i = 0; i < minDim; ++i)
558 {
559 // Set the current diagonal index for all dimensions
560 for (my_size_t d = 0; d < getNumDims(); ++d) {
561 indices[d] = i; // Set the diagonal index, others to zero
562 }
563
564 // Calculate the index in the flat array and set the value
565 data_[computeIndex(indices)] = _val;
566 }
567 return *this;
568 }
569
571 {
572 static_assert(sizeof...(Dims) >= 2, "Identity requires at least 2 dimensions.");
573 static_assert(all_equal<Dims...>(), "All dimensions must be equal for an identity tensor");
574
575 this->setDiagonal(1);
576 return *this;
577 }
578
579 static TensorND I(void)
580 {
581 static_assert(sizeof...(Dims) >= 2, "Identity requires at least 2 dimensions.");
582 static_assert(all_equal<Dims...>(), "All dimensions must be equal for an identity tensor");
583
584 TensorND<T, Dims...> _outp;
585 _outp.setDiagonal(1);
586 return _outp;
587 }
588
590 {
591 for (my_size_t i = 0; i < totalSize; ++i)
592 {
593 data_[i] = i;
594 }
595 return *this;
596 }
597
598 template<my_size_t DiagonalSize>
600 {
601 static_assert(sizeof...(Dims) >= 2, "Getting diagonal entries requires at least 2 dimensions.");
602 // Calculate the minimum dimension
603 my_size_t minDim = std::min({Dims...}); // Using initializer list to find the minimum
604 my_size_t indices[getNumDims()] = {0}; // Initialize all indices to zero
605
606 for (my_size_t i = 0; i < minDim; ++i)
607 {
608 // Set the current diagonal index for all dimensions
609 for (my_size_t d = 0; d < getNumDims(); ++d) {
610 indices[d] = i; // Set the diagonal index, others to zero
611 }
612
613 // Calculate the index in the flat array and set the value
614 diagonalEntries(i, 0) = data_[computeIndex(indices)];
615 }
616 }
617
618 // contract two tensors along a specific dimension (axis) and return the result
619 template <my_size_t... Dims1, my_size_t... Dims2>
620 static TensorND einsum(const TensorND<T, Dims1...>& _tensor1, const TensorND<T, Dims2...>& _tensor2, my_size_t a, my_size_t b)
621 {
622 static_assert(sizeof...(Dims1) >= 2 , "Tensor 1 must have at least 2 dimension");
623 static_assert(sizeof...(Dims2) >= 2 , "Tensor 2 must have at least 2 dimension");
624
625 // check if a and b are valid dimensions
626 if (a >= sizeof...(Dims1) || b >= sizeof...(Dims2))
627 {
628 MyErrorHandler::error("Invalid dimensions");
629 }
630
631 // check if the a axis of tensor1 is equal to the b axis of tensor2
632 if (_tensor1.getDim(a) != _tensor2.getDim(b))
633 {
634 MyErrorHandler::error("Dimensions mismatch");
635 }
636
637 // calculate the new dimensions
638 constexpr my_size_t n_newDims = sizeof...(Dims1) + sizeof...(Dims2) - 2;
639 my_size_t newDims[n_newDims];
640 my_size_t k = 0;
641 for (my_size_t i = 0; i < sizeof...(Dims1); ++i)
642 {
643 if (i != a)
644 {
645 newDims[k++] = _tensor1.getDim(i);
646 }
647 }
648
649 for (my_size_t i = 0; i < sizeof...(Dims2); ++i)
650 {
651 if (i != b)
652 {
653 newDims[k++] = _tensor2.getDim(i);
654 }
655 }
656
657 // print the new dimensions
658 // std::cout << "New dimensions: ";
659 // for (my_size_t i = 0; i < n_newDims; ++i)
660 // {
661 // std::cout << newDims[i] << " ";
662 // }
663 // std::cout << std::endl;
664
665 // create a new tensor with the new dimensions
666 TensorND<T, Dims...> _outp;
667
668 // check if the new dimensions one by one are the same as the dimensions of the new tensor
669 for (my_size_t i = 0; i < n_newDims; ++i)
670 {
671 if (newDims[i] != _outp.getDim(i))
672 {
673 MyErrorHandler::error("Dimensions mismatch");
674 }
675 }
676
677 // calculate the total number of combinations and create a 2D array to store them
678 constexpr my_size_t total_combinations = (1 * ... * Dims);
679 my_size_t combinations[total_combinations][n_newDims];
680
681 // generate all the combinations
682 generate_combinations(newDims, combinations);
683
684 // print_combinations(combinations);
685
686 // calculate the contraction
687 for (my_size_t comb = 0; comb < total_combinations; ++comb)
688 {
689 T sum = 0;
690
691 // // print the sum with the output tensor
692 // std::cout << std::endl << "---------------" << std::endl << "_outp(";
693 // for (my_size_t i = 0; i < n_newDims; ++i)
694 // {
695 // std::cout << combinations[comb][i] << (i < n_newDims - 1 ? ", " : "");
696 // }
697 // std::cout << ") = " << "sum" << ";" << std::endl << std::endl;
698
699 my_size_t K = _tensor1.getDim(a); // or _tensor2.getDim(b) since they are equal
700 for (my_size_t k = 0; k < K; ++k)
701 {
702 my_size_t indices1[sizeof...(Dims1)] = {0};
703 my_size_t indices2[sizeof...(Dims2)] = {0};
704
705 my_size_t l = 0;
706 for (my_size_t i = 0; i < sizeof...(Dims1); ++i)
707 {
708 if (i != a)
709 {
710 indices1[i] = combinations[comb][l++];
711 }
712 else
713 {
714 indices1[i] = k;
715 }
716 }
717
718 l = sizeof...(Dims1) - 1;
719 for (my_size_t i = 0; i < sizeof...(Dims2); ++i)
720 {
721 if (i != b)
722 {
723 indices2[i] = combinations[comb][l++];
724 }
725 else
726 {
727 indices2[i] = k;
728 }
729 }
730
731 // // print the sumation operation with the indices of the tensors
732 // std::cout << "Sum += _tensor1(";
733 // for (my_size_t i = 0; i < sizeof...(Dims1); ++i)
734 // {
735 // std::cout << indices1[i] << (i < sizeof...(Dims1) - 1 ? ", " : "");
736 // }
737 // std::cout << ") * _tensor2(";
738 // for (my_size_t i = 0; i < sizeof...(Dims2); ++i)
739 // {
740 // std::cout << indices2[i] << (i < sizeof...(Dims2) - 1 ? ", " : "");
741 // }
742 // std::cout << ");" << std::endl;
743
744 sum += _tensor1(indices1) * _tensor2(indices2);
745 }
746 _outp(combinations[comb]) = sum;
747 }
748 return _outp;
749 }
750
751 /* Insert submatrix into matrix at _posRow & _posCol position
752 * Example: A = Matrix 4x4, B = Matrix 2x3
753 *
754 * C = A.InsertSubMatrix(B, 1, 1);
755 *
756 * A = [A00 A01 A02 A03] B = [B00 B01 B02]
757 * [A10 A11 A12 A13] [B10 B11 B12]
758 * [A20 A21 A22 A23]
759 * [A30 A31 A32 A33]
760 *
761 *
762 * C = [A00 A01 A02 A03]
763 * [A10 B00 B01 B02]
764 * [A20 B10 B11 B12]
765 * [A30 A31 A32 A33]
766 */
767 // template<my_size_t... DimsB>
768 // template<typename... insertion_coordinates>
769 // TensorND& InsertSubMatrix(const TensorND<T, DimsB...>& _subMatrix, insertion_coordinates... _insertion_coordinates)
770 // {
771 // // check if the tensor is 2D
772 // static_assert(sizeof...(Dims) > 3, "InsertSubMatrix is only supported for MAX 3D tensors");
773 // static_assert(sizeof...(DimsB) > 3, "InsertSubMatrix is only supported for MAX 3D tensors");
774
775 // // check if the submatrix fits into the matrix
776 // if ((_subMatrix.dims[0] + _insertion_coordinates... > dims[0]) || (_subMatrix.dims[1] + _insertion_coordinates... > dims[1]))
777 // {
778 // MyErrorHandler::error("Submatrix does not fit into the matrix");
779 // }
780
781 // for (my_size_t i = 0; i < _subMatrix.dims[0]; ++i)
782 // {
783 // for (my_size_t j = 0; j < _subMatrix.dims[1]; ++j)
784 // {
785 // (*this)(_insertion_coordinates... + i, _insertion_coordinates... + j) = _subMatrix(i, j);
786 // }
787 // }
788 // return *this;
789 // }
790 // {
791 // // check if the tensor is 2D
792 // static_assert(sizeof...(Dims) == 2, "InsertSubMatrix is only supported for 2D tensors");
793 // static_assert(sizeof...(DimsB) == 2, "InsertSubMatrix is only supported for 2D tensors");
794
795 // // check if the submatrix fits into the matrix
796 // if ((_subMatrix.dims[0] + _posRow > dims[0]) || (_subMatrix.dims[1] + _posCol > dims[1]))
797 // {
798 // MyErrorHandler::error("Submatrix does not fit into the matrix");
799 // }
800
801 // for (my_size_t i = 0; i < _subMatrix.dims[0]; ++i)
802 // {
803 // for (my_size_t j = 0; j < _subMatrix.dims[1]; ++j)
804 // {
805 // (*this)(_posRow + i, _posCol + j) = _subMatrix(i, j);
806 // }
807 // }
808 // return *this;
809 // }
810
811 // Function to print the contents of the tensor
812 void print() const {
813 static_assert(sizeof...(Dims) <= 4, "Printing not supported for tensors with more than 4 dimensions");
814
815 if constexpr (sizeof...(Dims) == 1) {
816 print1D();
817 }
818 else if constexpr (sizeof...(Dims) == 2) {
819 print2D();
820 }
821 else if constexpr (sizeof...(Dims) == 3) {
822 print3D();
823 }
824 else if constexpr (sizeof...(Dims) == 4) {
825 print4D();
826 }
827 }
828
829 // getter for dims
831 {
832 return dims[transposeOrder_[i]];
833 }
834
835private:
836 // Calculate total number of elements at compile time
837 static constexpr my_size_t totalSize = (Dims * ...);
838 static constexpr my_size_t dims[] = {Dims...}; // Fixed array of dimensions
839 static constexpr my_size_t numDims = sizeof...(Dims);
840
841 // These vars are being set in runtime
842 my_size_t transposeOrder_[sizeof...(Dims)];
843 bool transposeOrderSet_ = false;
844 T data_[totalSize]; // Contiguous storage of elements in a flat array
845
846 template <my_size_t... Dims1>
847 inline void checkDimensionsMismatch(const TensorND<T, Dims1...> &other) const
848 {
849 // check if the dimensions of the tensors are the same taking into account the transpose order
850 for (my_size_t i = 0; i < getNumDims(); ++i)
851 {
852 if (this->getDim(i) != other.getDim(i))
853 {
854 MyErrorHandler::error("Dimensions mismatch");
855 }
856 }
857 }
858
859 template <my_size_t N, my_size_t M>
860 static void print_combinations(const my_size_t (&combinations)[M][N])
861 {
862 for (my_size_t i = 0; i < M; ++i)
863 {
865 for (my_size_t j = 0; j < N; ++j)
866 {
867 MyErrorHandler::log(combinations[i][j]);
868 MyErrorHandler::log(j < N - 1 ? ", " : " ");
869 }
870 MyErrorHandler::log("}\n");
871 }
872 }
873
874 // Template function to generate all combinations and store them in a 2D array
875 template <my_size_t N, my_size_t M>
876 static void generate_combinations(const my_size_t (&max_values)[N], my_size_t (&combinations)[M][N])
877 {
878 my_size_t combination[N] = {0}; // Initialize the first combination with all 0s
879
880 // Fill each row in `combinations` with the next combination
881 for (my_size_t row = 0; row < M; ++row)
882 {
883 for (my_size_t i = 0; i < N; ++i)
884 {
885 combinations[row][i] = combination[i];
886 }
887
888 // print the combination
889 // here you can calculate the contraction of the tensor
890 // if you don't want to store all the combinations
891 // you can calculate the contraction here
892 // for now comment this print statement
893 // for (my_size_t i = 0; i < N; ++i)
894 // {
895 // std::cout << combination[i] << ", ";
896 // }
897 // std::cout << std::endl;
898
899 // Increment combination like a counter with custom max values
900 int position = N - 1; // TODO: do not use int. Make the loop safe -> to not overflow
901 while (position >= 0)
902 {
903 ++combination[position];
904 if (combination[position] < max_values[position])
905 {
906 break;
907 }
908 combination[position] = 0;
909 --position;
910 }
911 }
912 }
913
914 // init the transpose order
915 void initTransposeOrder()
916 {
917 // check if the transpose order is preset
918 if (transposeOrderSet_)
919 {
920 return;
921 }
922
923 // if not set the transpose order to the default order
924 for (my_size_t i = 0; i < getNumDims(); ++i)
925 {
926 transposeOrder_[i] = i;
927 }
928 }
929
930 // 1D print function
931 void print1D() const {
932 for (my_size_t i = 0; i < getDim(0); ++i)
933 {
934 MyErrorHandler::log((*this)(i));
936 }
938 }
939
940 // 2D print function
941 void print2D() const {
942 // account for the trnaspose order as well
943 for (my_size_t i = 0; i < getDim(0); ++i)
944 {
945 for (my_size_t j = 0; j < getDim(1); ++j)
946 {
947 MyErrorHandler::log((*this)(i, j));
949 }
951 }
952 }
953
954 // 3D print function
955 void print3D() const {
956 for (my_size_t k = 0; k < getDim(2); ++k) {
957 for (my_size_t i = 0; i < getDim(0); ++i) {
958 for (my_size_t j = 0; j < getDim(1); ++j) {
959 MyErrorHandler::log((*this)(i, j, k));
961 }
963 }
965 }
966 }
967
968 void print4D() const {
969 for (my_size_t l = 0; l < getDim(3); ++l) {
970 MyErrorHandler::log("Slice [");
972 MyErrorHandler::log("]:\n");
973 for (my_size_t k = 0; k < getDim(2); ++k) {
974 MyErrorHandler::log(" Sub-Slice [");
976 MyErrorHandler::log("]:\n");
977 for (my_size_t i = 0; i < getDim(0); ++i) {
978 MyErrorHandler::log(" [ ");
979 for (my_size_t j = 0; j < getDim(1); ++j) {
980 MyErrorHandler::log(operator()(i, j, k, l));
982 }
984 }
986 }
988 }
989 }
990
991 // Compute the flat index from multi-dimensional indices
992 my_size_t computeIndex(const my_size_t indices[numDims]) const {
993 my_size_t index = 0;
994 my_size_t factor = 1;
995
996 // for (my_size_t i = getNumDims() - 1; i >= 0; --i) {
997 for (my_size_t i = getNumDims(); i-- > 0; ) {
998 my_size_t dimIndex = transposeOrder_[i]; // Get dimension according to transpose order
999
1000 #ifdef RUNTIME_USE_BOUNDS_CHECKING
1001 if (indices[dimIndex] >= dims[i]) {
1002 MyErrorHandler::error("Index out of range");
1003 }
1004 #endif
1005
1006 index += indices[dimIndex] * factor; // Use the indices in the transpose order
1007 factor *= dims[i]; // Update the factor for the next dimension
1008 }
1009 return index; // Return the computed flat index
1010 }
1011};
1012
1013#endif // TENSORND_H
static void log(const T &msg, ErrorLevel level=ErrorLevel::Plain)
Definition error_handler.h:18
static void error(const T &msg)
Definition error_handler.h:30
Definition tensor.h:15
TensorND & setSequencial(void)
Definition tensor.h:589
friend TensorND operator-(const T scalar, const TensorND &tensor)
Definition tensor.h:213
TensorND(const TensorND &other)
Definition tensor.h:31
TensorND()
Definition tensor.h:18
friend TensorND operator+(const T scalar, const TensorND &tensor)
Definition tensor.h:165
void inplace_transpose(void)
Definition tensor.h:481
std::string getShape() const
Definition tensor.h:509
friend TensorND operator*(const T scalar, const TensorND &tensor)
Definition tensor.h:278
TensorND & operator=(const TensorND &other)
Definition tensor.h:136
constexpr my_size_t getTotalSize() const
Definition tensor.h:497
TensorND & setRandom(my_size_t _maxRand, my_size_t _minRand)
Definition tensor.h:535
TensorND operator/(const T scalar) const
Definition tensor.h:315
TensorND operator*(const T scalar) const
Definition tensor.h:267
TensorND operator+(const TensorND< T, Dims1... > &other) const
Definition tensor.h:172
TensorND operator-(void) const
Definition tensor.h:225
TensorND operator-(const TensorND< T, Dims1... > &other) const
Definition tensor.h:237
TensorND & setDiagonal(T _val)
Definition tensor.h:546
my_size_t getDim(my_size_t i) const
Definition tensor.h:830
TensorND & setToZero(void)
Definition tensor.h:523
T & operator()(Indices... indices)
Definition tensor.h:52
const T & operator()(Indices... indices) const
Definition tensor.h:64
static TensorND I(void)
Definition tensor.h:579
TensorND & setIdentity(void)
Definition tensor.h:570
bool operator!=(const TensorND< T, Dims1... > &other) const
Definition tensor.h:130
constexpr my_size_t getNumDims() const
Definition tensor.h:503
TensorND operator*(const TensorND< T, Dims1... > &other) const
Definition tensor.h:285
void print() const
Definition tensor.h:812
TensorND(TensorND &&other) noexcept
Definition tensor.h:41
friend TensorND operator/(const T scalar, const TensorND &tensor)
Definition tensor.h:326
void getDiagonalEntries(TensorND< T, DiagonalSize, 1 > &diagonalEntries) const
Definition tensor.h:599
const T & operator()(my_size_t(&indices)[length]) const
Definition tensor.h:86
bool isIdentity() const
Definition tensor.h:388
constexpr bool areDimsEqual() const
Definition tensor.h:376
static TensorND einsum(const TensorND< T, Dims1... > &_tensor1, const TensorND< T, Dims2... > &_tensor2, my_size_t a, my_size_t b)
Definition tensor.h:620
void inplace_transpose(const my_size_t order[sizeof...(Dims)])
Definition tensor.h:451
TensorND operator/(const TensorND< T, Dims1... > &other) const
Definition tensor.h:342
TensorND operator-(const T scalar) const
Definition tensor.h:202
TensorND transposed(void) const
Definition tensor.h:460
bool operator==(const TensorND< T, Dims1... > &other) const
Definition tensor.h:97
TensorND transposed(const my_size_t order[sizeof...(Dims)]) const
Definition tensor.h:440
TensorND(T initValue)
Definition tensor.h:24
TensorND & setHomogen(T _val)
Definition tensor.h:529
T & operator()(my_size_t(&indices)[length])
Definition tensor.h:76
TensorND operator+(const T scalar) const
Definition tensor.h:154
Global configuration for the tesseract tensor library.
#define my_size_t
Size/index type used throughout the library.
Definition config.h:126
#define PRECISION_TOLERANCE
Tolerance for floating-point comparisons (e.g. symmetry checks, Cholesky).
Definition config.h:117
consteval bool all_equal()
Check if all values in a parameter pack are equal.
Definition helper_traits.h:20
STL-free memory utilities.
Expr::value_type sum(const BaseExpr< Expr > &expr)
Definition reductions.h:30