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.
Old URL: Previously at mir-algorithm.libmir.org/mir_ndslice_topology.html. Redirects here.
mir.ndslice.topology contains the lazy view combinators — operations that return a new Slice pointing into the same memory (or generating values on demand) without copying data.
Import
import mir.ndslice.topology;
// or selectively:
import mir.ndslice.topology : iota, map, zip, transposed, reshape, flattened;Views into existing data
transposed — swap axes
auto m = iota(3, 4);
auto t = m.transposed; // 4×3, no copy
// m[i, j] == t[j, i]For higher-dimensional slices, transposed accepts axis indices:
auto t3 = volume.transposed!(2, 0, 1); // permute axesflattened — collapse to 1-D
auto m = iota(3, 4);
auto v = m.flattened; // length-12 1-D slice
// v[i] == m[i / 4, i % 4]Works on contiguous slices only (use .assumeContiguous after a universal-stride slice if you know the data is actually contiguous).
reshape — change shape, same elements
auto m = iota(12).reshape(3, 4);
auto t = iota(3, 4).reshape(2, 6);Fails to compile if the element count doesn't match.
diagonal — main diagonal as 1-D view
auto m = iota(4, 4);
auto d = m.diagonal; // [0, 5, 10, 15] — no copySlicing sub-ranges
auto m = iota(5, 5);
auto block = m[1 .. 4, 1 .. 4]; // 3×3 sub-matrix (view)
auto row = m[2]; // row 2 as 1-D slice
auto col = m[0 .. $, 2]; // column 2 as 1-D sliceGenerating slices (no backing memory)
iota — integer sequence
auto v = iota(6); // [0, 1, 2, 3, 4, 5]
auto m = iota(3, 4); // [[0..3], [4..7], [8..11]]
auto m2 = iota([3, 4], 1); // start at 1 → [[1..4], [5..8], [9..12]]iota is lazy — iterating it computes start + linear_index, never stores the values.
repeat — broadcast a scalar or slice
auto r = repeat(42.0, 3, 4); // 3×4 slice, all 42.0 (lazy)
auto tiled = iota(1, 3).repeat(4); // 4×1×3 — tile a row 4 timeslinspace (from mir-algorithm extras)
import mir.math.numeric : linspace;
auto x = linspace!double(0.0, 1.0, 100); // 100 evenly spaced valuesCombining slices
zip — pair-wise combine
auto a = iota!double(5);
auto b = iota!double(5).map!(x => x * x);
auto pairs = zip(a, b); // Slice of tuples (a[i], b[i])
pairs.each!((t) { writefln("%s → %s", t[0], t[1]); });unzip — split a zipped slice
auto (left, right) = pairs.unzip;cartesian — outer product of indices
auto grid = cartesian(iota(3), iota(4)); // 3×4 slice of (i, j) pairsElement-wise transformations
map — lazy element transform
auto m = iota!double(3, 4);
auto scaled = m.map!(x => x * 0.1); // lazy, no allocation
auto abs_m = m.map!abs; // using std.math.absmap returns a slice of the same shape with a custom iterator — still a proper Slice, so all topology operations compose on it.
as — element type cast
auto ints = iota(3, 4);
auto doubles = ints.as!double; // lazy cast, no copyCombining topology operations
All topology operations compose freely because they all return Slice:
// Transpose, then flatten, then map
auto result = iota(4, 4)
.transposed
.flattened
.map!(x => x * x);
// Sub-block of a 3-D tensor, then reduce
import mir.algorithm.iteration : reduce;
double sum = volume[1..3, 2..5, 0..4]
.flattened
.reduce!"a + b"(0.0);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.
ndslice field — Custom Iterators
mir.ndslice.field lets you build ndslice-compatible iterators over arbitrary memory layouts, computed values, or external data structures without copying.