ndslice — Multidimensional Arrays
mir.ndslice is the core data structure in mir-algorithm. It provides N-dimensional array slices with contiguous or strided memory, lazy views, and full BetterC support.
mir.ndslice is mir-algorithm's answer to NumPy's ndarray. A Slice holds a pointer, a shape (array of lengths per dimension), and strides — giving you zero-copy views, reshapes, and transposes without ever copying data.
Old URL: This page was previously at mir-algorithm.libmir.org/mir_ndslice.html. That URL now redirects here.
The Slice type
struct Slice(Iterator, size_t N = 1, SliceKind kind = Contiguous) { ... }- Iterator — usually a pointer (
T*) for contiguous memory, or a custom iterator - N — number of dimensions (compile-time constant)
- kind —
Contiguous,Canonical, orUniversal(with full strides)
In practice you rarely name the full type; use auto or the helper aliases.
Creating slices
Allocate new memory
import mir.ndslice;
auto v = slice!double(5); // 1-D, length 5
auto m = slice!int(3, 4); // 2-D, 3 rows × 4 cols
auto t = slice!float(2, 3, 4); // 3-D tensorslice!T(...) allocates GC memory. For @nogc code use rcslice (reference-counted) or wrap raw memory with sliced.
Wrap existing memory
double[12] buf;
auto m = buf[].sliced(3, 4); // no allocation, owns nothingFrom an array literal
auto v = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0].sliced(2, 3);
// [[1, 2, 3],
// [4, 5, 6]]iota — index sequences
import mir.ndslice.topology : iota;
auto idx = iota(3, 4);
// [[0, 1, 2, 3],
// [4, 5, 6, 7],
// [8, 9, 10, 11]]iota produces a lazy slice — no memory allocated.
Indexing and slicing
auto m = iota(4, 5);
m[2, 3] // scalar element at row=2, col=3 → 13
m[1 .. 3, 1 .. 4] // sub-slice rows 1–2, cols 1–3 (view, no copy)
m[0] // first row as a 1-D slice
m[$ - 1] // last rowShape, length, strides
auto m = slice!double(3, 4);
m.shape // [3, 4] (static array of lengths)
m.length // 3 (length of first dimension)
m.length!1 // 4 (length of second dimension)
m.elementCount // 12
m.strides // [4, 1] in a contiguous sliceAssignment and filling
auto m = slice!double(3, 4);
m[] = 0.0; // fill all elements
m[] = iota!double(3, 4); // copy from iota
m[0] = [1.0, 2.0, 3.0, 4.0]; // assign first row
// Element-wise arithmetic (via topology.map):
import mir.ndslice.topology : map;
auto doubled = m.map!(x => x * 2); // lazy, no copyIteration
import mir.ndslice.topology : flattened;
import mir.algorithm.iteration : each, reduce;
// Iterate all elements
m.each!(x => writeln(x));
// Flatten to 1-D for reduction
double sum = m.flattened.reduce!"a + b"(0.0);
// Index-aware iteration
m.each!((ref double v, size_t i, size_t j) {
v = cast(double)(i + j);
});Memory kinds
| Kind | Description | Use case |
|---|---|---|
Contiguous | Single pointer, no strides stored | Freshly allocated slices |
Canonical | Last dimension stride is 1, others stored | Slices of rows |
Universal | All strides stored | Transposed views, arbitrary strides |
Most operations accept all three kinds. Algorithms in mir.algorithm work on all kinds.
Reference-counted slices (rcslice)
For @nogc and -betterC code without the GC:
import mir.rc.array : rcslice;
auto m = rcslice!double(3, 4); // reference-counted, no GC
// Freed automatically when last reference goes out of scopeComparison with std.array / built-in arrays
// D built-in 2-D array (jagged)
double[][] mat = new double[][](3, 4); // 3 separate heap allocations
mat[1][2] = 5.0;
// ndslice (contiguous)
auto mat2 = slice!double(3, 4); // 1 heap allocation
mat2[1, 2] = 5.0;
// mat2.ptr is a single double* — directly usable with BLASmir-algorithm
The core Mir library for D — multidimensional arrays (ndslice), functional iteration, and numerical algorithms. Replaces the D standard library's std.algorithm for numerical computing workloads.
ndslice topology — Lazy Views
mir.ndslice.topology provides zero-copy transformations of ndslice: reshape, transposed, diagonal, zip, iota, repeat, and more. All views are lazy — no data is copied.