diff --git a/src/Numerics/Distributions/Beta.cs b/src/Numerics/Distributions/Beta.cs index 151df34d..8b8a59da 100644 --- a/src/Numerics/Distributions/Beta.cs +++ b/src/Numerics/Distributions/Beta.cs @@ -4,7 +4,7 @@ // http://github.com/mathnet/mathnet-numerics // http://mathnetnumerics.codeplex.com // -// Copyright (c) 2009-2014 Math.NET +// 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 @@ -456,14 +456,14 @@ namespace MathNet.Numerics.Distributions /// The α shape parameter of the Beta distribution. Range: α ≥ 0. /// The β shape parameter of the Beta distribution. Range: β ≥ 0. /// a random number from the Beta distribution. - static double SampleUnchecked(System.Random rnd, double a, double b) + static internal double SampleUnchecked(System.Random rnd, double a, double b) { var x = Gamma.SampleUnchecked(rnd, a, 1.0); var y = Gamma.SampleUnchecked(rnd, b, 1.0); return x/(x + y); } - static void SamplesUnchecked(System.Random rnd, double[] values, double a, double b) + static internal void SamplesUnchecked(System.Random rnd, double[] values, double a, double b) { var y = new double[values.Length]; Gamma.SamplesUnchecked(rnd, values, a, 1.0); diff --git a/src/Numerics/Distributions/BetaScaled.cs b/src/Numerics/Distributions/BetaScaled.cs new file mode 100644 index 00000000..f7ec5bcc --- /dev/null +++ b/src/Numerics/Distributions/BetaScaled.cs @@ -0,0 +1,582 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// http://mathnetnumerics.codeplex.com +// +// 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.Properties; +using MathNet.Numerics.Random; +using MathNet.Numerics.Threading; + +namespace MathNet.Numerics.Distributions +{ + public class BetaScaled : IContinuousDistribution + { + System.Random _random; + + readonly double _shapeA; + readonly double _shapeB; + readonly double _location; + readonly double _scale; + + /// + /// Initializes a new instance of the BetaScaled class. + /// + /// The α shape parameter of the BetaScaled distribution. Range: α > 0. + /// The β shape parameter of the BetaScaled distribution. Range: β > 0. + /// The location (μ) of the distribution. + /// The scale (σ) of the distribution. Range: σ > 0. + public BetaScaled(double a, double b, double location, double scale) + { + if (!IsValidParameterSet(a, b, location, scale)) + { + throw new ArgumentException(Resources.InvalidDistributionParameters); + } + + _random = SystemRandomSource.Default; + _shapeA = a; + _shapeB = b; + _location = location; + _scale = scale; + } + + /// + /// Initializes a new instance of the BetaScaled class. + /// + /// The α shape parameter of the BetaScaled distribution. Range: α > 0. + /// The β shape parameter of the BetaScaled distribution. Range: β > 0. + /// The location (μ) of the distribution. + /// The scale (σ) of the distribution. Range: σ > 0. + /// The random number generator which is used to draw random samples. + public BetaScaled(double a, double b, double location, double scale, System.Random randomSource) + { + if (!IsValidParameterSet(a, b, location, scale)) + { + throw new ArgumentException(Resources.InvalidDistributionParameters); + } + + _random = SystemRandomSource.Default; + _shapeA = a; + _shapeB = b; + _location = location; + _scale = scale; + } + + /// + /// A string representation of the distribution. + /// + /// A string representation of the BetaScaled distribution. + public override string ToString() + { + return "BetaScaled(α = " + _shapeA + ", β = " + _shapeB + ", μ = " + _location + ", σ = " + _scale + ")"; + } + + /// + /// Tests whether the provided values are valid parameters for this distribution. + /// + /// The α shape parameter of the BetaScaled distribution. Range: α > 0. + /// The β shape parameter of the BetaScaled distribution. Range: β > 0. + /// The location (μ) of the distribution. + /// The scale (σ) of the distribution. Range: σ > 0. + public static bool IsValidParameterSet(double a, double b, double location, double scale) + { + return a > 0.0 && b > 0.0 && scale > 0.0 && !double.IsNaN(location); + } + + /// + /// Gets or sets the α shape parameter of the BetaScaled distribution. Range: α > 0. + /// + public double A + { + get { return _shapeA; } + } + + /// + /// Gets or sets the β shape parameter of the BetaScaled distribution. Range: β > 0. + /// + public double B + { + get { return _shapeB; } + } + + /// + /// Gets or sets the location (μ) of the BetaScaled distribution. + /// + public double Location + { + get { return _location; } + } + + /// + /// Gets or sets the scale (σ) of the BetaScaled distribution. Range: σ > 0. + /// + public double Scale + { + get { return _scale; } + } + + /// + /// Gets or sets the random number generator which is used to draw random samples. + /// + public System.Random RandomSource + { + get { return _random; } + set { _random = value ?? SystemRandomSource.Default; } + } + + /// + /// Gets the mean of the BetaScaled distribution. + /// + public double Mean + { + get + { + if (double.IsPositiveInfinity(_shapeA) && double.IsPositiveInfinity(_shapeB)) + { + return _location + 0.5 * _scale; + } + + if (double.IsPositiveInfinity(_shapeA)) + { + return _location + _scale; + } + + if (double.IsPositiveInfinity(_shapeB)) + { + return _location; + } + + return (_shapeB*_location + _shapeA*(_location + _scale))/(_shapeA + _shapeB); + } + } + + /// + /// Gets the variance of the BetaScaled distribution. + /// + public double Variance + { + get + { + double sum = _shapeA + _shapeB; + return (_shapeA*_shapeB*_scale*_scale)/(sum*sum*(1.0 + sum)); + } + } + + /// + /// Gets the standard deviation of the BetaScaled distribution. + /// + public double StdDev + { + get { return Math.Sqrt(Variance); } + } + + /// + /// Gets the entropy of the BetaScaled distribution. + /// + public double Entropy + { + get { throw new NotSupportedException(); } + } + + /// + /// Gets the skewness of the BetaScaled distribution. + /// + public double Skewness + { + get + { + if (double.IsPositiveInfinity(_shapeA) && double.IsPositiveInfinity(_shapeB)) + { + return 0.0; + } + + if (double.IsPositiveInfinity(_shapeA)) + { + return -2.0*_scale/Math.Sqrt(_shapeB*_scale*_scale); + } + + if (double.IsPositiveInfinity(_shapeB)) + { + return 2.0*_scale/Math.Sqrt(_shapeA*_scale*_scale); + } + + double sum = _shapeA + _shapeB; + double variance = (_shapeA * _shapeB * _scale * _scale) / (sum * sum * (1.0 + sum)); + return 2.0*(_shapeB - _shapeA)*_scale/(sum*(2.0 + sum)*Math.Sqrt(variance)); + } + } + + /// + /// Gets the mode of the BetaScaled distribution; when there are multiple answers, this routine will return 0.5. + /// + public double Mode + { + get + { + if (double.IsPositiveInfinity(_shapeA) && double.IsPositiveInfinity(_shapeB)) + { + return _location + 0.5 * _scale; + } + + if (double.IsPositiveInfinity(_shapeA)) + { + return _location + _scale; + } + + if (double.IsPositiveInfinity(_shapeB)) + { + return _location; + } + + if (_shapeA == 1.0 && _shapeB == 1.0) + { + return _location + 0.5 * _scale; + } + + return ((_shapeA - 1)/(_shapeA + _shapeB - 2))*_scale + _location; + } + } + + /// + /// Gets the median of the BetaScaled distribution. + /// + public double Median + { + get { throw new NotSupportedException(); } + } + + /// + /// Gets the minimum of the BetaScaled distribution. + /// + public double Minimum + { + get { return _location; } + } + + /// + /// Gets the maximum of the BetaScaled distribution. + /// + public double Maximum + { + get { return _location + _scale; } + } + + /// + /// 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(_shapeA, _shapeB, _location, _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(_shapeA, _shapeB, _location, _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(_shapeA, _shapeB, _location, _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 . + /// + /// WARNING: currently not an explicit implementation, hence slow and unreliable. + public double InverseCumulativeDistribution(double p) + { + return InvCDF(_shapeA, _shapeB, _location, _scale, p); + } + + /// + /// Generates a sample from the distribution. + /// + /// a sample from the distribution. + public double Sample() + { + return SampleUnchecked(_random, _shapeA, _shapeB, _location, _scale); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + public void Samples(double[] values) + { + SamplesUnchecked(_random, values, _shapeA, _shapeB, _location, _scale); + } + + /// + /// Generates a sequence of samples from the distribution. + /// + /// a sequence of samples from the distribution. + public IEnumerable Samples() + { + return SamplesUnchecked(_random, _shapeA, _shapeB, _location, _scale); + } + + static double SampleUnchecked(System.Random rnd, double a, double b, double location, double scale) + { + return Beta.SampleUnchecked(rnd, a, b)*scale + location; + } + + static void SamplesUnchecked(System.Random rnd, double[] values, double a, double b, double location, double scale) + { + Beta.SamplesUnchecked(rnd, values, a, b); + CommonParallel.For(0, values.Length, 4096, (aa, bb) => + { + for (int i = aa; i < bb; i++) + { + values[i] = values[i]*scale + location; + } + }); + } + + static IEnumerable SamplesUnchecked(System.Random rnd, double a, double b, double location, double scale) + { + while (true) + { + yield return SampleUnchecked(rnd, a, b, location, scale); + } + } + + /// + /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x. + /// + /// The α shape parameter of the BetaScaled distribution. Range: α > 0. + /// The β shape parameter of the BetaScaled distribution. Range: β > 0. + /// The location (μ) of the distribution. + /// The scale (σ) of the distribution. Range: σ > 0. + /// The location at which to compute the density. + /// the density at . + /// + public static double PDF(double a, double b, double location, double scale, double x) + { + if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location)) + { + throw new ArgumentException(Resources.InvalidDistributionParameters); + } + + return Beta.PDF(a, b, (x - location)/scale)/Math.Abs(scale); + } + + /// + /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x). + /// + /// The α shape parameter of the BetaScaled distribution. Range: α > 0. + /// The β shape parameter of the BetaScaled distribution. Range: β > 0. + /// The location (μ) of the distribution. + /// The scale (σ) of the distribution. Range: σ > 0. + /// The location at which to compute the density. + /// the log density at . + /// + public static double PDFLn(double a, double b, double location, double scale, double x) + { + if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location)) + { + throw new ArgumentException(Resources.InvalidDistributionParameters); + } + + return Beta.PDFLn(a, b, (x - location)/scale) - Math.Log(Math.Abs(scale)); + } + + /// + /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x). + /// + /// The α shape parameter of the BetaScaled distribution. Range: α > 0. + /// The β shape parameter of the BetaScaled distribution. Range: β > 0. + /// The location (μ) of the distribution. + /// The scale (σ) of the distribution. Range: σ > 0. + /// The location at which to compute the cumulative distribution function. + /// the cumulative distribution at location . + /// + public static double CDF(double a, double b, double location, double scale, double x) + { + if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location)) + { + throw new ArgumentException(Resources.InvalidDistributionParameters); + } + + return Beta.CDF(a, b, (x - location) / scale); + } + + /// + /// 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 α shape parameter of the BetaScaled distribution. Range: α > 0. + /// The β shape parameter of the BetaScaled distribution. Range: β > 0. + /// The location (μ) of the distribution. + /// The scale (σ) of the distribution. Range: σ > 0. + /// The location at which to compute the inverse cumulative density. + /// the inverse cumulative density at . + /// + /// WARNING: currently not an explicit implementation, hence slow and unreliable. + public static double InvCDF(double a, double b, double location, double scale, double p) + { + if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location)) + { + throw new ArgumentException(Resources.InvalidDistributionParameters); + } + + return Beta.InvCDF(a, b, p)*scale + location; + } + + /// + /// Generates a sample from the distribution. + /// + /// The random number generator to use. + /// The α shape parameter of the BetaScaled distribution. Range: α > 0. + /// The β shape parameter of the BetaScaled distribution. Range: β > 0. + /// The location (μ) of the distribution. + /// The scale (σ) of the distribution. Range: σ > 0. + /// a sample from the distribution. + public static double Sample(System.Random rnd, double a, double b, double location, double scale) + { + if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location)) + { + throw new ArgumentException(Resources.InvalidDistributionParameters); + } + + return SampleUnchecked(rnd, a, b, location, scale); + } + + /// + /// Generates a sequence of samples from the distribution. + /// + /// The random number generator to use. + /// The α shape parameter of the BetaScaled distribution. Range: α > 0. + /// The β shape parameter of the BetaScaled distribution. Range: β > 0. + /// The location (μ) of the distribution. + /// The scale (σ) of the distribution. Range: σ > 0. + /// a sequence of samples from the distribution. + public static IEnumerable Samples(System.Random rnd, double a, double b, double location, double scale) + { + if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location)) + { + throw new ArgumentException(Resources.InvalidDistributionParameters); + } + + return SamplesUnchecked(rnd, a, b, 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 α shape parameter of the BetaScaled distribution. Range: α > 0. + /// The β shape parameter of the BetaScaled distribution. Range: β > 0. + /// The location (μ) 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 a, double b, double location, double scale) + { + if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location)) + { + throw new ArgumentException(Resources.InvalidDistributionParameters); + } + + SamplesUnchecked(rnd, values, a, b, location, scale); + } + + /// + /// Generates a sample from the distribution. + /// + /// The α shape parameter of the BetaScaled distribution. Range: α > 0. + /// The β shape parameter of the BetaScaled distribution. Range: β > 0. + /// The location (μ) of the distribution. + /// The scale (σ) of the distribution. Range: σ > 0. + /// a sample from the distribution. + public static double Sample(double a, double b, double location, double scale) + { + if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location)) + { + throw new ArgumentException(Resources.InvalidDistributionParameters); + } + + return SampleUnchecked(SystemRandomSource.Default, a, b, location, scale); + } + + /// + /// Generates a sequence of samples from the distribution. + /// + /// The α shape parameter of the BetaScaled distribution. Range: α > 0. + /// The β shape parameter of the BetaScaled distribution. Range: β > 0. + /// The location (μ) of the distribution. + /// The scale (σ) of the distribution. Range: σ > 0. + /// a sequence of samples from the distribution. + public static IEnumerable Samples(double a, double b, double location, double scale) + { + if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location)) + { + throw new ArgumentException(Resources.InvalidDistributionParameters); + } + + return SamplesUnchecked(SystemRandomSource.Default, a, b, location, scale); + } + + /// + /// Fills an array with samples generated from the distribution. + /// + /// The array to fill with the samples. + /// The α shape parameter of the BetaScaled distribution. Range: α > 0. + /// The β shape parameter of the BetaScaled distribution. Range: β > 0. + /// The location (μ) of the distribution. + /// The scale (σ) of the distribution. Range: σ > 0. + /// a sequence of samples from the distribution. + public static void Samples(double[] values, double a, double b, double location, double scale) + { + if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location)) + { + throw new ArgumentException(Resources.InvalidDistributionParameters); + } + + SamplesUnchecked(SystemRandomSource.Default, values, a, b, location, scale); + } + } +} diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj index b7eeb8b1..9bfd671b 100644 --- a/src/Numerics/Numerics.csproj +++ b/src/Numerics/Numerics.csproj @@ -88,6 +88,7 @@ + diff --git a/src/UnitTests/DistributionTests/CommonDistributionTests.cs b/src/UnitTests/DistributionTests/CommonDistributionTests.cs index bac16578..601c8b60 100644 --- a/src/UnitTests/DistributionTests/CommonDistributionTests.cs +++ b/src/UnitTests/DistributionTests/CommonDistributionTests.cs @@ -4,7 +4,7 @@ // http://github.com/mathnet/mathnet-numerics // http://mathnetnumerics.codeplex.com // -// Copyright (c) 2009-2013 Math.NET +// 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 @@ -70,6 +70,7 @@ namespace MathNet.Numerics.UnitTests.DistributionTests new List { new Beta(1.0, 1.0), + new BetaScaled(1.0, 1.5, 0.5, 2.0), new Cauchy(1.0, 1.0), new Chi(3.0), new ChiSquared(3.0), diff --git a/src/UnitTests/DistributionTests/Continuous/BetaScaledTests.cs b/src/UnitTests/DistributionTests/Continuous/BetaScaledTests.cs new file mode 100644 index 00000000..7b937f78 --- /dev/null +++ b/src/UnitTests/DistributionTests/Continuous/BetaScaledTests.cs @@ -0,0 +1,311 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// http://mathnetnumerics.codeplex.com +// +// 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.Linq; +using MathNet.Numerics.Distributions; +using NUnit.Framework; + +namespace MathNet.Numerics.UnitTests.DistributionTests.Continuous +{ + using Random = System.Random; + + /// + /// BetaScaled distribution tests. + /// + [TestFixture, Category("Distributions")] + public class BetaScaledTests + { + /// + /// Can create BetaScaled distribution. + /// + [TestCase(1.0, 1.0, -1.0, 1.0)] + [TestCase(9.0, 1.0, 0.0, 1.0)] + [TestCase(5.0, 100.0, 0.0, 1.0)] + [TestCase(1.0, Double.PositiveInfinity, 0.0, 1.0)] + [TestCase(Double.PositiveInfinity, 1.0, 0.0, 1.0)] + [TestCase(1.0, Double.PositiveInfinity, 0.0, Double.PositiveInfinity)] + [TestCase(Double.PositiveInfinity, 1.0, Double.PositiveInfinity, 1.0)] + public void CanCreateBetaScaled(double a, double b, double location, double scale) + { + var n = new BetaScaled(a, b, location, scale); + Assert.AreEqual(a, n.A); + Assert.AreEqual(b, n.B); + Assert.AreEqual(location, n.Location); + Assert.AreEqual(scale, n.Scale); + } + + /// + /// BetaScaled create fails with bad parameters. + /// + [Test] + public void BetaScaledCreateFailsWithBadParameters() + { + Assert.That(() => new BetaScaled(Double.NaN, 1.0, 0.0, 1.0), Throws.ArgumentException); + Assert.That(() => new BetaScaled(1.0, Double.NaN, 0.0, 1.0), Throws.ArgumentException); + Assert.That(() => new BetaScaled(Double.NaN, Double.NaN, 0.0, 1.0), Throws.ArgumentException); + Assert.That(() => new BetaScaled(1.0, 1.0, Double.NaN, 1.0), Throws.ArgumentException); + Assert.That(() => new BetaScaled(1.0, 1.0, 1.0, Double.NaN), Throws.ArgumentException); + Assert.That(() => new BetaScaled(1.0, 0.0, 0.0, 1.0), Throws.ArgumentException); + Assert.That(() => new BetaScaled(0.0, 1.0, 0.0, 1.0), Throws.ArgumentException); + Assert.That(() => new BetaScaled(-1.0, -1.0, 0.0, 1.0), Throws.ArgumentException); + Assert.That(() => new BetaScaled(1.0, 1.0, 1.0, 0.0), Throws.ArgumentException); + Assert.That(() => new BetaScaled(1.0, 1.0, 1.0, -1.0), Throws.ArgumentException); + } + + /// + /// Validate to string. + /// + [Test] + public void ValidateToString() + { + var n = new BetaScaled(1d, 2d, 0.0, 1.0); + Assert.AreEqual("BetaScaled(α = 1, β = 2, μ = 0, σ = 1)", n.ToString()); + } + + /// + /// Validate mean. + /// + [TestCase(1.0, 1.0, 0.0, 1.0, 0.5)] + [TestCase(9.0, 1.0, 0.0, 1.0, 0.9)] + [TestCase(5.0, 100.0, 0.0, 1.0, 0.047619047619047619047616)] + [TestCase(1.0, Double.PositiveInfinity, 0.0, 1.0, 0.0)] + [TestCase(Double.PositiveInfinity, 1.0, 0.0, 1.0, 1.0)] + public void ValidateMean(double a, double b, double location, double scale, double mean) + { + var n = new BetaScaled(a, b, location, scale); + Assert.AreEqual(mean, n.Mean); + } + + /// + /// Validate skewness. + /// + [TestCase(1.0, 1.0, 0.0, 1.0, 0.0)] + [TestCase(9.0, 1.0, 0.0, 1.0, -1.4740554623801777107177478829647496373009282424841579)] + [TestCase(5.0, 100.0, 0.0, 1.0, 0.81759410927553430354583159143895018978562196953345572)] + [TestCase(1.0, Double.PositiveInfinity, 0.0, 1.0, 2.0)] + [TestCase(Double.PositiveInfinity, 1.0, 0.0, 1.0, -2.0)] + public void ValidateSkewness(double a, double b, double location, double scale, double skewness) + { + var n = new BetaScaled(a, b, location, scale); + AssertHelpers.AlmostEqualRelative(skewness, n.Skewness, 14); + } + + /// + /// Validate mode. + /// + [TestCase(1.0, 1.0, 0.0, 1.0, 0.5)] + [TestCase(9.0, 1.0, 0.0, 1.0, 1.0)] + [TestCase(5.0, 100.0, 0.0, 1.0, 0.038834951456310676243255386452801758423447608947753906)] + [TestCase(1.0, Double.PositiveInfinity, 0.0, 1.0, 0.0)] + [TestCase(Double.PositiveInfinity, 1.0, 0.0, 1.0, 1.0)] + public void ValidateMode(double a, double b, double location, double scale, double mode) + { + var n = new BetaScaled(a, b, location, scale); + Assert.AreEqual(mode, n.Mode); + } + + /// + /// Validate median throws NotSupportedException. + /// + [Test] + public void ValidateMedianThrowsNotSupportedException() + { + var n = new BetaScaled(1.0, 1.0, 0.0, 1.0); + Assert.Throws(() => { var m = n.Median; }); + } + + /// + /// Validate minimum. + /// + [Test] + public void ValidateMinimum() + { + var n = new BetaScaled(1.0, 1.0, 0.0, 1.0); + Assert.AreEqual(0.0, n.Minimum); + } + + /// + /// Validate maximum. + /// + [Test] + public void ValidateMaximum() + { + var n = new BetaScaled(1.0, 1.0, 0.0, 1.0); + Assert.AreEqual(1.0, n.Maximum); + } + + /// + /// Can sample static. + /// + [Test] + public void CanSampleStatic() + { + BetaScaled.Sample(new Random(0), 2.0, 3.0, 0.0, 1.0); + } + + /// + /// Can sample sequence static. + /// + [Test] + public void CanSampleSequenceStatic() + { + var ied = BetaScaled.Samples(new Random(0), 2.0, 3.0, 0.0, 1.0); + GC.KeepAlive(ied.Take(5).ToArray()); + } + + /// + /// Fail sample static with wrong parameters. + /// + [Test] + public void FailSampleStatic() + { + Assert.That(() => BetaScaled.Sample(new Random(0), 1.0, -1.0, 0.0, 1.0), Throws.ArgumentException); + } + + /// + /// Fail sample sequence static with wrong parameters. + /// + [Test] + public void FailSampleSequenceStatic() + { + Assert.That(() => BetaScaled.Samples(new Random(0), 1.0, -1.0, 0.0, 1.0).First(), Throws.ArgumentException); + } + + /// + /// Can sample. + /// + [Test] + public void CanSample() + { + var n = new BetaScaled(2.0, 3.0, 0.0, 1.0); + n.Sample(); + } + + /// + /// Can sample sequence. + /// + [Test] + public void CanSampleSequence() + { + var n = new BetaScaled(2.0, 3.0, 0.0, 1.0); + var ied = n.Samples(); + GC.KeepAlive(ied.Take(5).ToArray()); + } + + /// Reference: N[PDF[TransformedDistribution[l + s d, d \[Distributed] BetaDistribution[a, b]], x], 20] + [TestCase(1.0, 1.0, 0.0, 1.0, 0.0, 1.0)] + [TestCase(1.0, 1.0, 0.0, 1.0, 0.5, 1.0)] + [TestCase(1.0, 1.0, 0.0, 1.0, 1.0, 1.0)] + [TestCase(9.0, 1.0, 0.0, 1.0, 0.0, 0.0)] + [TestCase(9.0, 1.0, 0.0, 1.0, 0.5, 0.03515625)] + [TestCase(9.0, 1.0, 0.0, 1.0, 1.0, 9.0)] + [TestCase(9.0, 1.0, 0.0, 1.0, -1.0, 0.0)] + [TestCase(9.0, 1.0, 0.0, 1.0, 2.0, 0.0)] + [TestCase(9.0, 1.0, -2.0, 2.0, -0.5, 0.450508)] + [TestCase(5.0, 100, 0.0, 1.0, 0.0, 0.0)] + [TestCase(5.0, 100, 0.0, 1.0, 0.5, 1.0881845516040810386311829462908430145307026037926335e-21)] + [TestCase(5.0, 100, 0.0, 1.0, 1.0, 0.0)] + [TestCase(1.0, Double.PositiveInfinity, 0.0, 1.0, 0.0, Double.PositiveInfinity)] + [TestCase(1.0, Double.PositiveInfinity, 0.0, 1.0, 0.5, 0.0)] + [TestCase(1.0, Double.PositiveInfinity, 0.0, 1.0, 1.0, 0.0)] + [TestCase(Double.PositiveInfinity, 1.0, 0.0, 1.0, 0.0, 0.0)] + [TestCase(Double.PositiveInfinity, 1.0, 0.0, 1.0, 0.5, 0.0)] + [TestCase(Double.PositiveInfinity, 1.0, 0.0, 1.0, 1.0, Double.PositiveInfinity)] + public void ValidateDensity(double a, double b, double location, double scale, double x, double pdf) + { + var n = new BetaScaled(a, b, location, scale); + AssertHelpers.AlmostEqualRelative(pdf, n.Density(x), 5); + AssertHelpers.AlmostEqualRelative(pdf, BetaScaled.PDF(a, b, location, scale, x), 5); + } + + /// Reference: N[Log[PDF[TransformedDistribution[l + s d, d \[Distributed] BetaDistribution[a, b]], x]], 20] + [TestCase(1.0, 1.0, 0.0, 1.0, 0.0, 0.0)] + [TestCase(1.0, 1.0, 0.0, 1.0, 0.5, 0.0)] + [TestCase(1.0, 1.0, 0.0, 1.0, 1.0, 0.0)] + [TestCase(9.0, 1.0, 0.0, 1.0, 0.0, Double.NegativeInfinity)] + [TestCase(9.0, 1.0, 0.0, 1.0, 0.5, -3.3479528671433430925473664978203611353090199592365458)] + [TestCase(9.0, 1.0, 0.0, 1.0, 1.0, 2.1972245773362193827904904738450514092949811156454996)] + [TestCase(9.0, 1.0, 0.0, 1.0, -1.0, Double.NegativeInfinity)] + [TestCase(9.0, 1.0, 0.0, 1.0, 2.0, Double.NegativeInfinity)] + [TestCase(9.0, 1.0, -2.0, 2.0, -0.5, -0.797379)] + [TestCase(5.0, 100, 0.0, 1.0, 0.0, Double.NegativeInfinity)] + [TestCase(5.0, 100, 0.0, 1.0, 0.5, -51.447830024537682154565870837960406410586196074573801)] + [TestCase(5.0, 100, 0.0, 1.0, 1.0, Double.NegativeInfinity)] + [TestCase(1.0, Double.PositiveInfinity, 0.0, 1.0, 0.0, Double.PositiveInfinity)] + [TestCase(1.0, Double.PositiveInfinity, 0.0, 1.0, 0.5, Double.NegativeInfinity)] + [TestCase(1.0, Double.PositiveInfinity, 0.0, 1.0, 1.0, Double.NegativeInfinity)] + [TestCase(Double.PositiveInfinity, 1.0, 0.0, 1.0, 0.0, Double.NegativeInfinity)] + [TestCase(Double.PositiveInfinity, 1.0, 0.0, 1.0, 0.5, Double.NegativeInfinity)] + [TestCase(Double.PositiveInfinity, 1.0, 0.0, 1.0, 1.0, Double.PositiveInfinity)] + public void ValidateDensityLn(double a, double b, double location, double scale, double x, double pdfln) + { + var n = new BetaScaled(a, b, location, scale); + AssertHelpers.AlmostEqualRelative(pdfln, n.DensityLn(x), 5); + AssertHelpers.AlmostEqualRelative(pdfln, BetaScaled.PDFLn(a, b, location, scale, x), 5); + } + + /// Reference: N[CDF[TransformedDistribution[l + s d, d \[Distributed] BetaDistribution[a, b]], x], 20] + [TestCase(1.0, 1.0, 0.0, 1.0, 0.0, 0.0)] + [TestCase(1.0, 1.0, 0.0, 1.0, 0.5, 0.5)] + [TestCase(1.0, 1.0, 0.0, 1.0, 1.0, 1.0)] + [TestCase(9.0, 1.0, 0.0, 1.0, 0.0, 0.0)] + [TestCase(9.0, 1.0, 0.0, 1.0, 0.5, 0.001953125)] + [TestCase(9.0, 1.0, 0.0, 1.0, 1.0, 1.0)] + [TestCase(9.0, 1.0, -2.0, 2.0, -0.5, 0.0750847)] + [TestCase(5.0, 100, 0.0, 1.0, 0.0, 0.0)] + [TestCase(5.0, 100, 0.0, 1.0, 0.5, 1.0)] + [TestCase(5.0, 100, 0.0, 1.0, 1.0, 1.0)] + [TestCase(1.0, Double.PositiveInfinity, 0.0, 1.0, 0.0, 1.0)] + [TestCase(1.0, Double.PositiveInfinity, 0.0, 1.0, 0.5, 1.0)] + [TestCase(1.0, Double.PositiveInfinity, 0.0, 1.0, 1.0, 1.0)] + [TestCase(Double.PositiveInfinity, 1.0, 0.0, 1.0, 0.0, 0.0)] + [TestCase(Double.PositiveInfinity, 1.0, 0.0, 1.0, 0.5, 0.0)] + [TestCase(Double.PositiveInfinity, 1.0, 0.0, 1.0, 1.0, 1.0)] + public void ValidateCumulativeDistribution(double a, double b, double location, double scale, double x, double p) + { + var dist = new BetaScaled(a, b, location, scale); + Assert.That(dist.CumulativeDistribution(x), Is.EqualTo(p).Within(1e-5)); + Assert.That(BetaScaled.CDF(a, b, location, scale, x), Is.EqualTo(p).Within(1e-5)); + } + + [TestCase(1.0, 1.0, 0.0, 1.0, 1.0, 1.0)] + [TestCase(9.0, 1.0, 0.0, 1.0, 0.0, 0.0)] + [TestCase(9.0, 1.0, 0.0, 1.0, 0.5, 0.001953125)] + [TestCase(9.0, 1.0, 0.0, 1.0, 1.0, 1.0)] + [TestCase(5.0, 100, 0.0, 1.0, 0.0, 0.0)] + public void ValidateInverseCumulativeDistribution(double a, double b, double location, double scale, double x, double p) + { + var dist = new BetaScaled(a, b, location, scale); + Assert.That(dist.InverseCumulativeDistribution(p), Is.EqualTo(x).Within(1e-6)); + Assert.That(BetaScaled.InvCDF(a, b, location, scale, p), Is.EqualTo(x).Within(1e-6)); + } + } +} diff --git a/src/UnitTests/DistributionTests/Continuous/BetaTests.cs b/src/UnitTests/DistributionTests/Continuous/BetaTests.cs index 7c0efb51..6e07f1f2 100644 --- a/src/UnitTests/DistributionTests/Continuous/BetaTests.cs +++ b/src/UnitTests/DistributionTests/Continuous/BetaTests.cs @@ -356,7 +356,7 @@ namespace MathNet.Numerics.UnitTests.DistributionTests.Continuous AssertHelpers.AlmostEqualRelative(pdfln, n.DensityLn(x), 13); AssertHelpers.AlmostEqualRelative(pdfln, Beta.PDFLn(a, b, x), 13); } - + [TestCase(0.0, 0.0, 0.0, 0.5)] [TestCase(0.0, 0.0, 0.5, 0.5)] [TestCase(0.0, 0.0, 1.0, 1.0)] diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj index 75021311..91b8405d 100644 --- a/src/UnitTests/UnitTests.csproj +++ b/src/UnitTests/UnitTests.csproj @@ -90,6 +90,7 @@ +