// // Math.NET Numerics, part of the Math.NET Project // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // // Copyright (c) 2009-2020 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. // // // Andrew J. Willshire // using System; using System.Collections.Generic; using IStation.Numerics.Random; namespace IStation.Numerics.Distributions { /// /// Discrete Univariate Beta-Binomial distribution. /// The beta-binomial distribution is a family of discrete probability distributions on a finite support of non-negative integers arising /// when the probability of success in each of a fixed or known number of Bernoulli trials is either unknown or random. /// The beta-binomial distribution is the binomial distribution in which the probability of success at each of n trials is not fixed but randomly drawn from a beta distribution. /// It is frequently used in Bayesian statistics, empirical Bayes methods and classical statistics to capture overdispersion in binomial type distributed data. /// Wikipedia - Beta-Binomial distribution. /// public class BetaBinomial : IDiscreteDistribution { System.Random _random; readonly int _n; readonly double _a; readonly double _b; /// /// Initializes a new instance of the class. /// /// The number of Bernoulli trials n - n is a positive integer /// Shape parameter alpha of the Beta distribution. Range: a > 0. /// Shape parameter beta of the Beta distribution. Range: b > 0. public BetaBinomial(int n, double a, double b) { if (!IsValidParameterSet(n, a, b)) { throw new ArgumentException("Invalid parametrization for the distribution."); } _random = SystemRandomSource.Default; _n = n; _a = a; _b = b; } /// /// Initializes a new instance of the class. /// /// The number of Bernoulli trials n - n is a positive integer /// Shape parameter alpha of the Beta distribution. Range: a > 0. /// Shape parameter beta of the Beta distribution. Range: b > 0. /// The random number generator which is used to draw random samples. public BetaBinomial(int n, double a, double b, System.Random randomSource) { if (!IsValidParameterSet(n,a,b)) { throw new ArgumentException("Invalid parametrization for the distribution."); } _random = randomSource ?? SystemRandomSource.Default; _n = n; _a = a; _b = b; } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { return $"BetaBinomial(n = {_n}, a = {_a}, b = {_b})"; } /// /// Tests whether the provided values are valid parameters for this distribution. /// /// The number of Bernoulli trials n - n is a positive integer /// Shape parameter alpha of the Beta distribution. Range: a > 0. /// Shape parameter beta of the Beta distribution. Range: b > 0. public static bool IsValidParameterSet(int n, double a, double b) { return n >= 1.0 && a > 0.0 && b > 0.0; } /// /// Tests whether the provided values are valid parameters for this distribution. /// /// The number of Bernoulli trials n - n is a positive integer /// Shape parameter alpha of the Beta distribution. Range: a > 0. /// Shape parameter beta of the Beta distribution. Range: b > 0. /// The location in the domain where we want to evaluate the probability mass function. public static bool IsValidParameterSet(int n, double a, double b, int k) { return n >= 1.0 && a > 0.0 && b > 0.0 && k >=0 && k <=n; } public int N => _n; public double A => _a; public double B => _b; public System.Random RandomSource { get => _random; set => _random = value ?? SystemRandomSource.Default; } /// /// Gets the mean of the distribution. /// public double Mean => (_n * _a) / (_a + _b); /// /// Gets the variance of the distribution. /// public double Variance => (_n*_a*_b*(_a+_b+_n))/(Math.Pow((_a+_b),2) * (_a+_b+1)); /// /// Gets the standard deviation of the distribution. /// public double StdDev => Math.Sqrt((_n * _a * _b * (_a + _b + _n)) / (Math.Pow((_a + _b), 2) * (_a + _b + 1))); /// /// Gets the entropy of the distribution. /// double IUnivariateDistribution.Entropy => throw new NotSupportedException(); /// /// Gets the skewness of the distribution. /// public double Skewness => (_a + _b + 2 * _n) * (_b - _a) / (_a + _b + 2) * Math.Sqrt((1 + _a + _b) / (_n * _a * _b * (_n + _a + _b))); /// /// Gets the mode of the distribution /// int IDiscreteDistribution.Mode => throw new NotSupportedException(); /// /// Gets the median of the distribution. /// double IUnivariateDistribution.Median => throw new NotSupportedException(); /// /// Gets the smallest element in the domain of the distributions which can be represented by an integer. /// public int Minimum => 0; /// /// Gets the largest element in the domain of the distributions which can be represented by an integer. /// public int Maximum => int.MaxValue; /// /// Computes the probability mass (PMF) at k, i.e. P(X = k). /// /// The location in the domain where we want to evaluate the probability mass function. /// the probability mass at location . public double Probability(int k) { return PMF(_n, _a, _b, k); } /// /// Computes the log probability mass (lnPMF) at k, i.e. ln(P(X = k)). /// /// The location in the domain where we want to evaluate the log probability mass function. /// the log probability mass at location . public double ProbabilityLn(int k) { return PMFLn(_n, _a, _b, k); } /// /// 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(_n, _a, _b, (int)Math.Floor(x)); } /// /// Computes the probability mass (PMF) at k, i.e. P(X = k). /// /// The number of Bernoulli trials n - n is a positive integer /// Shape parameter alpha of the Beta distribution. Range: a > 0. /// Shape parameter beta of the Beta distribution. Range: b > 0. /// The location in the domain where we want to evaluate the probability mass function. /// the probability mass at location . public static double PMF(int n, double a, double b, int k) { if (!IsValidParameterSet(n, a, b, k)) { throw new ArgumentException("Invalid parametrization for the distribution."); } if (k > n) { return 0.0; } else { return Math.Exp(PMFLn(n, a, b, k)); } } /// /// Computes the log probability mass (lnPMF) at k, i.e. ln(P(X = k)). /// /// The number of Bernoulli trials n - n is a positive integer /// Shape parameter alpha of the Beta distribution. Range: a > 0. /// Shape parameter beta of the Beta distribution. Range: b > 0. /// The location in the domain where we want to evaluate the probability mass function. /// the log probability mass at location . public static double PMFLn(int n, double a, double b, int k) { if (!IsValidParameterSet(n, a, b, k)) { throw new ArgumentException("Invalid parametrization for the distribution."); } return SpecialFunctions.BinomialLn((n), k) + SpecialFunctions.BetaLn(k + a, n - k + b) - SpecialFunctions.BetaLn(a, b); } /// /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x). /// /// The number of Bernoulli trials n - n is a positive integer /// Shape parameter alpha of the Beta distribution. Range: a > 0. /// Shape parameter beta of the Beta distribution. Range: b > 0. /// The location at which to compute the cumulative distribution function. /// the cumulative distribution at location . /// public static double CDF(int n, double a, double b, int x) { if (!IsValidParameterSet(n,a,b,x)) { throw new ArgumentException("Invalid parametrization for the distribution."); } double accumulator = 0; for (int i = 0; i<=x; i++) { accumulator += PMF(n, a, b, i); } return accumulator; } /// /// Samples BetaBinomial distributed random variables by sampling a Beta distribution then passing to a Binomial distribution. /// /// The random number generator to use. /// The α shape parameter of the Beta distribution. Range: α ≥ 0. /// The β shape parameter of the Beta distribution. Range: β ≥ 0. /// The number of trials (n). Range: n ≥ 0. /// a random number from the BetaBinomial distribution. static int SampleUnchecked(System.Random rnd, int n, double a, double b) { var p = Beta.SampleUnchecked(rnd, a, b); var x = Binomial.SampleUnchecked(rnd, p, n); return x; } static void SamplesUnchecked(System.Random rnd, int[] values, int n, double a, double b) { for (int i = 0; i < values.Length; i++) { values[i] = SampleUnchecked(rnd, n, a, b); } } static IEnumerable SamplesUnchecked(System.Random rnd, int n, double a, double b) { while (true) { yield return SampleUnchecked(rnd, n, a, b); } } /// /// Samples a BetaBinomial distributed random variable. /// /// a sample from the distribution. public int Sample() { return SampleUnchecked(_random, _n, _a, _b); } /// /// Fills an array with samples generated from the distribution. /// public void Samples(int[] values) { SamplesUnchecked(_random, values, _n, _a, _b); } /// /// Samples an array of BetaBinomial distributed random variables. /// /// a sequence of samples from the distribution. public IEnumerable Samples() { return SamplesUnchecked(_random, _n, _a, _b); } /// /// Samples a BetaBinomial distributed random variable. /// /// The random number generator to use. /// The α shape parameter of the Beta distribution. Range: α ≥ 0. /// The β shape parameter of the Beta distribution. Range: β ≥ 0. /// The number of trials (n). Range: n ≥ 0. /// a sample from the distribution. public int Sample(System.Random rnd, int n, double a, double b) { if (!IsValidParameterSet(n,a,b)) { throw new ArgumentException("Invalid parametrization for the distribution."); } return SampleUnchecked(rnd, n, a, b); } /// /// 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 Beta distribution. Range: α ≥ 0. /// The β shape parameter of the Beta distribution. Range: β ≥ 0. /// The number of trials (n). Range: n ≥ 0. public void Samples(System.Random rnd, int[] values, int n, double a, double b) { if (!IsValidParameterSet(n, a, b)) { throw new ArgumentException("Invalid parametrization for the distribution."); } SamplesUnchecked(rnd, values, n, a, b); } /// /// Samples an array of BetaBinomial distributed random variables. /// /// The α shape parameter of the Beta distribution. Range: α ≥ 0. /// The β shape parameter of the Beta distribution. Range: β ≥ 0. /// The number of trials (n). Range: n ≥ 0. /// a sequence of samples from the distribution. public IEnumerable Samples(int n, double a, double b) { if (!IsValidParameterSet(n, a, b)) { throw new ArgumentException("Invalid parametrization for the distribution."); } return SamplesUnchecked(_random, n, a, b); } /// /// Fills an array with samples generated from the distribution. /// /// The array to fill with the samples. /// The α shape parameter of the Beta distribution. Range: α ≥ 0. /// The β shape parameter of the Beta distribution. Range: β ≥ 0. /// The number of trials (n). Range: n ≥ 0. public void Samples(int[] values, int n, double a, double b) { if (!IsValidParameterSet(n, a, b)) { throw new ArgumentException("Invalid parametrization for the distribution."); } SamplesUnchecked(_random, values, n, a, b); } } }