Compare commits
6 Commits
master
...
truncatedn
| Author | SHA1 | Date |
|---|---|---|
|
|
7e0b98c625 | 10 years ago |
|
|
701027ff3c | 10 years ago |
|
|
656f235e31 | 10 years ago |
|
|
ad0cc0c7e8 | 11 years ago |
|
|
a75d88464c | 11 years ago |
|
|
b260c3ec99 | 11 years ago |
5 changed files with 613 additions and 0 deletions
@ -0,0 +1,344 @@ |
|||||
|
// <copyright file="TruncatedNormal.cs" company="Math.NET">
|
||||
|
// 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.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using MathNet.Numerics.Properties; |
||||
|
using MathNet.Numerics.Random; |
||||
|
|
||||
|
namespace MathNet.Numerics.Distributions { |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Truncated Normal Distribution.
|
||||
|
/// For more details about this distribution, see
|
||||
|
/// <a href="https://en.wikipedia.org/wiki/Truncated_normal_distribution">Wikipedia - Truncated normal distribution</a>
|
||||
|
/// </summary>
|
||||
|
public class TruncatedNormal : IContinuousDistribution |
||||
|
{ |
||||
|
System.Random _random; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Mean of the untruncated normal distribution.
|
||||
|
/// </summary>
|
||||
|
readonly double _mu; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Standard deviation of the uncorrected normal distribution.
|
||||
|
/// </summary>
|
||||
|
readonly double _sigma; |
||||
|
|
||||
|
readonly double _lowerBound; |
||||
|
readonly double _upperBound; |
||||
|
readonly Normal _standardNormal = new Normal(0.0, 1.0); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Position in the standard normal distribution of the lower bound.
|
||||
|
/// </summary>
|
||||
|
readonly double _alpha; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Position in the standard normal distribution of the upper bound.
|
||||
|
/// </summary>
|
||||
|
readonly double _beta; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The total density of the uncorrected normal distribution which is within the lower and upper bounds.
|
||||
|
/// Referred to as "Z" in the wikipedia equations. Z = Φ(UpperBound) - Φ(LowerBound).
|
||||
|
/// </summary>
|
||||
|
readonly double _cumulativeDensityWithinBounds; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the TruncatedNormal class. The distribution will
|
||||
|
/// be initialized with the default <seealso cref="System.Random"/> random number generator. The mean
|
||||
|
/// and standard deviation are that of the untruncated normal distribution.
|
||||
|
/// </summary>
|
||||
|
/// <param name="mean">The mean (μ) of the untruncated distribution.</param>
|
||||
|
/// <param name="stddev">The standard deviation (σ) of the untruncated distribution. Range: σ > 0.</param>
|
||||
|
/// <param name="lowerBound">The inclusive lower bound of the truncated distribution. Default is double.NegativeInfinity.</param>
|
||||
|
/// <param name="upperBound">The inclusive upper bound of the truncated distribution. Must be larger than <paramref name="lowerBound"/>.
|
||||
|
/// Default is double.PositiveInfinity.</param>
|
||||
|
public TruncatedNormal(double mean, double stddev, double lowerBound = double.NegativeInfinity, double upperBound = double.PositiveInfinity) |
||||
|
: this(mean, stddev, SystemRandomSource.Default, lowerBound, upperBound) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the TruncatedNormal class. The distribution will
|
||||
|
/// be initialized with the provided <seealso cref="System.Random"/> random number generator.
|
||||
|
/// </summary>
|
||||
|
/// <param name="untruncatedMean">The mean (μ) of the untruncated normal distribution.</param>
|
||||
|
/// <param name="untruncatedStdDev">The standard deviation (σ) of the untruncated normal distribution. Range: σ > 0.</param>
|
||||
|
/// <param name="randomSource">The random number generator which is used to draw random samples.</param>
|
||||
|
/// <param name="lowerBound">The inclusive lower bound of the truncated distribution. Default is double.NegativeInfinity.</param>
|
||||
|
/// <param name="upperBound">The inclusive upper bound of the truncated distribution. Must be larger than <paramref name="lowerBound"/>.
|
||||
|
/// Default is double.PositiveInfinity.</param>
|
||||
|
|
||||
|
public TruncatedNormal(double untruncatedMean, double untruncatedStdDev, System.Random randomSource, double lowerBound = double.NegativeInfinity, double upperBound = double.PositiveInfinity) |
||||
|
{ |
||||
|
if (!IsValidParameterSet(untruncatedMean, untruncatedStdDev, lowerBound, upperBound)) |
||||
|
{ |
||||
|
throw new ArgumentException(Resources.InvalidDistributionParameters); |
||||
|
} |
||||
|
|
||||
|
_random = randomSource ?? SystemRandomSource.Default; |
||||
|
_mu = untruncatedMean; |
||||
|
_sigma = untruncatedStdDev; |
||||
|
_lowerBound = lowerBound; |
||||
|
_upperBound = upperBound; |
||||
|
_alpha = (_lowerBound - _mu) / _sigma; |
||||
|
_beta = (_upperBound - _mu) / _sigma; |
||||
|
|
||||
|
_cumulativeDensityWithinBounds = _standardNormal.CumulativeDistribution(_beta) - _standardNormal.CumulativeDistribution(_alpha); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Tests whether the provided values are valid parameters for this distribution.
|
||||
|
/// </summary>
|
||||
|
/// <param name="mean">The mean (μ) of the normal distribution.</param>
|
||||
|
/// <param name="stddev">The standard deviation (σ) of the normal distribution. Range: σ > 0.</param>
|
||||
|
public static bool IsValidParameterSet(double mean, double stddev, double lowerBound, double upperBound) |
||||
|
{ |
||||
|
bool normalRequirements = Normal.IsValidParameterSet(mean, stddev) && stddev > 0; |
||||
|
bool boundsAreOrdered = lowerBound < upperBound; |
||||
|
return normalRequirements && boundsAreOrdered; |
||||
|
} |
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return "TruncatedNormal(μ = " + _mu + ", σ = " + _sigma +", LowerBound = " + _lowerBound + ", UpperBound = " + _upperBound + ")"; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the mode of the truncated normal distribution.
|
||||
|
/// </summary>
|
||||
|
public double Mode |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (_mu < _lowerBound) |
||||
|
return _lowerBound; |
||||
|
if (_mu > _upperBound) |
||||
|
return _upperBound; |
||||
|
return _mu; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the minimum of the truncated normal distribution.
|
||||
|
/// </summary>
|
||||
|
public double Minimum |
||||
|
{ |
||||
|
get { return _lowerBound; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the maximum of the truncated normal distribution.
|
||||
|
/// </summary>
|
||||
|
public double Maximum |
||||
|
{ |
||||
|
get { return _upperBound; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the mean (μ) of the truncated normal distribution.
|
||||
|
/// </summary>
|
||||
|
public double Mean |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
var pdfDifference = _standardNormal.Density(_alpha) - _standardNormal.Density(_beta); |
||||
|
var diffFromUncorrected = pdfDifference * _sigma / _cumulativeDensityWithinBounds; |
||||
|
return _mu + diffFromUncorrected; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the variance of the truncated normal distribution.
|
||||
|
/// </summary>
|
||||
|
public double Variance |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
//Apparently "Barr and Sherrill (1999)" has a simpler expression for one sided truncations, if anyone has access...
|
||||
|
|
||||
|
//TODO might need special handling for cases where either or both bounds are infinity
|
||||
|
var densityAtLower = double.IsNegativeInfinity(_lowerBound) ? 0.0 : _standardNormal.Density(_alpha); |
||||
|
var densityAtUpper = double.IsPositiveInfinity(_upperBound) ? 0.0 : _standardNormal.Density(_beta); |
||||
|
|
||||
|
var standardisedLower = double.IsNegativeInfinity(_lowerBound) ? 0.0 : _alpha; |
||||
|
var standardisedUpper = double.IsPositiveInfinity(_upperBound) ? 0.0 : _beta; |
||||
|
|
||||
|
//Second term
|
||||
|
var secondNumerator = standardisedLower * densityAtLower - standardisedUpper * densityAtUpper; |
||||
|
var secordTerm = secondNumerator / _cumulativeDensityWithinBounds; |
||||
|
|
||||
|
//Third term
|
||||
|
var thirdNumerator = densityAtLower - densityAtUpper; |
||||
|
var thirdTerm = (thirdNumerator / _cumulativeDensityWithinBounds) * (thirdNumerator / _cumulativeDensityWithinBounds); |
||||
|
|
||||
|
var sumOfTerms = 1 + secordTerm + thirdTerm; |
||||
|
|
||||
|
return _sigma * _sigma * sumOfTerms; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the standard deviation (σ) of the truncated normal distribution. Range: σ > 0.
|
||||
|
/// </summary>
|
||||
|
public double StdDev |
||||
|
{ |
||||
|
get { return Math.Sqrt(Variance); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the entropy of the truncated normal distribution.
|
||||
|
/// </summary>
|
||||
|
public double Entropy |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
var firstTerm = Constants.LogSqrt2PiE + Math.Log(_sigma + _cumulativeDensityWithinBounds); |
||||
|
|
||||
|
var secondNumerator = _lowerBound * _standardNormal.Density(_alpha) - _upperBound * _standardNormal.Density(_beta); |
||||
|
var secondTerm = secondNumerator / (2 * _cumulativeDensityWithinBounds); |
||||
|
|
||||
|
return firstTerm + secondTerm; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public double Skewness |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the median of the truncated distribution.
|
||||
|
/// </summary>
|
||||
|
public double Median |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return InverseCumulativeDistribution(0.5); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the random number generator which is used to draw random samples.
|
||||
|
/// </summary>
|
||||
|
public System.Random RandomSource |
||||
|
{ |
||||
|
get { return _random; } |
||||
|
set { _random = value ?? SystemRandomSource.Default; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
|
||||
|
/// </summary>
|
||||
|
/// <param name="x">The location at which to compute the density.</param>
|
||||
|
/// <returns>the density at <paramref name="x"/>.</returns>
|
||||
|
/// <seealso cref="PDF"/>
|
||||
|
public double Density(double x) |
||||
|
{ |
||||
|
if (x < _lowerBound || _upperBound < x) |
||||
|
return 0d; |
||||
|
|
||||
|
return _standardNormal.Density((x - _mu) / _sigma) / (_sigma * _cumulativeDensityWithinBounds); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
|
||||
|
/// </summary>
|
||||
|
/// <param name="x">The location at which to compute the log density.</param>
|
||||
|
/// <returns>the log density at <paramref name="x"/>.</returns>
|
||||
|
/// <seealso cref="PDFLn"/>
|
||||
|
public double DensityLn(double x) |
||||
|
{ |
||||
|
return _standardNormal.DensityLn((x - _mu) / _sigma) - Math.Log(_sigma) - Math.Log(_cumulativeDensityWithinBounds); |
||||
|
} |
||||
|
|
||||
|
public double Sample() |
||||
|
{ |
||||
|
//TODO: implement sampling more efficiently/accurately, use method described by Mazet here: http://miv.u-strasbg.fr/mazet/rtnorm/
|
||||
|
// see implementations listed on that page for examples.
|
||||
|
return InverseCumulativeDistribution(RandomSource.NextDouble()); |
||||
|
} |
||||
|
|
||||
|
public void Samples(double[] values) |
||||
|
{ |
||||
|
for(int i = 0; i < values.Length; i++) |
||||
|
{ |
||||
|
values[i] = Sample(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public IEnumerable<double> Samples() |
||||
|
{ |
||||
|
while (true) |
||||
|
{ |
||||
|
yield return Sample(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
|
||||
|
/// </summary>
|
||||
|
/// <param name="x">The location at which to compute the cumulative distribution function.</param>
|
||||
|
/// <returns>the cumulative distribution at location <paramref name="x"/>.</returns>
|
||||
|
/// <seealso cref="CDF"/>
|
||||
|
public double CumulativeDistribution(double x) |
||||
|
{ |
||||
|
if (x < _lowerBound) |
||||
|
return 0d; |
||||
|
if (x > _upperBound) |
||||
|
return 1d; |
||||
|
|
||||
|
double cumulative = _standardNormal.CumulativeDistribution((x - _mu) / _sigma) - _standardNormal.CumulativeDistribution(_alpha); |
||||
|
return cumulative / _cumulativeDensityWithinBounds; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 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.
|
||||
|
/// </summary>
|
||||
|
/// <param name="p">The location at which to compute the inverse cumulative density.</param>
|
||||
|
/// <returns>the inverse cumulative density at <paramref name="p"/>.</returns>
|
||||
|
/// <seealso cref="InvCDF"/>
|
||||
|
public double InverseCumulativeDistribution(double p) |
||||
|
{ |
||||
|
//TODO check that this is correct with someone.
|
||||
|
var pUntruncated = p * _cumulativeDensityWithinBounds + _standardNormal.CumulativeDistribution(_alpha); |
||||
|
|
||||
|
return _standardNormal.InverseCumulativeDistribution(pUntruncated) * _sigma + _mu; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,265 @@ |
|||||
|
// <copyright file="TruncatedNormalTests.cs" company="Math.NET">
|
||||
|
// 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.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
using MathNet.Numerics.Distributions; |
||||
|
using NUnit.Framework; |
||||
|
using System; |
||||
|
using System.Linq; |
||||
|
|
||||
|
namespace MathNet.Numerics.UnitTests.DistributionTests.Continuous |
||||
|
{ |
||||
|
[TestFixture, Category("Distributions")] |
||||
|
public class TruncatedNormalTests |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Can create a truncated normal without bounds.
|
||||
|
/// </summary>
|
||||
|
[TestCase(0.0, 0.0)] |
||||
|
[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 CanCreateWithoutBounds(double mean, double stdDev) |
||||
|
{ |
||||
|
var truncatedNormal = new TruncatedNormal(mean, stdDev); |
||||
|
Assert.IsTrue(double.IsNegativeInfinity(truncatedNormal.Minimum)); |
||||
|
Assert.IsTrue(double.IsPositiveInfinity(truncatedNormal.Maximum)); |
||||
|
Assert.AreEqual(mean, truncatedNormal.Mean); |
||||
|
Assert.AreEqual(stdDev, truncatedNormal.StdDev); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Constructor fails with negative stdDev or incorrectly ordered bounds.
|
||||
|
/// </summary>
|
||||
|
/// <param name="mean"></param>
|
||||
|
/// <param name="stdDev"></param>
|
||||
|
/// <param name="lower"></param>
|
||||
|
/// <param name="upper"></param>
|
||||
|
[TestCase(0.0, -1.0,-10d, 10d)] |
||||
|
[TestCase(0.0, 1.0, 10d, 9d)] |
||||
|
public void TruncatedNormalCreateFailsWithBadParameters(double mean, double stdDev, double lower, double upper) |
||||
|
{ |
||||
|
Assert.That(() => new TruncatedNormal(mean, stdDev, lower, upper), Throws.ArgumentException); |
||||
|
} |
||||
|
|
||||
|
[TestCase(0.0, 1.0, double.NegativeInfinity, double.PositiveInfinity)] |
||||
|
[TestCase(0.0, 1.0, -5.0, 5.0)] |
||||
|
[TestCase(-6.0, 1.0, -5.0, 5.0)] |
||||
|
[TestCase(8.0, 1.0, -5.0, 5.0)] |
||||
|
[TestCase(15, 20.0, -20.0, 0.0)] |
||||
|
public void ValidateMode(double mean, double stdDev, double lower, double upper) |
||||
|
{ |
||||
|
double mode; |
||||
|
if(mean < lower) { |
||||
|
mode = lower; |
||||
|
} else if(mean <= upper) { |
||||
|
mode = mean; |
||||
|
} else { |
||||
|
mode = upper; |
||||
|
} |
||||
|
var truncatedNormal = new TruncatedNormal(mean, stdDev, lower, upper); |
||||
|
Assert.AreEqual(mode, truncatedNormal.Mode); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Validate cumulative distribution. Uses the same test cases as for the normal distribution
|
||||
|
/// as they should be equivalent.
|
||||
|
/// </summary>
|
||||
|
/// <param name="x">Input X value.</param>
|
||||
|
/// <param name="p">Expected value.</param>
|
||||
|
[TestCase(Double.NegativeInfinity, 0.0)] |
||||
|
[TestCase(-5.0, 0.00000028665157187919391167375233287464535385442301361187883)] |
||||
|
[TestCase(-2.0, 0.0002326290790355250363499258867279847735487493358890356)] |
||||
|
[TestCase(-0.0, 0.0062096653257761351669781045741922211278977469230927036)] |
||||
|
[TestCase(0.0, .0062096653257761351669781045741922211278977469230927036)] |
||||
|
[TestCase(4.0, .30853753872598689636229538939166226011639782444542207)] |
||||
|
[TestCase(5.0, .5)] |
||||
|
[TestCase(6.0, .69146246127401310363770461060833773988360217555457859)] |
||||
|
[TestCase(10.0, 0.9937903346742238648330218954258077788721022530769078)] |
||||
|
[TestCase(Double.PositiveInfinity, 1.0)] |
||||
|
public void ValidateCumulativeNoBounds(double x, double p) |
||||
|
{ |
||||
|
var truncatedNormal = new TruncatedNormal(5.0, 2.0); |
||||
|
AssertHelpers.AlmostEqualRelative(p, truncatedNormal.CumulativeDistribution(x), 14); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Validate inverse cumulative distribution. Uses the same test cases as for the normal distribution
|
||||
|
/// as they should be equivalent.
|
||||
|
/// </summary>
|
||||
|
/// <param name="x">Expected value</param>
|
||||
|
/// <param name="p">Input quantile</param>
|
||||
|
[TestCase(Double.NegativeInfinity, 0.0)] |
||||
|
[TestCase(-5.0, 0.00000028665157187919391167375233287464535385442301361187883)] |
||||
|
[TestCase(-2.0, 0.0002326290790355250363499258867279847735487493358890356)] |
||||
|
[TestCase(-0.0, 0.0062096653257761351669781045741922211278977469230927036)] |
||||
|
[TestCase(0.0, 0.0062096653257761351669781045741922211278977469230927036)] |
||||
|
[TestCase(4.0, .30853753872598689636229538939166226011639782444542207)] |
||||
|
[TestCase(5.0, .5)] |
||||
|
[TestCase(6.0, .69146246127401310363770461060833773988360217555457859)] |
||||
|
[TestCase(10.0, 0.9937903346742238648330218954258077788721022530769078)] |
||||
|
[TestCase(Double.PositiveInfinity, 1.0)] |
||||
|
public void ValidateInverseCumulativeNoBounds(double x, double p) |
||||
|
{ |
||||
|
var truncatedNormal = new TruncatedNormal(5.0, 2.0); |
||||
|
AssertHelpers.AlmostEqualRelative(x, truncatedNormal.InverseCumulativeDistribution(p), 14); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Validate density when no bounds are specified. Uses same
|
||||
|
/// test cases as the Normal distribution as should be equivalent in this case.
|
||||
|
/// </summary>
|
||||
|
/// <param name="mean">Mean value.</param>
|
||||
|
/// <param name="sdev">Standard deviation value.</param>
|
||||
|
[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 ValidateDensityNoBounds(double mean, double sdev) |
||||
|
{ |
||||
|
var n = new TruncatedNormal(mean, sdev); |
||||
|
for (var i = 0; i < 11; i++) { |
||||
|
var x = i - 5.0; |
||||
|
var d = (mean - x) / sdev; |
||||
|
var pdf = Math.Exp(-0.5 * d * d) / (sdev * Constants.Sqrt2Pi); |
||||
|
AssertHelpers.AlmostEqualRelative(pdf, n.Density(x), 14); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Validate density when only one bound is are specified.
|
||||
|
/// </summary>
|
||||
|
/// <param name="mean">Mean value.</param>
|
||||
|
/// <param name="sdev">Standard deviation value.</param>
|
||||
|
[TestCase(10.0, 0.1, -5.0)] |
||||
|
[TestCase(-5.0, 1.0, 3.0)] |
||||
|
[TestCase(0.0, 10.0, -10.0)] |
||||
|
[TestCase(10.0, 100.0, 15.0)] |
||||
|
[TestCase(-5.0, Double.PositiveInfinity, -5.0)] |
||||
|
public void ValidateDensitySemiFinite(double mean, double sdev, double lowerBound) |
||||
|
{ |
||||
|
var truncatedNormal = new TruncatedNormal(mean, sdev, lowerBound); |
||||
|
var normal = new Normal(mean, sdev); |
||||
|
for (var i = 0; i < 11; i++) |
||||
|
{ |
||||
|
var x = i - 5.0; |
||||
|
double density; |
||||
|
if(x < lowerBound) |
||||
|
{ |
||||
|
density = 0d; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var d = (mean - x) / sdev; |
||||
|
var pdf = Math.Exp(-0.5 * d * d) / (sdev * Constants.Sqrt2Pi); |
||||
|
density = pdf / (1.0 - normal.CumulativeDistribution(lowerBound)); |
||||
|
} |
||||
|
AssertHelpers.AlmostEqualRelative(density, truncatedNormal.Density(x), 14); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Validate density when both bounds are specified.
|
||||
|
/// </summary>
|
||||
|
/// <param name="mean">Mean value.</param>
|
||||
|
/// <param name="sdev">Standard deviation value.</param>
|
||||
|
[TestCase(10.0, 0.1, -5.0, 5.0)] |
||||
|
[TestCase(-5.0, 1.0, double.NegativeInfinity, -5.0)] |
||||
|
[TestCase(0.0, 10.0, -10.0, 15.0)] |
||||
|
[TestCase(10.0, 100.0, 15.0, 100.0)] |
||||
|
[TestCase(-5.0, Double.PositiveInfinity, -5.0, 0.0)] |
||||
|
public void ValidateDensityFinite(double mean, double sdev, double lowerBound, double upperBound) |
||||
|
{ |
||||
|
var truncatedNormal = new TruncatedNormal(mean, sdev, lowerBound, upperBound); |
||||
|
var normal = new Normal(mean, sdev); |
||||
|
for (var i = 0; i < 11; i++) |
||||
|
{ |
||||
|
var x = i - 5.0; |
||||
|
double density; |
||||
|
if (x < lowerBound || upperBound < x) |
||||
|
{ |
||||
|
density = 0d; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var d = (mean - x) / sdev; |
||||
|
var pdf = Math.Exp(-0.5 * d * d) / (sdev * Constants.Sqrt2Pi); |
||||
|
density = pdf / (normal.CumulativeDistribution(upperBound) - normal.CumulativeDistribution(lowerBound)); |
||||
|
} |
||||
|
AssertHelpers.AlmostEqualRelative(density, truncatedNormal.Density(x), 14); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Validate density log when no bounds are specified. Uses same
|
||||
|
/// test cases as the Normal distribution as should be equivalent in this case.
|
||||
|
/// </summary>
|
||||
|
/// <param name="mean">Mean value.</param>
|
||||
|
/// <param name="sdev">Standard deviation value.</param>
|
||||
|
[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 ValidateDensityLnNoBounds(double mean, double sdev) |
||||
|
{ |
||||
|
var n = new TruncatedNormal(mean, sdev); |
||||
|
for (var i = 0; i < 11; i++) |
||||
|
{ |
||||
|
var x = i - 5.0; |
||||
|
var d = (mean - x) / sdev; |
||||
|
var pdfln = (-0.5 * (d * d)) - Math.Log(sdev) - Constants.LogSqrt2Pi; |
||||
|
AssertHelpers.AlmostEqualRelative(pdfln, n.DensityLn(x), 14); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Test] |
||||
|
public void CanSample() |
||||
|
{ |
||||
|
var truncatedNormal = new TruncatedNormal(5.0, 2.0, -10, 10.0); |
||||
|
truncatedNormal.Sample(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Can sample sequence.
|
||||
|
/// </summary>
|
||||
|
[Test] |
||||
|
public void CanSampleSequence() |
||||
|
{ |
||||
|
var truncatedNormal = new TruncatedNormal(5.0, 2.0, -10, 10.0); |
||||
|
var ied = truncatedNormal.Samples(); |
||||
|
GC.KeepAlive(ied.Take(5).ToArray()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue