Arrays

namespace ndarray

Variables

constexpr std::nullopt_t _ = std::nullopt

Convenience nullopt alias, e.g., Slice(_, 5)

template<typename T, typename S = uint32_t>
class NDArray
#include <ndarray.h>

An owning multi-dimensional array class.

This class provides a multi-dimensional array, similar to NumPy’s ndarray. It uses a Single Allocation Strategy where metadata (shape, strides) and the data elements are stored in a single contiguous memory block. This minimizes heap allocations and improves cache locality.

The array owns the data and is movable but not copyable to prevent accidental deep copies of large datasets.

Template Parameters:
  • T – The type of the elements in the array.

  • S – The type of the indices and dimensions.

Iterators

inline T *begin() noexcept

Get an iterator to the beginning of the array.

Returns:

An iterator to the beginning of the array.

inline const T *begin() const noexcept

Get a const iterator to the beginning of the array.

Returns:

A const iterator to the beginning of the array.

inline const T *cbegin() const noexcept

Get a const iterator to the beginning of the array.

Returns:

A const iterator to the beginning of the array.

inline T *end() noexcept

Get an iterator to the end of the array.

Returns:

An iterator to the end of the array.

inline const T *end() const noexcept

Get a const iterator to the end of the array.

Returns:

A const iterator to the end of the array.

inline const T *cend() const noexcept

Get a const iterator to the end of the array.

Returns:

A const iterator to the end of the array.

Lifecycle

inline NDArray(NDArray &&other) noexcept

Move constructor.

Transfers ownership of the memory block and metadata from other to this. other is left in an empty/reset state.

Parameters:

other – The array to move from.

inline NDArray &operator=(NDArray &&other) noexcept

Move assignment operator.

Replaces the current contents with those of other. The previous data is destructed (if needed) and memory released.

Parameters:

other – The array to move from.

Returns:

A reference to this.

Flat (linear) element access

inline T &operator[](S idx) noexcept

Access an element of the array using a flat index.

Parameters:

idx – The flat index of the element to access.

Returns:

A reference to the element.

inline const T &operator[](S idx) const noexcept

Access an element of the array using a flat index.

Parameters:

idx – The flat index of the element to access.

Returns:

A const reference to the element.

Public Functions

inline NDArray()

Default constructor.

Creates an empty NDArray with 0 dimensions and no allocated memory.

inline NDArray(S ndim, const S *shape)

Construct a new NDArray from dimensions and a shape array.

Parameters:
  • ndim – The number of dimensions of the array.

  • shape – A pointer to an array containing the size of each dimension.

inline NDArray(const std::vector<S> &shape)

Construct a new NDArray from a vector of shapes.

Parameters:

shape – A vector containing the size of each dimension.

inline NDArray(std::initializer_list<S> shape)

Construct a new NDArray from an initializer list of shapes.

Parameters:

shape – An initializer list containing the size of each dimension.

inline NDArray(std::span<const S> shape)

Construct a new NDArray from a span of shapes.

Parameters:

shape – A span containing the size of each dimension.

inline ~NDArray()

Destructor.

Manually destructs elements if T is not trivially destructible. The memory block is automatically released by the unique_ptr.

inline S ndim() const noexcept

Get the number of dimensions of the array.

Returns:

The number of dimensions.

inline std::span<const S> shape() const noexcept

Get the shape of the array.

Returns:

A span containing the size of each dimension.

inline std::span<const S> strides() const noexcept

Get the strides of the array.

Returns:

A span containing the stride of each dimension.

inline S size() const noexcept

Get the total number of elements in the array.

Returns:

The total number of elements.

inline bool empty() const noexcept

Check if the array is empty (has zero total elements).

Returns:

True if the array is empty, false otherwise.

inline T *data() noexcept

Get a pointer to the underlying data.

Returns:

A pointer to the underlying data.

inline const T *data() const noexcept

Get a const pointer to the underlying data.

Returns:

A const pointer to the underlying data.

template<typename ...Indices>
inline T &operator()(Indices... indices) noexcept

Access an element of the array using multi-dimensional indices.

Template Parameters:

Indices – The types of the indices.

Parameters:

