From d9f89290678c7a67f8fe774ff7a853b0c15742cb Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sun, 15 Jan 2017 16:42:58 +0100 Subject: [PATCH] Random: ephemeral serialization support for random sources #462 --- src/Numerics/Random/Mcg31m1.cs | 6 ++ src/Numerics/Random/Mcg59.cs | 6 ++ src/Numerics/Random/MersenneTwister.cs | 6 ++ src/Numerics/Random/Mrg32k3a.cs | 12 +++ src/Numerics/Random/Palf.cs | 8 ++ src/Numerics/Random/RandomSource.cs | 3 + src/Numerics/Random/SystemRandomSource.cs | 5 + src/Numerics/Random/WH1982.cs | 8 ++ src/Numerics/Random/WH2006.cs | 9 ++ src/Numerics/Random/Xorshift.cs | 8 ++ .../Random/RandomSerializationTests.cs | 97 +++++++++++++++++++ src/UnitTests/UnitTests.csproj | 1 + 12 files changed, 169 insertions(+) create mode 100644 src/UnitTests/Random/RandomSerializationTests.cs diff --git a/src/Numerics/Random/Mcg31m1.cs b/src/Numerics/Random/Mcg31m1.cs index 2daffa3e..6e8de9e8 100644 --- a/src/Numerics/Random/Mcg31m1.cs +++ b/src/Numerics/Random/Mcg31m1.cs @@ -27,7 +27,9 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using System; using System.Collections.Generic; +using System.Runtime.Serialization; #if !PORTABLE using System.Runtime; @@ -38,11 +40,15 @@ namespace MathNet.Numerics.Random /// /// Multiplicative congruential generator using a modulus of 2^31-1 and a multiplier of 1132489760. /// + [Serializable] + [DataContract(Namespace = "urn:MathNet/Numerics/Random")] public class Mcg31m1 : RandomSource { const ulong Modulus = 2147483647; const ulong Multiplier = 1132489760; const double Reciprocal = 1.0/Modulus; + + [DataMember(Order = 1)] ulong _xn; /// diff --git a/src/Numerics/Random/Mcg59.cs b/src/Numerics/Random/Mcg59.cs index 87611790..9968c8bc 100644 --- a/src/Numerics/Random/Mcg59.cs +++ b/src/Numerics/Random/Mcg59.cs @@ -27,7 +27,9 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using System; using System.Collections.Generic; +using System.Runtime.Serialization; #if !PORTABLE using System.Runtime; @@ -38,11 +40,15 @@ namespace MathNet.Numerics.Random /// /// Multiplicative congruential generator using a modulus of 2^59 and a multiplier of 13^13. /// + [Serializable] + [DataContract(Namespace = "urn:MathNet/Numerics/Random")] public class Mcg59 : RandomSource { const ulong Modulus = 576460752303423488; const ulong Multiplier = 302875106592253; const double Reciprocal = 1.0/Modulus; + + [DataMember(Order = 1)] ulong _xn; /// diff --git a/src/Numerics/Random/MersenneTwister.cs b/src/Numerics/Random/MersenneTwister.cs index 6cce7d08..345ea3ec 100644 --- a/src/Numerics/Random/MersenneTwister.cs +++ b/src/Numerics/Random/MersenneTwister.cs @@ -65,7 +65,9 @@ email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) */ +using System; using System.Collections.Generic; +using System.Runtime.Serialization; #if PORTABLE using System; @@ -79,6 +81,8 @@ namespace MathNet.Numerics.Random /// /// Random number generator using Mersenne Twister 19937 algorithm. /// + [Serializable] + [DataContract(Namespace = "urn:MathNet/Numerics/Random")] public class MersenneTwister : RandomSource { /// @@ -119,11 +123,13 @@ namespace MathNet.Numerics.Random /// /// Mersenne twister constant. /// + [DataMember(Order = 1)] readonly uint[] _mt = new uint[N]; /// /// Mersenne twister constant. /// + [DataMember(Order = 2)] int _mti = N + 1; /// diff --git a/src/Numerics/Random/Mrg32k3a.cs b/src/Numerics/Random/Mrg32k3a.cs index d00618e0..5c27c8d8 100644 --- a/src/Numerics/Random/Mrg32k3a.cs +++ b/src/Numerics/Random/Mrg32k3a.cs @@ -27,7 +27,9 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using System; using System.Collections.Generic; +using System.Runtime.Serialization; #if !PORTABLE using System.Runtime; @@ -39,6 +41,9 @@ namespace MathNet.Numerics.Random /// A 32-bit combined multiple recursive generator with 2 components of order 3. /// /// Based off of P. L'Ecuyer, "Combined Multiple Recursive Random Number Generators," Operations Research, 44, 5 (1996), 816--822. + /// + [Serializable] + [DataContract(Namespace = "urn:MathNet/Numerics/Random")] public class Mrg32k3a : RandomSource { const double A12 = 1403580; @@ -49,11 +54,18 @@ namespace MathNet.Numerics.Random const double Modulus2 = 4294944443; const double Reciprocal = 1.0/Modulus1; + + [DataMember(Order = 1)] double _xn1 = 1; + [DataMember(Order = 2)] double _xn2 = 1; + [DataMember(Order = 3)] double _xn3; + [DataMember(Order = 4)] double _yn1 = 1; + [DataMember(Order = 5)] double _yn2 = 1; + [DataMember(Order = 6)] double _yn3 = 1; /// diff --git a/src/Numerics/Random/Palf.cs b/src/Numerics/Random/Palf.cs index e899d8a2..d10e2481 100644 --- a/src/Numerics/Random/Palf.cs +++ b/src/Numerics/Random/Palf.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; +using System.Runtime.Serialization; using MathNet.Numerics.Properties; #if !PORTABLE @@ -46,6 +47,8 @@ namespace MathNet.Numerics.Random /// 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:MathNet/Numerics/Random")] public class Palf : RandomSource { /// @@ -147,23 +150,28 @@ namespace MathNet.Numerics.Random /// /// 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; /// diff --git a/src/Numerics/Random/RandomSource.cs b/src/Numerics/Random/RandomSource.cs index cce5a1f8..6a2211f4 100644 --- a/src/Numerics/Random/RandomSource.cs +++ b/src/Numerics/Random/RandomSource.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; +using System.Runtime.Serialization; using MathNet.Numerics.Properties; namespace MathNet.Numerics.Random @@ -38,6 +39,8 @@ namespace MathNet.Numerics.Random /// and the Math.Net Numerics random number generators to provide thread safety. /// When used directly it use the System.Random as random number source. /// + [Serializable] + [DataContract(Namespace = "urn:MathNet/Numerics/Random")] public abstract class RandomSource : System.Random { readonly bool _threadSafe; diff --git a/src/Numerics/Random/SystemRandomSource.cs b/src/Numerics/Random/SystemRandomSource.cs index e7554c3c..06a64c6a 100644 --- a/src/Numerics/Random/SystemRandomSource.cs +++ b/src/Numerics/Random/SystemRandomSource.cs @@ -27,7 +27,9 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using System; using System.Collections.Generic; +using System.Runtime.Serialization; using MathNet.Numerics.Threading; #if PORTABLE @@ -42,8 +44,11 @@ namespace MathNet.Numerics.Random /// /// A random number generator based on the class in the .NET library. /// + [Serializable] + [DataContract(Namespace = "urn:MathNet/Numerics/Random")] public class SystemRandomSource : RandomSource { + [DataMember(Order = 1)] readonly System.Random _random; /// diff --git a/src/Numerics/Random/WH1982.cs b/src/Numerics/Random/WH1982.cs index 4d4309d9..9dde71e4 100644 --- a/src/Numerics/Random/WH1982.cs +++ b/src/Numerics/Random/WH1982.cs @@ -27,7 +27,9 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using System; using System.Collections.Generic; +using System.Runtime.Serialization; #if !PORTABLE using System.Runtime; @@ -41,6 +43,8 @@ namespace MathNet.Numerics.Random /// See: Wichmann, B. A. & Hill, I. D. (1982), "Algorithm AS 183: /// An efficient and portable pseudo-random number generator". Applied Statistics 31 (1982) 188-190 /// + [Serializable] + [DataContract(Namespace = "urn:MathNet/Numerics/Random")] public class WH1982 : RandomSource { const uint Modx = 30269; @@ -49,8 +53,12 @@ namespace MathNet.Numerics.Random const double ModyRecip = 1.0/Mody; const uint Modz = 30323; const double ModzRecip = 1.0/Modz; + + [DataMember(Order = 1)] uint _xn; + [DataMember(Order = 2)] uint _yn = 1; + [DataMember(Order = 3)] uint _zn = 1; /// diff --git a/src/Numerics/Random/WH2006.cs b/src/Numerics/Random/WH2006.cs index 4ec45db0..a0034748 100644 --- a/src/Numerics/Random/WH2006.cs +++ b/src/Numerics/Random/WH2006.cs @@ -27,7 +27,9 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using System; using System.Collections.Generic; +using System.Runtime.Serialization; #if !PORTABLE using System.Runtime; @@ -41,6 +43,8 @@ namespace MathNet.Numerics.Random /// See: Wichmann, B. A. & Hill, I. D. (2006), "Generating good pseudo-random numbers". /// Computational Statistics & Data Analysis 51:3 (2006) 1614-1622 /// + [Serializable] + [DataContract(Namespace = "urn:MathNet/Numerics/Random")] public class WH2006 : RandomSource { const uint Modw = 2147483123; @@ -51,9 +55,14 @@ namespace MathNet.Numerics.Random const double ModyRecip = 1.0/Mody; const uint Modz = 2147483423; const double ModzRecip = 1.0/Modz; + + [DataMember(Order = 1)] ulong _wn = 1; + [DataMember(Order = 2)] ulong _xn; + [DataMember(Order = 3)] ulong _yn = 1; + [DataMember(Order = 4)] ulong _zn = 1; /// diff --git a/src/Numerics/Random/Xorshift.cs b/src/Numerics/Random/Xorshift.cs index 1c64226f..3553ce84 100644 --- a/src/Numerics/Random/Xorshift.cs +++ b/src/Numerics/Random/Xorshift.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; +using System.Runtime.Serialization; using MathNet.Numerics.Properties; #if !PORTABLE @@ -42,6 +43,8 @@ namespace MathNet.Numerics.Random /// Xn = a * Xn−3 + c mod 2^32 /// http://www.jstatsoft.org/v08/i14/paper /// + [Serializable] + [DataContract(Namespace = "urn:MathNet/Numerics/Random")] public class Xorshift : RandomSource { /// @@ -72,26 +75,31 @@ namespace MathNet.Numerics.Random /// /// Seed or last but three unsigned random number. /// + [DataMember(Order = 1)] ulong _x; /// /// Last but two unsigned random number. /// + [DataMember(Order = 2)] ulong _y; /// /// Last but one unsigned random number. /// + [DataMember(Order = 3)] ulong _z; /// /// The value of the carry over. /// + [DataMember(Order = 4)] ulong _c; /// /// The multiplier. /// + [DataMember(Order = 5)] readonly ulong _a; /// diff --git a/src/UnitTests/Random/RandomSerializationTests.cs b/src/UnitTests/Random/RandomSerializationTests.cs new file mode 100644 index 00000000..f455cb20 --- /dev/null +++ b/src/UnitTests/Random/RandomSerializationTests.cs @@ -0,0 +1,97 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// +// Copyright (c) 2009-2017 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.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using MathNet.Numerics.Random; +using NUnit.Framework; + +namespace MathNet.Numerics.UnitTests.Random +{ + [TestFixture] + public class RandomSerializationTests + { + [Test] + [TestCase(typeof(MersenneTwister))] + [TestCase(typeof(Mcg59))] + [TestCase(typeof(Mcg31m1))] + [TestCase(typeof(Mrg32k3a))] + [TestCase(typeof(Palf))] + [TestCase(typeof(WH1982))] + [TestCase(typeof(WH2006))] + [TestCase(typeof(Xorshift))] + [TestCase(typeof(SystemRandomSource))] + public void DataContractSerializationTest(Type randomType) + { + var expected = (RandomSource)Activator.CreateInstance(randomType); + expected.NextDouble(); + expected.NextBoolean(); + + var serializer = new DataContractSerializer(randomType); + var stream = new MemoryStream(); + serializer.WriteObject(stream, expected); + + stream.Position = 0; + var actual = (RandomSource)serializer.ReadObject(stream); + + Assert.That(actual.GetType(), Is.EqualTo(randomType)); + Assert.That(actual.NextDoubleSequence().Take(10).ToArray(), Is.EqualTo(expected.NextDoubleSequence().Take(10).ToArray()).AsCollection); + } + + [Test] + [TestCase(typeof(MersenneTwister))] + [TestCase(typeof(Mcg59))] + [TestCase(typeof(Mcg31m1))] + [TestCase(typeof(Mrg32k3a))] + [TestCase(typeof(Palf))] + [TestCase(typeof(WH1982))] + [TestCase(typeof(WH2006))] + [TestCase(typeof(Xorshift))] + [TestCase(typeof(SystemRandomSource))] + public void BinaryFormatterSerializationTest(Type randomType) + { + var expected = (RandomSource)Activator.CreateInstance(randomType); + expected.NextDouble(); + expected.NextBoolean(); + + var serializer = new BinaryFormatter(); + var stream = new MemoryStream(); + serializer.Serialize(stream, expected); + + stream.Position = 0; + var actual = (RandomSource)serializer.Deserialize(stream); + + Assert.That(actual.GetType(), Is.EqualTo(randomType)); + Assert.That(actual.NextDoubleSequence().Take(10).ToArray(), Is.EqualTo(expected.NextDoubleSequence().Take(10).ToArray()).AsCollection); + } + } +} diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj index 77a3231b..25595c6b 100644 --- a/src/UnitTests/UnitTests.csproj +++ b/src/UnitTests/UnitTests.csproj @@ -365,6 +365,7 @@ +