/// <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); }
/// <summary> /// A function for computing bivariate normal probabilities. /// BVND calculates the probability that X > DH and Y > DK. /// </summary> /// /// <remarks> /// <para> /// This method is based on the work done by Alan Genz, Department of /// Mathematics, Washington State University. Pullman, WA 99164-3113 /// Email: [email protected]. This work was shared under a 3-clause BSD /// license. Please see source file for more details and the actual /// license text.</para> /// /// <para> /// This function is based on the method described by Drezner, Z and G.O. /// Wesolowsky, (1989), On the computation of the bivariate normal integral, /// Journal of Statist. Comput. Simul. 35, pp. 101-107, with major modifications /// for double precision, and for |R| close to 1.</para> /// </remarks> /// private static double BVND(double dh, double dk, double r) { // Copyright (C) 2013, Alan Genz, All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided the following conditions are met: // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in // the documentation and/or other materials provided with the // distribution. // 3. The contributor name(s) may not be used to endorse or promote // products derived from this software without specific prior // written permission. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR // TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. const double TWOPI = 2.0 * Math.PI; double[] x; double[] w; if (Math.Abs(r) < 0.3) { // Gauss Legendre Points and Weights N = 6 x = BVND_XN6; w = BVND_WN6; } else if (Math.Abs(r) < 0.75) { // Gauss Legendre Points and Weights N = 12 x = BVND_XN12; w = BVND_WN12; } else { // Gauss Legendre Points and Weights N = 20 x = BVND_XN20; w = BVND_WN20; } double h = dh; double k = dk; double hk = h * k; double bvn = 0; if (Math.Abs(r) < 0.925) { if (Math.Abs(r) > 0) { double sh = (h * h + k * k) / 2; double asr = Math.Asin(r); for (int i = 0; i < x.Length; i++) { for (int j = -1; j <= 1; j += 2) { double sn = Math.Sin(asr * (j * x[i] + 1) / 2); bvn = bvn + w[i] * Math.Exp((sn * hk - sh) / (1 - sn * sn)); } } bvn = bvn * asr / (2 * TWOPI); } return(bvn + Normal.Function(-h) * Normal.Function(-k)); } if (r < 0) { k = -k; hk = -hk; } if (Math.Abs(r) < 1) { double sa = (1 - r) * (1 + r); double A = Math.Sqrt(sa); double sb = (h - k); sb = sb * sb; double c = (4 - hk) / 8; double d = (12 - hk) / 16; double asr = -(sb / sa + hk) / 2; if (asr > -100) { bvn = A * Math.Exp(asr) * (1 - c * (sb - sa) * (1 - d * sb / 5) / 3 + c * d * sa * sa / 5); } if (-hk < 100) { double B = Math.Sqrt(sb); bvn = bvn - Math.Exp(-hk / 2) * Math.Sqrt(TWOPI) * Normal.Function(-B / A) * B * (1 - c * sb * (1 - d * sb / 5) / 3); } A = A / 2; for (int i = 0; i < x.Length; i++) { for (int j = -1; j <= 1; j += 2) { double xs = (A * (j * x[i] + 1)); xs = xs * xs; double rs = Math.Sqrt(1 - xs); asr = -(sb / xs + hk) / 2; if (asr > -100) { bvn = bvn + A * w[i] * Math.Exp(asr) * (Math.Exp(-hk * xs / (2 * (1 + rs) * (1 + rs))) / rs - (1 + c * xs * (1 + d * xs))); } } } bvn = -bvn / TWOPI; } if (r > 0) { return(bvn + Normal.Function(-Math.Max(h, k))); } bvn = -bvn; if (k <= h) { return(bvn); } if (h < 0) { return(bvn + Normal.Function(k) - Normal.Function(h)); } return(bvn + Normal.Function(-h) - Normal.Function(-k)); }