Tannic
A C++ Tensor Library
Loading...
Searching...
No Matches
tensor.hpp
Go to the documentation of this file.
1// Copyright 2025 Eric Hermosis
2//
3// This file is part of the Tannic Tensor Library.
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16//
17
18#ifndef TENSOR_HPP
19#define TENSOR_HPP
20
44#include <iostream>
45#include <memory>
46#include <cassert>
47#include <utility>
48#include <initializer_list>
49
50#include "types.hpp"
51#include "shape.hpp"
52#include "strides.hpp"
53#include "buffer.hpp"
54#include "slices.hpp"
55#include "views.hpp"
56#include "operations.hpp"
57#include "complex.hpp"
58#include "graph.hpp"
59
60namespace tannic {
61
99class Tensor {
100public:
101 using rank_type = uint8_t;
102 using size_type = std::size_t;
103
110 : dtype_(dtype)
111 , shape_(shape)
112 , strides_(shape_)
113 , offset_(0) {
114 std::size_t nelements = std::accumulate(shape_.begin(), shape_.end(), 1ULL, std::multiplies<>{});
115 nbytes_ = nbytesof(dtype, nelements);
116 }
117
125 Tensor(type dtype, Shape shape, Strides strides, std::ptrdiff_t offset = 0)
126 : dtype_(dtype)
127 , shape_(shape)
128 , strides_(strides)
129 , offset_(offset) {
130 if (rank() == 0) {
131 nbytes_ = dsizeof(dtype_);
132 }
133 else {
134 std::size_t nelements = 0;
135 std::size_t expected = 1;
136 for (std::size_t dimension = 0; dimension < rank(); ++dimension) {
137 nelements += strides_[dimension] * (shape_[dimension] - 1);
138 if (strides_[dimension] != expected) {
139 is_contiguous_ = false;
140 }
141 expected *= shape_[dimension];
142 }
143 nbytes_ = nbytesof(dtype, nelements + 1);
144 }
145 }
146
152 template <Expression Expression>
153 Tensor(const Expression& expression) {
154 *this = expression.forward();
155 }
156
163 template <Expression Expression>
164 Tensor& operator=(const Expression& expression) {
165 *this = expression.forward();
166 return *this;
167 }
168
169public:
172
174 type dtype() const {
175 return dtype_;
176 }
177
179 rank_type rank() const {
180 return shape_.rank();
181 }
182
184 Shape const& shape() const {
185 return shape_;
186 }
187
189 Shape::size_type size(int dimension) const {
190 return shape_[dimension];
191 }
192
194 Strides const& strides() const {
195 return strides_;
196 }
197
199 std::ptrdiff_t offset() const {
200 return offset_;
201 }
202
204 std::size_t nbytes() const {
205 return nbytes_;
206 }
207
209 bool is_contiguous() const {
210 return is_contiguous_;
211 }
212
215 if (!is_initialized())
216 initialize();
217 return *this;
218 }
219
221 Tensor const& forward() const {
222 if (!is_initialized())
223 initialize();
224 return *this;
225 }
227
228
229public:
232
238
243 std::byte* bytes() const {
244 return static_cast<std::byte*>(buffer_->address()) + offset_;
245 }
246
251 bool is_initialized() const {
252 return buffer_ ? true : false;
253 }
254
260 Environment const& environment() const {
261 if (!is_initialized())
262 throw Exception("Cannot get resource of an initializer tensor.");
263 return buffer_->environment();
264 }
266
267
268public:
284 template<typename T>
285 Tensor(std::initializer_list<T> const& values)
286 : dtype_(dtypeof<T>())
287 , shape_({values.size()})
288 , strides_(shape_)
289 , offset_(0) {
290 if (dtype_ == boolean) {
291 nbytes_ = (values.size() + 7) / 8;
292 initialize();
293 std::ptrdiff_t index = 0;
294 for (auto const& value : values) {
295 assign((bool const*)(&value), index);
296 ++index;
297 }
298 }
299
300 else {
301 nbytes_ = values.size() * dsizeof(dtype_);
302 initialize();
303 size_t index = 0;
304 for (auto const& value : values) {
305 assign((std::byte const*)(&value), index * dsizeof(dtype_));
306 ++index;
307 }
308 }
309 }
310
329 template<typename T>
330 Tensor(std::initializer_list<std::initializer_list<T>> const& values)
331 : dtype_(dtypeof<T>())
332 , shape_({values.size(), values.begin()->size()})
333 , strides_(shape_)
334 , offset_(0)
335 {
336
337 if (dtype_ == boolean) {
338 nbytes_ = (shape_[0] * shape_[1] + 7) / 8;
339 initialize();
340 std::ptrdiff_t index = 0;
341 for (auto const& row : values) {
342 if (row.size() != shape_[1])
343 throw Exception("All rows must have the same number of columns");
344 for (auto const& value : row) {
345 assign((bool const*)(&value), index);
346 ++index;
347 }
348 }
349 }
350
351 else {
352 nbytes_ = shape_[0] * shape_[1] * dsizeof(dtype_);
353 initialize();
354 size_t index = 0;
355 for (auto row : values) {
356 if (row.size() != shape_[1])
357 throw Exception("All rows must have the same number of columns");
358 for (auto const& value : row) {
359 assign((std::byte const*)(&value), index * dsizeof(dtype_));
360 ++index;
361 }
362 }
363 }
364 }
365
391 template<typename T>
392 Tensor(std::initializer_list<std::initializer_list<std::initializer_list<T>>> const& values)
393 : dtype_(dtypeof<T>())
394 , shape_({values.size(), values.begin()->size(), values.begin()->begin()->size()})
395 , strides_(shape_)
396 , offset_(0)
397 {
398 if (dtype_ == boolean) {
399 nbytes_ = (shape_[0] * shape_[1] * shape_[2] + 7) / 8;
400 initialize();
401 std::ptrdiff_t index = 0;
402 for (auto const& matrix : values) {
403 if (matrix.size() != shape_[1])
404 throw Exception("All matrices must have the same number of rows");
405 for (auto const& row : matrix) {
406 if (row.size() != shape_[2])
407 throw Exception("All rows must have the same number of columns");
408 for (auto const& value : row) {
409 assign((bool const*)(&value), index);
410 ++index;
411 }
412 }
413 }
414 }
415
416 else {
417 nbytes_ = std::accumulate(shape_.begin(), shape_.end(), 1ULL, std::multiplies<>{}) * dsizeof(dtype_);
418 initialize();
419 std::cout << shape_ << std::endl;
420 std::cout << nbytes_ << std::endl;
421 size_t index = 0;
422 for (auto const& matrix : values) {
423 if (matrix.size() != shape_[1])
424 throw Exception("All matrices must have the same number of rows");
425 for (auto const& row : matrix) {
426 if (row.size() != shape_[2])
427 throw Exception("All rows must have the same number of columns");
428 for (auto const& value : row) {
429 assign((std::byte const*)(&value), index * dsizeof(dtype_));
430 ++index;
431 }
432 }
433 }
434 }
435 }
436
437
474 template<typename T>
475 Tensor(std::initializer_list<std::initializer_list<std::initializer_list<std::initializer_list<T>>>> const& values)
476 : dtype_(dtypeof<T>())
477 , shape_({
478 values.size(),
479 values.begin()->size(),
480 values.begin()->begin()->size(),
481 values.begin()->begin()->begin()->size()
482 })
483 , strides_(shape_)
484 , offset_(0)
485 {
486
487 if (dtype_ == boolean) {
488 nbytes_ = (shape_[0] * shape_[1] * shape_[2] * shape_[3] + 7) / 8;
489 initialize();
490 std::ptrdiff_t index = 0;
491 for (auto const& tensor3D : values) {
492 if (tensor3D.size() != shape_[1])
493 throw Exception("All 3D tensors must have the same number of matrices");
494 for (auto const& matrix : tensor3D) {
495 if (matrix.size() != shape_[2])
496 throw Exception("All matrices must have the same number of rows");
497 for (auto const& row : matrix) {
498 if (row.size() != shape_[3])
499 throw Exception("All rows must have the same number of columns");
500 for (auto const& value : row) {
501 assign((bool const*)(&value), index);
502 ++index;
503 }
504 }
505 }
506 }
507 }
508
509 else {
510 nbytes_ = std::accumulate(shape_.begin(), shape_.end(), 1ULL, std::multiplies<>{}) * dsizeof(dtype_);
511 initialize();
512
513 size_t index = 0;
514 for (auto const& tensor3D : values) {
515 if (tensor3D.size() != shape_[1])
516 throw Exception("All 3D tensors must have the same number of matrices");
517 for (auto const& matrix : tensor3D) {
518 if (matrix.size() != shape_[2])
519 throw Exception("All matrices must have the same number of rows");
520 for (auto const& row : matrix) {
521 if (row.size() != shape_[3])
522 throw Exception("All rows must have the same number of columns");
523 for (auto const& value : row) {
524 assign((std::byte const*)(&value), index * dsizeof(dtype_));
525 ++index;
526 }
527 }
528 }
529 }
530 }
531 }
532
533
534public:
559 template<typename T>
560 void initialize(std::initializer_list<T> values, Environment environment = Host{}) {
561 if (!is_contiguous())
562 throw Exception("Assign to initializer list supported only for contiguous tensors");
563
564 if (rank() != 1 || shape_[0] != values.size())
565 throw Exception("Shape mismatch in assignment from initializer_list");
566
567 if (!is_initialized())
569
570 if (dtype_ == boolean) {
571 std::ptrdiff_t index = 0;
572 for (auto const& value : values) {
573 assign((bool const*)(&value), index);
574 ++index;
575 }
576 }
577
578 else {
579 auto fill = [this, &values](auto cast) {
580 using Cast = decltype(cast);
581 size_t index = 0;
582 for (auto value : values) {
583 Cast casted = value;
584 assign(expression::tobytes(casted), index * dsizeof(dtype_));
585 ++index;
586 }
587 };
588
589 switch (dtype_) {
590 case int8: fill(int8_t{}); break;
591 case int16: fill(int16_t{}); break;
592 case int32: fill(int32_t{}); break;
593 case int64: fill(int64_t{}); break;
594 case float32: fill(float{}); break;
595 case float64: fill(double{}); break;
596 default: throw Exception("Unsupported dtype in assignment");
597 }
598 }
599 }
600
627 template<typename T>
628 void initialize(std::initializer_list<std::initializer_list<T>> const & values, Environment environment = Host{}) {
629 if (!is_contiguous())
630 throw Exception("Assign to initializer list supported only for contiguous tensors");
631
632 if (rank() != 2 || shape_[0] != values.size() || shape_[1] != values.begin()->size())
633 throw Exception("Shape mismatch in assignment from nested initializer_list");
634
635 if (!is_initialized())
637
638 if (dtype_ == boolean) {
639 std::ptrdiff_t index = 0;
640 for (auto const& row : values) {
641 if (row.size() != shape_[1])
642 throw Exception("Row length mismatch in assignment from initializer_list");
643 for (auto const& value : row) {
644 assign((bool const*)(&value), index);
645 ++index;
646 }
647 }
648 }
649
650 else {
651 auto fill = [this, &values](auto cast) {
652 using Cast = decltype(cast);
653 size_t index = 0;
654 for (auto const& row : values) {
655 if (row.size() != shape_[1])
656 throw Exception("Row length mismatch in assignment from initializer_list");
657
658 for (auto value : row) {
659 Cast casted = value;
660 assign(expression::tobytes(casted), index * dsizeof(dtype_));
661 ++index;
662 }
663 }
664 };
665
666 switch (dtype_) {
667 case int8: fill(int8_t{}); break;
668 case int16: fill(int16_t{}); break;
669 case int32: fill(int32_t{}); break;
670 case int64: fill(int64_t{}); break;
671 case float32: fill(float{}); break;
672 case float64: fill(double{}); break;
673 default: throw Exception("Unsupported dtype in assignment");
674 }
675 }
676 }
677
678
679
680
710 template<typename T>
711 void initialize(std::initializer_list<std::initializer_list<std::initializer_list<T>>> const& values, Environment environment = Host{}) {
712 if (!is_contiguous())
713 throw Exception("Assign to initializer list supported only for contiguous tensors");
714
715 if (rank() != 3
716 || shape_[0] != values.size()
717 || shape_[1] != values.begin()->size()
718 || shape_[2] != values.begin()->begin()->size())
719 throw Exception("Shape mismatch in assignment from triple-nested initializer_list");
720
721 if (!is_initialized())
723
724 if (dtype_ == boolean) {
725 std::ptrdiff_t index = 0;
726 for (auto const& matrix : values) {
727 if (matrix.size() != shape_[1])
728 throw Exception("Matrix row count mismatch");
729 for (auto const& row : matrix) {
730 if (row.size() != shape_[2])
731 throw Exception("Row length mismatch");
732 for (auto const& value : row) {
733 assign((bool const*)(&value), index);
734 ++index;
735 }
736 }
737 }
738 }
739
740 else {
741 auto fill = [this, &values](auto cast) {
742 using Cast = decltype(cast);
743 size_t index = 0;
744 for (auto const& matrix : values) {
745 if (matrix.size() != shape_[1])
746 throw Exception("Matrix row count mismatch");
747 for (auto const& row : matrix) {
748 if (row.size() != shape_[2])
749 throw Exception("Row length mismatch");
750 for (auto value : row) {
751 Cast casted = value;
752 assign(expression::tobytes(casted), index * dsizeof(dtype_));
753 ++index;
754 }
755 }
756 }
757 };
758
759 switch (dtype_) {
760 case int8: fill(int8_t{}); break;
761 case int16: fill(int16_t{}); break;
762 case int32: fill(int32_t{}); break;
763 case int64: fill(int64_t{}); break;
764 case float32: fill(float{}); break;
765 case float64: fill(double{}); break;
766 default: throw Exception("Unsupported dtype in assignment");
767 }
768 }
769}
770
811 template<typename T>
812 void initialize(std::initializer_list<std::initializer_list<std::initializer_list<std::initializer_list<T>>>> const & values, Environment environment = Host{}) {
813 if (!is_contiguous())
814 throw Exception("Assign to initializer list supported only for contiguous tensors");
815
816 if (rank() != 4
817 || shape_[0] != values.size()
818 || shape_[1] != values.begin()->size()
819 || shape_[2] != values.begin()->begin()->size()
820 || shape_[3] != values.begin()->begin()->begin()->size())
821 throw Exception("Shape mismatch in assignment from quadruple-nested initializer_list");
822
823 if (!is_initialized())
825
826 if (dtype_ == boolean) {
827 std::ptrdiff_t index = 0;
828 for (auto const& tensor3D : values) {
829 if (tensor3D.size() != shape_[1])
830 throw Exception("3D tensor count mismatch");
831
832 for (auto const& matrix : tensor3D) {
833 if (matrix.size() != shape_[2])
834 throw Exception("Matrix row count mismatch");
835
836 for (auto const& row : matrix) {
837 if (row.size() != shape_[3])
838 throw Exception("Row length mismatch");
839
840 for (auto const& value : row) {
841 assign((bool const*)(&value), index);
842 ++index;
843 }
844 }
845 }
846 }
847 }
848
849 else {
850 auto fill = [this, &values](auto cast) {
851 using Cast = decltype(cast);
852 size_t index = 0;
853 for (auto const& tensor3D : values) {
854 if (tensor3D.size() != shape_[1])
855 throw Exception("3D tensor count mismatch");
856
857 for (auto const& matrix : tensor3D) {
858 if (matrix.size() != shape_[2])
859 throw Exception("Matrix row count mismatch");
860
861 for (auto const& row : matrix) {
862 if (row.size() != shape_[3])
863 throw Exception("Row length mismatch");
864
865 for (auto value : row) {
866 Cast casted = value;
867 assign(expression::tobytes(casted), index * dsizeof(dtype_));
868 ++index;
869 }
870 }
871 }
872 }
873 };
874
875 switch (dtype_) {
876 case int8: fill(int8_t{}); break;
877 case int16: fill(int16_t{}); break;
878 case int32: fill(int32_t{}); break;
879 case int64: fill(int64_t{}); break;
880 case float32: fill(float{}); break;
881 case float64: fill(double{}); break;
882 default: throw Exception("Unsupported dtype in assignment");
883 }
884 }
885 }
886
887public:
890
914 template<Integral Index>
915 auto operator[](Index index) const {
916 if (!is_initialized())
917 initialize();
918 return expression::Slice<Tensor, Index>(*this, std::make_tuple(index));
919 }
920
936 if (!is_initialized())
937 initialize();
938 return expression::Slice<Tensor, indexing::Range>(*this, std::make_tuple(range));
939 }
940
958 template<class ... Indexes>
959 auto operator[](Indexes... indexes) const {
960 if (!is_initialized())
961 initialize();
962 return expression::Slice<Tensor, Indexes...>(*this, std::make_tuple(indexes...));
963 }
964
965
966
974 auto transpose(int first = -1, int second = -2) const {
975 if (!is_initialized())
976 initialize();
977 return expression::Transpose<Tensor>(*this, std::make_pair<int, int>(std::move(first), std::move(second)));
978 }
979
986 template<Integral ... Indexes>
987 auto permute(Indexes... indexes) const {
988 if (!is_initialized())
989 initialize();
990 return expression::Permutation<Tensor, Indexes...>(*this, std::make_tuple(indexes...));
991 }
992
999 template<Integral ... Sizes>
1000 auto view(Sizes... sizes) const {
1001 if (!is_initialized())
1002 initialize();
1003 return expression::View<Tensor>(*this, sizes...);
1004 }
1005
1018 auto squeeze() const {
1019 if (!is_initialized())
1020 initialize();
1021 return expression::Squeeze<Tensor>(*this);
1022 }
1023
1024
1039 template<Integral... Axes>
1040 auto unsqueeze(Axes... axes) {
1041 return expression::Unsqueeze<Tensor>(*this, axes...);
1042 }
1044
1045public:
1046
1047 Tensor(type dtype, Shape shape, std::ptrdiff_t offset, std::shared_ptr<Buffer> storage)
1048 : dtype_(dtype)
1049 , shape_(shape)
1050 , strides_(shape)
1051 , offset_(offset)
1052 , buffer_(std::move(storage))
1053 {
1054 std::size_t nelements = std::accumulate(shape_.begin(), shape_.end(), 1ULL, std::multiplies<>{});
1055 nbytes_ = nbytesof(dtype_, nelements);
1056 node_ = std::make_shared<Node>(*this);
1057 }
1058
1059 Tensor(type dtype, Shape shape, Strides strides, std::ptrdiff_t offset, std::shared_ptr<Buffer> storage)
1060 : dtype_(dtype)
1061 , shape_(shape)
1062 , strides_(strides)
1063 , offset_(offset)
1064 , buffer_(std::move(storage))
1065 {
1066 if (rank() == 0) {
1067 nbytes_ = nbytesof(dtype_, 1);
1068 }
1069 else {
1070 std::size_t nelements = 0;
1071 std::size_t expected = 1;
1072 for (std::size_t dimension = 0; dimension < rank(); ++dimension) {
1073 nelements += strides_[dimension] * (shape_[dimension] - 1);
1074 if (strides_[dimension] != expected) {
1075 is_contiguous_ = false;
1076 }
1077 expected *= shape_[dimension];
1078 }
1079 nbytes_ = nbytesof(dtype, nelements + 1);
1080 }
1081 node_ = std::make_shared<Node>(*this);
1082 }
1083
1084protected:
1085 template <Expression Source, class... Indexes>
1086 friend class expression::Slice;
1087
1088 template <Expression Source>
1090
1091 template <Expression Source>
1092 friend class expression::View;
1093
1094 template <Expression Source>
1095 friend class expression::Squeeze;
1096
1097 template <Expression Source>
1099
1100 template <Expression Source, Integral... Indexes>
1102
1103 template <Expression Source>
1105
1106 template <Expression Source>
1108
1109 template <class Coordinates, Expression... Sources>
1111
1112 template <Expression Source>
1114
1115 void assign(std::byte const*, std::ptrdiff_t);
1116 void assign(bool const*, std::ptrdiff_t);
1117
1118 bool compare(std::byte const*, std::ptrdiff_t) const;
1119
1120
1121public:
1122 Node* node() const {
1123 return node_.get();
1124 }
1125
1126private:
1127 // Note: I didn't decide yet if put all this inside Node.
1128 // That will speed tensor copies but disallow make the tensors
1129 // only runtime since shared ptrs don't support constexpr.
1130 // For now kernel optimization is more important.
1131 type dtype_;
1132 Shape shape_;
1133 Strides strides_;
1134 std::size_t nbytes_ = 0;
1135 std::ptrdiff_t offset_ = 0;
1136 mutable std::shared_ptr<Buffer> buffer_ = nullptr;
1137 mutable std::shared_ptr<Node> node_ = nullptr;
1138 bool is_contiguous_ = true;
1139};
1140
1141std::ostream& operator<<(std::ostream& ostream, Tensor const& tensor);
1142
1143template<Expression Source>
1144inline std::ostream& operator<<(std::ostream& ostream, Source source) {
1145 Tensor tensor = source.forward();
1146 ostream << tensor;
1147 return ostream;
1148}
1149
1150template<class Operation, Expression Operand>
1152 Tensor result(dtype(), shape(), strides(), offset());
1153 operation.forward(operand, result);
1154 return result;
1155}
1156
1157template<Operator Operation, Expression Operand, Expression Cooperand>
1159 Tensor result(dtype(), shape(), strides());
1160 operation.forward(operand, cooperand, result);
1161 return result;
1162}
1163
1164template<Expression Source>
1166 Tensor source = source_.forward();
1167 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1168}
1169
1170template<Expression Source>
1172 Tensor source = source_.forward();
1173 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1174}
1175
1176template<Expression Source>
1178 Tensor source = source_.forward();
1179 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1180}
1181
1182template<Expression Source>
1184 Tensor source = source_.forward();
1185 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1186}
1187
1188template<Expression Source, Integral... Indexes>
1190 Tensor source = source_.forward();
1191 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1192}
1193
1194template<Expression Source>
1196 Tensor source = source_.forward();
1197 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1198}
1199
1200template<Expression Source>
1202 Tensor source = source_.forward();
1203 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1204}
1205
1206template<Expression Source, class... Indexes>
1208 Tensor source = source_.forward();
1209 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1210}
1211
1212template<Expression Source, class... Indexes>
1213void expression::Slice<Source, Indexes...>::assign(std::byte const* value, std::ptrdiff_t offset) {
1214 if constexpr (Trait<Source>::is_assignable) {
1215 source_.assign(value, offset);
1216 }
1217
1218 else {
1219 Tensor tensor = forward();
1220 tensor.assign(value, offset);
1221 }
1222}
1223
1224template<Expression Source, class... Indexes>
1225void expression::Slice<Source, Indexes...>::assign(bool const* value, std::ptrdiff_t offset) {
1226 if constexpr (Trait<Source>::is_assignable) {
1227 source_.assign(value, offset);
1228 }
1229
1230 else {
1231 Tensor tensor = forward();
1232 tensor.assign(value, offset);
1233 }
1234}
1235
1236
1237template<Expression Source, class... Indexes>
1238bool expression::Slice<Source, Indexes...>::compare(std::byte const* value, std::ptrdiff_t offset) const {
1239 if constexpr (Trait<Source>::is_comparable) {
1240 return source_.compare(value, offset);
1241 }
1242
1243 else {
1244 Tensor tensor = forward();
1245 return tensor.compare(value, offset);
1246 }
1247}
1248
1249template<class Coordinates, Expression Source>
1251 Tensor real = source.forward();
1252 Tensor complex(dtype(), shape(), strides(), offset(), real.buffer_);
1253 return complex;
1254}
1255
1256template<class Coordinates, Expression Real, Expression Imaginary>
1258 Tensor result(dtype(), shape(), strides(), offset());
1259 Coordinates::forward(real.forward(), imaginary.forward(), result);
1260 return result;
1261}
1262
1263template<Expression Source>
1265 Tensor complex = source.forward();
1266 Tensor real(dtype(), shape(), strides(), offset(), complex.buffer_);
1267 return real;
1268}
1269
1270} // namespace tannic
1271
1272#endif // TENSOR_HPP
Memory buffer management for tensor storage.
A simple generic exception type for the Tannic Tensor Library.
Definition: exceptions.hpp:44
Host memory domain.
Definition: resources.hpp:60
Represents the shape (dimensions) of an tensor-like expression.
Definition: shape.hpp:79
constexpr rank_type rank() const noexcept
Returns the number of dimensions (rank).
Definition: shape.hpp:207
constexpr auto begin()
Definition: shape.hpp:215
size_t size_type
Type used for size and shape dimensions.
Definition: shape.hpp:85
constexpr auto end()
Definition: shape.hpp:219
Represents the memory strides associated with a tensor shape.
Definition: strides.hpp:87
A multidimensional, strided tensor data structure.
Definition: tensor.hpp:99
void initialize(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< T > > > > const &values, Environment environment=Host{})
Assigns values to a 4D tensor from a quadruple-nested initializer list.
Definition: tensor.hpp:812
auto operator[](Indexes... indexes) const
Indexes the tensor with multiple indices (e.g., integers or ranges).
Definition: tensor.hpp:959
std::byte * bytes() const
Returns a pointer to the beginning of the tensor's data (accounting for offset).
Definition: tensor.hpp:243
Environment const & environment() const
Returns a reference to the environment variant used to allocate this tensor's buffer.
Definition: tensor.hpp:260
Tensor & forward()
Returns a reference to this tensor (const-qualified).
Definition: tensor.hpp:214
auto transpose(int first=-1, int second=-2) const
Returns a view of the tensor with two dimensions transposed.
Definition: tensor.hpp:974
Tensor(std::initializer_list< std::initializer_list< T > > const &values)
Constructs a 2D tensor from a nested initializer list.
Definition: tensor.hpp:330
void initialize(Environment environment=Host{}) const
Allocates the memory buffer for the tensor.
Tensor(type dtype, Shape shape, Strides strides, std::ptrdiff_t offset=0)
Constructs an uninitialized tensor with custom strides and offset.
Definition: tensor.hpp:125
rank_type rank() const
Returns the number of dimensions (rank) of the tensor.
Definition: tensor.hpp:179
void initialize(std::initializer_list< T > values, Environment environment=Host{})
Assigns values to a 1D tensor from an initializer list.
Definition: tensor.hpp:560
std::size_t nbytes() const
Returns the total number of bytes occupied by the tensor's elements.
Definition: tensor.hpp:204
Tensor(std::initializer_list< std::initializer_list< std::initializer_list< T > > > const &values)
Constructs a 3D tensor from a triple-nested initializer list.
Definition: tensor.hpp:392
Tensor(type dtype, Shape shape)
Constructs an uninitialized tensor with default (contiguous) strides.
Definition: tensor.hpp:109
Node * node() const
Definition: tensor.hpp:1122
auto operator[](indexing::Range range) const
Indexes the tensor with a Range object.
Definition: tensor.hpp:935
auto squeeze() const
Returns a view of the tensor with all dimensions of size 1 removed.
Definition: tensor.hpp:1018
Shape::size_type size(int dimension) const
Returns the tensor's size at a given dimension.
Definition: tensor.hpp:189
bool is_initialized() const
Checks whether the tensor has been initialized with memory.
Definition: tensor.hpp:251
std::ptrdiff_t offset() const
Returns the offset of the tensor in the current buffer.
Definition: tensor.hpp:199
std::size_t size_type
Type used for size and shape dimensions.
Definition: tensor.hpp:102
Shape const & shape() const
Returns the tensor's shape (dimension sizes per dimension).
Definition: tensor.hpp:184
Tensor(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< T > > > > const &values)
Constructs a 4D tensor from a quadruple-nested initializer list.
Definition: tensor.hpp:475
Strides const & strides() const
Returns the tensor's strides (step sizes per dimension).
Definition: tensor.hpp:194
Tensor & operator=(const Expression &expression)
Assigns the result of an expression to the tensor.
Definition: tensor.hpp:164
bool is_contiguous() const
Returns whether the tensor's elements are in contiguous layout or not.
Definition: tensor.hpp:209
uint8_t rank_type
Type used for rank (number of dimensions).
Definition: tensor.hpp:101
auto permute(Indexes... indexes) const
Returns a view of the tensor with dimensions permuted.
Definition: tensor.hpp:987
auto unsqueeze(Axes... axes)
Returns a view of the tensor with a dimension of size 1 inserted at the given axis.
Definition: tensor.hpp:1040
Tensor(type dtype, Shape shape, Strides strides, std::ptrdiff_t offset, std::shared_ptr< Buffer > storage)
Definition: tensor.hpp:1059
Tensor(const Expression &expression)
Constructs a tensor by forwarding an Expression-like object.
Definition: tensor.hpp:153
auto view(Sizes... sizes) const
Returns a view of the tensor with given sizes.
Definition: tensor.hpp:1000
Tensor(std::initializer_list< T > const &values)
Constructs a 1D tensor from an initializer list.
Definition: tensor.hpp:285
void initialize(std::initializer_list< std::initializer_list< std::initializer_list< T > > > const &values, Environment environment=Host{})
Assigns values to a 3D tensor from a triple-nested initializer list.
Definition: tensor.hpp:711
auto operator[](Index index) const
Indexes the tensor with a single integral index.
Definition: tensor.hpp:915
Tensor(type dtype, Shape shape, std::ptrdiff_t offset, std::shared_ptr< Buffer > storage)
Definition: tensor.hpp:1047
void assign(std::byte const *, std::ptrdiff_t)
type dtype() const
Returns the tensor's data type.
Definition: tensor.hpp:174
bool compare(std::byte const *, std::ptrdiff_t) const
void initialize(std::initializer_list< std::initializer_list< T > > const &values, Environment environment=Host{})
Assigns values to a 2D tensor from a nested initializer list.
Definition: tensor.hpp:628
Tensor const & forward() const
Returns a reference to this tensor (non-const).
Definition: tensor.hpp:221
void assign(bool const *, std::ptrdiff_t)
Creates a complex tensor view from real components.
Definition: complex.hpp:86
Expression template for expanding (broadcasting) singleton dimensions of a tensor.
Definition: views.hpp:398
Tensor forward() const
Definition: tensor.hpp:1201
Expression template for flattening a contiguous range of dimensions.
Definition: views.hpp:732
Tensor forward() const
Definition: tensor.hpp:1183
Expression template for reordering tensor dimensions according to a specified permutation.
Definition: views.hpp:303
Tensor forward() const
Definition: tensor.hpp:1189
Creates a real-valued view of complex tensor data.
Definition: complex.hpp:290
Tensor forward() const
Definition: tensor.hpp:1264
Expression template representing a tensor slice or subview.
Definition: slices.hpp:87
Tensor forward() const
Definition: tensor.hpp:1207
bool compare(std::byte const *value, std::ptrdiff_t offset) const
Definition: tensor.hpp:1238
void assign(std::byte const *value, std::ptrdiff_t offset)
Definition: tensor.hpp:1213
Expression template for removing singleton dimensions from a tensor.
Definition: views.hpp:523
Tensor forward() const
Definition: tensor.hpp:1171
Expression template for transposing two dimensions of a tensor.
Definition: views.hpp:211
Tensor forward() const
Definition: tensor.hpp:1195
Expression template for inserting singleton dimensions into a tensor.
Definition: views.hpp:619
Tensor forward() const
Definition: tensor.hpp:1177
Expression template for viewing a tensor with a new shape.
Definition: views.hpp:84
Tensor forward() const
Definition: tensor.hpp:1165
Tensor forward() const
Definition: tensor.hpp:1158
Tensor forward() const
Evaluates the unary expression and returns a Tensor.
Definition: tensor.hpp:1151
Complex number operations for the Tannic Tensor Library.
Defines the core protocol for all expression-like types in the Tannic Tensor Library.
Definition: concepts.hpp:86
Requires a type to be an integral type (e.g., int, std::size_t).
Definition: concepts.hpp:147
std::byte const * tobytes(T const &reference)
Definition: slices.hpp:343
constexpr auto complex(Real &&real, Imaginary &&imaginary)
Creates complex tensor from separate real and imaginary tensors
Definition: complex.hpp:426
Definition: buffer.hpp:41
constexpr type dtypeof()
Definition: types.hpp:224
std::ostream & operator<<(std::ostream &os, Shape const &shape)
Definition: shape.hpp:313
constexpr std::size_t nbytesof(type dtype, std::size_t nelements)
Returns the total number of bytes required to store nelements elements of the given data type.
Definition: types.hpp:127
std::variant< Host, Device > Environment
Memory environment variant type.
Definition: resources.hpp:204
constexpr std::size_t dsizeof(type type)
Returns the size in bytes of a given tensor data type.
Definition: types.hpp:93
Definition: graph.hpp:29
Definition: traits.hpp:27
Represents a half-open interval [start, stop) for slicing.
Definition: indexing.hpp:56