/// <summary> /// Computes Owen's T function for arbitrary H and A. /// </summary> /// /// <param name="h">Owen's T function argument H.</param> /// <param name="a">Owen's T function argument A.</param> /// /// <returns>The value of Owen's T function.</returns> /// public static double Function(double h, double a) { double absa; double absh; double ah; const double cut = 0.67; double normah; double normh; double value; absh = Math.Abs(h); absa = Math.Abs(a); ah = absa * absh; if (absa <= 1.0) { value = Function(absh, absa, ah); } else if (absh <= cut) { value = 0.25 - (-0.5 + Normal.Function(absh)) * (-0.5 + Normal.Function(ah)) - Function(ah, 1.0 / absa, absh); } else { normh = Normal.Complemented(absh); normah = Normal.Complemented(ah); value = 0.5 * (normh + normah) - normh * normah - Function(ah, 1.0 / absa, absh); } if (a < 0.0) { value = -value; } return(value); }
/// <summary> /// Owen's T function for a restricted range of parameters. /// </summary> /// /// <param name="h">Owen's T function argument H (where 0 <= H).</param> /// <param name="a">Owen's T function argument A (where 0 <= A <= 1).</param> /// <param name="ah">The value of A*H.</param> /// /// <returns>The value of Owen's T function.</returns> /// public static double Function(double h, double a, double ah) { double ai; double aj; double AS; double dhs; double dj; double gj; double hs; int i; int iaint; int icode; int ihint; int ii; int j; int jj; int m; int maxii; double normh; double r; const double rrtpi = 0.39894228040143267794; const double rtwopi = 0.15915494309189533577; double y; double yi; double z; double zi; double value = 0; double vi; double[] arange = { 0.025, 0.09, 0.15, 0.36, 0.5, 0.9, 0.99999 }; double[] c2 = { 0.99999999999999987510, -0.99999999999988796462, 0.99999999998290743652, -0.99999999896282500134, 0.99999996660459362918, -0.99999933986272476760, 0.99999125611136965852, -0.99991777624463387686, 0.99942835555870132569, -0.99697311720723000295, 0.98751448037275303682, -0.95915857980572882813, 0.89246305511006708555, -0.76893425990463999675, 0.58893528468484693250, -0.38380345160440256652, 0.20317601701045299653, -0.82813631607004984866E-01, 0.24167984735759576523E-01, -0.44676566663971825242E-02, 0.39141169402373836468E-03 }; double[] hrange = { 0.02, 0.06, 0.09, 0.125, 0.26, 0.4, 0.6, 1.6, 1.7, 2.33, 2.4, 3.36, 3.4, 4.8 }; int[] meth = { 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 4, 4, 4, 4, 5, 6 }; int[] ord = { 2, 3, 4, 5, 7, 10, 12, 18, 10, 20, 30, 20, 4, 7, 8, 20, 13, 0 }; double[] pts = { 0.35082039676451715489E-02, 0.31279042338030753740E-01, 0.85266826283219451090E-01, 0.16245071730812277011, 0.25851196049125434828, 0.36807553840697533536, 0.48501092905604697475, 0.60277514152618576821, 0.71477884217753226516, 0.81475510988760098605, 0.89711029755948965867, 0.95723808085944261843, 0.99178832974629703586 }; int[] select = { 1, 1, 2, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 9, 1, 2, 2, 3, 3, 5, 5, 14, 14, 15, 15, 16, 16, 16, 9, 2, 2, 3, 3, 3, 5, 5, 15, 15, 15, 15, 16, 16, 16, 10, 2, 2, 3, 5, 5, 5, 5, 7, 7, 16, 16, 16, 16, 16, 10, 2, 3, 3, 5, 5, 6, 6, 8, 8, 17, 17, 17, 12, 12, 11, 2, 3, 5, 5, 5, 6, 6, 8, 8, 17, 17, 17, 12, 12, 12, 2, 3, 4, 4, 6, 6, 8, 8, 17, 17, 17, 17, 17, 12, 12, 2, 3, 4, 4, 6, 6, 18, 18, 18, 18, 17, 17, 17, 12, 12 }; double[] wts = { 0.18831438115323502887E-01, 0.18567086243977649478E-01, 0.18042093461223385584E-01, 0.17263829606398753364E-01, 0.16243219975989856730E-01, 0.14994592034116704829E-01, 0.13535474469662088392E-01, 0.11886351605820165233E-01, 0.10070377242777431897E-01, 0.81130545742299586629E-02, 0.60419009528470238773E-02, 0.38862217010742057883E-02, 0.16793031084546090448E-02 }; /* * Determine appropriate method from t1...t6 */ ihint = 15; for (i = 1; i <= 14; i++) { if (h <= hrange[i - 1]) { ihint = i; break; } } iaint = 8; for (i = 1; i <= 7; i++) { if (a <= arange[i - 1]) { iaint = i; break; } } icode = select[ihint - 1 + (iaint - 1) * 15]; m = ord[icode - 1]; /* * t1(h, a, m) ; m = 2, 3, 4, 5, 7, 10, 12 or 18 * jj = 2j - 1 ; gj = exp(-h*h/2) * (-h*h/2)**j / j * aj = a**(2j-1) / (2*pi) */ if (meth[icode - 1] == 1) { hs = -0.5 * h * h; dhs = Math.Exp(hs); AS = a * a; j = 1; jj = 1; aj = rtwopi * a; value = rtwopi * Math.Atan(a); dj = dhs - 1.0; gj = hs * dhs; for (; ;) { value = value + dj * aj / (double)(jj); if (m <= j) { return(value); } j = j + 1; jj = jj + 2; aj = aj * AS; dj = gj - dj; gj = gj * hs / (double)(j); } } /* * t2(h, a, m) ; m = 10, 20 or 30 * z = (-1)^(i-1) * zi ; ii = 2i - 1 * vi = (-1)^(i-1) * a^(2i-1) * exp[-(a*h)^2/2] / sqrt(2*pi) */ else if (meth[icode - 1] == 2) { maxii = m + m + 1; ii = 1; value = 0.0; hs = h * h; AS = -a * a; vi = rrtpi * a * Math.Exp(-0.5 * ah * ah); z = 0.5 * (-0.5 + Normal.Function(ah)) / h; y = 1.0 / hs; for (; ;) { value = value + z; if (maxii <= ii) { value = value * rrtpi * Math.Exp(-0.5 * hs); return(value); } z = y * (vi - (double)(ii) * z); vi = AS * vi; ii = ii + 2; } } /* * t3(h, a, m) ; m = 20 * ii = 2i - 1 * vi = a**(2i-1) * exp[-(a*h)**2/2] / sqrt(2*pi) */ else if (meth[icode - 1] == 3) { i = 1; ii = 1; value = 0.0; hs = h * h; AS = a * a; vi = rrtpi * a * Math.Exp(-0.5 * ah * ah); zi = 0.5 * (-0.5 + Normal.Function(ah)) / h; y = 1.0 / hs; for (; ;) { value = value + zi * c2[i - 1]; if (m < i) { value = value * rrtpi * Math.Exp(-0.5 * hs); return(value); } zi = y * ((double)(ii) * zi - vi); vi = AS * vi; i = i + 1; ii = ii + 2; } } /* * t4(h, a, m) ; m = 4, 7, 8 or 20; ii = 2i + 1 * ai = a * exp[-h*h*(1+a*a)/2] * (-a*a)**i / (2*pi) */ else if (meth[icode - 1] == 4) { maxii = m + m + 1; ii = 1; hs = h * h; AS = -a * a; value = 0.0; ai = rtwopi * a * Math.Exp(-0.5 * hs * (1.0 - AS)); yi = 1.0; for (; ;) { value = value + ai * yi; if (maxii <= ii) { return(value); } ii = ii + 2; yi = (1.0 - hs * yi) / (double)(ii); ai = ai * AS; } } /* * t5(h, a, m) ; m = 13 * 2m - point gaussian quadrature */ else if (meth[icode - 1] == 5) { value = 0.0; AS = a * a; hs = -0.5 * h * h; for (i = 1; i <= m; i++) { r = 1.0 + AS * pts[i - 1]; value = value + wts[i - 1] * Math.Exp(hs * r) / r; } value = a * value; } /* * t6(h, a); approximation for a near 1, (a<=1) */ else if (meth[icode - 1] == 6) { normh = Normal.Complemented(h); value = 0.5 * normh * (1.0 - normh); y = 1.0 - a; r = Math.Atan(y / (1.0 + a)); if (r != 0.0) { value = value - rtwopi * r * Math.Exp(-0.5 * y * h * h / r); } } return(value); }
/// <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. double TWOPI = 2.0 * Math.PI; double[] x; double[] w; if (Math.Abs(R) < 0.3) { // Gauss Legendre Points and Weights N = 6 x = new double[] { -0.9324695142031522, -0.6612093864662647, -0.2386191860831970 }; w = new double[] { 0.1713244923791705, 0.3607615730481384, 0.4679139345726904 }; } else if (Math.Abs(R) < 0.75) { // Gauss Legendre Points and Weights N = 12 x = new double[] { -0.9815606342467191, -0.9041172563704750, -0.7699026741943050, -0.5873179542866171, -0.3678314989981802, -0.1252334085114692 }; w = new double[] { 0.04717533638651177, 0.1069393259953183, 0.1600783285433464, 0.2031674267230659, 0.2334925365383547, 0.2491470458134029, }; } else { // Gauss Legendre Points and Weights N = 20 x = new double[] { -0.9931285991850949, -0.9639719272779138, -0.9122344282513259, -0.8391169718222188, -0.7463319064601508, -0.6360536807265150, -0.5108670019508271, -0.3737060887154196, -0.2277858511416451, -0.07652652113349733 }; w = new double[] { 0.01761400713915212, 0.04060142980038694, 0.06267204833410906, 0.08327674157670475, 0.1019301198172404, 0.1181945319615184, 0.1316886384491766, 0.1420961093183821, 0.1491729864726037, 0.1527533871307259 }; } 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 HS = (H * H + K * K) / 2; double ASR = Math.Asin(R); for (int I = 0; I < x.Length; I++) { for (int IS = -1; IS <= 1; IS += 2) { double SN = Math.Sin(ASR * (IS * x[I] + 1) / 2); BVN = BVN + w[I] * Math.Exp((SN * HK - HS) / (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 AS = (1 - R) * (1 + R); double A = Math.Sqrt(AS); double BS = (H - K); BS = BS * BS; double C = (4 - HK) / 8; double D = (12 - HK) / 16; double ASR = -(BS / AS + HK) / 2; if (ASR > -100) { BVN = A * Math.Exp(ASR) * (1 - C * (BS - AS) * (1 - D * BS / 5) / 3 + C * D * AS * AS / 5); } if (-HK < 100) { double B = Math.Sqrt(BS); BVN = BVN - Math.Exp(-HK / 2) * Math.Sqrt(TWOPI) * Normal.Function(-B / A) * B * (1 - C * BS * (1 - D * BS / 5) / 3); } A = A / 2; for (int I = 0; I < x.Length; I++) { for (int IS = -1; IS <= 1; IS += 2) { double XS = (A * (IS * x[I] + 1)); XS = XS * XS; double RS = Math.Sqrt(1 - XS); ASR = -(BS / 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)); }
/// <summary> /// Owen's T function for a restricted range of parameters. /// </summary> /// /// <param name="h">Owen's T function argument H (where 0 <= H).</param> /// <param name="a">Owen's T function argument A (where 0 <= A <= 1).</param> /// <param name="ah">The value of A*H.</param> /// /// <returns>The value of Owen's T function.</returns> /// public static double Function(double h, double a, double ah) { double ai; double aj; double AS; double dhs; double dj; double gj; double hs; int i; int iaint; int icode; int ihint; int ii; int j; int jj; int m; int maxii; double normh; double r; const double rrtpi = 0.39894228040143267794; const double rtwopi = 0.15915494309189533577; double y; double yi; double z; double zi; double value = 0; double vi; /* * Determine appropriate method from t1...t6 */ ihint = 15; for (i = 1; i <= 14; i++) { if (h <= hrange[i - 1]) { ihint = i; break; } } iaint = 8; for (i = 1; i <= 7; i++) { if (a <= arange[i - 1]) { iaint = i; break; } } icode = select[ihint - 1 + (iaint - 1) * 15]; m = ord[icode - 1]; /* * t1(h, a, m) ; m = 2, 3, 4, 5, 7, 10, 12 or 18 * jj = 2j - 1 ; gj = exp(-h*h/2) * (-h*h/2)**j / j * aj = a**(2j-1) / (2*pi) */ if (meth[icode - 1] == 1) { hs = -0.5 * h * h; dhs = Math.Exp(hs); AS = a * a; j = 1; jj = 1; aj = rtwopi * a; value = rtwopi * Math.Atan(a); dj = dhs - 1.0; gj = hs * dhs; for (; ;) { value = value + dj * aj / (double)(jj); if (m <= j) { return(value); } j = j + 1; jj = jj + 2; aj = aj * AS; dj = gj - dj; gj = gj * hs / (double)(j); } } /* * t2(h, a, m) ; m = 10, 20 or 30 * z = (-1)^(i-1) * zi ; ii = 2i - 1 * vi = (-1)^(i-1) * a^(2i-1) * exp[-(a*h)^2/2] / sqrt(2*pi) */ else if (meth[icode - 1] == 2) { maxii = m + m + 1; ii = 1; value = 0.0; hs = h * h; AS = -a * a; vi = rrtpi * a * Math.Exp(-0.5 * ah * ah); z = 0.5 * (-0.5 + Normal.Function(ah)) / h; y = 1.0 / hs; for (; ;) { value = value + z; if (maxii <= ii) { value = value * rrtpi * Math.Exp(-0.5 * hs); return(value); } z = y * (vi - (double)(ii) * z); vi = AS * vi; ii = ii + 2; } } /* * t3(h, a, m) ; m = 20 * ii = 2i - 1 * vi = a**(2i-1) * exp[-(a*h)**2/2] / sqrt(2*pi) */ else if (meth[icode - 1] == 3) { i = 1; ii = 1; value = 0.0; hs = h * h; AS = a * a; vi = rrtpi * a * Math.Exp(-0.5 * ah * ah); zi = 0.5 * (-0.5 + Normal.Function(ah)) / h; y = 1.0 / hs; for (; ;) { value = value + zi * c2[i - 1]; if (m < i) { value = value * rrtpi * Math.Exp(-0.5 * hs); return(value); } zi = y * ((double)(ii) * zi - vi); vi = AS * vi; i = i + 1; ii = ii + 2; } } /* * t4(h, a, m) ; m = 4, 7, 8 or 20; ii = 2i + 1 * ai = a * exp[-h*h*(1+a*a)/2] * (-a*a)**i / (2*pi) */ else if (meth[icode - 1] == 4) { maxii = m + m + 1; ii = 1; hs = h * h; AS = -a * a; value = 0.0; ai = rtwopi * a * Math.Exp(-0.5 * hs * (1.0 - AS)); yi = 1.0; for (; ;) { value = value + ai * yi; if (maxii <= ii) { return(value); } ii = ii + 2; yi = (1.0 - hs * yi) / (double)(ii); ai = ai * AS; } } /* * t5(h, a, m) ; m = 13 * 2m - point gaussian quadrature */ else if (meth[icode - 1] == 5) { value = 0.0; AS = a * a; hs = -0.5 * h * h; for (i = 1; i <= m; i++) { r = 1.0 + AS * pts[i - 1]; value = value + wts[i - 1] * Math.Exp(hs * r) / r; } value = a * value; } /* * t6(h, a); approximation for a near 1, (a<=1) */ else if (meth[icode - 1] == 6) { normh = Normal.Complemented(h); value = 0.5 * normh * (1.0 - normh); y = 1.0 - a; r = Math.Atan(y / (1.0 + a)); if (r != 0.0) { value = value - rtwopi * r * Math.Exp(-0.5 * y * h * h / r); } } return(value); }
/// <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)); }