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 nelements_ = std::accumulate(shape_.begin(), shape_.end(), 1ULL, std::multiplies<>{});
115 }
116
124 Tensor(type dtype, Shape shape, Strides strides, std::ptrdiff_t offset = 0)
125 : dtype_(dtype)
126 , shape_(shape)
127 , strides_(strides)
128 , offset_(offset)
129 {
130 std::size_t expected = 1;
131 for (std::size_t dimension = 0; dimension < rank(); ++dimension) {
132 std::size_t index = rank() - 1 - dimension;
133 if (strides_[index] != expected) {
134 is_contiguous_ = false;
135 }
136 nelements_ *= shape_[index];
137 expected *= shape_[index];
138 }
139 }
140
141
147 template <Expression Expression>
148 Tensor(const Expression& expression) {
149 *this = expression.forward();
150 }
151
158 template <Expression Expression>
159 Tensor& operator=(const Expression& expression) {
160 *this = expression.forward();
161 return *this;
162 }
163
164public:
167
169 type dtype() const {
170 return dtype_;
171 }
172
174 rank_type rank() const {
175 return shape_.rank();
176 }
177
179 Shape const& shape() const {
180 return shape_;
181 }
182
184 Shape::size_type size(int dimension) const {
185 return shape_[dimension];
186 }
187
189 Strides const& strides() const {
190 return strides_;
191 }
192
194 std::ptrdiff_t offset() const {
195 return offset_;
196 }
197
199 std::size_t nelements() const {
200 return nelements_;
201 }
202
204 std::size_t nbytes() const {
205 return nbytesof(dtype_, nelements_);
206 }
207
209 bool is_contiguous() const {
210 return is_contiguous_;
211 }
212
213 bool is_singleton() const {
214 return (nelements_ == 1);
215 }
216
219 if (!is_initialized())
220 initialize();
221 return *this;
222 }
223
225 Tensor const& forward() const {
226 if (!is_initialized())
227 initialize();
228 return *this;
229 }
231
232
233public:
236
242
247 std::byte* bytes() const {
248 return static_cast<std::byte*>(buffer_->address()) + offset_;
249 }
250
255 bool is_initialized() const {
256 return buffer_ ? true : false;
257 }
258
264 Environment const& environment() const {
265 if (!is_initialized())
266 throw Exception("Cannot get resource of an initializer tensor.");
267 return buffer_->environment();
268 }
270
271public:
287 template<typename T>
288 Tensor(std::initializer_list<T> const& values)
289 : dtype_(dtypeof<T>())
290 , shape_({values.size()})
291 , strides_(shape_)
292 , offset_(0)
293 , nelements_(shape_[0]) {
294 if (dtype_ == boolean) {
295 initialize();
296 std::ptrdiff_t index = 0;
297 for (auto const& value : values) {
298 assign((bool const*)(&value), index);
299 ++index;
300 }
301 }
302
303 else {
304 initialize();
305 size_t index = 0;
306 for (auto const& value : values) {
307 assign((std::byte const*)(&value), index * dsizeof(dtype_));
308 ++index;
309 }
310 }
311 }
312
331 template<typename T>
332 Tensor(std::initializer_list<std::initializer_list<T>> const& values)
333 : dtype_(dtypeof<T>())
334 , shape_({values.size(), values.begin()->size()})
335 , strides_(shape_)
336 , offset_(0)
337 , nelements_(shape_[0] * shape_[1])
338 {
339
340 if (dtype_ == boolean) {
341 initialize();
342 std::ptrdiff_t index = 0;
343 for (auto const& row : values) {
344 if (row.size() != shape_[1])
345 throw Exception("All rows must have the same number of columns");
346 for (auto const& value : row) {
347 assign((bool const*)(&value), index);
348 ++index;
349 }
350 }
351 }
352
353 else {
354 initialize();
355 size_t index = 0;
356 for (auto row : values) {
357 if (row.size() != shape_[1])
358 throw Exception("All rows must have the same number of columns");
359 for (auto const& value : row) {
360 assign((std::byte const*)(&value), index * dsizeof(dtype_));
361 ++index;
362 }
363 }
364 }
365 }
366
392 template<typename T>
393 Tensor(std::initializer_list<std::initializer_list<std::initializer_list<T>>> const& values)
394 : dtype_(dtypeof<T>())
395 , shape_({values.size(), values.begin()->size(), values.begin()->begin()->size()})
396 , strides_(shape_)
397 , offset_(0)
398 , nelements_(shape_[0] * shape_[1] * shape_[2])
399
400 {
401 if (dtype_ == boolean) {
402 initialize();
403 std::ptrdiff_t index = 0;
404 for (auto const& matrix : values) {
405 if (matrix.size() != shape_[1])
406 throw Exception("All matrices must have the same number of rows");
407 for (auto const& row : matrix) {
408 if (row.size() != shape_[2])
409 throw Exception("All rows must have the same number of columns");
410 for (auto const& value : row) {
411 assign((bool const*)(&value), index);
412 ++index;
413 }
414 }
415 }
416 }
417
418 else {
419 initialize();
420 size_t index = 0;
421 for (auto const& matrix : values) {
422 if (matrix.size() != shape_[1])
423 throw Exception("All matrices must have the same number of rows");
424 for (auto const& row : matrix) {
425 if (row.size() != shape_[2])
426 throw Exception("All rows must have the same number of columns");
427 for (auto const& value : row) {
428 assign((std::byte const*)(&value), index * dsizeof(dtype_));
429 ++index;
430 }
431 }
432 }
433 }
434 }
435
436
473 template<typename T>
474 Tensor(std::initializer_list<std::initializer_list<std::initializer_list<std::initializer_list<T>>>> const& values)
475 : dtype_(dtypeof<T>())
476 , shape_({
477 values.size(),
478 values.begin()->size(),
479 values.begin()->begin()->size(),
480 values.begin()->begin()->begin()->size()
481 })
482 , strides_(shape_)
483 , offset_(0)
484 , nelements_(shape_[0] * shape_[1] * shape_[2] * shape_[3])
485 {
486
487 if (dtype_ == boolean) {
488 initialize();
489 std::ptrdiff_t index = 0;
490 for (auto const& tensor3D : values) {
491 if (tensor3D.size() != shape_[1])
492 throw Exception("All 3D tensors must have the same number of matrices");
493 for (auto const& matrix : tensor3D) {
494 if (matrix.size() != shape_[2])
495 throw Exception("All matrices must have the same number of rows");
496 for (auto const& row : matrix) {
497 if (row.size() != shape_[3])
498 throw Exception("All rows must have the same number of columns");
499 for (auto const& value : row) {
500 assign((bool const*)(&value), index);
501 ++index;
502 }
503 }
504 }
505 }
506 }
507
508 else {
509 initialize();
510
511 size_t index = 0;
512 for (auto const& tensor3D : values) {
513 if (tensor3D.size() != shape_[1])
514 throw Exception("All 3D tensors must have the same number of matrices");
515 for (auto const& matrix : tensor3D) {
516 if (matrix.size() != shape_[2])
517 throw Exception("All matrices must have the same number of rows");
518 for (auto const& row : matrix) {
519 if (row.size() != shape_[3])
520 throw Exception("All rows must have the same number of columns");
521 for (auto const& value : row) {
522 assign((std::byte const*)(&value), index * dsizeof(dtype_));
523 ++index;
524 }
525 }
526 }
527 }
528 }
529 }
530
531
532public:
557 template<typename T>
558 void initialize(std::initializer_list<T> values, Environment environment = Host{}) {
559 if (!is_contiguous())
560 throw Exception("Assign to initializer list supported only for contiguous tensors");
561
562 if (rank() != 1 || shape_[0] != values.size())
563 throw Exception("Shape mismatch in assignment from initializer_list");
564
565 if (!is_initialized())
567
568 if (dtype_ == boolean) {
569 std::ptrdiff_t index = 0;
570 for (auto const& value : values) {
571 assign((bool const*)(&value), index);
572 ++index;
573 }
574 }
575
576 else {
577 auto fill = [this, &values](auto cast) {
578 using Cast = decltype(cast);
579 size_t index = 0;
580 for (auto value : values) {
581 if constexpr (std::is_same_v<Cast, float16_t>) {
582 float as_float = value;
583 float16_t casted = float32_to_float16(as_float);
584 assign(expression::tobytes(casted), index * dsizeof(dtype_));
585 } else {
586 Cast casted = value;
587 assign(expression::tobytes(casted), index * dsizeof(dtype_));
588 }
589 ++index;
590 }
591 };
592
593 switch (dtype_) {
594 case int8: fill(int8_t{}); break;
595 case int16: fill(int16_t{}); break;
596 case int32: fill(int32_t{}); break;
597 case int64: fill(int64_t{}); break;
598 case float16: fill(float16_t{}); break;
599 case float32: fill(float{}); break;
600 case float64: fill(double{}); break;
601 default: throw Exception("Unsupported dtype in assignment");
602 }
603 }
604 }
605
632 template<typename T>
633 void initialize(std::initializer_list<std::initializer_list<T>> const & values, Environment environment = Host{}) {
634 if (!is_contiguous())
635 throw Exception("Assign to initializer list supported only for contiguous tensors");
636
637 if (rank() != 2 || shape_[0] != values.size() || shape_[1] != values.begin()->size())
638 throw Exception("Shape mismatch in assignment from nested initializer_list");
639
640 if (!is_initialized())
642
643 if (dtype_ == boolean) {
644 std::ptrdiff_t index = 0;
645 for (auto const& row : values) {
646 if (row.size() != shape_[1])
647 throw Exception("Row length mismatch in assignment from initializer_list");
648 for (auto const& value : row) {
649 assign((bool const*)(&value), index);
650 ++index;
651 }
652 }
653 }
654
655 else {
656 auto fill = [this, &values](auto cast) {
657 using Cast = decltype(cast);
658 size_t index = 0;
659 for (auto const& row : values) {
660 if (row.size() != shape_[1])
661 throw Exception("Row length mismatch in assignment from initializer_list");
662
663 for (auto value : row) {
664 if constexpr (std::is_same_v<Cast, float16_t>) {
665 float as_float = value;
666 float16_t casted = float32_to_float16(as_float);
667 assign(expression::tobytes(casted), index * dsizeof(dtype_));
668 } else {
669 Cast casted = value;
670 assign(expression::tobytes(casted), index * dsizeof(dtype_));
671 }
672 ++index;
673 }
674 }
675 };
676
677 switch (dtype_) {
678 case int8: fill(int8_t{}); break;
679 case int16: fill(int16_t{}); break;
680 case int32: fill(int32_t{}); break;
681 case int64: fill(int64_t{}); break;
682 case float16: fill(float16_t{}); break;
683 case float32: fill(float{}); break;
684 case float64: fill(double{}); break;
685 default: throw Exception("Unsupported dtype in assignment");
686 }
687 }
688 }
689
690
691
692
722 template<typename T>
723 void initialize(std::initializer_list<std::initializer_list<std::initializer_list<T>>> const& values, Environment environment = Host{}) {
724 if (!is_contiguous())
725 throw Exception("Assign to initializer list supported only for contiguous tensors");
726
727 if (rank() != 3
728 || shape_[0] != values.size()
729 || shape_[1] != values.begin()->size()
730 || shape_[2] != values.begin()->begin()->size())
731 throw Exception("Shape mismatch in assignment from triple-nested initializer_list");
732
733 if (!is_initialized())
735
736 if (dtype_ == boolean) {
737 std::ptrdiff_t index = 0;
738 for (auto const& matrix : values) {
739 if (matrix.size() != shape_[1])
740 throw Exception("Matrix row count mismatch");
741 for (auto const& row : matrix) {
742 if (row.size() != shape_[2])
743 throw Exception("Row length mismatch");
744 for (auto const& value : row) {
745 assign((bool const*)(&value), index);
746 ++index;
747 }
748 }
749 }
750 }
751
752 else {
753 auto fill = [this, &values](auto cast) {
754 using Cast = decltype(cast);
755 size_t index = 0;
756 for (auto const& matrix : values) {
757 if (matrix.size() != shape_[1])
758 throw Exception("Matrix row count mismatch");
759 for (auto const& row : matrix) {
760 if (row.size() != shape_[2])
761 throw Exception("Row length mismatch");
762 for (auto value : row) {
763 if constexpr (std::is_same_v<Cast, float16_t>) {
764 float as_float = value;
765 float16_t casted = float32_to_float16(as_float);
766 assign(expression::tobytes(casted), index * dsizeof(dtype_));
767 } else {
768 Cast casted = value;
769 assign(expression::tobytes(casted), index * dsizeof(dtype_));
770 }
771 ++index;
772 }
773 }
774 }
775 };
776
777 switch (dtype_) {
778 case int8: fill(int8_t{}); break;
779 case int16: fill(int16_t{}); break;
780 case int32: fill(int32_t{}); break;
781 case int64: fill(int64_t{}); break;
782 case float16: fill(float16_t{}); break;
783 case float32: fill(float{}); break;
784 case float64: fill(double{}); break;
785 default: throw Exception("Unsupported dtype in assignment");
786 }
787 }
788}
789
830 template<typename T>
831 void initialize(std::initializer_list<std::initializer_list<std::initializer_list<std::initializer_list<T>>>> values, Environment environment = Host{}) {
832 if (!is_contiguous())
833 throw Exception("Assign to initializer list supported only for contiguous tensors");
834
835 if (rank() != 4
836 || shape_[0] != values.size()
837 || shape_[1] != values.begin()->size()
838 || shape_[2] != values.begin()->begin()->size()
839 || shape_[3] != values.begin()->begin()->begin()->size())
840 throw Exception("Shape mismatch in assignment from quadruple-nested initializer_list");
841
842 if (!is_initialized())
844
845 if (dtype_ == boolean) {
846 std::ptrdiff_t index = 0;
847 for (auto const& tensor3D : values) {
848 if (tensor3D.size() != shape_[1])
849 throw Exception("3D tensor count mismatch");
850
851 for (auto const& matrix : tensor3D) {
852 if (matrix.size() != shape_[2])
853 throw Exception("Matrix row count mismatch");
854
855 for (auto const& row : matrix) {
856 if (row.size() != shape_[3])
857 throw Exception("Row length mismatch");
858
859 for (auto const& value : row) {
860 assign((bool const*)(&value), index);
861 ++index;
862 }
863 }
864 }
865 }
866 }
867
868 else {
869 auto fill = [this, &values](auto cast) {
870 using Cast = decltype(cast);
871 size_t index = 0;
872 for (auto const& tensor3D : values) {
873 if (tensor3D.size() != shape_[1])
874 throw Exception("3D tensor count mismatch");
875
876 for (auto const& matrix : tensor3D) {
877 if (matrix.size() != shape_[2])
878 throw Exception("Matrix row count mismatch");
879
880 for (auto const& row : matrix) {
881 if (row.size() != shape_[3])
882 throw Exception("Row length mismatch");
883
884 for (auto value : row) {
885 if constexpr (std::is_same_v<Cast, float16_t>) {
886 float as_float = value;
887 float16_t casted = float32_to_float16(as_float);
888 assign(expression::tobytes(casted), index * dsizeof(dtype_));
889 } else {
890 Cast casted = value;
891 assign(expression::tobytes(casted), index * dsizeof(dtype_));
892 }
893 ++index;
894 }
895 }
896 }
897 }
898 };
899
900 switch (dtype_) {
901 case int8: fill(int8_t{}); break;
902 case int16: fill(int16_t{}); break;
903 case int32: fill(int32_t{}); break;
904 case int64: fill(int64_t{}); break;
905 case float16: fill(float16_t{}); break;
906 case float32: fill(float{}); break;
907 case float64: fill(double{}); break;
908 default: throw Exception("Unsupported dtype in assignment");
909 }
910 }
911 }
912
913public:
916
940 template<Integral Index>
941 auto operator[](Index index) const {
942 if (!is_initialized())
943 initialize();
944 return expression::Slice<Tensor, Index>(*this, std::make_tuple(index));
945 }
946
962 if (!is_initialized())
963 initialize();
964 return expression::Slice<Tensor, indexing::Range>(*this, std::make_tuple(range));
965 }
966
984 template<class ... Indexes>
985 auto operator[](Indexes... indexes) const {
986 if (!is_initialized())
987 initialize();
988 return expression::Slice<Tensor, Indexes...>(*this, std::make_tuple(indexes...));
989 }
990
991
999 auto transpose(int first = -1, int second = -2) const {
1000 if (!is_initialized())
1001 initialize();
1002 return expression::Transpose<Tensor>(*this, std::make_pair<int, int>(std::move(first), std::move(second)));
1003 }
1004
1011 template<Integral ... Indexes>
1012 auto permute(Indexes... indexes) const {
1013 if (!is_initialized())
1014 initialize();
1015 return expression::Permutation<Tensor, Indexes...>(*this, std::make_tuple(indexes...));
1016 }
1017
1024 template<Integral ... Sizes>
1025 auto view(Sizes... sizes) const {
1026 if (!is_initialized())
1027 initialize();
1028 return expression::View<Tensor>(*this, sizes...);
1029 }
1030
1043 auto squeeze() const {
1044 if (!is_initialized())
1045 initialize();
1046 return expression::Squeeze<Tensor>(*this);
1047 }
1048
1049
1064 template<Integral... Axes>
1065 auto unsqueeze(Axes... axes) {
1066 return expression::Unsqueeze<Tensor>(*this, axes...);
1067 }
1069
1070public:
1071
1072 Tensor(type dtype, Shape shape, std::ptrdiff_t offset, std::shared_ptr<Buffer> storage)
1073 : dtype_(dtype)
1074 , shape_(shape)
1075 , strides_(shape)
1076 , offset_(offset)
1077 , buffer_(std::move(storage))
1078 {
1079 nelements_ = std::accumulate(shape_.begin(), shape_.end(), 1ULL, std::multiplies<>{});
1080 node_ = std::make_shared<Node>(*this);
1081 }
1082
1083 Tensor(type dtype, Shape shape, Strides strides, std::ptrdiff_t offset, std::shared_ptr<Buffer> storage)
1084 : dtype_(dtype)
1085 , shape_(shape)
1086 , strides_(strides)
1087 , offset_(offset)
1088 , buffer_(std::move(storage))
1089 {
1090 std::size_t expected = 1;
1091 for (std::size_t dimension = 0; dimension < rank(); ++dimension) {
1092 std::size_t index = rank() - 1 - dimension; // walk backward logically
1093 if (strides_[index] != expected) {
1094 is_contiguous_ = false;
1095 }
1096 nelements_ *= shape_[index];
1097 expected *= shape_[index];
1098 }
1099 node_ = std::make_shared<Node>(*this);
1100 }
1101
1102protected:
1103 template <Expression Source, class... Indexes>
1104 friend class expression::Slice;
1105
1106 template <Expression Source>
1108
1109 template <Expression Source>
1110 friend class expression::View;
1111
1112 template <Expression Source>
1113 friend class expression::Squeeze;
1114
1115 template <Expression Source>
1117
1118 template <Expression Source, Integral... Indexes>
1120
1121 template <Expression Source>
1123
1124 template <Expression Source>
1126
1127 template <class Coordinates, Expression... Sources>
1129
1130 template <Expression Source>
1132
1133 void assign(std::byte const*, std::ptrdiff_t);
1134 void assign(bool const*, std::ptrdiff_t);
1135
1136 bool compare(std::byte const*, std::ptrdiff_t) const;
1137
1138
1139public:
1140 Node* node() const {
1141 return node_.get();
1142 }
1143
1144private:
1145 // Note: I didn't decide yet if put all this inside Node.
1146 // That will speed tensor copies but disallow make the tensors
1147 // only runtime since shared ptrs don't support constexpr.
1148 // For now kernel optimization is more important.
1149 type dtype_;
1150 Shape shape_;
1151 Strides strides_;
1152 std::size_t nelements_ = 1;
1153 std::ptrdiff_t offset_ = 0;
1154 mutable std::shared_ptr<Buffer> buffer_ = nullptr;
1155 mutable std::shared_ptr<Node> node_ = nullptr;
1156 bool is_contiguous_ = true;
1157};
1158
1159std::ostream& operator<<(std::ostream& ostream, Tensor const& tensor);
1160
1161template<Expression Source>
1162inline std::ostream& operator<<(std::ostream& ostream, Source source) {
1163 Tensor tensor = source.forward();
1164 ostream << tensor;
1165 return ostream;
1166}
1167
1168template<class Operation, Expression Operand>
1170 Tensor result(dtype(), shape(), strides(), offset());
1171 operation.forward(operand, result);
1172 return result;
1173}
1174
1175template<Operator Operation, Expression Operand, Expression Cooperand>
1177 Tensor result(dtype(), shape(), strides());
1178 operation.forward(operand, cooperand, result);
1179 return result;
1180}
1181
1182template<Expression Source>
1184 Tensor source = source_.forward();
1185 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1186}
1187
1188template<Expression Source>
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, Integral... Indexes>
1208 Tensor source = source_.forward();
1209 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1210}
1211
1212template<Expression Source>
1214 Tensor source = source_.forward();
1215 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1216}
1217
1218template<Expression Source>
1220 Tensor source = source_.forward();
1221 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1222}
1223
1224template<Expression Source, class... Indexes>
1226 Tensor source = source_.forward();
1227 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1228}
1229
1230template<Expression Source, class... Indexes>
1231void expression::Slice<Source, Indexes...>::assign(std::byte const* value, std::ptrdiff_t offset) {
1232 if constexpr (Trait<Source>::is_assignable) {
1233 source_.assign(value, offset);
1234 }
1235
1236 else {
1237 Tensor tensor = forward();
1238 tensor.assign(value, offset);
1239 }
1240}
1241
1242template<Expression Source, class... Indexes>
1243void expression::Slice<Source, Indexes...>::assign(bool const* value, std::ptrdiff_t offset) {
1244 if constexpr (Trait<Source>::is_assignable) {
1245 source_.assign(value, offset);
1246 }
1247
1248 else {
1249 Tensor tensor = forward();
1250 tensor.assign(value, offset);
1251 }
1252}
1253
1254
1255template<Expression Source, class... Indexes>
1256bool expression::Slice<Source, Indexes...>::compare(std::byte const* value, std::ptrdiff_t offset) const {
1257 if constexpr (Trait<Source>::is_comparable) {
1258 return source_.compare(value, offset);
1259 }
1260
1261 else {
1262 Tensor tensor = forward();
1263 return tensor.compare(value, offset);
1264 }
1265}
1266
1267template<class Coordinates, Expression Source>
1269 Tensor real = source.forward();
1270 Tensor complex(dtype(), shape(), strides(), offset(), real.buffer_);
1271 return complex;
1272}
1273
1274template<class Coordinates, Expression Real, Expression Imaginary>
1276 Tensor result(dtype(), shape(), strides(), offset());
1277 Coordinates::forward(real.forward(), imaginary.forward(), result);
1278 return result;
1279}
1280
1281template<Expression Source>
1283 Tensor complex = source.forward();
1284 Tensor real(dtype(), shape(), strides(), offset(), complex.buffer_);
1285 return real;
1286}
1287
1288} // namespace tannic
1289
1290#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
auto operator[](Indexes... indexes) const
Indexes the tensor with multiple indices (e.g., integers or ranges).
Definition: tensor.hpp:985
std::byte * bytes() const
Returns a pointer to the beginning of the tensor's data (accounting for offset).
Definition: tensor.hpp:247
void initialize(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< T > > > > values, Environment environment=Host{})
Assigns values to a 4D tensor from a quadruple-nested initializer list.
Definition: tensor.hpp:831
Environment const & environment() const
Returns a reference to the environment variant used to allocate this tensor's buffer.
Definition: tensor.hpp:264
Tensor & forward()
Returns a reference to this tensor (const-qualified).
Definition: tensor.hpp:218
auto transpose(int first=-1, int second=-2) const
Returns a view of the tensor with two dimensions transposed.
Definition: tensor.hpp:999
Tensor(std::initializer_list< std::initializer_list< T > > const &values)
Constructs a 2D tensor from a nested initializer list.
Definition: tensor.hpp:332
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:124
rank_type rank() const
Returns the number of dimensions (rank) of the tensor.
Definition: tensor.hpp:174
void initialize(std::initializer_list< T > values, Environment environment=Host{})
Assigns values to a 1D tensor from an initializer list.
Definition: tensor.hpp:558
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:393
Tensor(type dtype, Shape shape)
Constructs an uninitialized tensor with default (contiguous) strides.
Definition: tensor.hpp:109
Node * node() const
Definition: tensor.hpp:1140
auto operator[](indexing::Range range) const
Indexes the tensor with a Range object.
Definition: tensor.hpp:961
auto squeeze() const
Returns a view of the tensor with all dimensions of size 1 removed.
Definition: tensor.hpp:1043
bool is_singleton() const
Definition: tensor.hpp:213
Shape::size_type size(int dimension) const
Returns the tensor's size at a given dimension.
Definition: tensor.hpp:184
bool is_initialized() const
Checks whether the tensor has been initialized with memory.
Definition: tensor.hpp:255
std::ptrdiff_t offset() const
Returns the offset of the tensor in the current buffer.
Definition: tensor.hpp:194
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:179
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:474
Strides const & strides() const
Returns the tensor's strides (step sizes per dimension).
Definition: tensor.hpp:189
Tensor & operator=(const Expression &expression)
Assigns the result of an expression to the tensor.
Definition: tensor.hpp:159
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:1012
auto unsqueeze(Axes... axes)
Returns a view of the tensor with a dimension of size 1 inserted at the given axis.
Definition: tensor.hpp:1065
Tensor(type dtype, Shape shape, Strides strides, std::ptrdiff_t offset, std::shared_ptr< Buffer > storage)
Definition: tensor.hpp:1083
Tensor(const Expression &expression)
Constructs a tensor by forwarding an Expression-like object.
Definition: tensor.hpp:148
auto view(Sizes... sizes) const
Returns a view of the tensor with given sizes.
Definition: tensor.hpp:1025
Tensor(std::initializer_list< T > const &values)
@
Definition: tensor.hpp:288
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:723
auto operator[](Index index) const
Indexes the tensor with a single integral index.
Definition: tensor.hpp:941
Tensor(type dtype, Shape shape, std::ptrdiff_t offset, std::shared_ptr< Buffer > storage)
Definition: tensor.hpp:1072
void assign(std::byte const *, std::ptrdiff_t)
type dtype() const
Returns the tensor's data type.
Definition: tensor.hpp:169
bool compare(std::byte const *, std::ptrdiff_t) const
std::size_t nelements() const
Returns the total number of elements of the tensor.
Definition: tensor.hpp:199
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:633
Tensor const & forward() const
Returns a reference to this tensor (non-const).
Definition: tensor.hpp:225
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:1219
Expression template for flattening a contiguous range of dimensions.
Definition: views.hpp:732
Tensor forward() const
Definition: tensor.hpp:1201
Expression template for reordering tensor dimensions according to a specified permutation.
Definition: views.hpp:303
Tensor forward() const
Definition: tensor.hpp:1207
Creates a real-valued view of complex tensor data.
Definition: complex.hpp:290
Tensor forward() const
Definition: tensor.hpp:1282
Expression template representing a tensor slice or subview.
Definition: slices.hpp:87
Tensor forward() const
Definition: tensor.hpp:1225
bool compare(std::byte const *value, std::ptrdiff_t offset) const
Definition: tensor.hpp:1256
void assign(std::byte const *value, std::ptrdiff_t offset)
Definition: tensor.hpp:1231
Expression template for removing singleton dimensions from a tensor.
Definition: views.hpp:523
Tensor forward() const
Definition: tensor.hpp:1189
Expression template for transposing two dimensions of a tensor.
Definition: views.hpp:211
Tensor forward() const
Definition: tensor.hpp:1213
Expression template for inserting singleton dimensions into a tensor.
Definition: views.hpp:619
Tensor forward() const
Definition: tensor.hpp:1195
Expression template for viewing a tensor with a new shape.
Definition: views.hpp:84
Tensor forward() const
Definition: tensor.hpp:1183
Tensor forward() const
Definition: tensor.hpp:1176
Tensor forward() const
Evaluates the unary expression and returns a Tensor.
Definition: tensor.hpp:1169
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:85
Requires a type to be an integral type (e.g., int, std::size_t).
Definition: concepts.hpp:146
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:229
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:129
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:94
Defines expression templates tensor aritmetic operations.
Defines the tannic::Shape class for representing tensor dimensions.
Implements tensor slicing for expression templates in the Tannic Tensor Library.
Memory layout specification for tensor dimensions in the Tannic Tensor Library.
Definition: graph.hpp:29
Definition: traits.hpp:27
Represents a half-open interval [start, stop) for slicing.
Definition: indexing.hpp:56
Core type system for the Tannic Tensor Library.
Implements views for tensors in the Tannic Tensor Library.