/// <summary> /// Inverse of incomplete beta integral. /// </summary> /// /// <example> /// Please see <see cref="Beta"/> /// </example> /// public static double IncompleteInverse(double aa, double bb, double yy0) { double a, b, y0, d, y, x, x0, x1, lgm, yp, di, dithresh, yl, yh; int i, dir; bool nflg; bool rflg; if (yy0 <= 0) { return(0.0); } if (yy0 >= 1.0) { return(1.0); } if (aa <= 1.0 || bb <= 1.0) { nflg = true; dithresh = 4.0 * Constants.DoubleEpsilon; rflg = false; a = aa; b = bb; y0 = yy0; x = a / (a + b); y = Incomplete(a, b, x); goto ihalve; } else { nflg = false; dithresh = 1.0e-4; } /* approximation to inverse function */ yp = -Normal.Inverse(yy0); if (yy0 > 0.5) { rflg = true; a = bb; b = aa; y0 = 1.0 - yy0; yp = -yp; } else { rflg = false; a = aa; b = bb; y0 = yy0; } lgm = (yp * yp - 3.0) / 6.0; x0 = 2.0 / (1.0 / (2.0 * a - 1.0) + 1.0 / (2.0 * b - 1.0)); y = yp * Math.Sqrt(x0 + lgm) / x0 - (1.0 / (2.0 * b - 1.0) - 1.0 / (2.0 * a - 1.0)) * (lgm + 5.0 / 6.0 - 2.0 / (3.0 * x0)); y = 2.0 * y; if (y < Constants.LogMin) { x0 = 1.0; throw new ArithmeticException("underflow"); } x = a / (a + b * Math.Exp(y)); y = Incomplete(a, b, x); yp = (y - y0) / y0; if (Math.Abs(yp) < 1.0e-2) { goto newt; } ihalve: /* Resort to interval halving if not close enough */ x0 = 0.0; yl = 0.0; x1 = 1.0; yh = 1.0; di = 0.5; dir = 0; for (i = 0; i < 400; i++) { if (i != 0) { x = x0 + di * (x1 - x0); if (x == 1.0) { x = 1.0 - Constants.DoubleEpsilon; } y = Incomplete(a, b, x); yp = (x1 - x0) / (x1 + x0); if (Math.Abs(yp) < dithresh) { x0 = x; goto newt; } } if (y < y0) { x0 = x; yl = y; if (dir < 0) { dir = 0; di = 0.5; } else if (dir > 1) { di = 0.5 * di + 0.5; } else { di = (y0 - y) / (yh - yl); } dir += 1; if (x0 > 0.75) { if (rflg) { rflg = false; a = aa; b = bb; y0 = yy0; } else { rflg = true; a = bb; b = aa; y0 = 1.0 - yy0; } x = 1.0 - x; y = Incomplete(a, b, x); goto ihalve; } } else { x1 = x; if (rflg && x1 < Constants.DoubleEpsilon) { x0 = 0.0; goto done; } yh = y; if (dir > 0) { dir = 0; di = 0.5; } else if (dir < -1) { di = 0.5 * di; } else { di = (y - y0) / (yh - yl); } dir -= 1; } } if (x0 >= 1.0) { x0 = 1.0 - Constants.DoubleEpsilon; goto done; } if (x == 0.0) { throw new ArithmeticException("underflow"); } newt: if (nflg) { goto done; } x0 = x; lgm = Gamma.Log(a + b) - Gamma.Log(a) - Gamma.Log(b); for (i = 0; i < 10; i++) { /* Compute the function at this point. */ if (i != 0) { y = Incomplete(a, b, x0); } /* Compute the derivative of the function at this point. */ d = (a - 1.0) * Math.Log(x0) + (b - 1.0) * Math.Log(1.0 - x0) + lgm; if (d < Constants.LogMin) { throw new ArithmeticException("underflow"); } d = Math.Exp(d); /* compute the step to the next approximation of x */ d = (y - y0) / d; x = x0; x0 = x0 - d; if (x0 <= 0.0) { throw new ArithmeticException("underflow"); } if (x0 >= 1.0) { x0 = 1.0 - Constants.DoubleEpsilon; goto done; } if (Math.Abs(d / x0) < 64.0 * Constants.DoubleEpsilon) { goto done; } } done: if (rflg) { if (x0 <= double.Epsilon) { x0 = 1.0 - double.Epsilon; } else { x0 = 1.0 - x0; } } return(x0); }
private static double inverse(double a, double y) { // bound the solution var x0 = double.MaxValue; double yl = 0; double x1 = 0; var yh = 1.0; var dithresh = 5.0 * Constants.DoubleEpsilon; // approximation to inverse function var d = 1.0 / (9.0 * a); var yy = 1.0 - d - Normal.Inverse(y) * Math.Sqrt(d); var x = a * yy * yy * yy; var lgm = Log(a); for (var i = 0; i < 10; i++) { if (x > x0 || x < x1) { goto ihalve; } yy = UpperIncomplete(a, x); if (yy < yl || yy > yh) { goto ihalve; } if (yy < y) { x0 = x; yl = yy; } else { x1 = x; yh = yy; } // compute the derivative of the function at this point d = (a - 1.0) * Math.Log(x) - x - lgm; if (d < -Constants.LogMax) { goto ihalve; } d = -Math.Exp(d); // compute the step to the next approximation of x d = (yy - y) / d; if (Math.Abs(d / x) < Constants.DoubleEpsilon) { return(x); } x = x - d; } // Resort to interval halving if Newton iteration did not converge. ihalve: d = 0.0625; if (x0 == double.MaxValue) { if (x <= 0.0) { x = 1.0; } while (x0 == double.MaxValue && !double.IsNaN(x)) { x = (1.0 + d) * x; yy = UpperIncomplete(a, x); if (yy < y) { x0 = x; yl = yy; break; } d = d + d; } } d = 0.5; double dir = 0; for (var i = 0; i < 400; i++) { var t = x1 + d * (x0 - x1); if (double.IsNaN(t)) { break; } x = t; yy = UpperIncomplete(a, x); lgm = (x0 - x1) / (x1 + x0); if (Math.Abs(lgm) < dithresh) { break; } lgm = (yy - y) / y; if (Math.Abs(lgm) < dithresh) { break; } if (x <= 0.0) { break; } if (yy >= y) { x1 = x; yh = yy; if (dir < 0) { dir = 0; d = 0.5; } else if (dir > 1) { d = 0.5 * d + 0.5; } else { d = (y - yl) / (yh - yl); } dir += 1; } else { x0 = x; yl = yy; if (dir > 0) { dir = 0; d = 0.5; } else if (dir < -1) { d = 0.5 * d; } else { d = (y - yl) / (yh - yl); } dir -= 1; } } if (x == 0.0 || double.IsNaN(x)) { throw new ArithmeticException(); } return(x); }