indices – The indices of the element to access.

Returns:

A reference to the element.

template<typename ...Indices>
inline const T &operator()(Indices... indices) const noexcept

Access an element of the array using multi-dimensional indices.

Template Parameters:

Indices – The types of the indices.

Parameters:

indices – The indices of the element to access.

Returns:

A const reference to the element.

inline void fill(const T &value)

Fills the entire array with a specified value.

Parameters:

value – The value to fill the array with.

inline NDView<T, S> view() const

Creates a non-owning view of the entire array.

Returns:

An NDView that points to this array’s data.

inline NDView<T, S> slice(const std::vector<Slice<S>> &slices) const

Creates a non-owning view representing a slice of the array.

Parameters:

slices – A vector of Slice objects defining the slicing parameters.

Returns:

An NDView representing the specified slice.

inline NDView<T, S> reshape(const std::vector<S> &shape) const

Creates a non-owning view with a reshaped view of the array data.

Parameters:

shape – A vector specifying the new shape of the view.

Returns:

An NDView with the reshaped dimensions.

inline NDView<T, S> diagonal(S dim1, S dim2) const

Extracts a diagonal from the array.

Parameters:
  • dim1 – The first dimension to form the diagonal.

  • dim2 – The second dimension to form the diagonal.

Returns:

An NDView representing the diagonal.

inline void serialize(std::ostream &out, bool with_type = false) const noexcept

Serializes the array’s metadata and data to a stream.

Parameters:
  • out – The output stream to write to.

  • with_type – If true, includes type information in the serialization.

inline void deserialize(std::istream &in, bool with_type = false)

Deserializes the array’s metadata and data from a stream.

Parameters:
  • in – The input stream to read from.

  • with_type – If true, expects and verifies type information in the stream.

Throws:

std::runtime_error – if type information mismatches when with_type is true.

template<typename T, typename S>
class NDView
#include <ndview.h>

A non-owning view into an NDArray.

This class provides a view into a potentially multi-dimensional array without owning the underlying data. It allows for flexible manipulation and slicing of array data without copying the data itself.

Optimization: Metadata (shape and strides) are stored in a single contiguous memory block to minimize allocations.

Template Parameters:
  • T – The type of the elements in the array.

  • S – The type of the indices and dimensions.

Public Functions

inline NDView()

Default constructor.

Creates an empty NDView with 0 dimensions and no data.

inline NDView(T *data, const S *shape, const S *strides, S ndim)

Constructs an NDView by copying shape and strides.

Allocates a single block for metadata and copies the provided shape and strides.

Parameters:
  • data – A raw pointer to the start of the data.

  • shape – Pointer to the shape array.

  • strides – Pointer to the strides array.

  • ndim – The number of dimensions.

NDView(NDArray<T, S> &arr)

Implicit conversion constructor from an NDArray.

This allows an NDView to be easily created from an NDArray instance, effectively creating a view of the entire array.

Parameters:

arr – The NDArray to create a view of.

inline S ndim() const noexcept

Get the number of dimensions of the view.

Returns:

The number of dimensions.

inline std::span<const S> shape() const noexcept

Get the shape of the view.

Returns:

A span containing the size of each dimension.

inline std::span<const S> strides() const noexcept

Get the strides of the view.

Returns:

A span containing the stride of each dimension.

inline S size() const noexcept

Get the total number of elements in the view.

Returns:

The total number of elements.

inline bool empty() const noexcept

Check if the view is empty.

Returns:

True if the view is empty, false otherwise.

inline T *data() noexcept

Access the raw data pointer.

Returns:

A pointer to the underlying data.

inline const T *data() const noexcept

Access the raw data pointer (const).

Returns:

A const pointer to the underlying data.

template<typename ...Indices>
inline T &operator()(Indices... indices) noexcept

Access an element of the view using multi-dimensional indices.

Template Parameters:

Indices – The types of the indices.

Parameters:

indices – The indices of the element to access.

Returns:

A reference to the element.

template<typename ...Indices>
inline const T &operator()(Indices... indices) const noexcept

Access an element of the view using multi-dimensional indices.

Template Parameters:

Indices – The types of the indices.

