ndslice
callable object, structure, delegate, or function pointer.
import mir.ndslice.topology : iota; static struct Mul { double factor; this(double f) { factor = f; } auto opCall(long x) const {return x * factor; } auto lightConst()() const @property { return Mul(factor); } } auto callable = Mul(3); auto s = iota(2, 3).vmap(callable); assert(s == [[ 0, 3, 6], [ 9, 12, 15]]);
Packed tensors.
import mir.math.sum: sum; import mir.ndslice.topology : iota, windows; // iota windows vmap scaled sums // -------------- // ------- | --- --- | ----- // | 0 1 2 | => || 0 1 || 1 2 || => | 4 6 | // | 3 4 5 | || 3 4 || 4 5 || ----- // ------- | --- --- | // -------------- struct Callable { double factor; this(double f) {factor = f;} auto opCall(S)(S x) { return x.sum * factor; } auto lightConst()() const @property { return Callable(factor); } auto lightImmutable()() immutable @property { return Callable(factor); } } auto callable = Callable(0.5); auto s = iota(2, 3) .windows(2, 2) .vmap(callable); assert(s == [[4, 6]]);
Zipped tensors
import mir.ndslice.topology : iota, zip; struct Callable { double factor; this(double f) {factor = f;} auto opCall(S, T)(S x, T y) { return x + y * factor; } auto lightConst()() const { return Callable(factor); } auto lightImmutable()() immutable { return Callable(factor); } } auto callable = Callable(10); // 0 1 2 // 3 4 5 auto sl1 = iota(2, 3); // 1 2 3 // 4 5 6 auto sl2 = iota([2, 3], 1); auto z = zip(sl1, sl2); assert(zip(sl1, sl2).vmap(callable) == [[10, 21, 32], [43, 54, 65]]);
Use vmap with byDim/alongDim to apply functions to each dimension
import mir.ndslice.fuse: fuse; import mir.math.stat: mean; import mir.algorithm.iteration: all; import mir.math.common: approxEqual; auto x = [ [0.0, 1.0, 1.5, 2.0, 3.5, 4.25], [2.0, 7.5, 5.0, 1.0, 1.5, 0.0] ].fuse; static struct Callable { double factor; this(double f) {factor = f;} auto opCall(U)(U x) const {return x.mean + factor; } auto lightConst()() const @property { return Callable(factor); } } auto callable = Callable(0.0); // Use byDim/alongDim with map to compute callable of row/column. assert(x.byDim!0.vmap(callable).all!approxEqual([12.25 / 6, 17.0 / 6])); assert(x.byDim!1.vmap(callable).all!approxEqual([1, 4.25, 3.25, 1.5, 2.5, 2.125])); assert(x.alongDim!1.vmap(callable).all!approxEqual([12.25 / 6, 17.0 / 6])); assert(x.alongDim!0.vmap(callable).all!approxEqual([1, 4.25, 3.25, 1.5, 2.5, 2.125]));
Use vmap with a lambda and with byDim/alongDim, but may need to allocate result. This example uses fuse, which allocates. Note: fuse!1 will transpose the result.
import mir.ndslice.topology: iota, alongDim, map; import mir.ndslice.fuse: fuse; import mir.ndslice.slice: sliced; static struct Mul(T) { T factor; this(T f) { factor = f; } auto opCall(U)(U x) {return x * factor; } auto lightConst()() const @property { return Mul!(typeof(factor.lightConst))(factor.lightConst); } } auto a = [1, 2, 3].sliced; auto b = [1, 2].sliced; auto A = Mul!(typeof(a))(a); auto B = Mul!(typeof(b))(b); auto x = [ [0, 1, 2], [3, 4, 5] ].fuse; auto s1 = x.byDim!0.vmap(A).fuse; assert(s1 == [[ 0, 2, 6], [ 3, 8, 15]]); auto s2 = x.byDim!1.vmap(B).fuse!1; assert(s2 == [[ 0, 1, 2], [ 6, 8, 10]]); auto s3 = x.alongDim!1.vmap(A).fuse; assert(s1 == [[ 0, 2, 6], [ 3, 8, 15]]); auto s4 = x.alongDim!0.vmap(B).fuse!1; assert(s2 == [[ 0, 1, 2], [ 6, 8, 10]]);
Implements the homonym function (also known as transform) present in many languages of functional flavor. The call slice.vmap(fun) returns a slice of which elements are obtained by applying fun for all elements in slice. The original slices are not changed. Evaluation is done lazily.
Note: transposed and pack can be used to specify dimensions.