//
// 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.
//
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
#if !NETSTANDARD1_3
using System.Runtime;
#endif
namespace IStation.Numerics.Random
{
///
/// Represents a Parallel Additive Lagged Fibonacci pseudo-random number generator.
///
///
/// The type bases upon the implementation in the
/// Boost Random Number Library.
/// It uses the modulus 232 and by default the "lags" 418 and 1279. Some popular pairs are presented on
/// Wikipedia - Lagged Fibonacci generator.
///
[Serializable]
[DataContract(Namespace = "urn:IStation/Numerics/Random")]
public class Palf : RandomSource
{
///
/// Default value for the ShortLag
///
const int DefaultShortLag = 418;
///
/// Default value for the LongLag
///
const int DefaultLongLag = 1279;
///
/// The multiplier to compute a double-precision floating point number [0, 1)
///
const double Reciprocal = 1.0/4294967296.0; // 1.0/(uint.MaxValue + 1.0)
///
/// Initializes a new instance of the class using
/// a seed based on time and unique GUIDs.
///
/// If the seed value is zero, it is set to one. Uses the
/// value of to
/// set whether the instance is thread safe.
public Palf() : this(RandomSeed.Robust(), Control.ThreadSafeRandomNumberGenerators, DefaultShortLag, DefaultLongLag)
{
}
///
/// Initializes a new instance of the class using
/// a seed based on time and unique GUIDs.
///
/// if set to true , the class is thread safe.
public Palf(bool threadSafe) : this(RandomSeed.Robust(), threadSafe, DefaultShortLag, DefaultLongLag)
{
}
///
/// Initializes a new instance of the class.
///
/// The seed value.
/// If the seed value is zero, it is set to one. Uses the
/// value of to
/// set whether the instance is thread safe.
public Palf(int seed) : this(seed, Control.ThreadSafeRandomNumberGenerators, DefaultShortLag, DefaultLongLag)
{
}
///
/// Initializes a new instance of the class.
///
/// The seed value.
/// if set to true , the class is thread safe.
public Palf(int seed, bool threadSafe) : this(seed, threadSafe, DefaultShortLag, DefaultLongLag)
{
}
///
/// Initializes a new instance of the class.
///
/// The seed value.
/// if set to true, the class is thread safe.
/// The ShortLag value
/// TheLongLag value
public Palf(int seed, bool threadSafe, int shortLag, int longLag) : base(threadSafe)
{
if (shortLag < 1)
{
throw new ArgumentException("Value must be positive.", nameof(shortLag));
}
if (longLag <= shortLag)
{
throw new ArgumentException("The upper bound must be strictly larger than the lower bound.", nameof(longLag));
}
if (seed == 0)
{
seed = 1;
}
_threads = Control.MaxDegreeOfParallelism;
ShortLag = shortLag;
// Align LongLag to number of worker threads.
if (longLag%_threads == 0)
{
LongLag = longLag;
}
else
{
LongLag = ((longLag/_threads) + 1)*_threads;
}
_x = Generate.Map(MersenneTwister.Doubles(LongLag, seed), uniform => (uint)(uniform*uint.MaxValue));
_k = LongLag;
}
///
/// Gets the short lag of the Lagged Fibonacci pseudo-random number generator.
///
[DataMember(Order = 1)]
public int ShortLag { get; private set; }
///
/// Gets the long lag of the Lagged Fibonacci pseudo-random number generator.
///
[DataMember(Order = 2)]
public int LongLag { get; private set; }
///
/// Stores an array of random numbers
///
[DataMember(Order = 3)]
readonly uint[] _x;
[DataMember(Order = 4)]
readonly int _threads;
///
/// Stores an index for the random number array element that will be accessed next.
///
[DataMember(Order = 5)]
int _k;
///
/// Fills the array with new unsigned random numbers.
///
///
/// Generated random numbers are 32-bit unsigned integers greater than or equal to 0
/// and less than or equal to .
///
void Fill()
{
//CommonParallel.For(0, Control.NumberOfParallelWorkerThreads, (u, v) =>
//{
// for (int index = u; index < v; index++)
// {
// // Two loops to avoid costly modulo operations
// for (var j = index; j < ShortLag; j = j + Control.NumberOfParallelWorkerThreads)
// {
// _x[j] += _x[j + (LongLag - ShortLag)];
// }
// for (var j = ShortLag + index; j < LongLag; j = j + Control.NumberOfParallelWorkerThreads)
// {
// _x[j] += _x[j - ShortLag - index];
// }
// }
//});
for (int index = 0; index < _threads; index++)
{
// Two loops to avoid costly modulo operations
for (var j = index; j < ShortLag; j = j + _threads)
{
_x[j] += _x[j + (LongLag - ShortLag)];
}
for (var j = ShortLag + index; j < LongLag; j = j + _threads)
{
_x[j] += _x[j - ShortLag - index];
}
}
_k = 0;
}
///
/// Returns a random double-precision floating point number greater than or equal to 0.0, and less than 1.0.
///
protected sealed override double DoSample()
{
if (_k >= LongLag)
{
Fill();
}
return _x[_k++] * Reciprocal;
}
///
/// Returns a random 32-bit signed integer greater than or equal to zero and less than
///
protected override int DoSampleInteger()
{
if (_k >= LongLag)
{
Fill();
}
uint uint32 = _x[_k++];
int int31 = (int)(uint32 >> 1);
if (int31 == int.MaxValue)
{
return DoSampleInteger();
}
return int31;
}
///
/// Fills an array with random numbers greater than or equal to 0.0 and less than 1.0.
///
/// Supports being called in parallel from multiple threads.
public static void Doubles(double[] values, int seed)
{
if (seed == 0)
{
seed = 1;
}
int threads = Control.MaxDegreeOfParallelism;
const int shortLag = DefaultShortLag;
var longLag = DefaultLongLag;
// Align LongLag to number of worker threads.
if (longLag%threads != 0)
{
longLag = ((longLag/threads) + 1)*threads;
}
var x = Generate.Map(MersenneTwister.Doubles(longLag, seed), uniform => (uint)(uniform*uint.MaxValue));
var k = longLag;
for (int i = 0; i < values.Length; i++)
{
if (k >= longLag)
{
for (int index = 0; index < threads; index++)
{
// Two loops to avoid costly modulo operations
for (var j = index; j < shortLag; j = j + threads)
{
x[j] += x[j + (longLag - shortLag)];
}
for (var j = shortLag + index; j < longLag; j = j + threads)
{
x[j] += x[j - shortLag - index];
}
}
k = 0;
}
values[i] = x[k++]*Reciprocal;
}
}
///
/// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0.
///
/// Supports being called in parallel from multiple threads.
[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;
}
///
/// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0.
///
/// Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each.
public static IEnumerable DoubleSequence(int seed)
{
if (seed == 0)
{
seed = 1;
}
int threads = Control.MaxDegreeOfParallelism;
const int shortLag = DefaultShortLag;
var longLag = DefaultLongLag;
// Align LongLag to number of worker threads.
if (longLag%threads != 0)
{
longLag = ((longLag/threads) + 1)*threads;
}
var x = Generate.Map(MersenneTwister.Doubles(longLag, seed), uniform => (uint)(uniform*uint.MaxValue));
var k = longLag;
while (true)
{
if (k >= longLag)
{
for (int index = 0; index < threads; index++)
{
// Two loops to avoid costly modulo operations
for (var j = index; j < shortLag; j = j + threads)
{
x[j] += x[j + (longLag - shortLag)];
}
for (var j = shortLag + index; j < longLag; j = j + threads)
{
x[j] += x[j - shortLag - index];
}
}
k = 0;
}
yield return x[k++]*Reciprocal;
}
}
}
}