Skip to content

strata::Tensor

Part of Tensors.

template <typename T, shape_like S, order_like O = ColumnMajor, typename Memory = Aligned<>>
class strata::Tensor;

A tensor that owns its elements: dense memory in order O.

Memory says where the elements come from: a policy, Aligned<N> by default, under which small static tensors keep their elements inside the object and the others take them from AlignedAllocator<T, N>; or any allocator, rebound to T, which every tensor then takes its elements from, std::pmr::polymorphic_allocator<> included.

It reads and slices like a view of itself and turns into one wherever a view is wanted; expressions take it as a view, so that they never copy it, and never take a temporary one, whose elements would be gone before the expression is computed.

Tensor(shape) value-initializes, as std::vector does; Tensor(shape, uninitialized) does not, for memory written before it is read. A tensor made from an expression is computed in one pass, without initializing it first.

Template parameters

Name Description
T The element type.
S The shape.
O The order of the axes in memory, column-major by default.
Memory Where the elements come from: a policy, or an allocator.

Members

Tensor

constexpr Tensor()

A tensor of the default shape: every dynamic extent 0, and for a static shape, value-initialized elements.

explicit constexpr Tensor(const S &shape, const Allocator &allocator=Allocator())

A tensor of shape with value-initialized elements.

constexpr Tensor(const S &shape, const T &value, const Allocator &allocator=Allocator())

A tensor of shape with every element a copy of value.

constexpr Tensor(const S &shape, uninitialized_t tag, const Allocator &allocator=Allocator())

A tensor of shape with uninitialized elements, for memory written before it is read.

Note

Only for element types that can be left uninitialized.

constexpr Tensor(std::initializer_list<T> values, const Allocator &allocator=Allocator())

The elements of a list, in the order of the memory: Tensor<int, Shape<2, 3>>{1, 2, 3, 4, 5, 6}.

Precondition

The list has as many elements as the shape.

constexpr Tensor(const S &shape, std::initializer_list<T> values, const Allocator &allocator=Allocator())

The elements of a list in the order of the memory, for a shape that is given.

constexpr Tensor(detail::nested_list_t<T, S::rank()> rows, const Allocator &allocator=Allocator())

A tensor of nested lists, as written: Tensor<int, Shape<2, 3>>{{1, 2, 3}, {4, 5, 6}} has the rows [1, 2, 3] and [4, 5, 6], whatever the order of its memory.

The extents of the lists are those of the tensor, dynamic ones taken from them. Elements are default-constructed and assigned.

Precondition

The lists are as long as the static extents, and the rows of a list are of one length.

constexpr Tensor(const Tensor &other, const Allocator &allocator)

A copy with another allocator; the memory comes from it, whatever other used.

constexpr Tensor(Tensor &&other, const Allocator &allocator)

A move with another allocator; the memory comes from it, whatever other used.

explicit

template <tensor_like E>
constexpr explicit(!std::same_as<value_t<E>, T>) Tensor(const E &expression

The values of expression, computed straight into new memory.

Implicit only where no conversion of values is involved: Tensor c = a + b, but Tensor<float, ...>{doubles}.

shape

constexpr const S & shape() const noexcept

The shape, the extents at run time.

mapping

constexpr const mapping_type & mapping() const noexcept

The index to offset mapping.

data

template <typename Self>
constexpr auto * data(this Self &&self) noexcept

The pointer to the elements, read-only where the tensor is const.

A temporary's too, as with std::vector: they live as long as it does.

view

template <typename Self>
constexpr auto view(this Self &&self) noexcept

A view of the elements, read-only where the tensor is const.

Only of a tensor that will still be there: not of a temporary.

get_allocator

constexpr Allocator requires(!inline_storage) get_allocator() const noexcept

The allocator the elements come from.

Note

Not available for a tensor that keeps its elements inside itself.

operator[]

template <typename Self, index_like... Indices>
constexpr auto & operator[](this Self &&self, Indices... indices) noexcept

The element at the given indices: tensor[i, j, k].

Precondition

Each index is within the extent of its axis; the mapping checks it.

operator TensorView< U, OtherShape, OtherLayout >

template <typename U, typename OtherShape, typename OtherLayout>
constexpr operator TensorView<U, OtherShape, OtherLayout>() &noexcept

Converts to any view that the tensor's own converts to, in one conversion, as a function taking a view needs.

Tensor<float, Shape<3, 4>> gives TensorView<const float, Shape<Dyn, Dyn>>. Not available on a temporary.

cursor

constexpr auto cursor() const noexcept

Where a traversal starts: the same as a view's.

operator=

template <tensor_like E>
constexpr Tensor & operator=(const E &expression)

Assigns the values of expression.

In place where the extents agree, as assign would; otherwise in new memory from the same allocator, which replaces the old only once it is computed, so that the expression may read this tensor.

template <tensor_like E>
constexpr Tensor & operator=(const NoAlias<E> &promised)

Assigns the values of an expression promised not to alias (see NoAlias).

In place without the aliasing check, in new memory as above.

template <typename E>
constexpr Tensor & operator=(const E &value)

Every element takes the value: tensor = 0.

resize

constexpr void requires(S::dyn_rank()> 0) resize(const S &shape)

Gives the tensor a new shape of the same static extents, its elements unspecified: those of an old position are not kept at it.

Cheaper than conservative_resize: no copy, and no allocation where the number of elements does not change. For a shape with a dynamic axis.

Strong: if the allocation throws, the tensor is as it was.

conservative_resize

constexpr void requires(S::dyn_rank()> 0) conservative_resize(const S &shape, const T &fill=T{})

Gives the tensor a new shape and keeps the elements that are at a position of both shapes, at it; the others are fill.

For a shape with a dynamic axis.

Strong: if the allocation or a copy throws, the tensor is as it was.

operator+=

template <typename E>
constexpr Tensor & operator+=(const E &source)

tensor += source and the other compound operators: the elements of source, a tensor of this shape or a scalar, combined with the tensor's, as for TensorView.

The shape does not change.

swap

constexpr void swap(Tensor &other) noexcept

Exchanges the shape and the elements with other.

constexpr void swap(Tensor &lhs, Tensor &rhs) noexcept

Exchanges two tensors.

slice

template <std::size_t... Axes, typename Self>
constexpr auto slice(Self &tensor, const std::array<std::size_t, sizeof...(Axes)> &indices)

The view operations of an lvalue are those of its view: views of its elements, writable for a mutable tensor.

A temporary has none of its own: the nodes of slice, permute, block, diagonal and expand own it.