/// <summary> /// Returns the partial derivative of IBeta(a,b,x) with respect to <paramref name="x"/> /// <para>IbetaDerivative(a, b, x) = ∂( I<sub>x</sub>(a,b) )/∂x = (1-x)^(b-1)*x^(a-1)/B(a,b)</para> /// </summary> /// <param name="a">Requires a ≥ 0 and not both a and b are zero</param> /// <param name="b">Requires b ≥ 0 and not both a and b are zero</param> /// <param name="x">Defined for 0 ≤ x ≤ 1</param> public static double IbetaDerivative(double a, double b, double x) { if (!(a > 0) || !(b > 0) || !(x >= 0 && x <= 1)) { Policies.ReportDomainError("IbetaDerivative(a: {0}, b: {1}, x: {2}): Requires a,b >= 0; x in [0,1]", a, b, x); return(double.NaN); } // // Now the corner cases: // if (x == 0) { if (a > 1) { return(0); } if (a == 1) { return(1 / Math2.Beta(a, b)); } return(double.PositiveInfinity); } if (x == 1) { if (b > 1) { return(0); } if (b == 1) { return(1 / Math2.Beta(a, b)); } return(double.PositiveInfinity); } // handle denorm x if (x < DoubleLimits.MinNormalValue) { double logValue = a * Math.Log(x) + b * Math2.Log1p(-x) - Math.Log(x * (1 - x)); logValue -= Math2.LogBeta(a, b); double result = Math.Exp(logValue); #if EXTRA_DEBUG Debug.WriteLine("IbetaDerivative(a: {0}, b: {1}, x: {2}): Denorm = {3}", a, b, x, result); #endif return(result); } // // Now the regular cases: // double mult = (1 - x) * x; double f1 = _Ibeta.PowerTerms(a, b, x, 1 - x, true, 1 / mult); return(f1); }
/// <summary> /// Returns the binomial coefficient /// <para>BinomialCoefficient(n,k) = n! / (k!(n-k)!)</para> /// </summary> /// <param name="n">Requires n ≥ 0</param> /// <param name="k">Requires k in [0,n]</param> public static double BinomialCoefficient(int n, int k) { if ((n < 0) || (k < 0 || k > n)) { Policies.ReportDomainError("BinomialCoefficient(n: {0},k: {1}): Requires n >= 0; k in [0,n]", n, k); return(double.NaN); } if (k == 0 || k == n) { return(1); } if (k == 1 || k == n - 1) { return(n); } double result; if (n < Math2.FactorialTable.Length) { // Use fast table lookup: result = (FactorialTable[n] / FactorialTable[n - k]) / FactorialTable[k]; } else { // Use the beta function: if (k < n - k) { result = k * Math2.Beta(k, n - k + 1); } else { result = (n - k) * Math2.Beta(k + 1, n - k); } if (result == 0) { return(double.PositiveInfinity); } result = 1 / result; } // convert to nearest integer: return(Math.Ceiling(result - 0.5)); }
/// <summary> /// Returns the Incomplete Beta Complement /// <para>Betac(a, b, x) = B(a,b) - B<sub>x</sub>(a,b)</para> /// </summary> /// <param name="a">Requires a ≥ 0 and not (a == b == 0)</param> /// <param name="b">Requires b ≥ 0 and not (a == b == 0)</param> /// <param name="x">0 ≤ x ≤ 1</param> public static double Betac(double a, double b, double x) { if ((!(a > 0) || double.IsInfinity(a)) || (!(b > 0) || double.IsInfinity(b)) || !(x >= 0 && x <= 1)) { Policies.ReportDomainError("Betac(a: {0}, b: {1}, x: {2}): Requires finite a,b > 0; x in [0,1]", a, b, x); return(double.NaN); } // special values if (x == 0) { return(Math2.Beta(a, b)); } if (x == 1) { return(0); } return(_Ibeta.BetaImp(a, b, x, true)); }
static double IbetaLargeASmallBSeries(double a, double b, double x, double y, bool normalised, double mult) { Debug.Assert(a >= 15, "Requires a >= 15: a= " + a); Debug.Assert(b >= 0 && b <= 1, "Requires b >= 0 && b <= 1: b= " + b); Debug.Assert(mult >= 0); // // Routine for a > 15, b < 1 // // Begin by figuring out how large our table of Pn's should be, // quoted accuracies are "guestimates" based on empiracal observation. // Note that the table size should never exceed the size of our // tables of factorials. // const int Pn_size = 30; // 16-20 digit accuracy // // This is DiDonato and Morris's BGRAT routine, see Eq's 9 through 9.6. // // Some values we'll need later, these are Eq 9.1: // double bm1 = b - 1; double t = a + bm1 / 2; double lx = (y < 0.35) ? Math2.Log1p(-y) : Math.Log(x); double u = -t * lx; // and from from 9.2: double prefix; double j; // = Math2.GammaQ(b, u) / IgammaPrefixRegularized(b, u); if (-u > DoubleLimits.MinLogValue) { double xpt = (y < 0.35) ? Math.Exp(-u) : Math.Pow(x, t); // h = u^b * e^-u double h = Math.Pow(u, b) * xpt; //IgammaPrefix(b, u); j = Math2.Tgamma(b, u) / h; prefix = Math.Pow(-lx, b) * xpt; if (normalised) { prefix /= Math2.Beta(a, b); } prefix *= mult; } else { // if we use the previous equation, we'll end up with denorm/denorm or 0/0 // So, use the asymptotic expansion of Γ(a, x) and cancel terms // Γ(b, u) = e^-u * u^(a-1) * TgammaAsymSeries(b, u) j = _Igamma.Asym_SeriesLargeZ(b, u) / u; // use logs, though we'll probably get zero double lVal = -u + Math.Log(Math.Pow(-lx, b) * mult); if (normalised) { lVal -= Math2.LogBeta(a, b); } prefix = Math.Exp(lVal); } if (prefix == 0) { return(0); } // // now we need the quantity Pn, unfortunatately this is computed // recursively, and requires a full history of all the previous values // so no choice but to declare a big table and hope it's big enough... // double[] p = new double[Pn_size]; p[0] = 1; // see 9.3. // // Now we can start to pull things together and evaluate the sum in Eq 9: // double sum = j; // Value at N = 0 // some variables we'll need: uint tnp1 = 1; // 2*N+1 double lx2 = lx / 2; lx2 *= lx2; double lxp = 1; double t4 = 4 * t * t; double b2n = b; for (int n = 1; n < p.Length; ++n) { /* * // debugging code, enable this if you want to determine whether * // the table of Pn's is large enough... * // * static int max_count = 2; * if(n > max_count) * { * max_count = n; * Debug.WriteLine("Max iterations in BGRAT was {0}", n); * } */ // // begin by evaluating the next Pn from Eq 9.4: // tnp1 += 2; p[n] = 0; double mbn = b - n; int tmp1 = 3; for (int m = 1; m < n; ++m) { mbn = m * b - n; p[n] += mbn * p[n - m] / Math2.FactorialTable[tmp1]; tmp1 += 2; } p[n] /= n; p[n] += bm1 / Math2.FactorialTable[tnp1]; // // Now we want Jn from Jn-1 using Eq 9.6: // j = (b2n * (b2n + 1) * j + (u + b2n + 1) * lxp) / t4; lxp *= lx2; b2n += 2; // // pull it together with Eq 9: // double r = p[n] * j; double prevSum = sum; sum += r; if (prevSum == sum) { break; } } return(prefix * sum); }
/// <summary> /// Implementation for the non-regularized cases: Bx(a,b) and B(a,b)-Bx(a,b) /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <param name="x"></param> /// <param name="invert"></param> /// <returns></returns> public static double BetaImp(double a, double b, double x, bool invert) { Debug.Assert(a > 0); Debug.Assert(b > 0 && b < int.MaxValue); Debug.Assert(x > 0 && x < 1); const bool normalised = false; double fract; double y = 1 - x; if (a <= 1 && b <= 1) { if (x > 0.5) { Utility.Swap(ref a, ref b); Utility.Swap(ref x, ref y); invert = !invert; } // Both a,b < 1: if ((a >= Math.Min(0.2, b)) || (Math.Pow(x, a) <= 0.9)) { fract = Series1(a, b, x, y, false); return((invert) ? Math2.Beta(a, b) - fract : fract); } Utility.Swap(ref a, ref b); Utility.Swap(ref x, ref y); invert = !invert; if (y >= 0.3) { fract = Series1(a, b, x, y, false); return((invert) ? Math2.Beta(a, b) - fract : fract); } // Sidestep on a, and then use the series representation: double prefix = RisingFactorialRatio(a + b, a, 20); fract = IbetaAStep(a, b, x, y, 20, normalised); fract += IbetaLargeASmallBSeries(a + 20, b, x, y, normalised, prefix); return((invert) ? Math2.Beta(a, b) - fract : fract); } if (a <= 1 || b <= 1) { if (x > 0.5) { Utility.Swap(ref a, ref b); Utility.Swap(ref x, ref y); invert = !invert; } // One of a, b < 1 only: if ((b <= 1) || ((x < 0.1) && (Math.Pow(b * x, a) <= 0.7))) { fract = Series1(a, b, x, y, false); return((invert) ? Math2.Beta(a, b) - fract : fract); } Utility.Swap(ref a, ref b); Utility.Swap(ref x, ref y); invert = !invert; if (y >= 0.3) { fract = Series1(a, b, x, y, false); return((invert) ? Math2.Beta(a, b) - fract : fract); } if (a >= 15) { fract = IbetaLargeASmallBSeries(a, b, x, y, normalised, 1); return((invert) ? Math2.Beta(a, b) - fract : fract); } // Sidestep to improve errors: double prefix = RisingFactorialRatio(a + b, a, 20); fract = IbetaAStep(a, b, x, y, 20, normalised); fract += IbetaLargeASmallBSeries(a + 20, b, x, y, normalised, prefix); return((invert) ? Math2.Beta(a, b) - fract : fract); } // Both a,b >= 1: double lambda = (a < b) ? a - (a + b) * x : (a + b) * y - b; if (lambda < 0) { Utility.Swap(ref a, ref b); Utility.Swap(ref x, ref y); invert = !invert; } if (b >= 40) { fract = Fraction2(a, b, x, y, normalised); return(invert ? Math2.Beta(a, b) - fract : fract); } /* * if( IsInteger(a) && IsInteger(b)) { * // relate to the binomial distribution and use a finite sum: * double k = a - 1; * double n = b + k; * fract = binomial_ccdf(n, k, x, y); * if ( !invert) * return Math2.Beta(a, b) * fract; * return Math2.Beta(a, b) * (1.0 - fract); * } */ if (b * x <= 0.7) { fract = Series1(a, b, x, y, false); return((invert) ? Math2.Beta(a, b) - fract : fract); } if (a > 15) { // sidestep so we can use the series representation: int n = (int)Math.Floor(b); if (n == b) { --n; } double bbar = b - n; double prefix = RisingFactorialRatio(a + bbar, bbar, n); fract = IbetaAStep(bbar, a, y, x, n, normalised); fract += IbetaLargeASmallBSeries(a, bbar, x, y, normalised, 1); fract /= prefix; return(invert ? Math2.Beta(a, b) - fract : fract); } fract = Fraction2(a, b, x, y, normalised); return(invert ? Math2.Beta(a, b) - fract : fract); }