Stratax 0.3.1
Loading...
Searching...
No Matches
Tensor

Tensor

Version: v0.2.0

Status: Complete

Header: include/stratax/containers/Tensor.hpp


Overview

stratax::container::Tensor<T> is an arbitrary-rank owning array for types that satisfy the Numeric concept. It derives from core::ArrayBase<T> and stores elements contiguously using canonical row-major strides.

The default tensor is empty with shape {0} and rank one. Constructing from an explicit Shape{} instead creates an empty rank-zero tensor.

0.0);
tensor(1, 2, 3) = 7.0; // unchecked multidimensional access
tensor.at(-1, -1, -1); // 7.0; checked negative indices
tensor.at(-1); // checked flat access inherited from ArrayBase
Arbitrary-rank owning array of numeric values.
Definition Tensor.hpp:50
reference at(difference_type first, Rest... rest)
Returns an element using checked variadic indices.
Definition Tensor.hpp:191
Stores the dimensions of a multidimensional array.
Definition Shape.hpp:33

Responsibilities

Tensor<T> is responsible for:

  • Owning arbitrary-rank contiguous storage through ArrayBase<T>
  • Preserving explicit shape and row-major stride metadata
  • Providing unchecked variadic and vector-based multidimensional access
  • Providing checked signed variadic and vector-based multidimensional access
  • Retaining the inherited flat container interface
  • Supporting constant-time member and argument-dependent swap

Tensor<T> does not directly implement broadcasting, reshaping, slicing, or high-level numerical algorithms.


Representation and Invariants

Tensor<T>
└── core::ArrayBase<T>
├── core::Buffer<T> buffer_
├── core::Shape shape_
└── core::Shape strides_

For every normally constructed tensor:

  • size() == shape().elements()
  • rank() == shape().rank() == strides().rank()
  • strides() describes the canonical row-major layout of shape()
  • Elements occupy one contiguous memory range

Shapes of any rank are accepted, including rank zero and shapes containing zero dimensions. Element-count and stride multiplication are checked during construction.

A moved-from tensor remains destructible and assignable, but its previous contents and layout must not be relied upon.


Type Aliases

Tensor<T> republishes the complete container alias set from core::ArrayBase<T>:

using value_type = typename core::ArrayBase<T>::value_type;
using size_type = typename core::ArrayBase<T>::size_type;
using difference_type = typename core::ArrayBase<T>::difference_type;
using reference = typename core::ArrayBase<T>::reference;
using const_reference = typename core::ArrayBase<T>::const_reference;
using pointer = typename core::ArrayBase<T>::pointer;
using const_pointer = typename core::ArrayBase<T>::const_pointer;
using iterator = typename core::ArrayBase<T>::iterator;
using const_iterator = typename core::ArrayBase<T>::const_iterator;
using reverse_iterator = typename core::ArrayBase<T>::reverse_iterator;
using const_reverse_iterator = typename core::ArrayBase<T>::const_reverse_iterator;

Constructors

Default Constructor

Tensor();

Constructs an empty rank-one tensor with shape {0}.

Complexity: O(1).

Shape Constructor

explicit Tensor(const core::Shape& shape);

Constructs shape.elements() value-initialized elements. For arithmetic types, value initialization produces zero.

Complexity: O(shape.elements() + shape.rank()).

Throws:

  • Exceptions::DimensionError if the element count or a stride overflows
  • std::bad_alloc if allocation fails
  • Any exception propagated from value_type construction

Shape and Fill Constructor

Tensor(const core::Shape& shape, const_reference value);

Constructs shape.elements() copies of value.

Complexity: O(shape.elements() + shape.rank()).

It has the same overflow and allocation failure conditions as the shape constructor and may propagate exceptions from the value_type copy constructor.


Copy and Move Semantics

The compiler-generated special members use ArrayBase<T> semantics:

Tensor(const Tensor&) = default;
Tensor(Tensor&&) = default;
Tensor& operator=(const Tensor&) = default;
Tensor& operator=(Tensor&&) = default;
~Tensor() = default;

Copying duplicates element storage and metadata. Moving transfers their ownership. Copy operations are O(n), while move construction is O(1).


Unchecked Multidimensional Access

Variadic Indices

