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.random.engine.splitmix
SplitMix generator family.
An n-bit splitmix PRNG has an internal n-bit counter and an n-bit increment.
The state is advanced by adding the increment to the counter and output is
the counter's value mixed. The increment remains constant
for an instance over its lifetime, so each instance of the PRNG needs to
explicitly store its increment only if the split() operation is needed.
The first version of splitmix was described in
Fast Splittable
Pseudorandom Number Generators (2014) by Guy L. Steele Jr., Doug Lea, and
Christine H. Flood. A key selling point of the generator was the ability
to split the sequence:
"A conventional linear PRNG object provides a generate method that returns one pseudorandom value and updates the state of the PRNG, but a splittable PRNG object also has a second operation, split, that replaces the original PRNG object with two (seemingly) independent PRNG objects, by creating and returning a new such object and updating the state of the original object. Splittable PRNG objects make it easy to organize the use of pseudorandom numbers in multithreaded programs structured using fork-join parallelism."However, splitmix is also used as a non-splittable PRNG with a constant increment that does not vary from one instance to the next. This cuts the needed space in half. This module provides predefined fixed-increment SplitMix64 and splittable Splittable64.
- pure nothrow @nogc @safe ulong
fmix64
(ulong m1, ulong m2, uint shift1, uint shift2, uint shift3)(ulongx
); - 64-bit MurmurHash3-style bit mixer, parameterized.Pattern is:
ulong fmix64(ulong x) { x = (x ^ (x >>> shift1)) * m1; x = (x ^ (x >>> shift2)) * m2; return x ^ (x >>> shift3); }
As long as m1 and m2 are odd each operation is invertible with the consequence thatfmix64
(a) ==fmix64
(b) if and only if (a == b). Good parameters for fmix64 are found empirically. Several sets of suggested parameters are provided. - template
murmurHash3Mix
()
templatestaffordMix01
()
templatestaffordMix02
()
templatestaffordMix03
()
templatestaffordMix04
()
templatestaffordMix05
()
templatestaffordMix06
()
templatestaffordMix07
()
templatestaffordMix08
()
templatestaffordMix09
()
templatestaffordMix10
()
templatestaffordMix11
()
templatestaffordMix12
()
templatestaffordMix13
()
templatestaffordMix14
() - Well known sets of parameters for fmix64. Predefined are murmurHash3Mix and staffordMix01 through staffordMix14.See David Stafford's 2011 blog entry Better Bit Mixing - Improving on MurmurHash3's 64-bit Finalizer.Examples:
enum ulong x1 = murmurHash3Mix(0x1234_5678_9abc_defeUL);//Mix some number at compile time. static assert(x1 == 0xb194_3cfe_a4f7_8f08UL); immutable ulong x2 = murmurHash3Mix(0x1234_5678_9abc_defeUL);//Mix some number at run time. assert(x1 == x2);//Same result.
Examples://Verify all sets of predefined parameters are valid //and no two are identical. ulong[15] array; array[0] = murmurHash3Mix(1); array[1] = staffordMix01(1); array[2] = staffordMix02(1); array[3] = staffordMix03(1); array[4] = staffordMix04(1); array[5] = staffordMix05(1); array[6] = staffordMix06(1); array[7] = staffordMix07(1); array[8] = staffordMix08(1); array[9] = staffordMix09(1); array[10] = staffordMix10(1); array[11] = staffordMix11(1); array[12] = staffordMix12(1); array[13] = staffordMix13(1); array[14] = staffordMix14(1); foreach (i; 1 .. array.length) foreach (e; array[0 .. i]) if (e == array[i]) assert(0, "fmix64 predefines are not all distinct!");
- alias
SplitMix64
= SplitMixEngine!(staffordMix13, false).SplitMixEngine; - Canonical fixed increment (non-splittable) SplitMix64 engine.64 bits of state, period of 2 ^^ 64.Examples:
import mir.random; static assert(isSaturatedRandomEngine!SplitMix64); auto rng = SplitMix64(1u); ulong x = rng.rand!ulong; assert(x == 10451216379200822465UL);
Examples:import mir.random; import std.range.primitives: isRandomAccessRange; // SplitMix64 should be both a Mir-style saturated // random engine and a Phobos-style uniform RNG // and random access range. static assert(isPhobosUniformRNG!SplitMix64); static assert(isRandomAccessRange!SplitMix64); static assert(isSaturatedRandomEngine!SplitMix64); SplitMix64 a = SplitMix64(1); immutable ulong x = a.front; SplitMix64 b = a.save; assert (x == a.front); assert (x == b.front); assert (x == a[0]); immutable ulong y = a[1]; assert(x == a()); assert(x == b()); assert(a.front == y);
- alias
Splittable64
= SplitMixEngine!(staffordMix13, true).SplitMixEngine; - Canonical splittable (specifiable-increment) SplitMix64 engine.128 bits of state, period of 2 ^^ 64.Examples:
import mir.random; static assert(isSaturatedRandomEngine!Splittable64); auto rng = Splittable64(1u); ulong x = rng.rand!ulong; assert(x == 10451216379200822465UL); //Split example: auto rng1 = Splittable64(1u); auto rng2 = rng1.split(); assert(rng1.rand!ulong == 17911839290282890590UL); assert(rng2.rand!ulong == 14201552918486545593UL); assert(rng1.increment != rng2.increment);
Examples:import mir.random; import std.range.primitives: isRandomAccessRange; // Splittable64 should be both a Mir-style saturated // random engine and a Phobos-style uniform RNG // and random access range. static assert(isPhobosUniformRNG!Splittable64); static assert(isRandomAccessRange!Splittable64); static assert(isSaturatedRandomEngine!Splittable64); Splittable64 a = Splittable64(1); immutable ulong x = a.front; Splittable64 b = a.save; assert (x == a.front); assert (x == b.front); assert (x == a[0]); immutable ulong y = a[1]; assert(x == a()); assert(x == b()); assert(a.front == y);
- enum ulong
DEFAULT_SPLITMIX_INCREMENT
;
aliasGOLDEN_GAMMA
= DEFAULT_SPLITMIX_INCREMENT; - Default increment used by SplitMixEngine. Defined in Fast Splittable Pseudorandom Number Generators (2014) as "the odd integer closest to (2 ^^ 64)/φ, where φ = (1 + √5)/2 is the golden ratio." In the paper this constant is referred to as "GOLDEN_GAMMA".From the authors:
[...] our choice of the odd integer closest to (2 ^^ 64)/φ was based only on the intuition that it might be a good idea to keep γ values “well spread out” and the fact that prefixes of the Weyl sequence generated by 1/φ are known to be “well spread out” [citing vol. 3 of Knuth's The Art of Computer Programming, exercise 6.4-9]
- struct
SplitMixEngine
(alias mixer, bool split_enabled = false, OptionalArgs...) if ((__traits(compiles, () { static assert(__traits(isSame, TemplateOf!(mixer!()), fmix64)); } ) || __traits(compiles, () { static assert(__traits(isSame, TemplateOf!mixer, fmix64)); } )) && (OptionalArgs.length < 1 || is(typeof(OptionalArgs[1]) == ulong) && (OptionalArgs[1] != DEFAULT_SPLITMIX_INCREMENT)) && (OptionalArgs.length < 2)); - Generic SplitMixEngine.The first parameter mixer should be a explicit instantiation of fmix64 or a predefined parameterization of fmix64 such as murmurHash3Mix or staffordMix13. The second parameter is whether the split operation is enabled. Allows each instance to have a distinct increment, increasing the size from 64 bits to 128 bits. If split is not enabled then the opCall, seed, and skip operations will be shared provided the platform supports 64-bit atomic operations. The third parameter is the default_increment. If the SplitMixEngine has a fixed increment this value will be used for each instance. If omitted this parameter defaults to DEFAULT_SPLITMIX_INCREMENT.Examples:
//Can specify engine like this: alias RNG1 = SplitMixEngine!staffordMix13; alias RNG2 = SplitMixEngine!(fmix64!(0xbf58476d1ce4e5b9UL, 0x94d049bb133111ebUL, 30, 27, 31)); //Each way of writing it results in the same sequence. assert(RNG1(1).opCall() == RNG2(1).opCall()); //However not each result's name is equally informative. static assert(RNG1.stringof == `SplitMixEngine!(staffordMix13, false)`); static assert(RNG2.stringof == `SplitMixEngine!(fmix64, false)`);//Doesn't include parameters of fmix64!
- enum ulong
default_increment
; - Either DEFAULT_SPLITMIX_INCREMENT or the optional third argument of this template.
- enum bool
isRandomEngine
; - Marks as a Mir random engine.
- enum ulong
max
; - Largest generated value.
- enum uint
period_pow2
; - Full period (2 ^^ 64).
- enum bool
increment_specifiable
; - Whether each instance can set its increment individually. Enables the split operation at the cost of increasing size from 64 bits to 128 bits.
- ulong
state
; - Current internal state of the generator.
- ulong
increment
;
aliasgamma
= increment; - Either an enum or a settable value depending on whether increment_specifiable == true. This should always be an odd number. The paper refers to this as γ so it is aliased as
gamma
. - this()(ulong
x0
); - Constructs a SplitMixEngine generator seeded with x0.
- this()(ulong
x0
, ulongincrement
)
if (increment_specifiable); - Constructs a SplitMixEngine generator seeded with x0 using the specified increment.Note from the authors (the paper uses γ to refer to the increment):
[W]e tested PRNG objects with “sparse” γ values whose representations have either very few 1-bits or very few 0-bits, and found that such cases produce pseudorandom sequences that DieHarder regards as “weak” just a little more often than usual.
As a consequence the provided split function guards against this and also against increments that have long consecutive runs of either 1 or 0. However, this constructor only forces increment to be an odd number and performs no other transformation. - ulong
opCall
()();
shared ulongopCall
()()
if (!increment_specifiable); - Advances the random sequence.Examples:
auto rnd = SplitMixEngine!staffordMix13(1); assert(rnd() == staffordMix13(1 + GOLDEN_GAMMA));
- typeof(this)
split
()()
if (increment_specifiable); - Produces a splitmix generator with a different counter-value and increment-value than the current generator. Only available when increment_specifiable == true.Examples:
auto rnd1 = SplitMixEngine!(staffordMix13,true)(1); auto rnd2 = rnd1.split(); assert(rnd1.state != rnd2.state); assert(rnd1.increment != rnd2.increment); assert(rnd1() != rnd2());
- void
skip
()(size_tn
);
shared voidskip
()(size_tn
)
if (!increment_specifiable); - Skip forward in the random sequence in Ο(1) time.
- enum bool
isUniformRandom
;
enum ulongmin
;
enum boolempty
;
const @property ulongfront
()();
voidpopFront
()();
voidseed
()(ulongx0
);
shared voidseed
()(ulongx0
)
if (!increment_specifiable);
voidseed
()(ulongx0
, ulongincrement
)
if (increment_specifiable);
const @property typeof(this)save
()();
const ulongopIndex
()(size_tn
);
size_tpopFrontN
()(size_tn
);
templatepopFrontExactly
() - Compatibility with Phobos library methods. Presents this RNG as an InputRange.
Copyright © 2016-2021 by Ilya Yaroshenko | Page generated by
Ddoc on Tue Mar 23 21:30:37 2021