2 changed files with 429 additions and 0 deletions
@ -0,0 +1,54 @@ |
|||
// <copyright file="Xoshiro256StarStarTests.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
//
|
|||
// Copyright (c) 2009-2016 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
using MathNet.Numerics.Random; |
|||
using NUnit.Framework; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.Random |
|||
{ |
|||
/// <summary>
|
|||
/// Test for xoshiro256** pseudo random number generator.
|
|||
/// </summary>
|
|||
[TestFixture, Category("Random")] |
|||
public class Xoshiro256StarStarTests : RandomTests |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the Wh2006Tests class.
|
|||
/// </summary>
|
|||
public Xoshiro256StarStarTests() : base(typeof (Xoshiro256StarStar)) |
|||
{ |
|||
} |
|||
|
|||
[Test] |
|||
public void StaticSamplesConsistent() |
|||
{ |
|||
Assert.That(Xoshiro256StarStar.Doubles(1000, 1), Is.EqualTo(new Xoshiro256StarStar(1).NextDoubles(1000)).Within(1e-12).AsCollection); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,375 @@ |
|||
// <copyright file="Xoshiro256StarStar.cs" company="Math.NET">
|
|||
// Math.NET Numerics, part of the Math.NET Project
|
|||
// http://numerics.mathdotnet.com
|
|||
// http://github.com/mathnet/mathnet-numerics
|
|||
//
|
|||
// Copyright (c) 2009-2014 Math.NET
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
// </copyright>
|
|||
|
|||
/* |
|||
Original code's copyright and license: |
|||
Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) |
|||
|
|||
To the extent possible under law, the author has dedicated all copyright |
|||
and related and neighboring rights to this software to the public domain |
|||
worldwide. This software is distributed without any warranty. |
|||
|
|||
See <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|||
*/ |
|||
|
|||
using System.Collections.Generic; |
|||
using System.Runtime.Serialization; |
|||
|
|||
#if !NETSTANDARD1_3
|
|||
using System; |
|||
using System.Runtime; |
|||
#endif
|
|||
|
|||
namespace MathNet.Numerics.Random |
|||
{ |
|||
/// <summary>
|
|||
/// Xoshiro256** pseudo random number generator.
|
|||
/// A random number generator based on the <see cref="System.Random"/> class in the .NET library.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This is xoshiro256** 1.0, our all-purpose, rock-solid generator. It has
|
|||
/// excellent(sub-ns) speed, a state space(256 bits) that is large enough
|
|||
/// for any parallel application, and it passes all tests we are aware of.
|
|||
///
|
|||
/// For generating just floating-point numbers, xoshiro256+ is even faster.
|
|||
///
|
|||
/// The state must be seeded so that it is not everywhere zero.If you have
|
|||
/// a 64-bit seed, we suggest to seed a splitmix64 generator and use its
|
|||
/// output to fill s.
|
|||
///
|
|||
/// For further details see:
|
|||
/// David Blackman & Sebastiano Vigna (2018), "Scrambled Linear Pseudorandom Number Generators".
|
|||
/// https://arxiv.org/abs/1805.01407
|
|||
/// </remarks>
|
|||
[Serializable] |
|||
[DataContract(Namespace = "urn:MathNet/Numerics/Random")] |
|||
public class Xoshiro256StarStar : RandomSource |
|||
{ |
|||
// Constants.
|
|||
const double REAL_UNIT_UINT = 1.0 / (1UL << 53); |
|||
|
|||
// RNG state.
|
|||
[DataMember(Order = 1)] |
|||
ulong _s0; |
|||
[DataMember(Order = 2)] |
|||
ulong _s1; |
|||
[DataMember(Order = 3)] |
|||
ulong _s2; |
|||
[DataMember(Order = 4)] |
|||
ulong _s3; |
|||
|
|||
/// <summary>
|
|||
/// Construct a new random number generator with a random seed.
|
|||
/// </summary>
|
|||
public Xoshiro256StarStar() : this(RandomSeed.Robust()) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Construct a new random number generator with random seed.
|
|||
/// </summary>
|
|||
/// <param name="threadSafe">if set to <c>true</c> , the class is thread safe.</param>
|
|||
public Xoshiro256StarStar(bool threadSafe) : this(RandomSeed.Robust(), threadSafe) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Construct a new random number generator with random seed.
|
|||
/// </summary>
|
|||
/// <param name="seed">The seed value.</param>
|
|||
public Xoshiro256StarStar(int seed) |
|||
{ |
|||
Initialise(seed); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Construct a new random number generator with random seed.
|
|||
/// </summary>
|
|||
/// <param name="seed">The seed value.</param>
|
|||
/// <param name="threadSafe">if set to <c>true</c> , the class is thread safe.</param>
|
|||
public Xoshiro256StarStar(int seed, bool threadSafe) : base(threadSafe) |
|||
{ |
|||
Initialise(seed); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a random double-precision floating point number greater than or equal to 0.0, and less than 1.0.
|
|||
/// </summary>
|
|||
protected sealed override double DoSample() |
|||
{ |
|||
// Note. Here we generate a random integer between 0 and 2^53-1 (i.e. 53 binary 1s) and multiply
|
|||
// by the fractional unit value 1.0 / 2^53, thus the result has a max value of
|
|||
// 1.0 - (1.0 / 2^53), or 0.99999999999999989 in decimal.
|
|||
return (NextInnerULong() >> 11) * REAL_UNIT_UINT; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a random 32-bit signed integer greater than or equal to zero and less than <see cref="F:System.Int32.MaxValue"/>
|
|||
/// </summary>
|
|||
protected override int DoSampleInteger() |
|||
{ |
|||
retry: |
|||
// Handle the special case where the value int.MaxValue is generated; this is outside
|
|||
// the range of permitted return values for this method.
|
|||
ulong rtn = NextInnerULong() & 0x7fff_ffffUL; |
|||
if (rtn == 0x7fff_ffffUL) |
|||
{ |
|||
goto retry; |
|||
} |
|||
return (int)rtn; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Fills the elements of a specified array of bytes with random numbers in full range, including zero and 255 (<see cref="F:System.Byte.MaxValue"/>).
|
|||
/// </summary>
|
|||
protected override void DoSampleBytes(byte[] buffer) |
|||
{ |
|||
// For improved performance the below loop operates on these stack allocated copies of the heap variables.
|
|||
// Notes. doing this means that these heavily used variables are located near to other local/stack variables,
|
|||
// thus they will very likely be cached in the same CPU cache line.
|
|||
ulong s0 = _s0; |
|||
ulong s1 = _s1; |
|||
ulong s2 = _s2; |
|||
ulong s3 = _s3; |
|||
|
|||
int i = 0; |
|||
|
|||
// Fill up the bulk of the buffer in chunks of 8 bytes at a time.
|
|||
for (int bound = buffer.Length - 3; i < bound;) |
|||
{ |
|||
// Generate 64 random bits.
|
|||
ulong x = RotateLeft(s1 * 5, 7) * 9; |
|||
|
|||
// Update PRNG state.
|
|||
ulong t = s1 << 17; |
|||
s2 ^= s0; |
|||
s3 ^= s1; |
|||
s1 ^= s2; |
|||
s0 ^= s3; |
|||
s2 ^= t; |
|||
s3 = RotateLeft(s3, 45); |
|||
|
|||
// Assign bits to a segment of 8 bytes.
|
|||
buffer[i++] = (byte)x; |
|||
buffer[i++] = (byte)(x >> 8); |
|||
buffer[i++] = (byte)(x >> 16); |
|||
buffer[i++] = (byte)(x >> 24); |
|||
buffer[i++] = (byte)(x >> 32); |
|||
buffer[i++] = (byte)(x >> 40); |
|||
buffer[i++] = (byte)(x >> 48); |
|||
buffer[i++] = (byte)(x >> 56); |
|||
} |
|||
|
|||
// Fill up any remaining bytes in the buffer.
|
|||
if (i < buffer.Length) |
|||
{ |
|||
// Generate 64 random bits.
|
|||
ulong x = RotateLeft(s1 * 5, 7) * 9; |
|||
|
|||
// Update PRNG state.
|
|||
ulong t = s1 << 17; |
|||
s2 ^= s0; |
|||
s3 ^= s1; |
|||
s1 ^= s2; |
|||
s0 ^= s3; |
|||
s2 ^= t; |
|||
s3 = RotateLeft(s3, 45); |
|||
|
|||
// Allocate one byte at a time until we reach the end of the buffer.
|
|||
while (i < buffer.Length) |
|||
{ |
|||
buffer[i++] = (byte)x; |
|||
x >>= 8; |
|||
} |
|||
} |
|||
|
|||
// Update the state variables on the heap.
|
|||
_s0 = s0; |
|||
_s1 = s1; |
|||
_s2 = s2; |
|||
_s3 = s3; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a random N-bit signed integer greater than or equal to zero and less than 2^N.
|
|||
/// N (bit count) is expected to be greater than zero and less than 32 (not verified).
|
|||
/// </summary>
|
|||
protected override int DoSampleInt32WithNBits(int bitCount) |
|||
{ |
|||
return (int)(NextInnerULong() >> (64 - bitCount)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a random N-bit signed long integer greater than or equal to zero and less than 2^N.
|
|||
/// N (bit count) is expected to be greater than zero and less than 64 (not verified).
|
|||
/// </summary>
|
|||
protected override long DoSampleInt64WithNBits(int bitCount) |
|||
{ |
|||
return (long)(NextInnerULong() >> (64 - bitCount)); |
|||
} |
|||
|
|||
private void Initialise(int seed) |
|||
{ |
|||
// Notes.
|
|||
// xoroshiro256** requires that at least one of the state variable be non-zero, use of splitmix64
|
|||
// satisfies that requirement because its outputs are equidistributed, i.e. if a zero is output
|
|||
// then the next zero will be after a further 2^64 outputs.
|
|||
// Splitmix will also accept a zero input, thus all possible seeds can be accepted here and will
|
|||
// all generate good initial state for xoshiro256**.
|
|||
ulong longSeed = (ulong)seed; |
|||
|
|||
// Use the splitmix64 RNG to hash the seed.
|
|||
_s0 = Splitmix64(ref longSeed); |
|||
_s1 = Splitmix64(ref longSeed); |
|||
_s2 = Splitmix64(ref longSeed); |
|||
_s3 = Splitmix64(ref longSeed); |
|||
} |
|||
|
|||
private ulong NextInnerULong() |
|||
{ |
|||
ulong s0 = _s0; |
|||
ulong s1 = _s1; |
|||
ulong s2 = _s2; |
|||
ulong s3 = _s3; |
|||
|
|||
ulong result = RotateLeft(s1 * 5, 7) * 9; |
|||
|
|||
ulong t = s1 << 17; |
|||
s2 ^= s0; |
|||
s3 ^= s1; |
|||
s1 ^= s2; |
|||
s0 ^= s3; |
|||
s2 ^= t; |
|||
s3 = RotateLeft(s3, 45); |
|||
|
|||
_s0 = s0; |
|||
_s1 = s1; |
|||
_s2 = s2; |
|||
_s3 = s3; |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Fills an array with random numbers greater than or equal to 0.0 and less than 1.0.
|
|||
/// </summary>
|
|||
/// <remarks>Supports being called in parallel from multiple threads.</remarks>
|
|||
public static void Doubles(double[] values, int seed) |
|||
{ |
|||
// Init state.
|
|||
ulong longSeed = (ulong)seed; |
|||
ulong s0 = Splitmix64(ref longSeed); |
|||
ulong s1 = Splitmix64(ref longSeed); |
|||
ulong s2 = Splitmix64(ref longSeed); |
|||
ulong s3 = Splitmix64(ref longSeed); |
|||
|
|||
for (int i = 0; i < values.Length; i++) |
|||
{ |
|||
// Generate sample.
|
|||
values[i] = ((RotateLeft(s1 * 5, 7) * 9) >> 11) * REAL_UNIT_UINT; |
|||
|
|||
// Update PRNG state.
|
|||
ulong t = s1 << 17; |
|||
s2 ^= s0; |
|||
s3 ^= s1; |
|||
s1 ^= s2; |
|||
s0 ^= s3; |
|||
s2 ^= t; |
|||
s3 = RotateLeft(s3, 45); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0.
|
|||
/// </summary>
|
|||
/// <remarks>Supports being called in parallel from multiple threads.</remarks>
|
|||
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] |
|||
public static double[] Doubles(int length, int seed) |
|||
{ |
|||
var data = new double[length]; |
|||
Doubles(data, seed); |
|||
return data; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0.
|
|||
/// </summary>
|
|||
/// <remarks>Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each.</remarks>
|
|||
public static IEnumerable<double> DoubleSequence(int seed) |
|||
{ |
|||
// Init state.
|
|||
ulong longSeed = (ulong)seed; |
|||
ulong s0 = Splitmix64(ref longSeed); |
|||
ulong s1 = Splitmix64(ref longSeed); |
|||
ulong s2 = Splitmix64(ref longSeed); |
|||
ulong s3 = Splitmix64(ref longSeed); |
|||
|
|||
while (true) |
|||
{ |
|||
// Generate sample.
|
|||
double x = ((RotateLeft(s1 * 5, 7) * 9) >> 11) * REAL_UNIT_UINT; |
|||
|
|||
// Update PRNG state.
|
|||
ulong t = s1 << 17; |
|||
s2 ^= s0; |
|||
s3 ^= s1; |
|||
s1 ^= s2; |
|||
s0 ^= s3; |
|||
s2 ^= t; |
|||
s3 = RotateLeft(s3, 45); |
|||
|
|||
// Yield sample.
|
|||
yield return x; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Splitmix64 RNG.
|
|||
/// </summary>
|
|||
/// <param name="x">RNG state. This can take any value, including zero.</param>
|
|||
/// <returns>A new random UInt64.</returns>
|
|||
/// <remarks>
|
|||
/// Splitmix64 produces equidistributed outputs, thus if a zero is generated then the
|
|||
/// next zero will be after a further 2^64 outputs.
|
|||
/// </remarks>
|
|||
private static ulong Splitmix64(ref ulong x) |
|||
{ |
|||
ulong z = (x += 0x9E3779B97F4A7C15UL); |
|||
z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9UL; |
|||
z = (z ^ (z >> 27)) * 0x94D049BB133111EBUL; |
|||
return z ^ (z >> 31); |
|||
} |
|||
|
|||
private static ulong RotateLeft(ulong x, int k) |
|||
{ |
|||
// Note. RyuJIT will compile this to a single rotate CPU instruction (as of about .NET 4.6.1 and dotnet core 2.0).
|
|||
return (x << k) | (x >> (64 - k)); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue