Browse Source

BUG: Fix Hypergeometric CDF semantics, clarify distribution parameters

v2
Christoph Ruegg 13 years ago
parent
commit
e2d771946b
  1. 8
      src/Examples/DiscreteDistributions/HypergeometricDistribution.cs
  2. 242
      src/Numerics/Distributions/Discrete/Hypergeometric.cs
  3. 170
      src/UnitTests/DistributionTests/Discrete/HypergeometricTests.cs

8
src/Examples/DiscreteDistributions/HypergeometricDistribution.cs

@ -65,7 +65,7 @@ namespace Examples.DiscreteDistributionsExamples
{ {
// 1. Initialize the new instance of the Hypergeometric distribution class with parameters PopulationSize = 10, M = 2, N = 8 // 1. Initialize the new instance of the Hypergeometric distribution class with parameters PopulationSize = 10, M = 2, N = 8
var hypergeometric = new Hypergeometric(30, 15, 10); var hypergeometric = new Hypergeometric(30, 15, 10);
Console.WriteLine(@"1. Initialize the new instance of the Hypergeometric distribution class with parameters PopulationSize = {0}, M = {1}, N = {2}", hypergeometric.PopulationSize, hypergeometric.M, hypergeometric.N); Console.WriteLine(@"1. Initialize the new instance of the Hypergeometric distribution class with parameters Population = {0}, Success = {1}, Draws = {2}", hypergeometric.Population, hypergeometric.Success, hypergeometric.Draws);
Console.WriteLine(); Console.WriteLine();
// 2. Distributuion properties: // 2. Distributuion properties:
@ -125,9 +125,9 @@ namespace Examples.DiscreteDistributionsExamples
// 5. Generate 100000 samples of the Hypergeometric(52, 13, 5) distribution and display histogram // 5. Generate 100000 samples of the Hypergeometric(52, 13, 5) distribution and display histogram
Console.WriteLine(@"5. Generate 100000 samples of the Hypergeometric(52, 13, 5) distribution and display histogram"); Console.WriteLine(@"5. Generate 100000 samples of the Hypergeometric(52, 13, 5) distribution and display histogram");
hypergeometric.PopulationSize = 52; hypergeometric.Population = 52;
hypergeometric.M = 13; hypergeometric.Success = 13;
hypergeometric.N = 5; hypergeometric.Draws = 5;
for (var i = 0; i < data.Length; i++) for (var i = 0; i < data.Length; i++)
{ {
data[i] = hypergeometric.Sample(); data[i] = hypergeometric.Sample();

242
src/Numerics/Distributions/Discrete/Hypergeometric.cs

@ -4,7 +4,7 @@
// http://github.com/mathnet/mathnet-numerics // http://github.com/mathnet/mathnet-numerics
// http://mathnetnumerics.codeplex.com // http://mathnetnumerics.codeplex.com
// //
// Copyright (c) 2009-2010 Math.NET // Copyright (c) 2009-2013 Math.NET
// //
// Permission is hereby granted, free of charge, to any person // Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation // obtaining a copy of this software and associated documentation
@ -28,11 +28,12 @@
// OTHER DEALINGS IN THE SOFTWARE. // OTHER DEALINGS IN THE SOFTWARE.
// </copyright> // </copyright>
using MathNet.Numerics.Properties;
using System.Collections.Generic;
namespace MathNet.Numerics.Distributions namespace MathNet.Numerics.Distributions
{ {
using System; using System;
using System.Collections.Generic;
using Properties;
/// <summary> /// <summary>
/// This class implements functionality for the Hypergeometric distribution. This distribution is /// This class implements functionality for the Hypergeometric distribution. This distribution is
@ -50,19 +51,19 @@ namespace MathNet.Numerics.Distributions
public class Hypergeometric : IDiscreteDistribution public class Hypergeometric : IDiscreteDistribution
{ {
/// <summary> /// <summary>
/// The size of the population. /// The size of the population (N).
/// </summary> /// </summary>
int _populationSize; int _population;
/// <summary> /// <summary>
/// The m parameter of the distribution. /// The number successes within the population (K, M).
/// </summary> /// </summary>
int _m; int _success;
/// <summary> /// <summary>
/// The n parameter (number to draw) of the distribution. /// The number of draws without replacement (n).
/// </summary> /// </summary>
int _n; int _draws;
/// <summary> /// <summary>
/// The distribution's random number generator. /// The distribution's random number generator.
@ -72,61 +73,61 @@ namespace MathNet.Numerics.Distributions
/// <summary> /// <summary>
/// Initializes a new instance of the Hypergeometric class. /// Initializes a new instance of the Hypergeometric class.
/// </summary> /// </summary>
/// <param name="populationSize">The population size.</param> /// <param name="population">The size of the population (N).</param>
/// <param name="m">The m parameter of the distribution.</param> /// <param name="success">The number successes within the population (K, M).</param>
/// <param name="n">The n parameter of the distribution.</param> /// <param name="draws">The number of draws without replacement (n).</param>
public Hypergeometric(int populationSize, int m, int n) public Hypergeometric(int population, int success, int draws)
{ {
_random = new Random(); _random = new Random();
SetParameters(populationSize, m, n); SetParameters(population, success, draws);
} }
/// <summary> /// <summary>
/// Initializes a new instance of the Hypergeometric class. /// Initializes a new instance of the Hypergeometric class.
/// </summary> /// </summary>
/// <param name="populationSize">The population size.</param> /// <param name="population">The size of the population (N).</param>
/// <param name="m">The m parameter of the distribution.</param> /// <param name="success">The number successes within the population (K, M).</param>
/// <param name="n">The n parameter of the distribution.</param> /// <param name="draws">The number of draws without replacement (n).</param>
/// <param name="randomSource">The random number generator which is used to draw random samples.</param> /// <param name="randomSource">The random number generator which is used to draw random samples.</param>
public Hypergeometric(int populationSize, int m, int n, Random randomSource) public Hypergeometric(int population, int success, int draws, Random randomSource)
{ {
_random = randomSource ?? new Random(); _random = randomSource ?? new Random();
SetParameters(populationSize, m, n); SetParameters(population, success, draws);
} }
/// <summary> /// <summary>
/// Sets the parameters of the distribution after checking their validity. /// Sets the parameters of the distribution after checking their validity.
/// </summary> /// </summary>
/// <param name="total">The Total parameter of the distribution.</param> /// <param name="population">The size of the population (N).</param>
/// <param name="m">The m parameter of the distribution.</param> /// <param name="success">The number successes within the population (K, M).</param>
/// <param name="n">The n parameter of the distribution.</param> /// <param name="draws">The number of draws without replacement (n).</param>
void SetParameters(int total, int m, int n) void SetParameters(int population, int success, int draws)
{ {
if (Control.CheckDistributionParameters && !IsValidParameterSet(total, m, n)) if (Control.CheckDistributionParameters && !IsValidParameterSet(population, success, draws))
{ {
throw new ArgumentOutOfRangeException(Resources.InvalidDistributionParameters); throw new ArgumentOutOfRangeException(Resources.InvalidDistributionParameters);
} }
_populationSize = total; _population = population;
_m = m; _success = success;
_n = n; _draws = draws;
} }
/// <summary> /// <summary>
/// Checks whether the parameters of the distribution are valid. /// Checks whether the parameters of the distribution are valid.
/// </summary> /// </summary>
/// <param name="total">The Total parameter of the distribution.</param> /// <param name="population">The size of the population (N).</param>
/// <param name="m">The m parameter of the distribution.</param> /// <param name="success">The number successes within the population (K, M).</param>
/// <param name="n">The n parameter of the distribution.</param> /// <param name="draws">The number of draws without replacement (n).</param>
/// <returns><c>true</c> when the parameters are valid, <c>false</c> otherwise.</returns> /// <returns><c>true</c> when the parameters are valid, <c>false</c> otherwise.</returns>
static bool IsValidParameterSet(int total, int m, int n) static bool IsValidParameterSet(int population, int success, int draws)
{ {
if (total < 0 || m < 0 || n < 0) if (population < 0 || success < 0 || draws < 0)
{ {
return false; return false;
} }
if (m > total || n > total) if (success > population || draws > population)
{ {
return false; return false;
} }
@ -135,30 +136,60 @@ namespace MathNet.Numerics.Distributions
} }
/// <summary> /// <summary>
/// Gets or sets the population size. /// Gets or sets the size of the population (N).
/// </summary>
public int Population
{
get { return _population; }
set { SetParameters(value, _success, _draws); }
}
/// <summary>
/// Gets or sets the number of draws without replacement (n).
/// </summary>
public int Draws
{
get { return _draws; }
set { SetParameters(_population, value, _draws); }
}
/// <summary>
/// Gets or sets the number successes within the population (K, M).
/// </summary>
public int Success
{
get { return _success; }
set { SetParameters(_population, _success, value); }
}
/// <summary>
/// Gets or sets the size of the population (N).
/// </summary> /// </summary>
[Obsolete("Use Population instead. Scheduled for removal in v3.0.")]
public int PopulationSize public int PopulationSize
{ {
get { return _populationSize; } get { return _population; }
set { SetParameters(value, _m, _n); } set { SetParameters(value, _success, _draws); }
} }
/// <summary> /// <summary>
/// Gets or sets the n parameter of the distribution. /// Gets or sets the number of draws without replacement (n).
/// </summary> /// </summary>
[Obsolete("Use Draws instead. Scheduled for removal in v3.0.")]
public int N public int N
{ {
get { return _n; } get { return _draws; }
set { SetParameters(_populationSize, value, _n); } set { SetParameters(_population, value, _draws); }
} }
/// <summary> /// <summary>
/// Gets or sets the m parameter of the distribution. /// Gets or sets the number successes within the population (K, M).
/// </summary> /// </summary>
[Obsolete("Use Success instead. Scheduled for removal in v3.0.")]
public int M public int M
{ {
get { return _m; } get { return _success; }
set { SetParameters(_populationSize, _m, value); } set { SetParameters(_population, _success, value); }
} }
/// <summary> /// <summary>
@ -169,11 +200,9 @@ namespace MathNet.Numerics.Distributions
/// </returns> /// </returns>
public override string ToString() public override string ToString()
{ {
return "Hypergeometric(N = " + _populationSize + ", m = " + _m + ", n = " + _n + ")"; return "Hypergeometric(N = " + _population + ", M = " + _success + ", n = " + _draws + ")";
} }
#region IDistribution Members
/// <summary> /// <summary>
/// Gets or sets the random number generator which is used to draw random samples. /// Gets or sets the random number generator which is used to draw random samples.
/// </summary> /// </summary>
@ -196,7 +225,7 @@ namespace MathNet.Numerics.Distributions
/// </summary> /// </summary>
public double Mean public double Mean
{ {
get { return (double) _m*_n/_populationSize; } get { return (double) _success*_draws/_population; }
} }
/// <summary> /// <summary>
@ -204,7 +233,7 @@ namespace MathNet.Numerics.Distributions
/// </summary> /// </summary>
public double Variance public double Variance
{ {
get { return _n*_m*(_populationSize - _n)*(_populationSize - _m)/(_populationSize*_populationSize*(_populationSize - 1.0)); } get { return _draws*_success*(_population - _draws)*(_population - _success)/(_population*_population*(_population - 1.0)); }
} }
/// <summary> /// <summary>
@ -228,48 +257,15 @@ namespace MathNet.Numerics.Distributions
/// </summary> /// </summary>
public double Skewness public double Skewness
{ {
get { return (Math.Sqrt(_populationSize - 1.0)*(_populationSize - (2*_n))*(_populationSize - (2*_m)))/(Math.Sqrt(_n*_m*(_populationSize - _m)*(_populationSize - _n))*(_populationSize - 2.0)); } get { return (Math.Sqrt(_population - 1.0)*(_population - (2*_draws))*(_population - (2*_success)))/(Math.Sqrt(_draws*_success*(_population - _success)*(_population - _draws))*(_population - 2.0)); }
}
/// <summary>
/// Computes the cumulative distribution function of the distribution.
/// </summary>
/// <param name="x">The location at which to compute the cumulative density.</param>
/// <returns>the cumulative density at <paramref name="x"/>.</returns>
public double CumulativeDistribution(double x)
{
int alpha = Minimum;
int beta = Maximum;
if (x <= alpha)
{
return 0.0;
}
if (x > beta)
{
return 1.0;
}
var sum = 0.0;
var k = (int) Math.Ceiling(x - alpha) - 1;
for (var i = alpha; i <= alpha + k; i++)
{
sum += SpecialFunctions.Binomial(_m, i)*SpecialFunctions.Binomial(_populationSize - _m, _n - i);
}
return sum/SpecialFunctions.Binomial(_populationSize, _n);
} }
#endregion
#region IDiscreteDistribution Members
/// <summary> /// <summary>
/// Gets the mode of the distribution. /// Gets the mode of the distribution.
/// </summary> /// </summary>
public int Mode public int Mode
{ {
get { return (_n + 1)*(_m + 1)/(_populationSize + 2); } get { return (_draws + 1)*(_success + 1)/(_population + 2); }
} }
/// <summary> /// <summary>
@ -285,7 +281,7 @@ namespace MathNet.Numerics.Distributions
/// </summary> /// </summary>
public int Minimum public int Minimum
{ {
get { return Math.Max(0, _n + _m - _populationSize); } get { return Math.Max(0, _draws + _success - _population); }
} }
/// <summary> /// <summary>
@ -293,11 +289,11 @@ namespace MathNet.Numerics.Distributions
/// </summary> /// </summary>
public int Maximum public int Maximum
{ {
get { return Math.Min(_m, _n); } get { return Math.Min(_success, _draws); }
} }
/// <summary> /// <summary>
/// Computes values of the probability mass function. /// Computes values of the probability mass function (PMF), i.e. P(X = x).
/// </summary> /// </summary>
/// <param name="k">The location in the domain where we want to evaluate the probability mass function.</param> /// <param name="k">The location in the domain where we want to evaluate the probability mass function.</param>
/// <returns> /// <returns>
@ -305,11 +301,11 @@ namespace MathNet.Numerics.Distributions
/// </returns> /// </returns>
public double Probability(int k) public double Probability(int k)
{ {
return SpecialFunctions.Binomial(_m, k)*SpecialFunctions.Binomial(_populationSize - _m, _n - k)/SpecialFunctions.Binomial(_populationSize, _n); return SpecialFunctions.Binomial(_success, k)*SpecialFunctions.Binomial(_population - _success, _draws - k)/SpecialFunctions.Binomial(_population, _draws);
} }
/// <summary> /// <summary>
/// Computes values of the log probability mass function. /// Computes values of the log probability mass function (lnPMF), i.e. ln(P(X = x)).
/// </summary> /// </summary>
/// <param name="k">The location in the domain where we want to evaluate the log probability mass function.</param> /// <param name="k">The location in the domain where we want to evaluate the log probability mass function.</param>
/// <returns> /// <returns>
@ -320,33 +316,57 @@ namespace MathNet.Numerics.Distributions
return Math.Log(Probability(k)); return Math.Log(Probability(k));
} }
#endregion /// <summary>
/// Computes the cumulative distribution function (CDF) of the distribution, i.e. P(X &lt;= x).
/// </summary>
/// <param name="x">The location at which to compute the cumulative density.</param>
/// <returns>the cumulative density at <paramref name="x"/>.</returns>
public double CumulativeDistribution(double x)
{
if (x < Minimum)
{
return 0.0;
}
if (x >= Maximum)
{
return 1.0;
}
var k = (int) Math.Floor(x);
var denominatorLn = SpecialFunctions.BinomialLn(_population, _draws);
var sum = 0.0;
for (var i = 0; i <= k; i++)
{
sum += Math.Exp(SpecialFunctions.BinomialLn(_success, i) + SpecialFunctions.BinomialLn(_population - _success, _draws - i) - denominatorLn);
}
return sum;
}
/// <summary> /// <summary>
/// Generates a sample from the Hypergeometric distribution without doing parameter checking. /// Generates a sample from the Hypergeometric distribution without doing parameter checking.
/// </summary> /// </summary>
/// <param name="rnd">The random number generator to use.</param> /// <param name="rnd">The random number generator to use.</param>
/// <param name="size">The Total parameter of the distribution.</param> /// <param name="population">The size of the population (N).</param>
/// <param name="m">The m parameter of the distribution.</param> /// <param name="success">The number successes within the population (K, M).</param>
/// <param name="n">The n parameter of the distribution.</param> /// <param name="draws">The n parameter of the distribution.</param>
/// <returns>a random number from the Hypergeometric distribution.</returns> /// <returns>a random number from the Hypergeometric distribution.</returns>
internal static int SampleUnchecked(Random rnd, int size, int m, int n) internal static int SampleUnchecked(Random rnd, int population, int success, int draws)
{ {
var x = 0; var x = 0;
do do
{ {
var p = (double) m/size; var p = (double) success/population;
var r = rnd.NextDouble(); var r = rnd.NextDouble();
if (r < p) if (r < p)
{ {
x++; x++;
m--; success--;
} }
size--; population--;
n--; draws--;
} while (0 < n); } while (0 < draws);
return x; return x;
} }
@ -357,7 +377,7 @@ namespace MathNet.Numerics.Distributions
/// <returns>The number of successes in n trials.</returns> /// <returns>The number of successes in n trials.</returns>
public int Sample() public int Sample()
{ {
return SampleUnchecked(RandomSource, _populationSize, _m, _n); return SampleUnchecked(RandomSource, _population, _success, _draws);
} }
/// <summary> /// <summary>
@ -368,7 +388,7 @@ namespace MathNet.Numerics.Distributions
{ {
while (true) while (true)
{ {
yield return SampleUnchecked(RandomSource, _populationSize, _m, _n); yield return SampleUnchecked(RandomSource, _population, _success, _draws);
} }
} }
@ -376,36 +396,36 @@ namespace MathNet.Numerics.Distributions
/// Samples a random variable. /// Samples a random variable.
/// </summary> /// </summary>
/// <param name="rnd">The random number generator to use.</param> /// <param name="rnd">The random number generator to use.</param>
/// <param name="populationSize">The population size.</param> /// <param name="population">The size of the population (N).</param>
/// <param name="m">The m parameter of the distribution.</param> /// <param name="success">The number successes within the population (K, M).</param>
/// <param name="n">The n parameter of the distribution.</param> /// <param name="draws">The number of draws without replacement (n).</param>
public static int Sample(Random rnd, int populationSize, int m, int n) public static int Sample(Random rnd, int population, int success, int draws)
{ {
if (Control.CheckDistributionParameters && !IsValidParameterSet(populationSize, m, n)) if (Control.CheckDistributionParameters && !IsValidParameterSet(population, success, draws))
{ {
throw new ArgumentOutOfRangeException(Resources.InvalidDistributionParameters); throw new ArgumentOutOfRangeException(Resources.InvalidDistributionParameters);
} }
return SampleUnchecked(rnd, populationSize, m, n); return SampleUnchecked(rnd, population, success, draws);
} }
/// <summary> /// <summary>
/// Samples a sequence of this random variable. /// Samples a sequence of this random variable.
/// </summary> /// </summary>
/// <param name="rnd">The random number generator to use.</param> /// <param name="rnd">The random number generator to use.</param>
/// <param name="populationSize">The population size.</param> /// <param name="population">The size of the population (N).</param>
/// <param name="m">The m parameter of the distribution.</param> /// <param name="success">The number successes within the population (K, M).</param>
/// <param name="n">The n parameter of the distribution.</param> /// <param name="draws">The number of draws without replacement (n).</param>
public static IEnumerable<int> Samples(Random rnd, int populationSize, int m, int n) public static IEnumerable<int> Samples(Random rnd, int population, int success, int draws)
{ {
if (Control.CheckDistributionParameters && !IsValidParameterSet(populationSize, m, n)) if (Control.CheckDistributionParameters && !IsValidParameterSet(population, success, draws))
{ {
throw new ArgumentOutOfRangeException(Resources.InvalidDistributionParameters); throw new ArgumentOutOfRangeException(Resources.InvalidDistributionParameters);
} }
while (true) while (true)
{ {
yield return SampleUnchecked(rnd, populationSize, m, n); yield return SampleUnchecked(rnd, population, success, draws);
} }
} }
} }

170
src/UnitTests/DistributionTests/Discrete/HypergeometricTests.cs

@ -49,36 +49,36 @@ namespace MathNet.Numerics.UnitTests.DistributionTests.Discrete
/// <summary> /// <summary>
/// Can create Hypergeometric. /// Can create Hypergeometric.
/// </summary> /// </summary>
/// <param name="size">Population size.</param> /// <param name="population">Population size.</param>
/// <param name="m">M parameter.</param> /// <param name="success">M parameter.</param>
/// <param name="n">N parameter.</param> /// <param name="draws">N parameter.</param>
[TestCase(0, 0, 0)] [TestCase(0, 0, 0)]
[TestCase(1, 1, 1)] [TestCase(1, 1, 1)]
[TestCase(2, 1, 1)] [TestCase(2, 1, 1)]
[TestCase(2, 2, 2)] [TestCase(2, 2, 2)]
[TestCase(10, 1, 1)] [TestCase(10, 1, 1)]
[TestCase(10, 5, 3)] [TestCase(10, 5, 3)]
public void CanCreateHypergeometric(int size, int m, int n) public void CanCreateHypergeometric(int population, int success, int draws)
{ {
var d = new Hypergeometric(size, m, n); var d = new Hypergeometric(population, success, draws);
Assert.AreEqual(size, d.PopulationSize); Assert.AreEqual(population, d.Population);
Assert.AreEqual(m, d.M); Assert.AreEqual(success, d.Success);
Assert.AreEqual(n, d.N); Assert.AreEqual(draws, d.Draws);
} }
/// <summary> /// <summary>
/// Hypergeometric create fails with bad parameters. /// Hypergeometric create fails with bad parameters.
/// </summary> /// </summary>
/// <param name="size">Population size.</param> /// <param name="population">Population size.</param>
/// <param name="m">M parameter.</param> /// <param name="success">M parameter.</param>
/// <param name="n">N parameter.</param> /// <param name="n">N parameter.</param>
[TestCase(2, 3, 2)] [TestCase(2, 3, 2)]
[TestCase(10, 5, 20)] [TestCase(10, 5, 20)]
[TestCase(-2, 1, 1)] [TestCase(-2, 1, 1)]
[TestCase(0, 1, 1)] [TestCase(0, 1, 1)]
public void HypergeometricCreateFailsWithBadParameters(int size, int m, int n) public void HypergeometricCreateFailsWithBadParameters(int population, int success, int n)
{ {
Assert.Throws<ArgumentOutOfRangeException>(() => new Hypergeometric(size, m, n)); Assert.Throws<ArgumentOutOfRangeException>(() => new Hypergeometric(population, success, n));
} }
/// <summary> /// <summary>
@ -88,90 +88,90 @@ namespace MathNet.Numerics.UnitTests.DistributionTests.Discrete
public void ValidateToString() public void ValidateToString()
{ {
var d = new Hypergeometric(10, 1, 1); var d = new Hypergeometric(10, 1, 1);
Assert.AreEqual("Hypergeometric(N = 10, m = 1, n = 1)", d.ToString()); Assert.AreEqual("Hypergeometric(N = 10, M = 1, n = 1)", d.ToString());
} }
/// <summary> /// <summary>
/// Can set size. /// Can set size.
/// </summary> /// </summary>
/// <param name="size">Population size.</param> /// <param name="population">Population size.</param>
[TestCase(5)] [TestCase(5)]
[TestCase(10)] [TestCase(10)]
[TestCase(20)] [TestCase(20)]
public void CanSetSize(int size) public void CanSetPopulation(int population)
{ {
new Hypergeometric(10, 1, 1) new Hypergeometric(10, 1, 1)
{ {
PopulationSize = size Population = population
}; };
} }
/// <summary> /// <summary>
/// Set size fails with bad values. /// Set size fails with bad values.
/// </summary> /// </summary>
/// <param name="size">Population size.</param> /// <param name="population">Population size.</param>
[TestCase(-1)] [TestCase(-1)]
[TestCase(0)] [TestCase(0)]
public void SetSizeFails(int size) public void SetPopulationFails(int population)
{ {
var d = new Hypergeometric(10, 1, 1); var d = new Hypergeometric(10, 1, 1);
Assert.Throws<ArgumentOutOfRangeException>(() => d.PopulationSize = size); Assert.Throws<ArgumentOutOfRangeException>(() => d.Population = population);
} }
/// <summary> /// <summary>
/// Can set M. /// Can set M.
/// </summary> /// </summary>
/// <param name="m">M parameter.</param> /// <param name="success">M parameter.</param>
[TestCase(0)] [TestCase(0)]
[TestCase(1)] [TestCase(1)]
[TestCase(2)] [TestCase(2)]
[TestCase(5)] [TestCase(5)]
public void CanSetm(int m) public void CanSetSuccess(int success)
{ {
new Hypergeometric(10, 1, 1) new Hypergeometric(10, 1, 1)
{ {
M = m Success = success
}; };
} }
/// <summary> /// <summary>
/// Set M fails with bad values. /// Set M fails with bad values.
/// </summary> /// </summary>
/// <param name="m">M parameter.</param> /// <param name="success">M parameter.</param>
[TestCase(11)] [TestCase(11)]
[TestCase(-1)] [TestCase(-1)]
public void SetmFails(int m) public void SetSuccessFails(int success)
{ {
var d = new Hypergeometric(10, 1, 1); var d = new Hypergeometric(10, 1, 1);
Assert.Throws<ArgumentOutOfRangeException>(() => d.M = m); Assert.Throws<ArgumentOutOfRangeException>(() => d.Success = success);
} }
/// <summary> /// <summary>
/// Can set N. /// Can set N.
/// </summary> /// </summary>
/// <param name="n">N parameter.</param> /// <param name="draws">N parameter.</param>
[TestCase(0)] [TestCase(0)]
[TestCase(1)] [TestCase(1)]
[TestCase(2)] [TestCase(2)]
[TestCase(5)] [TestCase(5)]
public void CanSetn(int n) public void CanSetDraws(int draws)
{ {
new Hypergeometric(10, 1, 1) new Hypergeometric(10, 1, 1)
{ {
N = n Draws = draws
}; };
} }
/// <summary> /// <summary>
/// Set N fails with bad values. /// Set N fails with bad values.
/// </summary> /// </summary>
/// <param name="n">N parameter.</param> /// <param name="draws">N parameter.</param>
[TestCase(11)] [TestCase(11)]
[TestCase(-1)] [TestCase(-1)]
public void SetnFails(int n) public void SetDrawsFails(int draws)
{ {
var d = new Hypergeometric(10, 1, 1); var d = new Hypergeometric(10, 1, 1);
Assert.Throws<ArgumentOutOfRangeException>(() => d.N = n); Assert.Throws<ArgumentOutOfRangeException>(() => d.Draws = draws);
} }
/// <summary> /// <summary>
@ -187,37 +187,37 @@ namespace MathNet.Numerics.UnitTests.DistributionTests.Discrete
/// <summary> /// <summary>
/// Validate skewness. /// Validate skewness.
/// </summary> /// </summary>
/// <param name="size">Population size.</param> /// <param name="population">Population size.</param>
/// <param name="m">M parameter.</param> /// <param name="success">M parameter.</param>
/// <param name="n">N parameter.</param> /// <param name="draws">N parameter.</param>
[TestCase(0, 0, 0)] [TestCase(0, 0, 0)]
[TestCase(1, 1, 1)] [TestCase(1, 1, 1)]
[TestCase(2, 1, 1)] [TestCase(2, 1, 1)]
[TestCase(2, 2, 2)] [TestCase(2, 2, 2)]
[TestCase(10, 1, 1)] [TestCase(10, 1, 1)]
[TestCase(10, 5, 3)] [TestCase(10, 5, 3)]
public void ValidateSkewness(int size, int m, int n) public void ValidateSkewness(int population, int success, int draws)
{ {
var d = new Hypergeometric(size, m, n); var d = new Hypergeometric(population, success, draws);
Assert.AreEqual((Math.Sqrt(size - 1.0) * (size - (2 * n)) * (size - (2 * m))) / (Math.Sqrt(n * m * (size - m) * (size - n)) * (size - 2.0)), d.Skewness); Assert.AreEqual((Math.Sqrt(population - 1.0)*(population - (2*draws))*(population - (2*success)))/(Math.Sqrt(draws*success*(population - success)*(population - draws))*(population - 2.0)), d.Skewness);
} }
/// <summary> /// <summary>
/// Validate mode. /// Validate mode.
/// </summary> /// </summary>
/// <param name="size">Population size.</param> /// <param name="population">Population size.</param>
/// <param name="m">M parameter.</param> /// <param name="success">M parameter.</param>
/// <param name="n">N parameter.</param> /// <param name="draws">N parameter.</param>
[TestCase(0, 0, 0)] [TestCase(0, 0, 0)]
[TestCase(1, 1, 1)] [TestCase(1, 1, 1)]
[TestCase(2, 1, 1)] [TestCase(2, 1, 1)]
[TestCase(2, 2, 2)] [TestCase(2, 2, 2)]
[TestCase(10, 1, 1)] [TestCase(10, 1, 1)]
[TestCase(10, 5, 3)] [TestCase(10, 5, 3)]
public void ValidateMode(int size, int m, int n) public void ValidateMode(int population, int success, int draws)
{ {
var d = new Hypergeometric(size, m, n); var d = new Hypergeometric(population, success, draws);
Assert.AreEqual((n + 1) * (m + 1) / (size + 2), d.Mode); Assert.AreEqual((draws + 1)*(success + 1)/(population + 2), d.Mode);
} }
/// <summary> /// <summary>
@ -233,45 +233,45 @@ namespace MathNet.Numerics.UnitTests.DistributionTests.Discrete
/// <summary> /// <summary>
/// Validate minimum. /// Validate minimum.
/// </summary> /// </summary>
/// <param name="size">Population size.</param> /// <param name="population">Population size.</param>
/// <param name="m">M parameter.</param> /// <param name="success">M parameter.</param>
/// <param name="n">N parameter.</param> /// <param name="draws">N parameter.</param>
[TestCase(0, 0, 0)] [TestCase(0, 0, 0)]
[TestCase(1, 1, 1)] [TestCase(1, 1, 1)]
[TestCase(2, 1, 1)] [TestCase(2, 1, 1)]
[TestCase(2, 2, 2)] [TestCase(2, 2, 2)]
[TestCase(10, 1, 1)] [TestCase(10, 1, 1)]
[TestCase(10, 5, 3)] [TestCase(10, 5, 3)]
public void ValidateMinimum(int size, int m, int n) public void ValidateMinimum(int population, int success, int draws)
{ {
var d = new Hypergeometric(size, m, n); var d = new Hypergeometric(population, success, draws);
Assert.AreEqual(Math.Max(0, n + m - size), d.Minimum); Assert.AreEqual(Math.Max(0, draws + success - population), d.Minimum);
} }
/// <summary> /// <summary>
/// Validate maximum. /// Validate maximum.
/// </summary> /// </summary>
/// <param name="size">Population size.</param> /// <param name="population">Population size.</param>
/// <param name="m">M parameter.</param> /// <param name="success">M parameter.</param>
/// <param name="n">N parameter.</param> /// <param name="draws">N parameter.</param>
[TestCase(0, 0, 0)] [TestCase(0, 0, 0)]
[TestCase(1, 1, 1)] [TestCase(1, 1, 1)]
[TestCase(2, 1, 1)] [TestCase(2, 1, 1)]
[TestCase(2, 2, 2)] [TestCase(2, 2, 2)]
[TestCase(10, 1, 1)] [TestCase(10, 1, 1)]
[TestCase(10, 5, 3)] [TestCase(10, 5, 3)]
public void ValidateMaximum(int size, int m, int n) public void ValidateMaximum(int population, int success, int draws)
{ {
var d = new Hypergeometric(size, m, n); var d = new Hypergeometric(population, success, draws);
Assert.AreEqual(Math.Min(m, n), d.Maximum); Assert.AreEqual(Math.Min(success, draws), d.Maximum);
} }
/// <summary> /// <summary>
/// Validate probability. /// Validate probability.
/// </summary> /// </summary>
/// <param name="size">Population size.</param> /// <param name="population">Population size.</param>
/// <param name="m">M parameter.</param> /// <param name="success">M parameter.</param>
/// <param name="n">N parameter.</param> /// <param name="draws">N parameter.</param>
/// <param name="x">Input X value.</param> /// <param name="x">Input X value.</param>
[TestCase(0, 0, 0, 0)] [TestCase(0, 0, 0, 0)]
[TestCase(1, 1, 1, 1)] [TestCase(1, 1, 1, 1)]
@ -282,18 +282,18 @@ namespace MathNet.Numerics.UnitTests.DistributionTests.Discrete
[TestCase(10, 1, 1, 1)] [TestCase(10, 1, 1, 1)]
[TestCase(10, 5, 3, 1)] [TestCase(10, 5, 3, 1)]
[TestCase(10, 5, 3, 3)] [TestCase(10, 5, 3, 3)]
public void ValidateProbability(int size, int m, int n, int x) public void ValidateProbability(int population, int success, int draws, int x)
{ {
var d = new Hypergeometric(size, m, n); var d = new Hypergeometric(population, success, draws);
Assert.AreEqual(SpecialFunctions.Binomial(m, x) * SpecialFunctions.Binomial(size - m, n - x) / SpecialFunctions.Binomial(size, n), d.Probability(x)); Assert.AreEqual(SpecialFunctions.Binomial(success, x)*SpecialFunctions.Binomial(population - success, draws - x)/SpecialFunctions.Binomial(population, draws), d.Probability(x));
} }
/// <summary> /// <summary>
/// Validate probability log. /// Validate probability log.
/// </summary> /// </summary>
/// <param name="size">Population size.</param> /// <param name="population">Population size.</param>
/// <param name="m">M parameter.</param> /// <param name="success">M parameter.</param>
/// <param name="n">N parameter.</param> /// <param name="draws">N parameter.</param>
/// <param name="x">Input X value.</param> /// <param name="x">Input X value.</param>
[TestCase(0, 0, 0, 0)] [TestCase(0, 0, 0, 0)]
[TestCase(1, 1, 1, 1)] [TestCase(1, 1, 1, 1)]
@ -304,9 +304,9 @@ namespace MathNet.Numerics.UnitTests.DistributionTests.Discrete
[TestCase(10, 1, 1, 1)] [TestCase(10, 1, 1, 1)]
[TestCase(10, 5, 3, 1)] [TestCase(10, 5, 3, 1)]
[TestCase(10, 5, 3, 3)] [TestCase(10, 5, 3, 3)]
public void ValidateProbabilityLn(int size, int m, int n, int x) public void ValidateProbabilityLn(int population, int success, int draws, int x)
{ {
var d = new Hypergeometric(size, m, n); var d = new Hypergeometric(population, success, draws);
Assert.AreEqual(Math.Log(d.Probability(x)), d.ProbabilityLn(x)); Assert.AreEqual(Math.Log(d.Probability(x)), d.ProbabilityLn(x));
} }
@ -334,9 +334,9 @@ namespace MathNet.Numerics.UnitTests.DistributionTests.Discrete
/// <summary> /// <summary>
/// Validate cumulative distribution. /// Validate cumulative distribution.
/// </summary> /// </summary>
/// <param name="size">Population size.</param> /// <param name="population">Population size.</param>
/// <param name="m">M parameter.</param> /// <param name="success">M parameter.</param>
/// <param name="n">N parameter.</param> /// <param name="draws">N parameter.</param>
/// <param name="x">Input X value.</param> /// <param name="x">Input X value.</param>
/// <param name="cdf">Expected value.</param> /// <param name="cdf">Expected value.</param>
[TestCase(0, 0, 0, 0.5, 1.0)] [TestCase(0, 0, 0, 0.5, 1.0)]
@ -347,11 +347,23 @@ namespace MathNet.Numerics.UnitTests.DistributionTests.Discrete
[TestCase(10, 1, 1, 0.3, 0.9)] [TestCase(10, 1, 1, 0.3, 0.9)]
[TestCase(10, 1, 1, 1.2, 1.0)] [TestCase(10, 1, 1, 1.2, 1.0)]
[TestCase(10, 5, 3, 1.1, 0.5)] [TestCase(10, 5, 3, 1.1, 0.5)]
[TestCase(10, 5, 3, 3.0, 0.916666666666667)] [TestCase(10, 5, 3, 2.0, 11.0/12.0)]
public void ValidateCumulativeDistribution(int size, int m, int n, double x, double cdf) [TestCase(10, 5, 3, 3.0, 1.0)]
[TestCase(10000, 2, 9800, 0.0, 199.0/499950.0)]
[TestCase(10000, 2, 9800, 0.5, 199.0/499950.0)]
[TestCase(10000, 2, 9800, 1.5, 19799.0/499950.0)]
public void ValidateCumulativeDistribution(int population, int success, int draws, double x, double cdf)
{
var d = new Hypergeometric(population, success, draws);
AssertHelpers.AlmostEqual(cdf, d.CumulativeDistribution(x), 10);
}
[Test]
public void CumulativeDistributionMustNotOverflow_CodePlexIssue5729()
{ {
var d = new Hypergeometric(size, m, n); var d = new Hypergeometric(10000, 2, 9800);
AssertHelpers.AlmostEqual(cdf, d.CumulativeDistribution(x), 14); Assert.That(d.CumulativeDistribution(0.0), Is.Not.NaN);
Assert.That(d.CumulativeDistribution(0.1), Is.Not.NaN);
} }
} }
} }

Loading…
Cancel
Save