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