diff --git a/src/Numerics/Distributions/Continuous/StudentT.cs b/src/Numerics/Distributions/Continuous/StudentT.cs new file mode 100644 index 00000000..6caef011 --- /dev/null +++ b/src/Numerics/Distributions/Continuous/StudentT.cs @@ -0,0 +1,406 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://mathnet.opensourcedotnet.info +// +// Copyright (c) 2009 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. +// + +namespace MathNet.Numerics.Distributions +{ + using System; + using System.Collections.Generic; + using Properties; + + /// + /// Implements the univariate Student t-distribution. For details about this distribution, see + /// Wikipedia - Student's t-distribution. + /// + /// We use a slightly generalized version (compared to Wikipedia) of the Student t-distribution. + /// Namely, one which also parameterizes the location and scale. See the book "Bayesian Data Analysis" for more + /// details. + /// The distribution will use the by default. + /// Users can get/set the random number generator by using the property. + /// The statistics classes will check all the incoming parameters whether they are in the allowed + /// range. This might involve heavy computation. Optionally, by setting Control.CheckDistributionParameters + /// to false, all parameter checks can be turned off. + public class StudentT : IContinuousDistribution + { + /// + /// Keeps track of the location of the Student t-distribution. + /// + private double _location; + + /// + /// Keeps track of the degrees of freedom for the Student t-distribution. + /// + private double _dof; + + /// + /// Keeps track of the scale for the Student t-distribution. + /// + private double _scale; + + /// + /// The distribution's random number generator. + /// + private Random _random; + + /// + /// Initializes a new instance of the StudentT class. This is a Student t-distribution with location 0.0 + /// scale 1.0 and degrees of freedom 1. The distribution will + /// be initialized with the default random number generator. + /// + public StudentT() : this(0.0, 1.0, 1.0) + { + } + + /// + /// Initializes a new instance of the StudentT class with a particular location, scale and degrees of + /// freedom. The distribution will + /// be initialized with the default random number generator. + /// + /// The location of the Student t-distribution. + /// The scale of the Student t-distribution. + /// The degrees of freedom for the Student t-distribution. + public StudentT(double location, double scale, double dof) + { + SetParameters(location, scale, dof); + RandomSource = new Random(); + } + + /// + /// A string representation of the distribution. + /// + /// a string representation of the distribution. + public override string ToString() + { + return "StudentT(Location = " + _location + ", Scale = " + _scale + ", DoF = " + _dof + ")"; + } + + /// + /// Checks whether the parameters of the distribution are valid. + /// + /// The location of the Student t-distribution. + /// The scale of the Student t-distribution. + /// The degrees of freedom for the Student t-distribution. + /// True when the parameters are valid, false otherwise. + private static bool IsValidParameterSet(double location, double scale, double dof) + { + if (scale <= 0.0 || dof <= 0.0 || Double.IsNaN(scale) || Double.IsNaN(location) || Double.IsNaN(dof)) + { + return false; + } + + return true; + } + + /// + /// Sets the parameters of the distribution after checking their validity. + /// + /// The location of the Student t-distribution. + /// The scale of the Student t-distribution. + /// The degrees of freedom for the Student t-distribution. + /// When the parameters don't pass the function. + private void SetParameters(double location, double scale, double dof) + { + if (Control.CheckDistributionParameters && !IsValidParameterSet(location, scale, dof)) + { + throw new ArgumentOutOfRangeException(Resources.InvalidDistributionParameters); + } + + _location = location; + _scale = scale; + _dof = dof; + } + + /// + /// Gets or sets the location of the Student t-distribution. + /// + public double Location + { + get + { + return _location; + } + + set + { + SetParameters(value, _scale, _dof); + } + } + + /// + /// Gets or sets the scale of the Student t-distribution. + /// + public double Scale + { + get + { + return _scale; + } + + set + { + SetParameters(_location, value, _dof); + } + } + + /// + /// Gets or sets the degrees of freedom of the Student t-distribution. + /// + public double DegreesOfFreedom + { + get + { + return _dof; + } + + set + { + SetParameters(_location, _scale, value); + } + } + + #region IDistribution implementation + + /// + /// Gets or sets the random number generator which is used to draw random samples. + /// + public Random RandomSource + { + get + { + return _random; + } + + set + { + if (value == null) + { + throw new ArgumentNullException(); + } + + _random = value; + } + } + + /// + /// Gets or sets the mean of the Student t-distribution. + /// + public double Mean + { + get { return _location; } + } + + /// + /// Gets or sets the variance of the Student t-distribution. + /// + public double Variance + { + get + { + if (_dof > 2.0) + { + return _dof / (_dof - 2.0) / _scale; + } + else if (_dof > 1.0) + { + return Double.PositiveInfinity; + } + else + { + throw new Exception(Resources.UndefinedMoment); + } + } + } + + /// + /// Gets or sets the standard deviation of the Student t-distribution. + /// + public double StdDev + { + get + { + if (_dof > 2.0) + { + return Math.Sqrt(_dof / (_dof - 2.0)); + } + else if (_dof > 1.0) + { + return Double.PositiveInfinity; + } + else + { + throw new Exception(Resources.UndefinedMoment); + } + } + } + + /// + /// Gets the entropy of the Student t-distribution. + /// + public double Entropy + { + get { throw new NotImplementedException(); } + } + + /// + /// Gets the skewness of the Student t-distribution. + /// + public double Skewness + { + get { throw new NotImplementedException(); } + } + #endregion + + #region IContinuousDistribution implementation + + /// + /// Gets the mode of the Student t-distribution. + /// + public double Mode + { + get { return _location; } + } + + /// + /// Gets the median of the Student t-distribution. + /// + public double Median + { + get { return _location; } + } + + /// + /// Gets the minimum of the Student t-distribution. + /// + public double Minimum + { + get { return Double.NegativeInfinity; } + } + + /// + /// Gets the maximum of the Student t-distribution. + /// + public double Maximum + { + get { return Double.PositiveInfinity; } + } + + /// + /// Computes the density of the Student t-distribution. + /// + /// The location at which to compute the density. + /// the density at . + public double Density(double x) + { + double d = (x - _location) / _scale; + return SpecialFunctions.Gamma((_dof + 1.0) / 2.0) + * Math.Pow(1.0 + d * d / _dof, -0.5 * (_dof + 1.0)) + / SpecialFunctions.Gamma(_dof / 2.0) + / Math.Sqrt(_dof * Math.PI) + / _scale; + } + + /// + /// Computes the log density of the Student t-distribution. + /// + /// The location at which to compute the log density. + /// the log density at . + public double DensityLn(double x) + { + double d = (x - _location) / _scale; + return SpecialFunctions.GammaLn((_dof + 1.0) / 2.0) + - 0.5 * (_dof + 1.0) * Math.Log(1.0 + d * d / _dof) + - SpecialFunctions.GammaLn(_dof / 2.0) + -0.5 * Math.Log(_dof * Math.PI) + - Math.Log(_scale); + } + + /// + /// Computes the cumulative distribution function of the Student t-distribution. + /// + /// The location at which to compute the cumulative density. + /// the cumulative density at . + public double CumulativeDistribution(double x) + { + throw new NotImplementedException(); + } + + /// + /// Generates a sample from the Student t-distribution. + /// + /// a sample from the distribution. + public double Sample() + { + throw new NotImplementedException(); + } + + /// + /// Generates a sequence of samples from the Student t-distribution. + /// + /// a sequence of samples from the distribution. + public IEnumerable Samples() + { + throw new NotImplementedException(); + } + #endregion + + /// + /// Generates a sample from the Student t-distribution. + /// + /// The random number generator to use. + /// The location of the Student t-distribution. + /// The scale of the Student t-distribution. + /// The degrees of freedom for the Student t-distribution. + /// a sample from the distribution. + public static double Sample(Random rng, double location, double scale, double dof) + { + if (Control.CheckDistributionParameters && !IsValidParameterSet(location, scale, dof)) + { + throw new ArgumentOutOfRangeException(Resources.InvalidDistributionParameters); + } + + throw new NotImplementedException(); + } + + /// + /// Generates a sequence of samples from the Student t-distribution using the Box-Muller algorithm. + /// + /// The random number generator to use. + /// The location of the Student t-distribution. + /// The scale of the Student t-distribution. + /// The degrees of freedom for the Student t-distribution. + /// a sequence of samples from the distribution. + public static IEnumerable Samples(Random rng, double location, double scale, double dof) + { + if (Control.CheckDistributionParameters && !IsValidParameterSet(location, scale, dof)) + { + throw new ArgumentOutOfRangeException(Resources.InvalidDistributionParameters); + } + + throw new NotImplementedException(); + } + } +} diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj index 9d335228..a74a9378 100644 --- a/src/Numerics/Numerics.csproj +++ b/src/Numerics/Numerics.csproj @@ -212,6 +212,7 @@ SafeNativeMethods.cs + True True diff --git a/src/Numerics/Properties/Resources.Designer.cs b/src/Numerics/Properties/Resources.Designer.cs index df1d6f3e..d5b8053e 100644 --- a/src/Numerics/Properties/Resources.Designer.cs +++ b/src/Numerics/Properties/Resources.Designer.cs @@ -582,6 +582,15 @@ namespace MathNet.Numerics.Properties { } } + /// + /// Looks up a localized string similar to The moment of the distribution is undefined.. + /// + internal static string UndefinedMoment { + get { + return ResourceManager.GetString("UndefinedMoment", resourceCulture); + } + } + /// /// Looks up a localized string similar to A user defined provider has not been specified.. /// diff --git a/src/Numerics/Properties/Resources.resx b/src/Numerics/Properties/Resources.resx index bb058353..d7002782 100644 --- a/src/Numerics/Properties/Resources.resx +++ b/src/Numerics/Properties/Resources.resx @@ -294,4 +294,7 @@ The array arguments must have the same length. + + The moment of the distribution is undefined. + \ No newline at end of file diff --git a/src/Silverlight/Silverlight.csproj b/src/Silverlight/Silverlight.csproj index 5c86bdbd..068a9b4c 100644 --- a/src/Silverlight/Silverlight.csproj +++ b/src/Silverlight/Silverlight.csproj @@ -95,6 +95,9 @@ Distributions\Continuous\Normal.cs + + Distributions\Continuous\StudentT.cs + Distributions\Continuous\Weibull.cs diff --git a/src/UnitTests/DistributionTests/Continuous/StudentTTests.cs b/src/UnitTests/DistributionTests/Continuous/StudentTTests.cs new file mode 100644 index 00000000..e6571239 --- /dev/null +++ b/src/UnitTests/DistributionTests/Continuous/StudentTTests.cs @@ -0,0 +1,393 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://mathnet.opensourcedotnet.info +// +// Copyright (c) 2009 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. +// + +namespace MathNet.Numerics.UnitTests.DistributionTests +{ + using System; + using System.Linq; + using MbUnit.Framework; + using MathNet.Numerics.Distributions; + + [TestFixture] + public class StudentTTests + { + [SetUp] + public void SetUp() + { + Control.CheckDistributionParameters = true; + } + + [Test, MultipleAsserts] + public void CanCreateStandardStudentT() + { + var n = new StudentT(); + AssertEx.AreEqual(0.0, n.Location); + AssertEx.AreEqual(1.0, n.Scale); + AssertEx.AreEqual(1.0, n.DegreesOfFreedom); + } + + /*[Test, MultipleAsserts] + [Row(0.0, 0.0)] + [Row(0.0, 0.1)] + [Row(0.0, 1.0)] + [Row(0.0, 10.0)] + [Row(10.0, 1.0)] + [Row(-5.0, 100.0)] + [Row(0.0, Double.PositiveInfinity)] + public void CanCreateNormal(double mean, double sdev) + { + var n = new Normal(mean, sdev); + AssertEx.AreEqual(mean, n.Mean); + AssertEx.AreEqual(sdev, n.StdDev); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + [Row(Double.NaN, 1.0)] + [Row(1.0, Double.NaN)] + [Row(Double.NaN, Double.NaN)] + [Row(1.0, -1.0)] + public void NormalCreateFailsWithBadParameters(double mean, double sdev) + { + var n = new Normal(mean, sdev); + } + + [Test, MultipleAsserts] + [Row(0.0, 0.0)] + [Row(0.0, 0.1)] + [Row(0.0, 1.0)] + [Row(0.0, 10.0)] + [Row(10.0, 1.0)] + [Row(-5.0, 100.0)] + [Row(0.0, Double.PositiveInfinity)] + public void CanCreateNormalFromMeanAndStdDev(double mean, double sdev) + { + var n = Normal.WithMeanStdDev(mean, sdev); + AssertEx.AreEqual(mean, n.Mean); + AssertEx.AreEqual(sdev, n.StdDev); + } + + [Test, MultipleAsserts] + [Row(0.0, 0.0)] + [Row(0.0, 0.1)] + [Row(0.0, 1.0)] + [Row(0.0, 10.0)] + [Row(10.0, 1.0)] + [Row(-5.0, 100.0)] + [Row(0.0, Double.PositiveInfinity)] + public void CanCreateNormalFromMeanAndVariance(double mean, double var) + { + var n = Normal.WithMeanVariance(mean, var); + AssertHelpers.AlmostEqual(mean, n.Mean, 16); + AssertHelpers.AlmostEqual(var, n.Variance, 16); + } + + [Test, MultipleAsserts] + [Row(0.0, 0.0)] + [Row(0.0, 0.1)] + [Row(0.0, 1.0)] + [Row(0.0, 10.0)] + [Row(10.0, 1.0)] + [Row(-5.0, 100.0)] + [Row(0.0, Double.PositiveInfinity)] + public void CanCreateNormalFromMeanAndPrecision(double mean, double prec) + { + var n = Normal.WithMeanPrecision(mean, prec); + AssertHelpers.AlmostEqual(mean, n.Mean, 15); + AssertHelpers.AlmostEqual(prec, n.Precision, 15); + } + + [Test] + public void ValidateToString() + { + var n = new Normal(1.0, 2.0); + AssertEx.AreEqual("Normal(Mean = 1, StdDev = 2)", n.ToString()); + } + + [Test] + [Row(-0.0)] + [Row(0.0)] + [Row(0.1)] + [Row(1.0)] + [Row(10.0)] + [Row(Double.PositiveInfinity)] + public void CanSetPrecision(double prec) + { + var n = new Normal(); + n.Precision = prec; + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void SetPrecisionFailsWithNegativePrecision() + { + var n = new Normal(); + n.Precision = -1.0; + } + + [Test] + [Row(-0.0)] + [Row(0.0)] + [Row(0.1)] + [Row(1.0)] + [Row(10.0)] + [Row(Double.PositiveInfinity)] + public void CanSetVariance(double var) + { + var n = new Normal(); + n.Variance = var; + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void SetVarianceFailsWithNegativeVariance() + { + var n = new Normal(); + n.Variance = -1.0; + } + + [Test] + [Row(-0.0)] + [Row(0.0)] + [Row(0.1)] + [Row(1.0)] + [Row(10.0)] + [Row(Double.PositiveInfinity)] + public void CanSetStdDev(double sdev) + { + var n = new Normal(); + n.StdDev = sdev; + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void SetStdDevFailsWithNegativeStdDev() + { + var n = new Normal(); + n.StdDev = -1.0; + } + + [Test] + [Row(Double.NegativeInfinity)] + [Row(-0.0)] + [Row(0.0)] + [Row(0.1)] + [Row(1.0)] + [Row(10.0)] + [Row(Double.PositiveInfinity)] + public void CanSetMean(double mean) + { + var n = new Normal(); + n.Mean = mean; + } + + [Test] + [Row(-0.0)] + [Row(0.0)] + [Row(0.1)] + [Row(1.0)] + [Row(10.0)] + [Row(Double.PositiveInfinity)] + public void ValidateEntropy(double sdev) + { + var n = new Normal(1.0, sdev); + AssertEx.AreEqual(MathNet.Numerics.Constants.LogSqrt2PiE + Math.Log(n.StdDev), n.Entropy); + } + + [Test] + [Row(-0.0)] + [Row(0.0)] + [Row(0.1)] + [Row(1.0)] + [Row(10.0)] + [Row(Double.PositiveInfinity)] + public void ValidateSkewness(double sdev) + { + var n = new Normal(1.0, sdev); + AssertEx.AreEqual(0.0, n.Skewness); + } + + [Test] + [Row(Double.NegativeInfinity)] + [Row(-0.0)] + [Row(0.0)] + [Row(0.1)] + [Row(1.0)] + [Row(10.0)] + [Row(Double.PositiveInfinity)] + public void ValidateMode(double mean) + { + var n = new Normal(mean, 1.0); + AssertEx.AreEqual(mean, n.Mode); + } + + [Test] + [Row(Double.NegativeInfinity)] + [Row(-0.0)] + [Row(0.0)] + [Row(0.1)] + [Row(1.0)] + [Row(10.0)] + [Row(Double.PositiveInfinity)] + public void ValidateMedian(double mean) + { + var n = new Normal(mean, 1.0); + AssertEx.AreEqual(mean, n.Median); + } + + [Test] + public void ValidateMinimum() + { + var n = new Normal(); + AssertEx.AreEqual(System.Double.NegativeInfinity, n.Minimum); + } + + [Test] + public void ValidateMaximum() + { + var n = new Normal(); + AssertEx.AreEqual(System.Double.PositiveInfinity, n.Maximum); + } + + [Test] + [Row(0.0, 0.0)] + [Row(0.0, 0.1)] + [Row(0.0, 1.0)] + [Row(0.0, 10.0)] + [Row(10.0, 1.0)] + [Row(-5.0, 100.0)] + [Row(0.0, Double.PositiveInfinity)] + public void ValidateDensity(double mean, double sdev) + { + var n = Normal.WithMeanStdDev(mean, sdev); + for(int i = 0; i < 11; i++) + { + double x = i - 5.0; + double d = (mean - x)/sdev; + double pdf = Math.Exp(-0.5*d*d)/(sdev*Constants.Sqrt2Pi); + AssertEx.AreEqual(pdf, n.Density(x)); + } + } + + [Test] + [Row(0.0, 0.0)] + [Row(0.0, 0.1)] + [Row(0.0, 1.0)] + [Row(0.0, 10.0)] + [Row(10.0, 1.0)] + [Row(-5.0, 100.0)] + [Row(0.0, Double.PositiveInfinity)] + public void ValidateDensityLn(double mean, double sdev) + { + var n = Normal.WithMeanStdDev(mean, sdev); + for (int i = 0; i < 11; i++) + { + double x = i - 5.0; + double d = (mean - x) / sdev; + double pdfln = -0.5 * d * d - Math.Log(sdev) - Constants.LogSqrt2Pi; + AssertEx.AreEqual(pdfln, n.DensityLn(x)); + } + } + + [Test] + public void CanSampleStatic() + { + var d = Normal.Sample(new Random(), 0.0, 1.0); + } + + [Test] + public void CanSampleSequenceStatic() + { + var ied = Normal.Samples(new Random(), 0.0, 1.0); + var arr = ied.Take(5).ToArray(); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void FailSampleStatic() + { + var d = Normal.Sample(new Random(), 0.0, -1.0); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void FailSampleSequenceStatic() + { + var ied = Normal.Samples(new Random(), 0.0, -1.0).First(); + } + + [Test] + public void CanSample() + { + var n = new Normal(); + var d = n.Sample(); + } + + [Test] + public void CanSampleSequence() + { + var n = new Normal(); + var ied = n.Samples(); + var e = ied.Take(5).ToArray(); + } + + [Test] + [Row(Double.NegativeInfinity, 0.0)] + [Row(-5.0, 0.00000028665157187919391167375233287464535385442301361187883)] + [Row(-2.0, 0.0002326290790355250363499258867279847735487493358890356)] + [Row(-0.0, 0.0062096653257761351669781045741922211278977469230927036)] + [Row(0.0, 0.0062096653257761351669781045741922211278977469230927036)] + [Row(4.0, 0.30853753872598689636229538939166226011639782444542207)] + [Row(5.0, 0.5)] + [Row(6.0, 0.69146246127401310363770461060833773988360217555457859)] + [Row(10.0, 0.9937903346742238648330218954258077788721022530769078)] + [Row(Double.PositiveInfinity, 1.0)] + public void ValidateCumulativeDistribution(double x, double f) + { + var n = Normal.WithMeanStdDev(5.0, 2.0); + AssertHelpers.AlmostEqual(f, n.CumulativeDistribution(x), 10); + } + + [Test] + [Row(Double.NegativeInfinity, 0.0)] + [Row(-5.0, 0.00000028665157187919391167375233287464535385442301361187883)] + [Row(-2.0, 0.0002326290790355250363499258867279847735487493358890356)] + [Row(-0.0, 0.0062096653257761351669781045741922211278977469230927036)] + [Row(0.0, 0.0062096653257761351669781045741922211278977469230927036)] + [Row(4.0, 0.30853753872598689636229538939166226011639782444542207)] + [Row(5.0, 0.5)] + [Row(6.0, 0.69146246127401310363770461060833773988360217555457859)] + [Row(10.0, 0.9937903346742238648330218954258077788721022530769078)] + [Row(Double.PositiveInfinity, 1.0)] + public void ValidateInverseCumulativeDistribution(double x, double f) + { + var n = Normal.WithMeanStdDev(5.0, 2.0); + AssertHelpers.AlmostEqual(x, n.InverseCumulativeDistribution(f), 15); + }*/ + } +} diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj index bcb623ce..d396294a 100644 --- a/src/UnitTests/UnitTests.csproj +++ b/src/UnitTests/UnitTests.csproj @@ -92,6 +92,7 @@ +