vmap

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.

  1. auto vmap(Slice!(Iterator, N, kind) slice, Callable callable)
  2. auto vmap(T[] array, Callable callable)
  3. auto vmap(T withAsSlice, Callable callable)
    @optmath
    vmap
    (
    T
    Callable
    )
    (,
    Callable callable
    )

Parameters

callable Callable

callable object, structure, delegate, or function pointer.

Examples

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]]);

See Also

Meta