Parameters:

indices – The indices of the element to access.

Returns:

A const reference to the element.

inline void fill(const T &value)

Fills the entire view with a specified value.

Parameters:

value – The value to fill the view with.

inline NDView<T, S> slice(const std::vector<Slice<S>> &slices) const

Creates a new NDView representing a slice of the current view.

Parameters:

slices – A vector of Slice objects, one for each dimension, defining the slicing parameters.

Returns:

A new NDView representing the specified slice.

inline NDView<T, S> reshape(const std::vector<S> &shape) const

Creates a new NDView with a reshaped view of the underlying data.

This operation requires the total number of elements to remain the same. Currently, it only works on contiguous views.

Parameters:

shape – A vector specifying the new shape of the view.

Throws:

std::runtime_error – if the view is not contiguous or total size changes.

Returns:

A new NDView with the reshaped dimensions.

inline NDView<T, S> diagonal(S dim1, S dim2) const

Extracts a diagonal from the view.

This creates a new NDView that represents the diagonal elements formed by two specified dimensions. The two dimensions must have the same size.

Parameters:
  • dim1 – The first dimension to form the diagonal.

  • dim2 – The second dimension to form the diagonal.

Throws:

std::runtime_error – if dim1 or dim2 are out of bounds, or if the shapes of dim1 and dim2 are not equal.

Returns:

A new NDView representing the diagonal.

template<typename S>
struct Slice
#include <slice.h>

Defines a slice for multi-dimensional array access.

This struct allows specifying start, stop, and step parameters for slicing multi-dimensional arrays, similar to Python’s slice objects. Optional values allow for default behavior (e.g., slicing from beginning to end).

Template Parameters:

S – The type used for size and index values (e.g., uint32_t).

Public Functions

inline constexpr Slice(S s)

Constructs a slice representing a single element.

Example: Slice(3) is equivalent to [3:4].

Parameters:

s – The single index to slice.

inline constexpr Slice()

Constructs a full slice (equivalent to [:]).

inline constexpr Slice(std::optional<S> s, std::optional<S> e, std::optional<S> st = std::nullopt)

Constructs an explicit slice with optional start, stop, and step.

Parameters:
  • s – The optional starting index.

  • e – The optional ending index.

  • st – The optional step size.

Public Members

std::optional<S> start

The starting index of the slice (inclusive).

std::optional<S> stop

The ending index of the slice (exclusive).

std::optional<S> step

The step size of the slice.

Public Static Functions

static inline constexpr Slice range(std::optional<S> s, std::optional<S> e, std::optional<S> st = std::nullopt)

Factory method to create a range slice.

Example: Slice::range(1, 5, 2) for [1:5:2].

Parameters:
  • s – The optional starting index.

  • e – The optional ending index.

  • st – The optional step size.

Returns:

A Slice object configured for the specified range.

static inline constexpr Slice all()

Factory method to create a full slice (equivalent to [:]).

Returns:

A Slice object configured for a full slice.

namespace detail

Functions

template<typename S>
inline S flat_index(const S *idx, const S *strides, const S *shape, S ndim) noexcept

Computes the flat index from multi-dimensional indices.

This function calculates the linear offset for a multi-dimensional array given a set of indices, strides, and the array shape.

Template Parameters:

S – The type used for size and indices.

Parameters:
  • idx – A pointer to the array of indices.

  • strides – A pointer to the array of strides.

  • shape – A pointer to the array of dimension sizes.

  • ndim – The number of dimensions.

Returns:

The calculated flat index.

template<typename S, typename Tuple, std::size_t... I>
inline S flat_index_unrolled(const Tuple &idx, const S *strides, std::index_sequence<I...>) noexcept

Computes the flat index from a fixed number of indices (unrolled).

This helper avoids loops by unrolling the index computation at compile time. Bounds checking is intentionally omitted for performance in release builds.

Template Parameters:
  • S – The type used for size and indices.

  • Tuple – A tuple holding the indices.

  • I – Index sequence for the tuple elements.

Parameters:
  • idx – A tuple containing the indices.

  • strides – A pointer to the array of strides.

Returns:

The calculated flat index.