The OpenD Programming Language

reduce

Implements the homonym function (also known as accumulate, compress, inject, or fold) present in various programming languages of functional flavor. The call reduce!(fun)(seed, slice1, ..., sliceN) first assigns seed to an internal variable result, also called the accumulator. Then, for each set of element x1, ..., xN in slice1, ..., sliceN, result = fun(result, x1, ..., xN) gets evaluated. Finally, result is returned.

reduce allows to iterate multiple slices in the lockstep.

Note: $(NDSLICEREF topology, pack) can be used to specify dimensions.

template reduce(alias fun)
static if(__traits(isSame, naryFun!fun, fun) && !Mir_disable_inlining_in_reduce)
reduce
(
S
Slices...
)
(,
Slices slices
)
if (
Slices.length
)

Members

Functions

reduce
auto reduce(S seed, Slices slices)

Parameters

fun

A function.

Examples

Ranges and arrays

auto ar = [1, 2, 3];
auto s = 0.reduce!"a + b"(ar);
assert (s == 6);

Single slice

import mir.ndslice.topology : iota;

//| 0 1 2 | => 3  |
//| 3 4 5 | => 12 | => 15
auto sl = iota(2, 3);

// sum of all element in the slice
auto res = size_t(0).reduce!"a + b"(sl);

assert(res == 15);

Multiple slices, dot product

import mir.ndslice.allocation : slice;
import mir.ndslice.topology : as, iota;

//| 0 1 2 |
//| 3 4 5 |
auto a = iota([2, 3], 0).as!double.slice;
//| 1 2 3 |
//| 4 5 6 |
auto b = iota([2, 3], 1).as!double.slice;

alias dot = reduce!"a + b * c";
auto res = dot(0.0, a, b);

// check the result:
import mir.ndslice.topology : flattened;
import std.numeric : dotProduct;
assert(res == dotProduct(a.flattened, b.flattened));

Zipped slices, dot product

import std.typecons : Yes;
import std.numeric : dotProduct;
import mir.ndslice.allocation : slice;
import mir.ndslice.topology : as, iota, zip, universal;
import mir.math.common : fmamath;

static @fmamath T fmuladd(T, Z)(const T a, Z z)
{
    return a + z.a * z.b;
}

// 0 1 2
// 3 4 5
auto sl1 = iota(2, 3).as!double.slice.universal;
// 1 2 3
// 4 5 6
auto sl2 = iota([2, 3], 1).as!double.slice;

// slices must have the same strides for `zip!true`.
assert(sl1.strides == sl2.strides);

auto z = zip!true(sl1, sl2);

auto dot = reduce!fmuladd(0.0, z);

assert(dot == dotProduct(iota(6), iota([6], 1)));

Tensor mutation on-the-fly

import mir.ndslice.allocation : slice;
import mir.ndslice.topology : as, iota;
import mir.math.common : fmamath;

static @fmamath T fun(T)(const T a, ref T b)
{
    return a + b++;
}

//| 0 1 2 |
//| 3 4 5 |
auto sl = iota(2, 3).as!double.slice;

auto res = reduce!fun(double(0), sl);

assert(res == 15);

//| 1 2 3 |
//| 4 5 6 |
assert(sl == iota([2, 3], 1));

Packed slices.

Computes minimum value of maximum values for each row.

import mir.math.common;
import mir.ndslice.allocation : slice;
import mir.ndslice.dynamic : transposed;
import mir.ndslice.topology : as, iota, pack, map, universal;

alias maxVal = (a) => reduce!fmax(-double.infinity, a);
alias minVal = (a) => reduce!fmin(double.infinity, a);
alias minimaxVal = (a) => minVal(a.pack!1.map!maxVal);

auto sl = iota(2, 3).as!double.slice;

// Vectorized computation: row stride equals 1.
//| 0 1 2 | => | 2 |
//| 3 4 5 | => | 5 | => 2
auto res = minimaxVal(sl);
assert(res == 2);

// Common computation: row stride does not equal 1.
//| 0 1 2 |    | 0 3 | => | 3 |
//| 3 4 5 | => | 1 4 | => | 4 |
//             | 2 5 | => | 5 | => 3
auto resT = minimaxVal(sl.universal.transposed);
assert(resT == 3);

Dlang Range API support.

import mir.algorithm.iteration: each;
import std.range: phobos_iota = iota;

int s;
// 0 1 2 3
4.phobos_iota.each!(i => s += i);
assert(s == 6);

See Also

Meta