From 4b7d61e920751eda6baaa1fb83eb6846ca2d5d0f Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Wed, 9 Apr 2014 22:43:47 +0200 Subject: [PATCH] Distributions: array-sampling 2 --- src/Numerics/Distributions/Exponential.cs | 72 ++++++++++++++--- src/Numerics/Distributions/LogNormal.cs | 1 - src/Numerics/Distributions/Pareto.cs | 75 +++++++++++++++--- src/Numerics/Distributions/Rayleigh.cs | 63 +++++++++++---- src/Numerics/Distributions/Triangular.cs | 87 +++++++++++++++------ src/Numerics/Distributions/Weibull.cs | 95 ++++++++++++++--------- 6 files changed, 298 insertions(+), 95 deletions(-) diff --git a/src/Numerics/Distributions/Exponential.cs b/src/Numerics/Distributions/Exponential.cs index e6fc1194..c6937557 100644 --- a/src/Numerics/Distributions/Exponential.cs +++ b/src/Numerics/Distributions/Exponential.cs @@ -30,8 +30,10 @@ using System; using System.Collections.Generic; +using System.Linq; using MathNet.Numerics.Properties; using MathNet.Numerics.Random; +using MathNet.Numerics.Threading; namespace MathNet.Numerics.Distributions { @@ -241,10 +243,7 @@ namespace MathNet.Numerics.Distributions /// a sequence of samples from the distribution. public IEnumerable Samples() { - while (true) - { - yield return SampleUnchecked(_random, _rate); - } + return SamplesUnchecked(_random, _rate); } /// @@ -261,7 +260,30 @@ namespace MathNet.Numerics.Distributions r = rnd.NextDouble(); } - return -Math.Log(r) / rate; + return -Math.Log(r)/rate; + } + + static IEnumerable SamplesUnchecked(System.Random rnd, double rate) + { + return rnd.NextDoubleSequence().Where(r => r != 0.0).Select(r => -Math.Log(r)/rate); + } + + static void SamplesUnchecked(System.Random rnd, double[] values, double rate) + { + rnd.NextDoubles(values); + CommonParallel.For(0, values.Length, 4096, (a, b) => + { + for (int i = a; i < b; i++) + { + // this happens very rarely + var r = values[i]; + while (r == 0.0) + { + r = rnd.NextDouble(); + } + values[i] = -Math.Log(r)/rate; + } + }); } /// @@ -344,10 +366,21 @@ namespace MathNet.Numerics.Distributions { if (rate < 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); - while (true) - { - yield return SampleUnchecked(rnd, rate); - } + return SamplesUnchecked(rnd, rate); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The random number generator to use. + /// The array to fill with the samples. + /// The rate (λ) parameter of the distribution. Range: λ ≥ 0. + /// a sequence of samples from the distribution. + public static void Samples(System.Random rnd, double[] values, double rate) + { + if (rate < 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(rnd, values, rate); } /// @@ -357,7 +390,9 @@ namespace MathNet.Numerics.Distributions /// A random number from this distribution. public static double Sample(double rate) { - return Sample(SystemRandomSource.Default, rate); + if (rate < 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SampleUnchecked(SystemRandomSource.Default, rate); } /// @@ -367,7 +402,22 @@ namespace MathNet.Numerics.Distributions /// a sequence of samples from the distribution. public static IEnumerable Samples(double rate) { - return Samples(SystemRandomSource.Default, rate); + if (rate < 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SamplesUnchecked(SystemRandomSource.Default, rate); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The array to fill with the samples. + /// The rate (λ) parameter of the distribution. Range: λ ≥ 0. + /// a sequence of samples from the distribution. + public static void Samples(double[] values, double rate) + { + if (rate < 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(SystemRandomSource.Default, values, rate); } } } diff --git a/src/Numerics/Distributions/LogNormal.cs b/src/Numerics/Distributions/LogNormal.cs index 73b71cd7..cbf8f243 100644 --- a/src/Numerics/Distributions/LogNormal.cs +++ b/src/Numerics/Distributions/LogNormal.cs @@ -31,7 +31,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection.Emit; using MathNet.Numerics.Properties; using MathNet.Numerics.Random; using MathNet.Numerics.Statistics; diff --git a/src/Numerics/Distributions/Pareto.cs b/src/Numerics/Distributions/Pareto.cs index ba962f8b..3f4d1e98 100644 --- a/src/Numerics/Distributions/Pareto.cs +++ b/src/Numerics/Distributions/Pareto.cs @@ -30,8 +30,10 @@ using System; using System.Collections.Generic; +using System.Linq; using MathNet.Numerics.Properties; using MathNet.Numerics.Random; +using MathNet.Numerics.Threading; namespace MathNet.Numerics.Distributions { @@ -266,7 +268,7 @@ namespace MathNet.Numerics.Distributions /// A random number from this distribution. public double Sample() { - return _scale*Math.Pow(_random.NextDouble(), -1.0/_shape); + return SampleUnchecked(_random, _scale, _shape); } /// @@ -275,11 +277,31 @@ namespace MathNet.Numerics.Distributions /// a sequence of samples from the distribution. public IEnumerable Samples() { - var power = -1.0/_shape; - while (true) + return SamplesUnchecked(_random, _scale, _shape); + } + + static double SampleUnchecked(System.Random rnd, double scale, double shape) + { + return scale*Math.Pow(rnd.NextDouble(), -1.0/shape); + } + + static IEnumerable SamplesUnchecked(System.Random rnd, double scale, double shape) + { + var power = -1.0/shape; + return rnd.NextDoubleSequence().Select(x => scale*Math.Pow(x, power)); + } + + static void SamplesUnchecked(System.Random rnd, double[] values, double scale, double shape) + { + var power = -1.0/shape; + rnd.NextDoubles(values); + CommonParallel.For(0, values.Length, 4096, (a, b) => { - yield return _scale*Math.Pow(_random.NextDouble(), power); - } + for (int i = a; i < b; i++) + { + values[i] = scale*Math.Pow(values[i], power); + } + }); } /// @@ -368,11 +390,22 @@ namespace MathNet.Numerics.Distributions { if (scale <= 0.0 || shape <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); - var power = -1.0 / shape; - while (true) - { - yield return scale*Math.Pow(rnd.NextDouble(), power); - } + return SamplesUnchecked(rnd, scale, shape); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The random number generator to use. + /// The array to fill with the samples. + /// The scale (xm) of the distribution. Range: xm > 0. + /// The shape (α) of the distribution. Range: α > 0. + /// a sequence of samples from the distribution. + public static void Samples(System.Random rnd, double[] values, double scale, double shape) + { + if (scale <= 0.0 || shape <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(rnd, values, scale, shape); } /// @@ -383,7 +416,9 @@ namespace MathNet.Numerics.Distributions /// a sample from the distribution. public static double Sample(double scale, double shape) { - return Sample(SystemRandomSource.Default, scale, shape); + if (scale <= 0.0 || shape <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SampleUnchecked(SystemRandomSource.Default, scale, shape); } /// @@ -394,7 +429,23 @@ namespace MathNet.Numerics.Distributions /// a sequence of samples from the distribution. public static IEnumerable Samples(double scale, double shape) { - return Samples(SystemRandomSource.Default, scale, shape); + if (scale <= 0.0 || shape <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SamplesUnchecked(SystemRandomSource.Default, scale, shape); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The array to fill with the samples. + /// The scale (xm) of the distribution. Range: xm > 0. + /// The shape (α) of the distribution. Range: α > 0. + /// a sequence of samples from the distribution. + public static void Samples(double[] values, double scale, double shape) + { + if (scale <= 0.0 || shape <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(SystemRandomSource.Default, values, scale, shape); } } } diff --git a/src/Numerics/Distributions/Rayleigh.cs b/src/Numerics/Distributions/Rayleigh.cs index e254eb80..d6e0c205 100644 --- a/src/Numerics/Distributions/Rayleigh.cs +++ b/src/Numerics/Distributions/Rayleigh.cs @@ -30,6 +30,7 @@ using System; using System.Collections.Generic; +using System.Linq; using MathNet.Numerics.Properties; using MathNet.Numerics.Random; using MathNet.Numerics.Threading; @@ -238,7 +239,7 @@ namespace MathNet.Numerics.Distributions /// A random number from this distribution. public double Sample() { - return _scale*Math.Sqrt(-2.0*Math.Log(_random.NextDouble())); + return SampleUnchecked(_random, _scale); } /// @@ -247,13 +248,20 @@ namespace MathNet.Numerics.Distributions /// a sequence of samples from the distribution. public IEnumerable Samples() { - while (true) - { - yield return _scale*Math.Sqrt(-2.0*Math.Log(_random.NextDouble())); - } + return SamplesUnchecked(_random, _scale); + } + + static double SampleUnchecked(System.Random rnd, double scale) + { + return scale*Math.Sqrt(-2.0*Math.Log(rnd.NextDouble())); + } + + static IEnumerable SamplesUnchecked(System.Random rnd, double scale) + { + return rnd.NextDoubleSequence().Select(x => scale*Math.Sqrt(-2.0*Math.Log(x))); } - static void SampleUnchecked(System.Random rnd, double[] values, double scale) + static void SamplesUnchecked(System.Random rnd, double[] values, double scale) { rnd.NextDoubles(values); CommonParallel.For(0, values.Length, 4096, (a, b) => @@ -332,7 +340,7 @@ namespace MathNet.Numerics.Distributions { if (scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); - return scale*Math.Sqrt(-2.0*Math.Log(rnd.NextDouble())); + return SampleUnchecked(rnd, scale); } /// @@ -345,10 +353,21 @@ namespace MathNet.Numerics.Distributions { if (scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); - while (true) - { - yield return scale*Math.Sqrt(-2.0*Math.Log(rnd.NextDouble())); - } + return SamplesUnchecked(rnd, scale); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The random number generator to use. + /// The array to fill with the samples. + /// The scale (σ) of the distribution. Range: σ > 0. + /// a sequence of samples from the distribution. + public static void Samples(System.Random rnd, double[] values, double scale) + { + if (scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(rnd, values, scale); } /// @@ -358,7 +377,9 @@ namespace MathNet.Numerics.Distributions /// a sample from the distribution. public static double Sample(double scale) { - return Sample(SystemRandomSource.Default, scale); + if (scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SampleUnchecked(SystemRandomSource.Default, scale); } /// @@ -368,7 +389,23 @@ namespace MathNet.Numerics.Distributions /// a sequence of samples from the distribution. public static IEnumerable Samples(double scale) { - return Samples(SystemRandomSource.Default, scale); + if (scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SamplesUnchecked(SystemRandomSource.Default, scale); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The random number generator to use. + /// The array to fill with the samples. + /// The scale (σ) of the distribution. Range: σ > 0. + /// a sequence of samples from the distribution. + public static void Samples(double[] values, double scale) + { + if (scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(SystemRandomSource.Default, values, scale); } } } diff --git a/src/Numerics/Distributions/Triangular.cs b/src/Numerics/Distributions/Triangular.cs index 991ea0d4..cb070b52 100644 --- a/src/Numerics/Distributions/Triangular.cs +++ b/src/Numerics/Distributions/Triangular.cs @@ -30,6 +30,7 @@ using System; using System.Collections.Generic; +using System.Linq; using MathNet.Numerics.Properties; using MathNet.Numerics.Random; using MathNet.Numerics.Threading; @@ -284,7 +285,7 @@ namespace MathNet.Numerics.Distributions /// a sample from the distribution. public double Sample() { - return Sample(_random, _lower, _upper, _mode); + return SampleUnchecked(_random, _lower, _upper, _mode); } /// @@ -293,22 +294,38 @@ namespace MathNet.Numerics.Distributions /// a sequence of samples from the distribution. public IEnumerable Samples() { - return Samples(_random, _lower, _upper, _mode); + return SamplesUnchecked(_random, _lower, _upper, _mode); } - static void SampleUnchecked(System.Random rnd, double[] values, double lower, double upper, double mode) + static double SampleUnchecked(System.Random rnd, double lower, double upper, double mode) { - double ml = mode - lower; - double ul = upper - lower; - double um = upper - mode; + var u = rnd.NextDouble(); + return u < (mode - lower)/(upper - lower) + ? lower + Math.Sqrt(u*(upper - lower)*(mode - lower)) + : upper - Math.Sqrt((1 - u)*(upper - lower)*(upper - mode)); + } + + static IEnumerable SamplesUnchecked(System.Random rnd, double lower, double upper, double mode) + { + double ml = mode - lower, ul = upper - lower, um = upper - mode; + double u = ml/ul, v = ul*ml, w = ul*um; + + return rnd.NextDoubleSequence().Select(x => x < u ? lower + Math.Sqrt(x*v) : upper - Math.Sqrt((1 - x)*w)); + } + + static void SamplesUnchecked(System.Random rnd, double[] values, double lower, double upper, double mode) + { + double ml = mode - lower, ul = upper - lower, um = upper - mode; + double u = ml/ul, v = ul*ml, w = ul*um; + rnd.NextDoubles(values); CommonParallel.For(0, values.Length, 4096, (a, b) => { for (int i = a; i < b; i++) { - values[i] = values[i] < ml/ul - ? lower + Math.Sqrt(values[i]*ul*ml) - : upper - Math.Sqrt((1 - values[i])*ul*um); + values[i] = values[i] < u + ? lower + Math.Sqrt(values[i]*v) + : upper - Math.Sqrt((1 - values[i])*w); } }); } @@ -409,14 +426,7 @@ namespace MathNet.Numerics.Distributions { if (!(upper >= mode && mode >= lower)) throw new ArgumentException(Resources.InvalidDistributionParameters); - var a = lower; - var b = upper; - var c = mode; - var u = rnd.NextDouble(); - - return u < (c - a)/(b - a) - ? a + Math.Sqrt(u*(b - a)*(c - a)) - : b - Math.Sqrt((1 - u)*(b - a)*(b - c)); + return SampleUnchecked(rnd, lower, upper, mode); } /// @@ -431,10 +441,22 @@ namespace MathNet.Numerics.Distributions { if (!(upper >= mode && mode >= lower)) throw new ArgumentException(Resources.InvalidDistributionParameters); - while (true) - { - yield return Sample(rnd, lower, upper, mode); - } + return SamplesUnchecked(rnd, lower, upper, mode); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The random number generator to use. + /// Lower bound. Range: lower ≤ mode ≤ upper + /// Upper bound. Range: lower ≤ mode ≤ upper + /// Mode (most frequent value). Range: lower ≤ mode ≤ upper + /// a sequence of samples from the distribution. + public static void Samples(System.Random rnd, double[] values, double lower, double upper, double mode) + { + if (!(upper >= mode && mode >= lower)) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(rnd, values, lower, upper, mode); } /// @@ -446,7 +468,9 @@ namespace MathNet.Numerics.Distributions /// a sample from the distribution. public static double Sample(double lower, double upper, double mode) { - return Sample(SystemRandomSource.Default, lower, upper, mode); + if (!(upper >= mode && mode >= lower)) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SampleUnchecked(SystemRandomSource.Default, lower, upper, mode); } /// @@ -458,7 +482,24 @@ namespace MathNet.Numerics.Distributions /// a sequence of samples from the distribution. public static IEnumerable Samples(double lower, double upper, double mode) { - return Samples(SystemRandomSource.Default, lower, upper, mode); + if (!(upper >= mode && mode >= lower)) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SamplesUnchecked(SystemRandomSource.Default, lower, upper, mode); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The random number generator to use. + /// Lower bound. Range: lower ≤ mode ≤ upper + /// Upper bound. Range: lower ≤ mode ≤ upper + /// Mode (most frequent value). Range: lower ≤ mode ≤ upper + /// a sequence of samples from the distribution. + public static void Samples(double[] values, double lower, double upper, double mode) + { + if (!(upper >= mode && mode >= lower)) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(SystemRandomSource.Default, values, lower, upper, mode); } } } diff --git a/src/Numerics/Distributions/Weibull.cs b/src/Numerics/Distributions/Weibull.cs index 9330789c..cbcfae15 100644 --- a/src/Numerics/Distributions/Weibull.cs +++ b/src/Numerics/Distributions/Weibull.cs @@ -30,6 +30,7 @@ using System; using System.Collections.Generic; +using System.Linq; using MathNet.Numerics.Properties; using MathNet.Numerics.Random; using MathNet.Numerics.Threading; @@ -276,32 +277,6 @@ namespace MathNet.Numerics.Distributions return -SpecialFunctions.ExponentialMinusOne(-Math.Pow(x, _shape)*_scalePowShapeInv); } - /// - /// Generates one sample from the Weibull distribution. This method doesn't perform - /// any parameter checks. - /// - /// The random number generator to use. - /// The shape (k) of the Weibull distribution. Range: k > 0. - /// The scale (λ) of the Weibull distribution. Range: λ > 0. - /// A sample from a Weibull distributed random variable. - static double SampleUnchecked(System.Random rnd, double shape, double scale) - { - var x = rnd.NextDouble(); - return scale*Math.Pow(-Math.Log(x), 1.0/shape); - } - - static void SampleUnchecked(System.Random rnd, double[] values, double shape, double scale) - { - rnd.NextDoubles(values); - CommonParallel.For(0, values.Length, 4096, (a, b) => - { - for (int i = a; i < b; i++) - { - values[i] = scale*Math.Pow(-Math.Log(values[i]), 1.0/shape); - } - }); - } - /// /// Generates a sample from the Weibull distribution. /// @@ -317,10 +292,32 @@ namespace MathNet.Numerics.Distributions /// a sequence of samples from the distribution. public IEnumerable Samples() { - while (true) + return SamplesUnchecked(_random, _shape, _scale); + } + + static double SampleUnchecked(System.Random rnd, double shape, double scale) + { + var x = rnd.NextDouble(); + return scale*Math.Pow(-Math.Log(x), 1.0/shape); + } + + static IEnumerable SamplesUnchecked(System.Random rnd, double shape, double scale) + { + var exponent = 1.0/shape; + return rnd.NextDoubleSequence().Select(x => scale*Math.Pow(-Math.Log(x), exponent)); + } + + static void SamplesUnchecked(System.Random rnd, double[] values, double shape, double scale) + { + var exponent = 1.0/shape; + rnd.NextDoubles(values); + CommonParallel.For(0, values.Length, 4096, (a, b) => { - yield return SampleUnchecked(_random, _shape, _scale); - } + for (int i = a; i < b; i++) + { + values[i] = scale*Math.Pow(-Math.Log(values[i]), exponent); + } + }); } /// @@ -421,10 +418,21 @@ namespace MathNet.Numerics.Distributions { if (shape <= 0.0 || scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); - while (true) - { - yield return SampleUnchecked(rnd, shape, scale); - } + return SamplesUnchecked(rnd, shape, scale); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The random number generator to use. + /// The shape (k) of the Weibull distribution. Range: k > 0. + /// The scale (λ) of the Weibull distribution. Range: λ > 0. + /// a sequence of samples from the distribution. + public static void Samples(System.Random rnd, double[] values, double shape, double scale) + { + if (shape <= 0.0 || scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(rnd, values, shape, scale); } /// @@ -435,7 +443,9 @@ namespace MathNet.Numerics.Distributions /// a sample from the distribution. public static double Sample(double shape, double scale) { - return Sample(SystemRandomSource.Default, shape, scale); + if (shape <= 0.0 || scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SampleUnchecked(SystemRandomSource.Default, shape, scale); } /// @@ -446,7 +456,22 @@ namespace MathNet.Numerics.Distributions /// a sequence of samples from the distribution. public static IEnumerable Samples(double shape, double scale) { - return Samples(SystemRandomSource.Default, shape, scale); + if (shape <= 0.0 || scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SamplesUnchecked(SystemRandomSource.Default, shape, scale); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The shape (k) of the Weibull distribution. Range: k > 0. + /// The scale (λ) of the Weibull distribution. Range: λ > 0. + /// a sequence of samples from the distribution. + public static void Samples(double[] values, double shape, double scale) + { + if (shape <= 0.0 || scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(SystemRandomSource.Default, values, shape, scale); } } }