Libmir Archive

ndslice Cookbook — Common Recipes for D Multidimensional Arrays

A practical, task-oriented cookbook for mir-algorithm's ndslice — create, reshape, slice, map, reduce, and fuse multidimensional arrays in D. Every example is compiled against current Mir source.

A task-oriented reference for mir.ndslice — "how do I do X" rather than "what is X". If you know NumPy, see also ndslice vs NumPy. For the full per-symbol API, browse mir-algorithm.libmir.org.

Every code block below is compiled against the current mir-algorithm / mir-core source. The core insight of ndslice: views are lazy and allocation-freetransposed, map, reversed, diagonal build a new view over the same memory; nothing is copied until you materialise it.

Create a slice

import mir.ndslice;

auto a = iota(3, 4);                 // 3×4, values 0,1,…,11 (lazy)
auto b = slice!double(3, 4);         // allocated, zero-filled
auto c = [1.0, 2, 3, 4, 5, 6].sliced(2, 3);  // from a 1-D array
  • iota(shape...) — a lazy index grid; great for examples and broadcasting.
  • slice!T(shape...) — the allocating constructor; use it when you need owned, mutable storage.
  • array.sliced(shape...) — wrap existing memory as an n-D view (no copy).

Reshape and reorder (lazy views)

import mir.ndslice;

int err;
auto t = iota(3, 4).transposed;          // 4×3 view, no copy
auto r = iota(12).reshape([3, 4], err);  // 1-D → 3×4 (err set if incompatible)
auto v = iota(3, 4).reversed!0;          // reverse along dimension 0

reshape is the one that can fail (when the element count doesn't divide), so it reports through an err out-parameter instead of throwing — convenient in @nogc / BetterC code.

Index and slice

import mir.ndslice;

auto m = iota(5, 5);
auto sub = m[1 .. 3, 2 .. 4];   // rows 1–2, cols 2–3 — a 2×2 view
auto one = m[2, 3];             // single element

Multidimensional indexing uses one set of brackets with comma-separated dimensions — m[1 .. 3, 2 .. 4], not m[1 .. 3][2 .. 4].

Map element-wise

import mir.ndslice;

auto doubled = iota(3, 4).map!(x => x * 2.0);  // lazy, element-wise

map returns a view — the lambda runs lazily as elements are read. Chain freely; see fusion below.

Reduce

import mir.ndslice;
import mir.math.sum: sum;

auto total = iota(3, 4).as!double.sum;   // sum of all elements

mir.math.sum provides numerically-careful summation (including Kahan/pairwise modes). as!double is a lazy element cast.

Reduce along a dimension

import mir.ndslice;
import mir.math.sum: sum;

auto rowSums = iota(3, 4).byDim!0.map!(row => row.sum);  // one sum per row

byDim!0 iterates the matrix as a range of rows (dimension 0); byDim!1 gives columns. Combine with map for axis-wise reductions — the NumPy axis= pattern.

Diagonal

import mir.ndslice;

auto d = iota(4, 4).diagonal;   // 1-D view of the main diagonal

Fuse a lazy pipeline

import mir.ndslice;

// transpose → add 1 → take the diagonal. Still a single fused view:
// no intermediate matrix is ever allocated.
auto pipeline = iota(3, 4).transposed.map!(x => x + 1).diagonal;

This is ndslice's signature strength: an entire chain of transformations compiles down to one fused traversal. To materialise the result into owned memory, end the chain with .slice (allocates) or .fuse (allocates a contiguous copy).

Where to go next

On this page