// // 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; } } } }