Report a bug
If you spot a problem with this page, click here to create a GitHub issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using a local clone.

mir.math.numeric

This module contains simple numeric algorithms.
Authors:
Ilya Yaroshenko

Sponsors This work has been sponsored by and Kaleidic Associates.

struct ProdAccumulator(T) if (isFloatingPoint!T);
Examples:
import mir.ndslice.slice: sliced;

ProdAccumulator!float x;
x.put([1, 2, 3].sliced);
assert(x.prod == 6f);
x.put(4);
assert(x.prod == 24f);
long exp;
F x;
alias mantissa = x;
pure nothrow @nogc @safe this(F value);
pure nothrow @nogc @safe this(long exp, F x);
pure nothrow @nogc @safe void put(U)(U e)
if (is(U : T));
pure nothrow @nogc @safe void put(ProdAccumulator!T value);
void put(Range)(Range r)
if (isIterable!Range);

void put(Range : Slice!(Iterator, N, kind), Iterator, size_t N, SliceKind kind)(Range r);
const pure nothrow @nogc @property scope @safe T prod();
const pure nothrow @nogc @safe ProdAccumulator!T ldexp(long exp);
const pure nothrow @nogc @safe ProdAccumulator!T opUnary(string op : "-")();
const pure nothrow @nogc @safe ProdAccumulator!T opUnary(string op : "+")();
F prod(F, Range)(Range r)
if (isFloatingPoint!F && isIterable!Range);
Calculates the product of the elements of the input.
This function uses a separate exponential accumulation algorithm to calculate the product. A consequence of this is that the result must be a floating point type. To calculate the product of a type that is not implicitly convertible to a floating point type, use mir.algorithm.iteration.reduce or mir.algorithm.iteration.fold.
/++
Parameters:
Range r finite iterable range
Returns:
The prduct of all the elements in r /
F prod(F, Range)(Range r, ref long exp)
if (isFloatingPoint!F && isIterable!Range);
Parameters:
Range r finite iterable range
long exp value of exponent
Returns:
The mantissa, such that the product equals the mantissa times 2^^exp
prodType!Range prod(Range)(Range r)
if (isIterable!Range);
Parameters:
Range r finite iterable range
Returns:
The prduct of all the elements in r
prodType!Range prod(Range)(Range r, ref long exp)
if (isIterable!Range);
Parameters:
Range r finite iterable range
long exp value of exponent
Returns:
The mantissa, such that the product equals the mantissa times 2^^exp
prodType!T prod(T)(scope const T[] ar...);
Parameters:
T[] ar values
Returns:
The prduct of all the elements in ar
Examples:
Product of arbitrary inputs
assert(prod(1.0, 3, 4) == 12.0);
assert(prod!float(1, 3, 4) == 12f);
Examples:
Product of arrays and ranges
import mir.math.common: approxEqual;

enum l = 2.0 ^^ (double.max_exp - 1);
enum s = 2.0 ^^ -(double.max_exp - 1);
auto r = [l, l, l, s, s, s, 0.8 * 2.0 ^^ 10];

assert(r.prod == 0.8 * 2.0 ^^ 10);

// Can get the mantissa and exponent
long e;
assert(r.prod(e).approxEqual(0.8));
assert(e == 10);
Examples:
Product of vector
import mir.ndslice.slice: sliced;
import mir.algorithm.iteration: reduce;
import mir.math.common: approxEqual;

enum l = 2.0 ^^ (double.max_exp - 1);
enum s = 2.0 ^^ -(double.max_exp - 1);
auto c = 0.8;
auto u = c * 2.0 ^^ 10;
auto r = [l, l, l, s, s, s, u, u, u].sliced;
          
assert(r.prod == reduce!"a * b"(1.0, [u, u, u]));

long e;
assert(r.prod(e).approxEqual(reduce!"a * b"(1.0, [c, c, c])));
assert(e == 30);
Examples:
Product of matrix
import mir.ndslice.fuse: fuse;
import mir.algorithm.iteration: reduce;

enum l = 2.0 ^^ (double.max_exp - 1);
enum s = 2.0 ^^ -(double.max_exp - 1);
auto c = 0.8;
auto u = c * 2.0 ^^ 10;
auto r = [
    [l, l, l],
    [s, s, s],
    [u, u, u]
].fuse;
          
assert(r.prod == reduce!"a * b"(1.0, [u, u, u]));

long e;
assert(r.prod(e) == reduce!"a * b"(1.0, [c, c, c]));
assert(e == 30);
Examples:
Column prod of matrix
import mir.ndslice.fuse: fuse;
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.topology: alongDim, byDim, map;

auto x = [
    [2.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 5.0]
].fuse;

auto result = [4.0, 7.5, 7.5, 2.0, 5.25, 21.25];

// Use byDim or alongDim with map to compute mean of row/column.
assert(x.byDim!1.map!prod.all!approxEqual(result));
assert(x.alongDim!0.map!prod.all!approxEqual(result));

// FIXME
// Without using map, computes the prod of the whole slice
// assert(x.byDim!1.prod.all!approxEqual(result));
// assert(x.alongDim!0.prod.all!approxEqual(result));
Examples:
Can also set output type
import mir.ndslice.slice: sliced;
import mir.math.common: approxEqual;
import mir.ndslice.topology: repeat;

auto x = [1, 2, 3].sliced;
assert(x.prod!float == 6f);
Examples:
Product of variables whose underlying types are implicitly convertible to double also have type double
static struct Foo
{
    int x;
    alias x this;
}

auto x = prod(1, 2, 3);
assert(x == 6.0);
static assert(is(typeof(x) == double));

auto y = prod([Foo(1), Foo(2), Foo(3)]);
assert(y == 6.0);
static assert(is(typeof(y) == double));
Unqual!(DeepElementType!Range) sumOfLog2s(Range)(Range r)
if (isFloatingPoint!(DeepElementType!Range));
Compute the sum of binary logarithms of the input range r. The error of this method is much smaller than with a naive sum of log2.
Examples:
alias isNaN = x => x != x;

assert(sumOfLog2s(new double[0]) == 0);
assert(sumOfLog2s([0.0L]) == -real.infinity);
assert(sumOfLog2s([-0.0L]) == -real.infinity);
assert(sumOfLog2s([2.0L]) == 1);
assert(isNaN(sumOfLog2s([-2.0L])));
assert(isNaN(sumOfLog2s([real.nan])));
assert(isNaN(sumOfLog2s([-real.nan])));
assert(sumOfLog2s([real.infinity]) == real.infinity);
assert(isNaN(sumOfLog2s([-real.infinity])));
assert(sumOfLog2s([ 0.25, 0.25, 0.25, 0.125 ]) == -9);