template<typename... Rest>
requires ((std::is_integral_v<Rest>) && ...)
reference operator()(size_type first, Rest... rest);
template<typename... Rest>
requires ((std::is_integral_v<Rest>) && ...)
const_reference operator()(size_type first, Rest... rest) const;

The supplied components are converted to size_type and combined with the row-major strides.

Preconditions:

  • Exactly rank() components are supplied
  • Every component is non-negative
  • Every component is smaller than its corresponding dimension

No rank or bounds validation is performed. Violating these preconditions can produce an invalid offset or undefined behavior.

Complexity: O(rank()).

Vector-based Indices

reference operator()(const std::vector<size_type>& indices);
const_reference operator()(const std::vector<size_type>& indices) const;

This overload has the same preconditions and unchecked behavior as the variadic overload. indices.size() must equal rank().

std::vector<std::size_t> indices{1, 2, 3};
tensor(indices); // equivalent to tensor(1, 2, 3)

Complexity: O(rank()).


Checked Multidimensional Access

Variadic Signed Indices

template<typename... Rest>
requires ((std::is_integral_v<Rest>) && ...)
reference at(difference_type first, Rest... rest);
template<typename... Rest>
requires ((std::is_integral_v<Rest>) && ...)
const_reference at(difference_type first, Rest... rest) const;

Exactly one component per tensor dimension must be supplied. Each signed component is normalized independently, and negative values count backward from the end of the corresponding dimension.

tensor.at(-1, -1, -1); // final element of a rank-three tensor

Complexity: O(rank()).

Throws Exceptions::IndexError with:

  • "Tensor multi-index rank must match tensor rank." for a rank mismatch
  • "Tensor multi-index component is out of bounds." for an invalid component

Vector-based Signed Indices

reference at(const std::vector<difference_type>& raw_indices);
const_reference at(const std::vector<difference_type>& raw_indices) const;

The vector overload performs the same rank validation, negative-index normalization, bounds checking, and error reporting as the variadic overload.

std::vector<std::ptrdiff_t> indices{-1, 0, -2};
tensor.at(indices);

Complexity: O(rank()).


Inherited Flat Interface

Tensor explicitly retains the one-argument checked ArrayBase<T>::at overloads:

reference operator[](size_type index) noexcept;
const_reference operator[](size_type index) const noexcept;
reference at(difference_type index);
const_reference at(difference_type index) const;

operator[] is unchecked flat access. The one-argument at(index) checks a flat index in [-size(), size()) and supports negative values. It throws Exceptions::IndexError when the flat index is invalid.

The inherited interface also provides:

  • size(), empty(), rank(), shape(), and strides()
  • data(), front(), and back()
  • Forward, const, and reverse iterators
  • fill()

front() and back() throw Exceptions::IndexError when the tensor is empty.

Metadata queries, iterator acquisition, and individual flat element access are O(1). Traversal and fill() are O(size()).


Swap

void swap(Tensor& other) noexcept;
friend void swap(Tensor& lhs, Tensor& rhs) noexcept;

Both overloads exchange the buffer, shape, and strides in O(1). The non-member overload supports argument-dependent lookup:

using std::swap;
swap(lhs, rhs);

Tensors of different ranks and shapes may be swapped.


Complexity Summary

Operation Complexity
Default construction O(1)
Shape or shape-and-fill construction O(elements + rank)
Copy construction or assignment O(n)
Move construction O(1)
Metadata query O(1)
Flat element access O(1)
Variadic or vector multidimensional access O(rank)
Iterator acquisition O(1)
Complete traversal O(n)
fill() O(n)
swap() O(1)

Examples

Construction

Checked and Unchecked Access

0.0);
tensor(1, 0, 1) = 5.0; // unchecked
tensor.at(1, 0, 1); // 5.0
tensor.at(-1, 0, -1); // 5.0
tensor.at(-1); // checked access to the final flat element

Iteration

for (double& value : tensor)
{
value += 1.0;
}

Design Notes

Keeping ownership and flat container behavior in ArrayBase<T> gives Vector, Matrix, and Tensor consistent storage and iterator semantics. Tensor<T> adds general row-major multi-index conversion for arbitrary ranks.

Unchecked access deliberately avoids validation for performance-sensitive code. Use at(...) when indices are external, signed, or otherwise untrusted.


See Also