dxorshift: random number generators from the extended Xorshift family
Joseph Rushton Wakeling via Digitalmars-d-announce
digitalmars-d-announce at puremagic.com
Sun May 15 06:26:52 PDT 2016
On Sunday, 15 May 2016 at 11:15:38 UTC, Joseph Rushton Wakeling
wrote:
> On Sunday, 15 May 2016 at 10:43:55 UTC, Joseph Rushton Wakeling
> wrote:
>> Probably the best way to handle this is to handle the
>> take-the-address side of things by a @trusted wrapper that
>> uses `return ref` to guarantee the pointer remains valid for
>> the lifetime of the wrapper itself.
>
> Note, I've been mulling over this myself for a while, so I'll
> probably put something together in a future dxorshift release
> (and probably try to get it in Phobos ASAP, as it will be very
> helpful in avoiding the worst cases of the existing RNG
> functionality).
Sneak preview to try out, before I tidy this up for actual
inclusion in the library (needs docs, etc):
/////////////////////////////////////////////////////////////////////
module dxorshift.wrapper;
import std.random : isUniformRNG;
public struct SafeUniformRNG (BaseRNG)
if (isUniformRNG!BaseRNG)
{
public:
this (return ref BaseRNG generator) @trusted
{
this.gen = &generator;
}
enum isUniformRandom = BaseRNG.isUniformRandom;
bool empty() @property
{
return this.gen.empty;
}
auto front() @property
{
return this.gen.front;
}
auto popFront()
{
this.gen.popFront();
}
auto seed(Seed...)(Seed s)
{
this.gen.seed(s);
}
private:
BaseRNG* gen;
invariant()
{
assert(this.gen !is null);
}
}
public auto uniformRNG(BaseRNG)(return ref BaseRNG generator)
{
return SafeUniformRNG!BaseRNG(generator);
}
// test `uniformRNG`'s behavior with phobos RNGs
@safe unittest
{
import std.array : array;
import std.random : isUniformRNG, isSeedable, PseudoRngTypes;
import std.range.primitives : isInputRange, isForwardRange;
import std.range : take;
foreach (RNG; PseudoRngTypes)
{
alias SafeRNG = SafeUniformRNG!RNG;
static assert(isUniformRNG!SafeRNG);
static assert(isSeedable!SafeRNG == isSeedable!RNG);
static assert(isInputRange!SafeRNG);
static assert(!isForwardRange!SafeRNG);
// assuming RNG is seedable, we validate
// expected differences between phobos
// RNGs' normal behaviour and how they
// behave when wrapped by `uniformRNG`
static if (isSeedable!RNG)
{
RNG gen;
gen.seed(123456);
// if we pass any normal phobos RNG
// directly into a range chain, it
// will (sadly) be copied by value
auto take1 = gen.take(10).array;
auto take2 = gen.take(10).array;
assert(take1 == take2);
gen.seed(123456);
// if however we wrap it with `uniformRNG`
// it will be passed by reference
auto safeGen = uniformRNG(gen);
auto take3 = safeGen.take(10).array;
auto take4 = safeGen.take(10).array;
assert(take3 == take1); // because we start from the
same seed
assert(take3 != take4);
// validate we can however re-seed the
// safely wrapped generator and get
// the same results once again
safeGen.seed(123456);
auto take5 = safeGen.take(10).array;
auto take6 = safeGen.take(10).array;
assert(take5 == take3);
assert(take6 == take4);
}
}
}
// validate it works with dxorshift RNGs
// and allows them to work in @safe code
@safe nothrow pure unittest
{
import std.array : array;
import std.meta : AliasSeq;
import std.random : isUniformRNG, isSeedable;
import std.range.primitives : isInputRange, isForwardRange;
import std.range : take;
import dxorshift : SplitMix64, Xoroshiro128plus,
Xorshift1024star;
foreach (RNG; AliasSeq!(SplitMix64, Xoroshiro128plus,
Xorshift1024star))
{
alias SafeRNG = SafeUniformRNG!RNG;
static assert(isUniformRNG!SafeRNG);
static assert(isSeedable!SafeRNG);
static assert(isInputRange!SafeRNG);
static assert(!isForwardRange!SafeRNG);
// dxorshift generators must be constructed
// with a seed
auto gen = RNG(123456);
// we can't copy dxorshift RNGs by value,
// and it's not safe to just take the
// pointer address, so let's just jump
// to wrapping them in `uniformRNG`
auto safeGen = uniformRNG(gen);
auto take1 = safeGen.take(10).array;
auto take2 = safeGen.take(10).array;
assert(take1 != take2);
// re-seeding should give us the same
// results once over
gen.seed(123456);
auto take3 = safeGen.take(10).array;
auto take4 = safeGen.take(10).array;
assert(take3 == take1);
assert(take4 == take2);
// re-seeding via the safe wrapper
// should produce the same results
safeGen.seed(123456);
auto take5 = safeGen.take(10).array;
auto take6 = safeGen.take(10).array;
assert(take5 == take1);
assert(take6 == take2);
}
}
More information about the Digitalmars-d-announce
mailing list