/// <summary> /// Compute the leading power terms in the incomplete Beta: /// <para>(x^a)(y^b)/Beta(a,b) when regularized</para> /// <para>(x^a)(y^b) otherwise</para> /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <param name="x"></param> /// <param name="y">1-x</param> /// <param name="normalised"></param> /// <param name="multiplier"></param> /// <returns></returns> /// <remarks> /// Almost all of the error in the incomplete beta comes from this /// function: particularly when a and b are large. Computing large /// powers are *hard* though, and using logarithms just leads to /// horrendous cancellation errors. /// </remarks> public static double PowerTerms(double a, double b, double x, double y, bool normalised, double multiplier) { if (multiplier == 0) { return(0); } if (!normalised) { return(multiplier * (Math.Pow(x, a) * Math.Pow(y, b))); } double c = a + b; // combine power terms with Lanczos approximation: double agh = a + (Lanczos.G - 0.5); double bgh = b + (Lanczos.G - 0.5); double cgh = c + (Lanczos.G - 0.5); // compute: // mutiplier * (x*cgh/agh)^a * (y*cgh/bgh)^b * Sqrt((agh*bgh)/(e*cgh)) * (Sum_expG_scaled(c) / (Sum_expG_scaled(a) * Sum_expG_scaled(b))) double factor = (Lanczos.SeriesExpGScaled(c) / Lanczos.SeriesExpGScaled(a)) / Lanczos.SeriesExpGScaled(b); if (a > b) { factor *= Math.Sqrt((bgh / Math.E) * (agh / cgh)); } else { factor *= Math.Sqrt((agh / Math.E) * (bgh / cgh)); } if (double.IsInfinity(factor * multiplier)) { #if EXTRA_DEBUG Debug.WriteLine("Using Logs in _Beta.PowerTerms: a = {0}; b = {1}; x = {2}; y= {3}; mult = {4}", a, b, x, y, multiplier); #endif // this will probably fail but... double logValue; if (x <= y) { logValue = a * Math.Log(x) + b * Math2.Log1p(-x); } else { logValue = a * Math2.Log1p(-y) + b * Math.Log(y); } logValue += Math.Log(multiplier); logValue -= Math2.LogBeta(a, b); return(Math.Exp(logValue)); } else { factor *= multiplier; } double result = 1; //Debug.WriteLine("IbetaPowerTerms(a: {0}, b: {1}, x: {2}, y: {3}) - Prefix = {4}", a, b, x, y, result); // l1 and l2 are the base of the exponents minus one: double l1 = (x * b - y * agh) / agh; double l2 = (y * a - x * bgh) / bgh; // t1 and t2 are the exponent terms double t1 = (x * cgh) / agh; double t2 = (y * cgh) / bgh; // if possible, set the result to partially cancel out with the first term bool sameDirection = false; if (t1 >= 1 && t2 >= 1) { sameDirection = true; if (factor < 1) { result = factor; factor = 1; } } else if (t1 <= 1 && t2 <= 1) { sameDirection = true; if (factor > 1) { result = factor; factor = 1; } } if (sameDirection) { // This first branch handles the simple case where the two power terms // both go in the same direction (towards zero or towards infinity). // In this case if either term overflows or underflows, // then the product of the two must do so also. if (Math.Abs(l1) < 0.5) { result *= Math.Exp(a * Math2.Log1p(l1)); } else { result *= Math.Pow(t1, a); } if (Math.Abs(l2) < 0.5) { result *= Math.Exp(b * Math2.Log1p(l2)); } else { result *= Math.Pow(t2, b); } result *= factor; } else { // This second branch handles the case where the two power terms // go in opposite directions (towards zero or towards infinity). Debug.Assert(result == 1); bool useExpT1 = false; bool useExpT2 = false; double logt1; if (Math.Abs(l1) < 0.5) { useExpT1 = true; logt1 = a * Math2.Log1p(l1); } else { logt1 = a * Math.Log(t1); } double logt2; if (Math.Abs(l2) < 0.5) { useExpT2 = true; logt2 = b * Math2.Log1p(l2); } else { logt2 = b * Math.Log(t2); } if ((logt1 >= DoubleLimits.MaxLogValue) || (logt1 <= DoubleLimits.MinLogValue) || (logt2 >= DoubleLimits.MaxLogValue) || (logt2 <= DoubleLimits.MinLogValue) ) { double logSum = logt1 + logt2; if ((logSum >= DoubleLimits.MaxLogValue) || (logSum <= DoubleLimits.MinLogValue)) { result = Math.Exp(logSum + Math.Log(factor)); } else { result = factor * Math.Exp(logSum); } } else { // ensure that t1 and result will partially cancel if (t1 >= 1) { if (factor < 1) { result = factor; factor = 1; } } else { if (factor > 1) { result = factor; factor = 1; } } if (useExpT1) { result *= Math.Exp(logt1); } else { result *= Math.Pow(t1, a); } if (useExpT2) { result *= Math.Exp(logt2); } else { result *= Math.Pow(t2, b); } result *= factor; } } return(result); }
/// <summary> /// Compute: multiplier * x^a/Beta(a,b) while trying to avoid overflows/underflows /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <param name="x"></param> /// <param name="y"></param> /// <param name="multiplier"></param> /// <returns></returns> static double SeriesRegularizedPrefix(double a, double b, double x, double y, double multiplier) { double c = a + b; // incomplete beta power term, combined with the Lanczos approximation: double agh = a + (Lanczos.G - 0.5); double bgh = b + (Lanczos.G - 0.5); double cgh = c + (Lanczos.G - 0.5); // compute: // mutiplier * (x*cgh/agh)^a * (cgh/bgh)^b * Sqrt((agh*bgh)/(e*cgh)) * (SumGScaled(c)/(SumGScaled(a)*SumGScaled(b)) double factor = (Lanczos.SeriesExpGScaled(c) / Lanczos.SeriesExpGScaled(a)) / Lanczos.SeriesExpGScaled(b); if (a > b) { factor *= Math.Sqrt((bgh / Math.E) * (agh / cgh)); } else { factor *= Math.Sqrt((agh / Math.E) * (bgh / cgh)); } factor *= multiplier; double result = 1; // l1 and l2 are the base of the exponents minus one: double l1 = (x * b - y * agh) / agh; double l2 = a / bgh; // t1 and t2 are the exponent terms double t1 = (x * cgh) / agh; double t2 = cgh / bgh; // if possible, set the result to partially cancel out with the first term Debug.Assert(t2 >= 1); if (t1 >= 1) { // This first branch handles the simple case where the two power terms // both go in the same direction (towards zero or towards infinity). // In this case if either term overflows or underflows, // then the product of the two must do so also. if (factor < 1) { result = factor; factor = 1; } if (Math.Abs(l1) < 0.5) { result *= Math.Exp(a * Math2.Log1p(l1)); } else { result *= Math.Pow(t1, a); } if (Math.Abs(l2) < 0.5) { result *= Math.Exp(b * Math2.Log1p(l2)); } else { result *= Math.Pow(t2, b); } result *= factor; } else { // This second branch handles the case where the two power terms // go in opposite directions (towards zero or towards infinity). Debug.Assert(result == 1); Debug.Assert(t1 < 1); bool useExpT1 = false; bool useExpT2 = false; double logt1; if (Math.Abs(l1) < 0.5) { useExpT1 = true; logt1 = a * Math2.Log1p(l1); } else { logt1 = a * Math.Log(t1); } double logt2; if (Math.Abs(l2) < 0.5) { useExpT2 = true; logt2 = b * Math2.Log1p(l2); } else { logt2 = b * Math.Log(t2); } if ((logt1 >= DoubleLimits.MaxLogValue) || (logt1 <= DoubleLimits.MinLogValue) || (logt2 >= DoubleLimits.MaxLogValue) || (logt2 <= DoubleLimits.MinLogValue) ) { double logSum = logt1 + logt2; if ((logSum >= DoubleLimits.MaxLogValue) || (logSum <= DoubleLimits.MinLogValue)) { result = Math.Exp(logSum + Math.Log(factor)); } else { result = factor * Math.Exp(logSum); } } else { // Set result so that t1 and result will partially cancel if (factor > 1) { result = factor; factor = 1; } if (useExpT1) { result *= Math.Exp(logt1); } else { result *= Math.Pow(t1, a); } if (useExpT2) { result *= Math.Exp(logt2); } else { result *= Math.Pow(t2, b); } result *= factor; } } return(result); }