48#include <initializer_list>
114 std::size_t nelements = std::accumulate(shape_.
begin(), shape_.
end(), 1ULL, std::multiplies<>{});
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;
141 expected *= shape_[dimension];
152 template <Expression Expression>
154 *
this = expression.forward();
163 template <Expression Expression>
180 return shape_.
rank();
190 return shape_[dimension];
210 return is_contiguous_;
244 return static_cast<std::byte*
>(buffer_->address()) + offset_;
252 return buffer_ ? true :
false;
262 throw Exception(
"Cannot get resource of an initializer tensor.");
263 return buffer_->environment();
285 Tensor(std::initializer_list<T>
const& values)
287 , shape_({values.size()})
290 if (dtype_ ==
boolean) {
291 nbytes_ = (values.size() + 7) / 8;
293 std::ptrdiff_t index = 0;
294 for (
auto const& value : values) {
295 assign((
bool const*)(&value), index);
301 nbytes_ = values.size() *
dsizeof(dtype_);
304 for (
auto const& value : values) {
330 Tensor(std::initializer_list<std::initializer_list<T>>
const& values)
332 , shape_({values.size(), values.begin()->size()})
337 if (dtype_ ==
boolean) {
338 nbytes_ = (shape_[0] * shape_[1] + 7) / 8;
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);
352 nbytes_ = shape_[0] * shape_[1] *
dsizeof(dtype_);
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) {
392 Tensor(std::initializer_list<std::initializer_list<std::initializer_list<T>>>
const& values)
394 , shape_({values.size(), values.begin()->size(), values.begin()->begin()->size()})
398 if (dtype_ ==
boolean) {
399 nbytes_ = (shape_[0] * shape_[1] * shape_[2] + 7) / 8;
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);
417 nbytes_ = std::accumulate(shape_.
begin(), shape_.
end(), 1ULL, std::multiplies<>{}) *
dsizeof(dtype_);
419 std::cout << shape_ << std::endl;
420 std::cout << nbytes_ << std::endl;
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) {
475 Tensor(std::initializer_list<std::initializer_list<std::initializer_list<std::initializer_list<T>>>>
const& values)
479 values.begin()->size(),
480 values.begin()->begin()->size(),
481 values.begin()->begin()->begin()->size()
487 if (dtype_ ==
boolean) {
488 nbytes_ = (shape_[0] * shape_[1] * shape_[2] * shape_[3] + 7) / 8;
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);
510 nbytes_ = std::accumulate(shape_.
begin(), shape_.
end(), 1ULL, std::multiplies<>{}) *
dsizeof(dtype_);
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) {
562 throw Exception(
"Assign to initializer list supported only for contiguous tensors");
564 if (
rank() != 1 || shape_[0] != values.size())
565 throw Exception(
"Shape mismatch in assignment from initializer_list");
570 if (dtype_ ==
boolean) {
571 std::ptrdiff_t index = 0;
572 for (
auto const& value : values) {
573 assign((
bool const*)(&value), index);
579 auto fill = [
this, &values](
auto cast) {
580 using Cast =
decltype(cast);
582 for (
auto value : values) {
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");
630 throw Exception(
"Assign to initializer list supported only for contiguous tensors");
632 if (
rank() != 2 || shape_[0] != values.size() || shape_[1] != values.begin()->size())
633 throw Exception(
"Shape mismatch in assignment from nested initializer_list");
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);
651 auto fill = [
this, &values](
auto cast) {
652 using Cast =
decltype(cast);
654 for (
auto const& row : values) {
655 if (row.size() != shape_[1])
656 throw Exception(
"Row length mismatch in assignment from initializer_list");
658 for (
auto value : row) {
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");
713 throw Exception(
"Assign to initializer list supported only for contiguous tensors");
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");
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])
732 for (
auto const& value : row) {
733 assign((
bool const*)(&value), index);
741 auto fill = [
this, &values](
auto cast) {
742 using Cast =
decltype(cast);
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) {
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");
814 throw Exception(
"Assign to initializer list supported only for contiguous tensors");
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");
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");
832 for (
auto const& matrix : tensor3D) {
833 if (matrix.size() != shape_[2])
834 throw Exception(
"Matrix row count mismatch");
836 for (
auto const& row : matrix) {
837 if (row.size() != shape_[3])
840 for (
auto const& value : row) {
841 assign((
bool const*)(&value), index);
850 auto fill = [
this, &values](
auto cast) {
851 using Cast =
decltype(cast);
853 for (
auto const& tensor3D : values) {
854 if (tensor3D.size() != shape_[1])
855 throw Exception(
"3D tensor count mismatch");
857 for (
auto const& matrix : tensor3D) {
858 if (matrix.size() != shape_[2])
859 throw Exception(
"Matrix row count mismatch");
861 for (
auto const& row : matrix) {
862 if (row.size() != shape_[3])
863 throw Exception(
"Row length mismatch");
865 for (
auto value : row) {
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");
914 template<Integral Index>
958 template<
class ... Indexes>
1052 , buffer_(std::move(storage))
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);
1064 , buffer_(std::move(storage))
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;
1077 expected *= shape_[dimension];
1081 node_ = std::make_shared<Node>(*
this);
1085 template <
Expression Source,
class... Indexes>
1088 template <Expression Source>
1091 template <Expression Source>
1094 template <Expression Source>
1097 template <Expression Source>
1103 template <Expression Source>
1106 template <Expression Source>
1109 template <
class Coordinates,
Expression... Sources>
1112 template <Expression Source>
1115 void assign(std::byte
const*, std::ptrdiff_t);
1118 bool compare(std::byte
const*, std::ptrdiff_t)
const;
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;
1143template<Expression Source>
1144inline std::ostream&
operator<<(std::ostream& ostream, Source source) {
1150template<
class Operation, Expression Operand>
1152 Tensor result(dtype(), shape(), strides(), offset());
1153 operation.forward(operand, result);
1157template<Operator Operation, Expression Operand, Expression Cooperand>
1159 Tensor result(dtype(), shape(), strides());
1160 operation.forward(operand, cooperand, result);
1164template<Expression Source>
1167 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1170template<Expression Source>
1173 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1176template<Expression Source>
1179 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1182template<Expression Source>
1185 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1191 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1194template<Expression Source>
1197 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1200template<Expression Source>
1203 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1209 return Tensor(dtype(), shape(), strides(), offset(), source.buffer_);
1215 source_.assign(value, offset);
1219 Tensor tensor = forward();
1220 tensor.
assign(value, offset);
1227 source_.assign(value, offset);
1231 Tensor tensor = forward();
1232 tensor.
assign(value, offset);
1240 return source_.compare(value, offset);
1244 Tensor tensor = forward();
1245 return tensor.
compare(value, offset);
1249template<
class Coordinates, Expression Source>
1252 Tensor complex(dtype(), shape(), strides(), offset(), real.buffer_);
1256template<
class Coordinates, Expression Real, Expression Imaginary>
1258 Tensor result(dtype(), shape(), strides(), offset());
1259 Coordinates::forward(real.forward(), imaginary.forward(), result);
1263template<Expression Source>
1266 Tensor real(dtype(), shape(), strides(), offset(),
complex.buffer_);
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: traits.hpp:27
Represents a half-open interval [start, stop) for slicing.
Definition: indexing.hpp:56