diff --git a/src/Numerics.Tests/DistributionTests/CommonDistributionTests.cs b/src/Numerics.Tests/DistributionTests/CommonDistributionTests.cs index c2fce2c4..61779afb 100644 --- a/src/Numerics.Tests/DistributionTests/CommonDistributionTests.cs +++ b/src/Numerics.Tests/DistributionTests/CommonDistributionTests.cs @@ -79,6 +79,7 @@ namespace MathNet.Numerics.UnitTests.DistributionTests new InverseGamma(1.0, 1.0), new InverseGaussian(1.0, 3.0), new Laplace(1.0, 0.5), + new Logistic(0.0, 1.0), new LogNormal(1.0, 1.0), new Normal(0.0, 1.0), new Pareto(1.0, 0.5), diff --git a/src/Numerics.Tests/DistributionTests/Continuous/LogisticTests.cs b/src/Numerics.Tests/DistributionTests/Continuous/LogisticTests.cs new file mode 100644 index 00000000..3b26ee00 --- /dev/null +++ b/src/Numerics.Tests/DistributionTests/Continuous/LogisticTests.cs @@ -0,0 +1,390 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// +// Copyright (c) 2009-2016 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.Linq; +using MathNet.Numerics.Distributions; +using NUnit.Framework; + +namespace MathNet.Numerics.UnitTests.DistributionTests.Continuous +{ + using Random = System.Random; + + /// + /// Logistic distribution tests. + /// + [TestFixture, Category("Distributions")] + public class LogisticTests + { + /// + /// Can create standard logistic. + /// + [Test] + public void CanCreateStandardLogistic() + { + var l = new Logistic(); + Assert.AreEqual(0.0, l.Mean); + Assert.AreEqual(1.0, l.Scale); + } + + /// + /// Can create logistic. + /// + /// Mean value. + /// Scale parameter value. + [TestCase(10.0, 0.1)] + [TestCase(-5.0, 1.0)] + [TestCase(0.0, 10.0)] + [TestCase(10.0, 100.0)] + [TestCase(-5.0, Double.PositiveInfinity)] + public void CanCreateLogistic(double mean, double scale) + { + var n = new Logistic(mean, scale); + Assert.AreEqual(mean, n.Mean); + Assert.AreEqual(scale, n.Scale); + } + + /// + /// Logistic create fails with bad parameters. + /// + /// Mean value. + /// Scale parameter value. + [TestCase(Double.NaN, 1.0)] + [TestCase(1.0, Double.NaN)] + [TestCase(Double.NaN, Double.NaN)] + [TestCase(1.0, -1.0)] + public void LogisticCreateFailsWithBadParameters(double mean, double scale) + { + Assert.That(() => new Logistic(mean, scale), Throws.ArgumentException); + } + + /// + /// Can create logistic from mean and scale parameter value. + /// + /// Mean value. + /// Scale parameter value. + [TestCase(10.0, 0.1)] + [TestCase(-5.0, 1.0)] + [TestCase(0.0, 10.0)] + [TestCase(10.0, 100.0)] + [TestCase(-5.0, Double.PositiveInfinity)] + public void CanCreateLogisticFromMeanAndScale(double mean, double scale) + { + var n = Logistic.WithMeanScale(mean, scale); + Assert.AreEqual(mean, n.Mean); + Assert.AreEqual(scale, n.Scale); + } + + /// + /// Can create logistic from mean and standard deviation. + /// + /// Mean value. + /// Standard deviation value. + [TestCase(10.0, 0.1)] + [TestCase(-5.0, 1.0)] + [TestCase(0.0, 10.0)] + [TestCase(10.0, 100.0)] + [TestCase(-5.0, Double.PositiveInfinity)] + public void CanCreateLogisticFromMeanAndStdDev(double mean, double sdev) + { + var n = Logistic.WithMeanStdDev(mean, sdev); + Assert.AreEqual(mean, n.Mean); + Assert.AreEqual(sdev, n.StdDev); + } + + /// + /// Can create logistic from mean and variance. + /// + /// Mean value. + /// Variance value. + [TestCase(10.0, 0.1)] + [TestCase(-5.0, 1.0)] + [TestCase(0.0, 10.0)] + [TestCase(10.0, 100.0)] + [TestCase(-5.0, Double.PositiveInfinity)] + public void CanCreateLogisticFromMeanAndVariance(double mean, double var) + { + var n = Logistic.WithMeanVariance(mean, var); + AssertHelpers.AlmostEqualRelative(mean, n.Mean, 15); + AssertHelpers.AlmostEqualRelative(var, n.Variance, 15); + } + + /// + /// Can create logistic from mean and precision. + /// + /// Mean value. + /// Precision value. + [TestCase(10.0, 0.1)] + [TestCase(-5.0, 1.0)] + [TestCase(0.0, 10.0)] + [TestCase(10.0, 100.0)] + public void CanCreateLogisticFromMeanAndPrecision(double mean, double prec) + { + var n = Logistic.WithMeanPrecision(mean, prec); + AssertHelpers.AlmostEqualRelative(mean, n.Mean, 15); + AssertHelpers.AlmostEqualRelative(prec, n.Precision, 15); + } + + /// + /// Validate ToString. + /// + [Test] + public void ValidateToString() + { + System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture; + var n = new Logistic(1d, 2d); + Assert.AreEqual("Logistic(μ = 1, s = 2)", n.ToString()); + } + + /// + /// Validate entropy. + /// + /// Scale parameter value. + [TestCase(0.1)] + [TestCase(1.0)] + [TestCase(10.0)] + [TestCase(Double.PositiveInfinity)] + public void ValidateEntropy(double scale) + { + var n = new Logistic(1.0, scale); + Assert.AreEqual(Math.Log(scale) + 2, n.Entropy); + } + + /// + /// Validate skewness. + /// + /// Scale parameter value. + [TestCase(0.1)] + [TestCase(1.0)] + [TestCase(10.0)] + [TestCase(Double.PositiveInfinity)] + public void ValidateSkewness(double scale) + { + var n = new Logistic(1.0, scale); + Assert.AreEqual(0.0, n.Skewness); + } + + /// + /// Validate mean. + /// + /// Mean value. + [TestCase(Double.NegativeInfinity)] + [TestCase(-0.0)] + [TestCase(0.0)] + [TestCase(0.1)] + [TestCase(1.0)] + [TestCase(10.0)] + [TestCase(Double.PositiveInfinity)] + public void ValidateMode(double mean) + { + var n = new Logistic(mean, 1.0); + Assert.AreEqual(mean, n.Mode); + } + + /// + /// Validate median. + /// + /// Mean value. + [TestCase(Double.NegativeInfinity)] + [TestCase(-0.0)] + [TestCase(0.0)] + [TestCase(0.1)] + [TestCase(1.0)] + [TestCase(10.0)] + [TestCase(Double.PositiveInfinity)] + public void ValidateMedian(double mean) + { + var n = new Logistic(mean, 1.0); + Assert.AreEqual(mean, n.Median); + } + + /// + /// Validate minimum. + /// + [Test] + public void ValidateMinimum() + { + var n = new Logistic(); + Assert.AreEqual(Double.NegativeInfinity, n.Minimum); + } + + /// + /// Validate maximum. + /// + [Test] + public void ValidateMaximum() + { + var n = new Logistic(); + Assert.AreEqual(Double.PositiveInfinity, n.Maximum); + } + + /// + /// Can sample static. + /// + [Test] + public void CanSampleStatic() + { + Logistic.Sample(new Random(0), 0.0, 1.0); + } + + /// + /// Can sample sequence static. + /// + [Test] + public void CanSampleSequenceStatic() + { + var ied = Logistic.Samples(new Random(0), 0.0, 1.0); + GC.KeepAlive(ied.Take(5).ToArray()); + } + + /// + /// Fail sample static with bad parameters. + /// + [Test] + public void FailSampleStatic() + { + Assert.That(() => { var d = Logistic.Sample(new Random(0), 0.0, -1.0); }, Throws.ArgumentException); + } + + /// + /// Fail sample sequence static with bad parameters. + /// + [Test] + public void FailSampleSequenceStatic() + { + Assert.That(() => { var ied = Logistic.Samples(new Random(0), 0.0, -1.0).First(); }, Throws.ArgumentException); + } + + /// + /// Can sample. + /// + [Test] + public void CanSample() + { + var n = new Logistic(); + n.Sample(); + } + + /// + /// Can sample sequence. + /// + [Test] + public void CanSampleSequence() + { + var n = new Logistic(); + var ied = n.Samples(); + GC.KeepAlive(ied.Take(5).ToArray()); + } + + /// + /// Validate density. + /// + /// Input X value. + /// Expected value. + [TestCase(Double.NegativeInfinity, double.NaN)] + [TestCase(-5.0, 0.00332402833539508)] + [TestCase(-2.0, 0.01422651193986778)] + [TestCase(0.0, 0.03505185827255409)] + [TestCase(4.0, 0.11750185610079725)] + [TestCase(5.0, 0.12500000000000000)] + [TestCase(6.0, 0.11750185610079725)] + [TestCase(10.0, 0.03505185827255409)] + [TestCase(Double.PositiveInfinity, 0)] + public void ValidateDensity(double x, double d) + { + var n = Logistic.WithMeanScale(5.0, 2.0); + AssertHelpers.AlmostEqualRelative(d, n.Density(x), 9); + AssertHelpers.AlmostEqualRelative(d, Logistic.PDF(5.0, 2.0, x), 9); + } + + /// + /// Validate density. + /// + /// Input X value. + /// Expected value. + [TestCase(Double.NegativeInfinity, double.NaN)] + [TestCase(-5.0, -5.70657787753818)] + [TestCase(-2.0, -4.25264801710519)] + [TestCase(0.0, -3.35092664914504)] + [TestCase(4.0, -2.14130114892016)] + [TestCase(5.0, -2.07944154167984)] + [TestCase(6.0, -2.14130114892016)] + [TestCase(10.0, -3.35092664914504)] + [TestCase(Double.PositiveInfinity, Double.NegativeInfinity)] + public void ValidateLogDensity(double x, double d) + { + var n = Logistic.WithMeanScale(5.0, 2.0); + AssertHelpers.AlmostEqualRelative(d, n.DensityLn(x), 9); + AssertHelpers.AlmostEqualRelative(d, Logistic.PDFLn(5.0, 2.0, x), 9); + } + + /// + /// Validate cumulative distribution. + /// + /// Input X value. + /// Expected value. + [TestCase(Double.NegativeInfinity, 0.0)] + [TestCase(-5.0, 0.00669285092428486)] + [TestCase(-2.0, 0.0293122307513563)] + [TestCase(0.0, 0.0758581800212435)] + [TestCase(4.0, 0.377540668798145)] + [TestCase(5.0, 0.5)] + [TestCase(6.0, 0.622459331201855)] + [TestCase(10.0, 0.924141819978757)] + [TestCase(Double.PositiveInfinity, 1.0)] + public void ValidateCumulativeDistribution(double x, double p) + { + var n = Logistic.WithMeanScale(5.0, 2.0); + AssertHelpers.AlmostEqualRelative(p, n.CumulativeDistribution(x), 9); + AssertHelpers.AlmostEqualRelative(p, Logistic.CDF(5.0, 2.0, x), 9); + } + + /// + /// Validate inverse cumulative distribution. + /// + /// Input X value. + /// Expected value. + [TestCase(Double.NegativeInfinity, 0.0)] + [TestCase(-5.0, 0.00669285092428486)] + [TestCase(-2.0, 0.0293122307513563)] + [TestCase(0.0, 0.0758581800212435)] + [TestCase(4.0, 0.377540668798145)] + [TestCase(5.0, 0.5)] + [TestCase(6.0, 0.622459331201855)] + [TestCase(10.0, 0.924141819978757)] + [TestCase(Double.PositiveInfinity, 1.0)] + public void ValidateInverseCumulativeDistribution(double x, double p) + { + var n = Logistic.WithMeanScale(5.0, 2.0); + AssertHelpers.AlmostEqualRelative(x, n.InverseCumulativeDistribution(p), 14); + AssertHelpers.AlmostEqualRelative(x, Logistic.InvCDF(5.0, 2.0, p), 14); + } + + } +} diff --git a/src/Numerics/Distributions/Logistic.cs b/src/Numerics/Distributions/Logistic.cs new file mode 100644 index 00000000..3c9c7ffd --- /dev/null +++ b/src/Numerics/Distributions/Logistic.cs @@ -0,0 +1,514 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// +// Copyright (c) 2009-2015 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 MathNet.Numerics.Random; +using MathNet.Numerics.Statistics; + +namespace MathNet.Numerics.Distributions +{ + /// + /// Continuous Univariate Logistic distribution. + /// For details about this distribution, see + /// Wikipedia - Logistic distribution. + /// + public class Logistic : IContinuousDistribution + { + System.Random _random; + + readonly double _mean; + readonly double _scale; + + /// + /// Initializes a new instance of the Logistic class. This is a logistic distribution with mean 0.0 + /// and scale 1.0. The distribution will be initialized with the default + /// random number generator. + /// + public Logistic() + : this(0.0, 1.0) + { + } + + /// + /// Initializes a new instance of the Logistic class. This is a logistic distribution with mean 0.0 + /// and scale 1.0. The distribution will be initialized with the default + /// random number generator. + /// + /// The random number generator which is used to draw random samples. + public Logistic(System.Random randomSource) + : this(0.0, 1.0, randomSource) + { + } + + /// + /// Initializes a new instance of the Logistic class with a particular mean and scale parameter. The + /// distribution will be initialized with the default random number generator. + /// + /// The mean (μ) of the logistic distribution. + /// The scale (s) of the logistic distribution. Range: s > 0. + public Logistic(double mean, double scale) + { + if (!IsValidParameterSet(mean, scale)) + { + throw new ArgumentException("Invalid parametrization for the distribution."); + } + + _random = SystemRandomSource.Default; + _mean = mean; + _scale = scale; + } + + /// + /// Initializes a new instance of the Logistic class with a particular mean and standard deviation. The distribution will + /// be initialized with the default random number generator. + /// + /// The mean (μ) of the logistic distribution. + /// The scale (s) of the logistic distribution. Range: s > 0. + /// The random number generator which is used to draw random samples. + public Logistic(double mean, double scale, System.Random randomSource) + { + if (!IsValidParameterSet(mean, scale)) + { + throw new ArgumentException("Invalid parametrization for the distribution."); + } + + _random = randomSource ?? SystemRandomSource.Default; + _mean = mean; + _scale = scale; + } + + /// + /// Constructs a logistic distribution from a mean and scale parameter. + /// + /// The mean (μ) of the logistic distribution. + /// The scale (s) of the logistic distribution. Range: s > 0. + /// The random number generator which is used to draw random samples. Optional, can be null. + /// a logistic distribution. + public static Logistic WithMeanScale(double mean, double scale, System.Random randomSource = null) + { + return new Logistic(mean, scale, randomSource); + } + + /// + /// Constructs a logistic distribution from a mean and standard deviation. + /// + /// The mean (μ) of the logistic distribution. + /// The standard deviation (σ) of the logistic distribution. Range: σ > 0. + /// The random number generator which is used to draw random samples. Optional, can be null. + /// a logistic distribution. + public static Logistic WithMeanStdDev(double mean, double stddev, System.Random randomSource = null) + { + var scale = Math.Sqrt(3) * stddev / Math.PI; + return new Logistic(mean, scale, randomSource); + } + + /// + /// Constructs a logistic distribution from a mean and variance. + /// + /// The mean (μ) of the logistic distribution. + /// The variance (σ^2) of the logistic distribution. Range: (σ^2) > 0. + /// The random number generator which is used to draw random samples. Optional, can be null. + /// A logistic distribution. + public static Logistic WithMeanVariance(double mean, double var, System.Random randomSource = null) + { + return WithMeanStdDev(mean, Math.Sqrt(var), randomSource); + } + + /// + /// Constructs a logistic distribution from a mean and precision. + /// + /// The mean (μ) of the logistic distribution. + /// The precision of the logistic distribution. Range: precision > 0. + /// The random number generator which is used to draw random samples. Optional, can be null. + /// A logistic distribution. + public static Logistic WithMeanPrecision(double mean, double precision, System.Random randomSource = null) + { + return WithMeanVariance(mean, 1 / precision, randomSource); + } + + /// + /// A string representation of the distribution. + /// + /// a string representation of the distribution. + public override string ToString() + { + return $"Logistic(μ = {_mean}, s = {_scale})"; + } + + /// + /// Tests whether the provided values are valid parameters for this distribution. + /// + /// The mean (μ) of the logistic distribution. + /// The scale (s) of the logistic distribution. Range: s > 0. + public static bool IsValidParameterSet(double mean, double scale) + { + return scale > 0.0 && !double.IsNaN(mean); + } + + /// + /// Gets the scale parameter of the Logistic distribution. Range: s > 0. + /// + public double Scale => _scale; + + /// + /// Gets the mean (μ) of the logistic distribution. + /// + public double Mean => _mean; + + /// + /// Gets the standard deviation (σ) of the logistic distribution. Range: σ > 0. + /// + public double StdDev => Math.Sqrt(Variance); + + /// + /// Gets the variance of the logistic distribution. + /// + public double Variance => (Math.Pow(_scale, 2) * Math.Pow(Math.PI,2))/3; + + /// + /// Gets the precision of the logistic distribution. + /// + public double Precision => 1.0/Variance; + + /// + /// Gets the random number generator which is used to draw random samples. + /// + public System.Random RandomSource + { + get => _random; + set => _random = value ?? SystemRandomSource.Default; + } + + /// + /// Gets the entropy of the logistic distribution. + /// + public double Entropy => Math.Log(_scale) + 2; + + /// + /// Gets the skewness of the logistic distribution. + /// + public double Skewness => 0.0; + + /// + /// Gets the mode of the logistic distribution. + /// + public double Mode => _mean; + + /// + /// Gets the median of the logistic distribution. + /// + public double Median => _mean; + + /// + /// Gets the minimum of the logistic distribution. + /// + public double Minimum => double.NegativeInfinity; + + /// + /// Gets the maximum of the logistic distribution. + /// + public double Maximum => double.PositiveInfinity; + + /// + /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x. + /// + /// The location at which to compute the density. + /// the density at . + /// + public double Density(double x) + { + return PDF(_mean, _scale, x); + } + + /// + /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x). + /// + /// The location at which to compute the log density. + /// the log density at . + /// + public double DensityLn(double x) + { + return PDFLn(_mean, _scale, x); + } + + /// + /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x). + /// + /// The location at which to compute the cumulative distribution function. + /// the cumulative distribution at location . + /// + public double CumulativeDistribution(double x) + { + return CDF(_mean, _scale, x); + } + + /// + /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution + /// at the given probability. This is also known as the quantile or percent point function. + /// + /// The location at which to compute the inverse cumulative density. + /// the inverse cumulative density at . + /// + public double InverseCumulativeDistribution(double p) + { + return InvCDF(_mean, _scale, p); + } + + /// + /// Generates a sample from the logistic distribution using the Box-Muller algorithm. + /// + /// a sample from the distribution. + public double Sample() + { + return SampleUnchecked(_random, _mean, _scale); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + public void Samples(double[] values) + { + SamplesUnchecked(_random, values, _mean, _scale); + } + + /// + /// Generates a sequence of samples from the logistic distribution using the Box-Muller algorithm. + /// + /// a sequence of samples from the distribution. + public IEnumerable Samples() + { + return SamplesUnchecked(_random, _mean, _scale); + } + + internal static double SampleUnchecked(System.Random rnd, double mean, double scale) + { + return InvCDF(mean, scale, rnd.NextDouble()); + } + + internal static IEnumerable SamplesUnchecked(System.Random rnd, double mean, double scale) + { + while (true) + { + yield return InvCDF(mean, scale, rnd.NextDouble()); + } + } + + internal static void SamplesUnchecked(System.Random rnd, double[] values, double mean, double scale) + { + if (values.Length == 0) + { + return; + } + + for (int i = 0; i < values.Length; i++) + { + values[i] = SampleUnchecked(rnd, mean, scale); + } + } + + /// + /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x. + /// + /// The mean (μ) of the logistic distribution. + /// The scale (s) of the logistic distribution. Range: s > 0. + /// The location at which to compute the density. + /// the density at . + /// + public static double PDF(double mean, double scale, double x) + { + if (scale <= 0.0) + { + throw new ArgumentException("Invalid parametrization for the distribution."); + } + + var z = (x - mean)/scale; + return Math.Exp(-z) / (scale * Math.Pow(1.0 + Math.Exp(-z), 2)); + } + + /// + /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x). + /// + /// The mean (μ) of the logistic distribution. + /// The scale (s) of the logistic distribution. Range: s > 0. + /// The location at which to compute the density. + /// the log density at . + /// + public static double PDFLn(double mean, double scale, double x) + { + if (scale <= 0.0) + { + throw new ArgumentException("Invalid parametrization for the distribution."); + } + + var z = (x - mean)/scale; + return -z - Math.Log(scale) - (2 * Math.Log(1+Math.Exp(-z))); + } + + /// + /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x). + /// + /// The location at which to compute the cumulative distribution function. + /// The mean (μ) of the logistic distribution. + /// The scale (s) of the logistic distribution. Range: s > 0. + /// the cumulative distribution at location . + /// + /// MATLAB: normcdf + public static double CDF(double mean, double scale, double x) + { + if (scale <= 0.0) + { + throw new ArgumentException("Invalid parametrization for the distribution."); + } + var z = (x - mean)/scale; + return 1 / (1 + Math.Exp(-z)); + } + + /// + /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution + /// at the given probability. This is also known as the quantile or percent point function. + /// + /// The location at which to compute the inverse cumulative density. + /// The mean (μ) of the logistic distribution. + /// The scale (s) of the logistic distribution. Range: s > 0. + /// the inverse cumulative density at . + /// + /// MATLAB: norminv + public static double InvCDF(double mean, double scale, double p) + { + if (scale <= 0.0) + { + throw new ArgumentException("Invalid parametrization for the distribution."); + } + + return mean + (scale*Math.Log(p / (1-p))); + } + + /// + /// Generates a sample from the logistic distribution using the Box-Muller algorithm. + /// + /// The random number generator to use. + /// The mean (μ) of the logistic distribution. + /// The scale (s) of the logistic distribution. Range: s > 0. + /// a sample from the distribution. + public static double Sample(System.Random rnd, double mean, double scale) + { + if (scale <= 0.0) + { + throw new ArgumentException("Invalid parametrization for the distribution."); + } + + return SampleUnchecked(rnd, mean, scale); + } + + /// + /// Generates a sequence of samples from the logistic distribution using the Box-Muller algorithm. + /// + /// The random number generator to use. + /// The mean (μ) of the logistic distribution. + /// The scale (s) of the logistic distribution. Range: s > 0. + /// a sequence of samples from the distribution. + public static IEnumerable Samples(System.Random rnd, double mean, double scale) + { + if (scale <= 0.0) + { + throw new ArgumentException("Invalid parametrization for the distribution."); + } + + return SamplesUnchecked(rnd, mean, scale); + } + + /// + /// 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 logistic distribution. + /// The scale (s) of the logistic distribution. Range: s > 0. + /// a sequence of samples from the distribution. + public static void Samples(System.Random rnd, double[] values, double mean, double scale) + { + if (scale <= 0.0) + { + throw new ArgumentException("Invalid parametrization for the distribution."); + } + + SamplesUnchecked(rnd, values, mean, scale); + } + + /// + /// Generates a sample from the logistic distribution using the Box-Muller algorithm. + /// + /// The mean (μ) of the logistic distribution. + /// The scale (s) of the logistic distribution. Range: s > 0. + /// a sample from the distribution. + public static double Sample(double mean, double scale) + { + if (scale <= 0.0) + { + throw new ArgumentException("Invalid parametrization for the distribution."); + } + + return SampleUnchecked(SystemRandomSource.Default, mean, scale); + } + + /// + /// Generates a sequence of samples from the logistic distribution using the Box-Muller algorithm. + /// + /// The mean (μ) of the logistic distribution. + /// The scale (s) of the logistic distribution. Range: s > 0. + /// a sequence of samples from the distribution. + public static IEnumerable Samples(double mean, double scale) + { + if (scale <= 0.0) + { + throw new ArgumentException("Invalid parametrization for the distribution."); + } + + return SamplesUnchecked(SystemRandomSource.Default, mean, scale); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The array to fill with the samples. + /// The mean (μ) of the logistic distribution. + /// The scale (s) of the logistic distribution. Range: s > 0. + /// a sequence of samples from the distribution. + public static void Samples(double[] values, double mean, double scale) + { + if (scale <= 0.0) + { + throw new ArgumentException("Invalid parametrization for the distribution."); + } + + SamplesUnchecked(SystemRandomSource.Default, values, mean, scale); + } + } +}