diff --git a/src/FSharp/Distributions.fs b/src/FSharp/Distributions.fs index ac079b1f..266c7839 100644 --- a/src/FSharp/Distributions.fs +++ b/src/FSharp/Distributions.fs @@ -44,117 +44,117 @@ module Sample = let mapSeq3 f dist1 dist2 dist3 : System.Random -> 'T seq = fun rng -> Seq.zip3 (dist1 rng) (dist2 rng) (dist3 rng) |> Seq.map (fun (d1, d2, d3) -> f d1 d2 d3) /// Bernoulli with probability (p). - let bernoulli p rng = Bernoulli.Sample(rng, p) - let bernoulliSeq p rng = Bernoulli.Samples(rng, p) + let bernoulli p (rng:System.Random) = Bernoulli.Sample(rng, p) + let bernoulliSeq p (rng:System.Random) = Bernoulli.Samples(rng, p) /// Beta with α and β shape parameters. - let beta a b rng = Beta.Sample(rng, a, b) - let betaSeq a b rng = Beta.Samples(rng, a, b) + let beta a b (rng:System.Random) = Beta.Sample(rng, a, b) + let betaSeq a b (rng:System.Random) = Beta.Samples(rng, a, b) /// Binomial with success probability (p) in each trial and number of trials (n). - let binomial p n rng = Binomial.Sample(rng, p, n) - let binomialSeq p n rng = Binomial.Samples(rng, p, n) + let binomial p n (rng:System.Random) = Binomial.Sample(rng, p, n) + let binomialSeq p n (rng:System.Random) = Binomial.Samples(rng, p, n) /// Categorical with an array of nonnegative ratios defining the relative probability mass (unnormalized). - let categorical probabilityMass rng = Categorical.Sample(rng, probabilityMass) - let categoricalSeq probabilityMass rng = Categorical.Samples(rng, probabilityMass) + let categorical probabilityMass (rng:System.Random) = Categorical.Sample(rng, probabilityMass) + let categoricalSeq probabilityMass (rng:System.Random) = Categorical.Samples(rng, probabilityMass) /// Cauchy with location (x0) and scale (γ). - let cauchy location scale rng = Cauchy.Sample(rng, location, scale) - let cauchySeq location scale rng = Cauchy.Samples(rng, location, scale) + let cauchy location scale (rng:System.Random) = Cauchy.Sample(rng, location, scale) + let cauchySeq location scale (rng:System.Random) = Cauchy.Samples(rng, location, scale) /// Chi with degrees of freedom (k). - let chi freedom rng = Chi.Sample(rng, freedom) - let chiSeq freedom rng = Chi.Samples(rng, freedom) + let chi freedom (rng:System.Random) = Chi.Sample(rng, freedom) + let chiSeq freedom (rng:System.Random) = Chi.Samples(rng, freedom) /// Chi-Squared with degrees of freedom (k). - let chiSquared freedom rng = ChiSquared.Sample(rng, freedom) - let chiSquaredSeq freedom rng = ChiSquared.Samples(rng, freedom) + let chiSquared freedom (rng:System.Random) = ChiSquared.Sample(rng, freedom) + let chiSquaredSeq freedom (rng:System.Random) = ChiSquared.Samples(rng, freedom) /// Continuous-Uniform with lower and upper bounds. - let continuousUniform lower upper rng = ContinuousUniform.Sample(rng, lower, upper) - let continuousUniformSeq lower upper rng = ContinuousUniform.Samples(rng, lower, upper) + let continuousUniform lower upper (rng:System.Random) = ContinuousUniform.Sample(rng, lower, upper) + let continuousUniformSeq lower upper (rng:System.Random) = ContinuousUniform.Samples(rng, lower, upper) /// Conway-Maxwell-Poisson with lambda (λ) and rate of decay (ν). - let conwayMaxwellPoisson lambda nu rng = ConwayMaxwellPoisson.Sample(rng, lambda, nu) - let conwayMaxwellPoissonSeq lambda nu rng = ConwayMaxwellPoisson.Samples(rng, lambda, nu) + let conwayMaxwellPoisson lambda nu (rng:System.Random) = ConwayMaxwellPoisson.Sample(rng, lambda, nu) + let conwayMaxwellPoissonSeq lambda nu (rng:System.Random) = ConwayMaxwellPoisson.Samples(rng, lambda, nu) /// Discrete-Uniform with lower and upper bounds (both inclusive). - let discreteUniform lower upper rng = DiscreteUniform.Sample(rng, lower, upper) - let discreteUniformSeq lower upper rng = DiscreteUniform.Samples(rng, lower, upper) + let discreteUniform lower upper (rng:System.Random) = DiscreteUniform.Sample(rng, lower, upper) + let discreteUniformSeq lower upper (rng:System.Random) = DiscreteUniform.Samples(rng, lower, upper) /// Erlang with shape (k) and rate or inverse scale (λ). - let erlang shape rate rng = Erlang.Sample(rng, shape, rate) - let erlangSeq shape rate rng = Erlang.Samples(rng, shape, rate) + let erlang shape rate (rng:System.Random) = Erlang.Sample(rng, shape, rate) + let erlangSeq shape rate (rng:System.Random) = Erlang.Samples(rng, shape, rate) /// Exponential with rate (λ). - let exponential rate rng = Exponential.Sample(rng, rate) - let exponentialSeq rate rng = Exponential.Samples(rng, rate) + let exponential rate (rng:System.Random) = Exponential.Sample(rng, rate) + let exponentialSeq rate (rng:System.Random) = Exponential.Samples(rng, rate) /// Fisher-Snedecor (F-Distribution) with first (d1) and second (d2) degree of freedom. - let fisherSnedecor d1 d2 rng = FisherSnedecor.Sample(rng, d1, d2) - let fisherSnedecorSeq d1 d2 rng = FisherSnedecor.Samples(rng, d1, d2) + let fisherSnedecor d1 d2 (rng:System.Random) = FisherSnedecor.Sample(rng, d1, d2) + let fisherSnedecorSeq d1 d2 (rng:System.Random) = FisherSnedecor.Samples(rng, d1, d2) /// Gamma with shape (k, α) and rate or inverse scale (β). - let gamma shape rate rng = Gamma.Sample(rng, shape, rate) - let gammaSeq shape rate rng = Gamma.Sample(rng, shape, rate) + let gamma shape rate (rng:System.Random) = Gamma.Sample(rng, shape, rate) + let gammaSeq shape rate (rng:System.Random) = Gamma.Sample(rng, shape, rate) /// Geometric with probability (p) of generating one. - let geometric p rng = Geometric.Sample(rng, p) - let geometricSeq p rng = Geometric.Samples(rng, p) + let geometric p (rng:System.Random) = Geometric.Sample(rng, p) + let geometricSeq p (rng:System.Random) = Geometric.Samples(rng, p) /// Hypergeometric with size of the population (N), number successes within the population (K, M) and number of draws without replacement (n). - let hypergeometric population success draws rng = Hypergeometric.Sample(rng, population, success, draws) - let hypergeometricSeq population success draws rng = Hypergeometric.Samples(rng, population, success, draws) + let hypergeometric population success draws (rng:System.Random) = Hypergeometric.Sample(rng, population, success, draws) + let hypergeometricSeq population success draws (rng:System.Random) = Hypergeometric.Samples(rng, population, success, draws) /// Inverse-Gamma with shape (α) and scale (β) - let inverseGamma shape scale rng = InverseGamma.Sample(rng, shape, scale) - let inverseGammaSeq shape scale rng = InverseGamma.Samples(rng, shape, scale) + let inverseGamma shape scale (rng:System.Random) = InverseGamma.Sample(rng, shape, scale) + let inverseGammaSeq shape scale (rng:System.Random) = InverseGamma.Samples(rng, shape, scale) /// Laplace with location (μ) and scale (b). - let laplace location scale rng = Laplace.Sample(rng, location, scale) - let laplaceSeq location scale rng = Laplace.Samples(rng, location, scale) + let laplace location scale (rng:System.Random) = Laplace.Sample(rng, location, scale) + let laplaceSeq location scale (rng:System.Random) = Laplace.Samples(rng, location, scale) /// Log-Normal with log-scale (μ) and shape (σ). - let logNormal mu sigma rng = LogNormal.Sample(rng, mu, sigma) - let logNormalSeq mu sigma rng = LogNormal.Samples(rng, mu, sigma) + let logNormal mu sigma (rng:System.Random) = LogNormal.Sample(rng, mu, sigma) + let logNormalSeq mu sigma (rng:System.Random) = LogNormal.Samples(rng, mu, sigma) /// Negative-Binomial with number of failures (r) until the experiment stopped and probability (p) of a trial resulting in success. - let negativeBinomial r p rng = NegativeBinomial.Sample(rng, r, p) - let negativeBinomialSeq r p rng = NegativeBinomial.Samples(rng, r, p) + let negativeBinomial r p (rng:System.Random) = NegativeBinomial.Sample(rng, r, p) + let negativeBinomialSeq r p (rng:System.Random) = NegativeBinomial.Samples(rng, r, p) /// Normal with mean (μ) and standard deviation (σ). - let normal mean stddev rng = Normal.Sample(rng, mean, stddev) - let normalSeq mean stddev rng = Normal.Samples(rng, mean, stddev) + let normal mean stddev (rng:System.Random) = Normal.Sample(rng, mean, stddev) + let normalSeq mean stddev (rng:System.Random) = Normal.Samples(rng, mean, stddev) /// Standard Gaussian. - let standard rng = Normal.Sample(rng, 0.0, 1.0) - let standardSeq rng = Normal.Samples(rng, 0.0, 1.0) + let standard (rng:System.Random) = Normal.Sample(rng, 0.0, 1.0) + let standardSeq (rng:System.Random) = Normal.Samples(rng, 0.0, 1.0) /// Pareto with scale (xm) and shape (α). - let pareto scale shape rng = Pareto.Sample(rng, scale, shape) - let paretoSeq scale shape rng = Pareto.Samples(rng, scale, shape) + let pareto scale shape (rng:System.Random) = Pareto.Sample(rng, scale, shape) + let paretoSeq scale shape (rng:System.Random) = Pareto.Samples(rng, scale, shape) /// Poisson with lambda (λ). - let poisson lambda rng = Poisson.Sample(rng, lambda) - let poissonSeq lambda rng = Poisson.Samples(rng, lambda) + let poisson lambda (rng:System.Random) = Poisson.Sample(rng, lambda) + let poissonSeq lambda (rng:System.Random) = Poisson.Samples(rng, lambda) /// Rayleigh with scale (σ). - let rayleigh scale rng = Rayleigh.Sample(rng, scale) - let rayleighSeq scale rng = Rayleigh.Sample(rng, scale) + let rayleigh scale (rng:System.Random) = Rayleigh.Sample(rng, scale) + let rayleighSeq scale (rng:System.Random) = Rayleigh.Sample(rng, scale) /// Stable with stability (α), skewness (β), scale (c) and location (μ). - let stable alpha beta scale location rng = Stable.Sample(rng, alpha, beta, scale, location) - let stableSeq alpha beta scale location rng = Stable.Samples(rng, alpha, beta, scale, location) + let stable alpha beta scale location (rng:System.Random) = Stable.Sample(rng, alpha, beta, scale, location) + let stableSeq alpha beta scale location (rng:System.Random) = Stable.Samples(rng, alpha, beta, scale, location) /// Student-T with location (μ), scale (σ) and degrees of freedom (ν). - let studentT location scale freedom rng = StudentT.Sample(rng, location, scale, freedom) - let studentTSeq location scale freedom rng = StudentT.Samples(rng, location, scale, freedom) + let studentT location scale freedom (rng:System.Random) = StudentT.Sample(rng, location, scale, freedom) + let studentTSeq location scale freedom (rng:System.Random) = StudentT.Samples(rng, location, scale, freedom) /// Weibull with shape (k) and scale (λ). - let weibull shape scale rng = Weibull.Sample(rng, shape, scale) - let weibullSeq shape scale rng = Weibull.Samples(rng, shape, scale) + let weibull shape scale (rng:System.Random) = Weibull.Sample(rng, shape, scale) + let weibullSeq shape scale (rng:System.Random) = Weibull.Samples(rng, shape, scale) /// Zipf with s and n parameters. - let zipf s n rng = Zipf.Sample(rng, s, n) - let zipfSeq s n rng = Zipf.Samples(rng, s, n) + let zipf s n (rng:System.Random) = Zipf.Sample(rng, s, n) + let zipfSeq s n (rng:System.Random) = Zipf.Samples(rng, s, n) diff --git a/src/Numerics/Distributions/Cauchy.cs b/src/Numerics/Distributions/Cauchy.cs index 84060c9d..ed1acaec 100644 --- a/src/Numerics/Distributions/Cauchy.cs +++ b/src/Numerics/Distributions/Cauchy.cs @@ -32,6 +32,7 @@ using System; using System.Collections.Generic; using MathNet.Numerics.Properties; using MathNet.Numerics.Random; +using MathNet.Numerics.Threading; namespace MathNet.Numerics.Distributions { @@ -254,7 +255,7 @@ namespace MathNet.Numerics.Distributions /// A random number from this distribution. public double Sample() { - return _location + _scale*Math.Tan(Constants.Pi*(_random.NextDouble() - 0.5)); + return SampleUnchecked(_random, _location, _scale); } /// @@ -262,13 +263,35 @@ namespace MathNet.Numerics.Distributions /// /// a sequence of samples from the distribution. public IEnumerable Samples() + { + return SamplesUnchecked(_random, _location, _scale); + } + + static double SampleUnchecked(System.Random rnd, double location, double scale) + { + return location + scale*Math.Tan(Constants.Pi*(rnd.NextDouble() - 0.5)); + } + + static IEnumerable SamplesUnchecked(System.Random rnd, double location, double scale) { while (true) { - yield return _location + _scale*Math.Tan(Constants.Pi*(_random.NextDouble() - 0.5)); + yield return location + scale*Math.Tan(Constants.Pi*(rnd.NextDouble() - 0.5)); } } + static void SamplesUnchecked(System.Random rnd, double[] values, double location, double scale) + { + rnd.NextDoubles(values); + CommonParallel.For(0, values.Length, 4096, (a, b) => + { + for (int i = a; i < b; i++) + { + values[i] = location + scale*Math.Tan(Constants.Pi*(values[i] - 0.5)); + } + }); + } + /// /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x. /// @@ -342,7 +365,7 @@ namespace MathNet.Numerics.Distributions { if (scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); - return location + scale*Math.Tan(Constants.Pi*(rnd.NextDouble() - 0.5)); + return SampleUnchecked(SystemRandomSource.Default, location, scale); } /// @@ -356,10 +379,21 @@ namespace MathNet.Numerics.Distributions { if (scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); - while (true) - { - yield return location + scale*Math.Tan(Constants.Pi*(rnd.NextDouble() - 0.5)); - } + return SamplesUnchecked(rnd, location, scale); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The random number generator to use. + /// The location (x0) of the distribution. + /// The scale (γ) of the distribution. Range: γ > 0. + /// a sequence of samples from the distribution. + public static void Samples(System.Random rnd, double[] values, double location, double scale) + { + if (scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(rnd, values, location, scale); } /// @@ -370,7 +404,9 @@ namespace MathNet.Numerics.Distributions /// a sample from the distribution. public static double Sample(double location, double scale) { - return Sample(SystemRandomSource.Default, location, scale); + if (scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SampleUnchecked(SystemRandomSource.Default, location, scale); } /// @@ -381,7 +417,23 @@ namespace MathNet.Numerics.Distributions /// a sequence of samples from the distribution. public static IEnumerable Samples(double location, double scale) { - return Samples(SystemRandomSource.Default, location, scale); + if (scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SamplesUnchecked(SystemRandomSource.Default, location, scale); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The random number generator to use. + /// The location (x0) of the distribution. + /// The scale (γ) of the distribution. Range: γ > 0. + /// a sequence of samples from the distribution. + public static void Samples(double[] values, double location, double scale) + { + if (scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(SystemRandomSource.Default, values, location, scale); } } } diff --git a/src/Numerics/Distributions/ContinuousUniform.cs b/src/Numerics/Distributions/ContinuousUniform.cs index f333d242..c8238022 100644 --- a/src/Numerics/Distributions/ContinuousUniform.cs +++ b/src/Numerics/Distributions/ContinuousUniform.cs @@ -32,6 +32,7 @@ using System; using System.Collections.Generic; using MathNet.Numerics.Properties; using MathNet.Numerics.Random; +using MathNet.Numerics.Threading; namespace MathNet.Numerics.Distributions { @@ -258,7 +259,7 @@ namespace MathNet.Numerics.Distributions /// a sample from the distribution. public double Sample() { - return _lower + _random.NextDouble()*(_upper - _lower); + return SampleUnchecked(_random, _lower, _upper); } /// @@ -267,12 +268,36 @@ namespace MathNet.Numerics.Distributions /// a sequence of samples from the distribution. public IEnumerable Samples() { + return SamplesUnchecked(_random, _lower, _upper); + } + + static double SampleUnchecked(System.Random rnd, double lower, double upper) + { + return lower + rnd.NextDouble()*(upper - lower); + } + + static IEnumerable SamplesUnchecked(System.Random rnd, double lower, double upper) + { + double difference = upper - lower; while (true) { - yield return _lower + _random.NextDouble()*(_upper - _lower); + yield return lower + rnd.NextDouble()*difference; } } + static void SamplesUnchecked(System.Random rnd, double[] values, double lower, double upper) + { + rnd.NextDoubles(values); + var difference = upper - lower; + CommonParallel.For(0, values.Length, 4096, (a, b) => + { + for (int i = a; i < b; i++) + { + values[i] = lower + values[i]*difference; + } + }); + } + /// /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x. /// @@ -345,7 +370,7 @@ namespace MathNet.Numerics.Distributions { if (upper < lower) throw new ArgumentException(Resources.InvalidDistributionParameters); - return lower + rnd.NextDouble()*(upper - lower); + return SampleUnchecked(rnd, lower, upper); } /// @@ -359,10 +384,22 @@ namespace MathNet.Numerics.Distributions { if (upper < lower) throw new ArgumentException(Resources.InvalidDistributionParameters); - while (true) - { - yield return lower + rnd.NextDouble()*(upper - lower); - } + return SamplesUnchecked(rnd, lower, upper); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The random number generator to use. + /// The array to fill with the samples. + /// Lower bound. Range: lower ≤ upper. + /// Upper bound. Range: lower ≤ upper. + /// a sequence of samples from the distribution. + public static void Samples(System.Random rnd, double[] values, double lower, double upper) + { + if (upper < lower) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(rnd, values, lower, upper); } /// @@ -373,7 +410,9 @@ namespace MathNet.Numerics.Distributions /// a uniformly distributed sample. public static double Sample(double lower, double upper) { - return Sample(SystemRandomSource.Default, lower, upper); + if (upper < lower) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SampleUnchecked(SystemRandomSource.Default, lower, upper); } /// @@ -384,7 +423,23 @@ namespace MathNet.Numerics.Distributions /// a sequence of uniformly distributed samples. public static IEnumerable Samples(double lower, double upper) { - return Samples(SystemRandomSource.Default, lower, upper); + if (upper < lower) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SamplesUnchecked(SystemRandomSource.Default, lower, upper); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The array to fill with the samples. + /// Lower bound. Range: lower ≤ upper. + /// Upper bound. Range: lower ≤ upper. + /// a sequence of samples from the distribution. + public static void Samples(double[] values, double lower, double upper) + { + if (upper < lower) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(SystemRandomSource.Default, values, lower, upper); } } } diff --git a/src/Numerics/Distributions/Laplace.cs b/src/Numerics/Distributions/Laplace.cs index fd8c6327..eafe9c7e 100644 --- a/src/Numerics/Distributions/Laplace.cs +++ b/src/Numerics/Distributions/Laplace.cs @@ -255,10 +255,7 @@ namespace MathNet.Numerics.Distributions /// a sample from the distribution. public IEnumerable Samples() { - while (true) - { - yield return SampleUnchecked(_random, _location, _scale); - } + return SamplesUnchecked(_random, _location, _scale); } /// @@ -271,7 +268,26 @@ namespace MathNet.Numerics.Distributions static double SampleUnchecked(System.Random rnd, double location, double scale) { var u = rnd.NextDouble() - 0.5; - return location - (scale * Math.Sign(u) * Math.Log(1.0 - (2.0 * Math.Abs(u)))); + return location - (scale*Math.Sign(u)*Math.Log(1.0 - (2.0*Math.Abs(u)))); + } + + static IEnumerable SamplesUnchecked(System.Random rnd, double location, double scale) + { + while (true) + { + var u = rnd.NextDouble() - 0.5; + yield return location - (scale*Math.Sign(u)*Math.Log(1.0 - (2.0*Math.Abs(u)))); + } + } + + static void SamplesUnchecked(System.Random rnd, double[] values, double location, double scale) + { + rnd.NextDoubles(values); + for (int i = 0; i < values.Length; i++) + { + var u = values[i] - 0.5; + values[i] = location - (scale*Math.Sign(u)*Math.Log(1.0 - (2.0*Math.Abs(u)))); + } } /// @@ -344,10 +360,22 @@ namespace MathNet.Numerics.Distributions { if (scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); - while (true) - { - yield return SampleUnchecked(rnd, location, scale); - } + return SamplesUnchecked(rnd, location, scale); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The random number generator to use. + /// The array to fill with the samples. + /// The location (μ) of the distribution. + /// The scale (b) of the distribution. Range: b > 0. + /// a sequence of samples from the distribution. + public static void Samples(System.Random rnd, double[] values, double location, double scale) + { + if (scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(rnd, values, location, scale); } /// @@ -358,7 +386,9 @@ namespace MathNet.Numerics.Distributions /// a sample from the distribution. public static double Sample(double location, double scale) { - return Sample(SystemRandomSource.Default, location, scale); + if (scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SampleUnchecked(SystemRandomSource.Default, location, scale); } /// @@ -369,7 +399,24 @@ namespace MathNet.Numerics.Distributions /// a sequence of samples from the distribution. public static IEnumerable Samples(double location, double scale) { - return Samples(SystemRandomSource.Default, location, scale); + if (scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SamplesUnchecked(SystemRandomSource.Default, location, scale); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The random number generator to use. + /// The array to fill with the samples. + /// The location (μ) of the distribution. + /// The scale (b) of the distribution. Range: b > 0. + /// a sequence of samples from the distribution. + public static void Samples(double[] values, double location, double scale) + { + if (scale <= 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(SystemRandomSource.Default, values, location, scale); } } } diff --git a/src/Numerics/Distributions/LogNormal.cs b/src/Numerics/Distributions/LogNormal.cs index 3e9af3a0..73b71cd7 100644 --- a/src/Numerics/Distributions/LogNormal.cs +++ b/src/Numerics/Distributions/LogNormal.cs @@ -31,9 +31,11 @@ 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; +using MathNet.Numerics.Threading; namespace MathNet.Numerics.Distributions { @@ -98,7 +100,7 @@ namespace MathNet.Numerics.Distributions public static LogNormal WithMeanVariance(double mean, double var, System.Random randomSource = null) { var sigma2 = Math.Log(var/(mean*mean) + 1.0); - return new LogNormal(Math.Log(mean) - sigma2 / 2.0, Math.Sqrt(sigma2), randomSource); + return new LogNormal(Math.Log(mean) - sigma2/2.0, Math.Sqrt(sigma2), randomSource); } /// @@ -264,7 +266,7 @@ namespace MathNet.Numerics.Distributions return 0.0; } - var a = (Math.Log(x) - _mu) / _sigma; + var a = (Math.Log(x) - _mu)/_sigma; return Math.Exp(-0.5*a*a)/(x*_sigma*Constants.Sqrt2Pi); } @@ -281,7 +283,7 @@ namespace MathNet.Numerics.Distributions return Double.NegativeInfinity; } - var a = (Math.Log(x) - _mu) / _sigma; + var a = (Math.Log(x) - _mu)/_sigma; return (-0.5*a*a) - Math.Log(x*_sigma) - Constants.LogSqrt2Pi; } @@ -316,7 +318,7 @@ namespace MathNet.Numerics.Distributions /// a sample from the distribution. public double Sample() { - return Math.Exp(Normal.Sample(_random, _mu, _sigma)); + return SampleUnchecked(_random, _mu, _sigma); } /// @@ -325,7 +327,29 @@ namespace MathNet.Numerics.Distributions /// a sequence of samples from the distribution. public IEnumerable Samples() { - return Normal.Samples(_random, _mu, _sigma).Select(Math.Exp); + return SamplesUnchecked(_random, _mu, _sigma); + } + + static double SampleUnchecked(System.Random rnd, double mu, double sigma) + { + return Math.Exp(Normal.SampleUnchecked(rnd, mu, sigma)); + } + + static IEnumerable SamplesUnchecked(System.Random rnd, double mu, double sigma) + { + return Normal.SamplesUnchecked(rnd, mu, sigma).Select(Math.Exp); + } + + static void SamplesUnchecked(System.Random rnd, double[] values, double mu, double sigma) + { + Normal.SamplesUnchecked(rnd, values, mu, sigma); + CommonParallel.For(0, values.Length, 4096, (a, b) => + { + for (int i = a; i < b; i++) + { + values[i] = Math.Exp(values[i]); + } + }); } /// @@ -346,7 +370,7 @@ namespace MathNet.Numerics.Distributions return 0.0; } - var a = (Math.Log(x) - mu) / sigma; + var a = (Math.Log(x) - mu)/sigma; return Math.Exp(-0.5*a*a)/(x*sigma*Constants.Sqrt2Pi); } @@ -367,7 +391,7 @@ namespace MathNet.Numerics.Distributions return Double.NegativeInfinity; } - var a = (Math.Log(x) - mu) / sigma; + var a = (Math.Log(x) - mu)/sigma; return (-0.5*a*a) - Math.Log(x*sigma) - Constants.LogSqrt2Pi; } @@ -415,7 +439,9 @@ namespace MathNet.Numerics.Distributions /// a sample from the distribution. public static double Sample(System.Random rnd, double mu, double sigma) { - return Math.Exp(Normal.Sample(rnd, mu, sigma)); + if (sigma < 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SampleUnchecked(rnd, mu, sigma); } /// @@ -427,7 +453,24 @@ namespace MathNet.Numerics.Distributions /// a sequence of samples from the distribution. public static IEnumerable Samples(System.Random rnd, double mu, double sigma) { - return Normal.Samples(rnd, mu, sigma).Select(Math.Exp); + if (sigma < 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SamplesUnchecked(rnd, mu, sigma); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The random number generator to use. + /// The array to fill with the samples. + /// The log-scale (μ) of the distribution. + /// The shape (σ) of the distribution. Range: σ ≥ 0. + /// a sequence of samples from the distribution. + public static void Samples(System.Random rnd, double[] values, double mu, double sigma) + { + if (sigma < 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(rnd, values, mu, sigma); } /// @@ -438,7 +481,9 @@ namespace MathNet.Numerics.Distributions /// a sample from the distribution. public static double Sample(double mu, double sigma) { - return Sample(SystemRandomSource.Default, mu, sigma); + if (sigma < 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SampleUnchecked(SystemRandomSource.Default, mu, sigma); } /// @@ -449,7 +494,23 @@ namespace MathNet.Numerics.Distributions /// a sequence of samples from the distribution. public static IEnumerable Samples(double mu, double sigma) { - return Samples(SystemRandomSource.Default, mu, sigma); + if (sigma < 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SamplesUnchecked(SystemRandomSource.Default, mu, sigma); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The array to fill with the samples. + /// The log-scale (μ) of the distribution. + /// The shape (σ) of the distribution. Range: σ ≥ 0. + /// a sequence of samples from the distribution. + public static void Samples(double[] values, double mu, double sigma) + { + if (sigma < 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(SystemRandomSource.Default, values, mu, sigma); } } } diff --git a/src/Numerics/Distributions/Normal.cs b/src/Numerics/Distributions/Normal.cs index e7097a9a..a2f436c8 100644 --- a/src/Numerics/Distributions/Normal.cs +++ b/src/Numerics/Distributions/Normal.cs @@ -324,7 +324,7 @@ namespace MathNet.Numerics.Distributions /// a sample from the distribution. public double Sample() { - return _mean + (_stdDev*SampleStandardBoxMuller(_random).Item1); + return SampleUnchecked(_random, _mean, _stdDev); } /// @@ -333,33 +333,93 @@ namespace MathNet.Numerics.Distributions /// a sequence of samples from the distribution. public IEnumerable Samples() { + return SamplesUnchecked(_random, _mean, _stdDev); + } + + internal static double SampleUnchecked(System.Random rnd, double mean, double stddev) + { + double x, y; + while (!PolarTransform(rnd.NextDouble(), rnd.NextDouble(), out x, out y)) + { + } + return mean + (stddev*x); + } + + internal static IEnumerable SamplesUnchecked(System.Random rnd, double mean, double stddev) + { + double x, y; while (true) { - var sample = SampleStandardBoxMuller(_random); - yield return _mean + (_stdDev*sample.Item1); - yield return _mean + (_stdDev*sample.Item2); + if (!PolarTransform(rnd.NextDouble(), rnd.NextDouble(), out x, out y)) + { + continue; + } + yield return mean + (stddev*x); + yield return mean + (stddev*y); } } - /// - /// Samples a pair of standard normal distributed random variables using the Box-Muller algorithm. - /// - /// The random number generator to use. - /// a pair of random numbers from the standard normal distribution. - static Tuple SampleStandardBoxMuller(System.Random rnd) + internal static void SamplesUnchecked(System.Random rnd, double[] values, double mean, double stddev) { - var v1 = (2.0 * rnd.NextDouble()) - 1.0; - var v2 = (2.0 * rnd.NextDouble()) - 1.0; - var r = (v1 * v1) + (v2 * v2); - while (r >= 1.0 || r == 0.0) + if (values.Length == 0) + { + return; + } + + // Since we only accept points within the unit circle + // we need to generate roughly 4/pi=1.27 times the numbers needed. + int n = (int)Math.Ceiling(values.Length*4*Constants.InvPi); + if (n.IsOdd()) + { + n++; + } + var uniform = rnd.NextDoubles(n); + + // Polar transform + double x, y; + int index = 0; + for (int i = 0; i < uniform.Length && index < values.Length; i += 2) { - v1 = (2.0 * rnd.NextDouble()) - 1.0; - v2 = (2.0 * rnd.NextDouble()) - 1.0; - r = (v1 * v1) + (v2 * v2); + if (!PolarTransform(uniform[i], uniform[i + 1], out x, out y)) + { + continue; + } + values[index++] = mean + stddev*x; + if (index == values.Length) return; + values[index++] = mean + stddev*y; + if (index == values.Length) return; } - var fac = Math.Sqrt(-2.0 * Math.Log(r) / r); - return new Tuple(v1 * fac, v2 * fac); + // remaining, if any + while (index < values.Length) + { + if (!PolarTransform(rnd.NextDouble(), rnd.NextDouble(), out x, out y)) + { + continue; + } + values[index++] = mean + stddev*x; + if (index == values.Length) return; + values[index++] = mean + stddev*y; + if (index == values.Length) return; + } + } + + static bool PolarTransform(double a, double b, out double x, out double y) + { + var v1 = (2.0*a) - 1.0; + var v2 = (2.0*b) - 1.0; + var r = (v1*v1) + (v2*v2); + if (r >= 1.0 || r == 0.0) + { + x = 0; + y = 0; + return false; + } + + var fac = Math.Sqrt(-2.0*Math.Log(r)/r); + x = v1*fac; + y = v2*fac; + return true; } /// @@ -439,7 +499,7 @@ namespace MathNet.Numerics.Distributions { if (stddev < 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); - return mean + (stddev*SampleStandardBoxMuller(rnd).Item1); + return SampleUnchecked(rnd, mean, stddev); } /// @@ -453,12 +513,22 @@ namespace MathNet.Numerics.Distributions { if (stddev < 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); - while (true) - { - var sample = SampleStandardBoxMuller(rnd); - yield return mean + (stddev*sample.Item1); - yield return mean + (stddev*sample.Item2); - } + return SamplesUnchecked(rnd, mean, stddev); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The random number generator to use. + /// The array to fill with the samples. + /// The mean (μ) of the normal distribution. + /// The standard deviation (σ) of the normal distribution. Range: σ ≥ 0. + /// a sequence of samples from the distribution. + public static void Samples(System.Random rnd, double[] values, double mean, double stddev) + { + if (stddev < 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(rnd, values, mean, stddev); } /// @@ -469,7 +539,9 @@ namespace MathNet.Numerics.Distributions /// a sample from the distribution. public static double Sample(double mean, double stddev) { - return Sample(SystemRandomSource.Default, mean, stddev); + if (stddev < 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SampleUnchecked(SystemRandomSource.Default, mean, stddev); } /// @@ -480,7 +552,23 @@ namespace MathNet.Numerics.Distributions /// a sequence of samples from the distribution. public static IEnumerable Samples(double mean, double stddev) { - return Samples(SystemRandomSource.Default, mean, stddev); + if (stddev < 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + return SamplesUnchecked(SystemRandomSource.Default, mean, stddev); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The array to fill with the samples. + /// The mean (μ) of the normal distribution. + /// The standard deviation (σ) of the normal distribution. Range: σ ≥ 0. + /// a sequence of samples from the distribution. + public static void Samples(double[] values, double mean, double stddev) + { + if (stddev < 0.0) throw new ArgumentException(Resources.InvalidDistributionParameters); + + SamplesUnchecked(SystemRandomSource.Default, values, mean, stddev); } } } diff --git a/src/Numerics/Distributions/Rayleigh.cs b/src/Numerics/Distributions/Rayleigh.cs index c1e2648a..e254eb80 100644 --- a/src/Numerics/Distributions/Rayleigh.cs +++ b/src/Numerics/Distributions/Rayleigh.cs @@ -32,6 +32,7 @@ using System; using System.Collections.Generic; using MathNet.Numerics.Properties; using MathNet.Numerics.Random; +using MathNet.Numerics.Threading; namespace MathNet.Numerics.Distributions { @@ -252,6 +253,18 @@ namespace MathNet.Numerics.Distributions } } + static void SampleUnchecked(System.Random rnd, double[] values, double scale) + { + rnd.NextDoubles(values); + CommonParallel.For(0, values.Length, 4096, (a, b) => + { + for (int i = a; i < b; i++) + { + values[i] = scale*Math.Sqrt(-2.0*Math.Log(values[i])); + } + }); + } + /// /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x. /// diff --git a/src/Numerics/Distributions/Triangular.cs b/src/Numerics/Distributions/Triangular.cs index ac0f690d..991ea0d4 100644 --- a/src/Numerics/Distributions/Triangular.cs +++ b/src/Numerics/Distributions/Triangular.cs @@ -32,6 +32,7 @@ using System; using System.Collections.Generic; using MathNet.Numerics.Properties; using MathNet.Numerics.Random; +using MathNet.Numerics.Threading; namespace MathNet.Numerics.Distributions { @@ -295,6 +296,23 @@ namespace MathNet.Numerics.Distributions return Samples(_random, _lower, _upper, _mode); } + static void SampleUnchecked(System.Random rnd, double[] values, double lower, double upper, double mode) + { + double ml = mode - lower; + double ul = upper - lower; + double um = upper - mode; + 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); + } + }); + } + /// /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x. /// diff --git a/src/Numerics/Distributions/Weibull.cs b/src/Numerics/Distributions/Weibull.cs index 88fa065b..9330789c 100644 --- a/src/Numerics/Distributions/Weibull.cs +++ b/src/Numerics/Distributions/Weibull.cs @@ -32,6 +32,7 @@ using System; using System.Collections.Generic; using MathNet.Numerics.Properties; using MathNet.Numerics.Random; +using MathNet.Numerics.Threading; namespace MathNet.Numerics.Distributions { @@ -289,6 +290,18 @@ namespace MathNet.Numerics.Distributions 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. /// diff --git a/src/Numerics/Random/RandomExtensions.cs b/src/Numerics/Random/RandomExtensions.cs index 45398c01..50aa5e73 100644 --- a/src/Numerics/Random/RandomExtensions.cs +++ b/src/Numerics/Random/RandomExtensions.cs @@ -46,7 +46,7 @@ namespace MathNet.Numerics.Random /// The array to fill with random values. /// /// This extension is thread-safe if and only if called on an random number - /// generator provided by Math.NET Nummerics or derived from the RandomSource class. + /// generator provided by Math.NET Numerics or derived from the RandomSource class. /// public static void NextDoubles(this System.Random rnd, double[] values) { @@ -70,7 +70,7 @@ namespace MathNet.Numerics.Random /// The size of the array to fill. /// /// This extension is thread-safe if and only if called on an random number - /// generator provided by Math.NET Nummerics or derived from the RandomSource class. + /// generator provided by Math.NET Numerics or derived from the RandomSource class. /// public static double[] NextDoubles(this System.Random rnd, int count) { @@ -84,7 +84,7 @@ namespace MathNet.Numerics.Random /// /// /// This extension is thread-safe if and only if called on an random number - /// generator provided by Math.NET Nummerics or derived from the RandomSource class. + /// generator provided by Math.NET Numerics or derived from the RandomSource class. /// public static IEnumerable NextDoubleSequence(this System.Random rnd) { @@ -112,7 +112,7 @@ namespace MathNet.Numerics.Random /// The size of the array to fill. /// /// This extension is thread-safe if and only if called on an random number - /// generator provided by Math.NET Nummerics or derived from the RandomSource class. + /// generator provided by Math.NET Numerics or derived from the RandomSource class. /// public static byte[] NextBytes(this System.Random rnd, int count) { @@ -132,7 +132,7 @@ namespace MathNet.Numerics.Random /// /// /// This extension is thread-safe if and only if called on an random number - /// generator provided by Math.NET Nummerics or derived from the RandomSource class. + /// generator provided by Math.NET Numerics or derived from the RandomSource class. /// public static long NextInt64(this System.Random rnd) { @@ -160,7 +160,7 @@ namespace MathNet.Numerics.Random /// /// /// This extension is thread-safe if and only if called on an random number - /// generator provided by Math.NET Nummerics or derived from the RandomSource class. + /// generator provided by Math.NET Numerics or derived from the RandomSource class. /// public static int NextFullRangeInt32(this System.Random rnd) { @@ -180,7 +180,7 @@ namespace MathNet.Numerics.Random /// /// /// This extension is thread-safe if and only if called on an random number - /// generator provided by Math.NET Nummerics or derived from the RandomSource class. + /// generator provided by Math.NET Numerics or derived from the RandomSource class. /// public static long NextFullRangeInt64(this System.Random rnd) { @@ -199,7 +199,7 @@ namespace MathNet.Numerics.Random /// /// /// This extension is thread-safe if and only if called on an random number - /// generator provided by Math.NET Nummerics or derived from the RandomSource class. + /// generator provided by Math.NET Numerics or derived from the RandomSource class. /// public static decimal NextDecimal(this System.Random rnd) {