Symmetric Stable PDF (McCulloch 95) /************* Symmetric Stable PDF (McCulloch 95) ********************* Subject: Symmetric Stable PDF (McCulloch) Posted by: J. Huston McCulloch Economics Dept. Ohio State Univ. 1945 N. High St. Columbus, OH 43210 (614) 292-0382 mcculloch.2@osu.edu [Revised 9/20/95]. These programs were written by J. Huston McCulloch, June 1993. The code is written and submitted for public, non-commerical use, with no performance guarantees. The computation is described in J. Huston McCulloch, "Numerical Approximation of the Symmetric Stable Distributions and Densities," Ohio State Univ. Econ Dept, Oct. 1994, which is available from the author on request. Any research use of this code should cite this working paper. There are 3 similar programs: symstb1 returns the symmetric stable density s(x; alpha) only. symstb2 returns s(x; alpha) along with d/dx s(x; alpha). symstb3 returns the complemented cdf 1-S(x; alpha), along with s(x; alpha) and d/dx s(x; alpha). two procs, named poly and dgamma, follow symstb3, and are used by all 3 programs. Accuracy: The expected relative density precision is 1.0e-6 for alpha in the range [.84, 2.00]. The programs have considerably reduced precision for alpha < .84, although no error message is given for lower alpha values. The absolute precision of the density is 6.6e-5 for alpha in the range [..92, 2.00], while that of the distribution is 2.2e-5 in the same range. See paper for details. Speed: Changing alpha induces set-up calculations, so submit as many x values as you can before changing alpha. Submit x in long vectors if possible. Densities takes an average of 33 microseconds per ordinate on a P-5/100 using GAUSS 3.2.12 if ordinates are submitted to symstb1 in repeated vectors of length 1000 with a common alpha value. Symstb2 and symstb3 are respectively slower. Compatibility: The programs use several global variables. Their names have all been "uglified" by use of a terminal underscore (_) to make conflicts unlikely. GAUSS code: ************************************************************/ proc symstb1(x,alpha); @ June 1993: GAUSS version @ @ If you use this program, please cite @ @ J. Huston McCulloch, "Numerical Approximation of the Symmetric @ @ Stable Distribution and Density," OSU Econ Dept., Oct. 1994. @ @ x may be a column vector; alpha is a scalar @ @ returns sden = symmetric stable density @ declare s_, sqpi_, a2_, cpxp0_, gpxp0_, cpxpp0_, gpxpp0_; declare cppp_, gppp_, c5i_; declare a_, alf2_, alf1_, pd_, p_, pdd_; declare first_ = 1; declare oldalf_ = -999.99; local r, q, znot, zn4, zn5; local i, i1, j, j1; local pialf, sp0, sppp0, xp0, xpp0, xppp0, spzp1; local rp0, rpp0, rppp0, rp1, b, c; local xa1, xa1a, z, x1, x2, zp, x1p, x2p, rpz, sden; local x11, x2a1, x2pp, cd, gd; fn afun(alpha) = 2^(1/alpha) - 1; fn cden(x) = 1/(pi*(1+x.*x)); fn gden(x) = exp(maxc(-700*ones(1,rows(x)) | -(x.*x)'/4)) / (2*sqpi_); fn zfun(x, alpha, a) = 1 - (1 + a*x) ^ (-alpha); fn zpfun(x, alpha, a) = alpha * a * (1+a*x) ^ (-alpha -1); fn xfun(z, alpha, a) = ((1-z)^(-1/alpha) - 1) / a; if(not first_); goto starta; endif; @ this s is the gij of the writeup @ s_ = {1.8514190959D2, -4.6769332663D2, 4.8424720302D2, -1.7639153404D2, -3.0236552164D2, 7.6351931975D2, -7.8560342101D2, 2.8426313374D2, 4.4078923600D2, -1.1181138121D3, 1.1548311335D3, -4.1969666223D2, -5.2448142165D2, 1.3224487717D3, -1.3555648053D3, 4.8834079950D2, 5.3530435018D2, -1.3374570340D3, 1.3660140118D3, -4.9286099583D2, -4.8988957866D2, 1.2091418165D3, -1.2285872257D3, 4.4063174114D2, 3.2905528742D2, -7.3211767697D2, 6.8183641829D2, -2.2824291084D2, -2.1495402244D2, 3.9694906604D2, -3.3695710692D2, 1.0905855709D2, 2.1112581866D2, -2.7921107017D2, 1.1717966020D2, 3.4394664342D0, -2.6486798043D2, 1.1999093707D2, 2.1044841328D2, -1.5110881541D2, 9.4105784123D2, -1.7221988478D3, 1.4087544698D3, -4.2472511892D2, -2.1990475933D3, 4.2637720422D3, -3.4723981786D3, 1.0174373627D3, 3.1047490290D3, -5.4204210990D3, 4.2221052925D3, -1.2345971177D3, -5.1408260668D3, 1.1090264364D4, -1.0270337246D4, 3.4243449595D3, 1.1215157876D4, -2.4243529825D4, 2.1536057267D4, -6.8490996103D3, -1.8120631586D4, 3.1430132257D4, -2.4164285641D4, 6.9126862826D3, 1.7388413126D4, -2.2108397686D4, 1.3397999271D4, -3.1246611987D3, -7.2435775303D3, 4.3545399418D3, 2.3616155949D2, -7.6571653073D2, -8.7376725439D3, 1.5510852129D4, -1.3789764138D4, 4.6387417712D3}; s_ = reshape (s_,19, 4); sqpi_ = sqrt(pi); a2_ = afun(2); cpxp0_ = 1/pi; gpxp0_ = 1/(4*sqpi_*a2_); cpxpp0_ = 2/pi; gpxpp0_ = gpxp0_ * 1.5; cppp_ = 4/pi; gppp_ = gpxpp0_ * 2.5 - 1/(32*sqpi_*a2_^3); c5i_ = {1, 5, 10, 10, 5, 1}; first_ = 0; starta: if alpha > 2.000001; format /rdn 20,15; print "alpha too big: " alpha; end; endif; if alpha >= 2; sden = gden(x); retp(sden); endif; if alpha == oldalf_; goto startx; endif; znot = seqa(1,1,19) /20; zn4 = (1-znot)^4; zn5 = zn4 .* (1-znot); a_ = afun(alpha); alf2_ = 2 - alpha; alf1_ = alpha - 1; pialf = pi * alpha; sp0 = dgamma(1/alpha) / pialf; sppp0 = -dgamma(3/alpha) / pialf; xp0 = 1/(alpha * a_); xpp0 = xp0 * (1+alpha) / alpha; xppp0 = xpp0 * (1+2*alpha)/alpha; rp0 = -sp0 * xp0 + alf2_ * cpxp0_ + alf1_ * gpxp0_; rpp0 = -sp0 * xpp0 + alf2_ * cpxpp0_ + alf1_ * gpxpp0_; rppp0 = -sp0 * xppp0 - sppp0 * xp0^3 + alf2_ * cppp_ + alf1_ * gppp_; spzp1 = (a_^alpha) * dgamma(alpha) * sin(pialf / 2) / pi; rp1 = -spzp1 + alf2_ / pi; q = zeros(6,1); q[2] = rp0; q[3] = rpp0/2; q[4] = rppp0/6; r = alf2_ * (s_ * (alf2_^seqa(1,1,4) - 1)); b = -sumc(q[2:4]) - sumc(r.*zn5); c = rp1 - q[2:4]' * seqa(1,1,3) - 5 * sumc(r.*zn4); q[5] = 5*b-c; q[6] = c-4*b; p_ = zeros(6,20); i = 0; do until i > 5; i1 = i + 1; j = 1; do until j > 19; j1 = j + 1; p_[i1, j1] = p_[i1, j] + r[j] * (-znot[j])^(5-i); j = j1; endo; i = i1; endo; p_ = q + c5i_ .* p_; pd_ = p_[2:6,.] .* seqa (1,1,5); oldalf_ = alpha; startx: xa1 = 1 + a_ * abs(x); xa1a = xa1 ^ (-alpha); z = 1 - xa1a; zp = alpha * a_ * xa1a ./ xa1; x11 = 1/xa1a; x1 = x11 - 1; x1p = x11 .* x11; x2a1 = 1/sqrt(xa1a); x2 = (x2a1 -1) /a2_; x2p = x2a1 ./ (2*a2_ * xa1a); cd = cden (x1); gd = gden(x2); j1 = trunc(20 * z) + 1; rpz = poly(pd_[.,j1], z, 4); sden = (alf2_ * cd .* x1p + alf1_ * gd .* x2p - rpz) .* zp; retp (sden); endp; proc(2) = symstb2(x,alpha); @ June 1993: GAUSS version @ @ If you use this program, please cite @ @ J. Huston McCulloch, "Numerical Approximation of the Symmetric @ @ Stable Distribution and Density," OSU Econ Dept., Oct. 1994. @ @ x may be a column vector; alpha is a scalar @ @ returns {sden,sdenp} @ @ sden = symmetric stable density @ @ sdenp = derivative of same wrt x @ declare s_, sqpi_, a2_, cpxp0_, gpxp0_, cpxpp0_, gpxpp0_; declare cppp_, gppp_, c5i_; declare a_, alf2_, alf1_, pd_, p_, pdd_; declare first_ = 1; declare oldalf_ = -999.99; local r, q, znot, zn4, zn5; local i, i1, j, j1; local pialf, sp0, sppp0, xp0, xpp0, xppp0, spzp1; local rp0, rpp0, rppp0, rp1, b, c; local xx, xa1, xa1a, z, x1, x2, zp, x1p, x2p, rpz, sden; local sign, zpp, x11, x1pp, x2a1, x2pp, cd, cdp, gd, gdp, rppz, cgr, sdenp; fn afun(alpha) = 2^(1/alpha) - 1; fn cden(x) = 1/(pi*(1+x.*x)); fn gden(x) = exp(maxc(-700*ones(1,rows(x)) | -(x.*x)'/4)) / (2*sqpi_); fn zfun(x, alpha, a) = 1 - (1 + a*x) ^ (-alpha); fn zpfun(x, alpha, a) = alpha * a * (1+a*x) ^ (-alpha -1); fn xfun(z, alpha, a) = ((1-z)^(-1/alpha) - 1) / a; if(not first_); goto starta; endif; @ this s is the gij of the writeup @ s_ = {1.8514190959D2, -4.6769332663D2, 4.8424720302D2, -1.7639153404D2, -3.0236552164D2, 7.6351931975D2, -7.8560342101D2, 2.8426313374D2, 4.4078923600D2, -1.1181138121D3, 1.1548311335D3, -4.1969666223D2, -5.2448142165D2, 1.3224487717D3, -1.3555648053D3, 4.8834079950D2, 5.3530435018D2, -1.3374570340D3, 1.3660140118D3, -4.9286099583D2, -4.8988957866D2, 1.2091418165D3, -1.2285872257D3, 4.4063174114D2, 3.2905528742D2, -7.3211767697D2, 6.8183641829D2, -2.2824291084D2, -2.1495402244D2, 3.9694906604D2, -3.3695710692D2, 1.0905855709D2, 2.1112581866D2, -2.7921107017D2, 1.1717966020D2, 3.4394664342D0, -2.6486798043D2, 1.1999093707D2, 2.1044841328D2, -1.5110881541D2, 9.4105784123D2, -1.7221988478D3, 1.4087544698D3, -4.2472511892D2, -2.1990475933D3, 4.2637720422D3, -3.4723981786D3, 1.0174373627D3, 3.1047490290D3, -5.4204210990D3, 4.2221052925D3, -1.2345971177D3, -5.1408260668D3, 1.1090264364D4, -1.0270337246D4, 3.4243449595D3, 1.1215157876D4, -2.4243529825D4, 2.1536057267D4, -6.8490996103D3, -1.8120631586D4, 3.1430132257D4, -2.4164285641D4, 6.9126862826D3, 1.7388413126D4, -2.2108397686D4, 1.3397999271D4, -3.1246611987D3, -7.2435775303D3, 4.3545399418D3, 2.3616155949D2, -7.6571653073D2, -8.7376725439D3, 1.5510852129D4, -1.3789764138D4, 4.6387417712D3}; s_ = reshape (s_,19, 4); sqpi_ = sqrt(pi); a2_ = afun(2); cpxp0_ = 1/pi; gpxp0_ = 1/(4*sqpi_*a2_); cpxpp0_ = 2/pi; gpxpp0_ = gpxp0_ * 1.5; cppp_ = 4/pi; gppp_ = gpxpp0_ * 2.5 - 1/(32*sqpi_*a2_^3); c5i_ = {1, 5, 10, 10, 5, 1}; first_ = 0; starta: if alpha > 2.000001; format /rdn 20,15; print "alpha too big: " alpha; end; endif; if alpha >= 2; sden = gden(x); sdenp = -sden .* x / 2; retp(sden, sdenp); endif; if alpha == oldalf_; goto startx; endif; znot = seqa(1,1,19) /20; zn4 = (1-znot)^4; zn5 = zn4 .* (1-znot); a_ = afun(alpha); alf2_ = 2 - alpha; alf1_ = alpha - 1; pialf = pi * alpha; sp0 = dgamma(1/alpha) / pialf; sppp0 = -dgamma(3/alpha) / pialf; xp0 = 1/(alpha * a_); xpp0 = xp0 * (1+alpha) / alpha; xppp0 = xpp0 * (1+2*alpha)/alpha; spzp1 = (a_^alpha) * dgamma(alpha) * sin(pialf / 2) / pi; rp0 = -sp0 * xp0 + alf2_ * cpxp0_ + alf1_ * gpxp0_; rpp0 = -sp0 * xpp0 + alf2_ * cpxpp0_ + alf1_ * gpxpp0_; rppp0 = -sp0 * xppp0 - sppp0 * xp0^3 + alf2_ * cppp_ + alf1_ * gppp_; rp1 = -spzp1 + alf2_ / pi; q = zeros(6,1); q[2] = rp0; q[3] = rpp0/2; q[4] = rppp0/6; r = alf2_ * (s_ * (alf2_^seqa(1,1,4) - 1)); b = -sumc(q[2:4]) - sumc(r.*zn5); c = rp1 - q[2:4]' * seqa(1,1,3) - 5 * sumc(r.*zn4); q[5] = 5*b-c; q[6] = c-4*b; p_ = zeros(6,20); i = 0; do until i > 5; i1 = i + 1; j = 1; do until j > 19; j1 = j + 1; p_[i1, j1] = p_[i1, j] + r[j] * (-znot[j])^(5-i); j = j1; endo; i = i1; endo; p_ = q + c5i_ .* p_; pd_ = p_[2:6,.] .* seqa (1,1,5); pdd_ = pd_[2:5,.] .* seqa (1,1,4); oldalf_ = alpha; startx: sign = 2 * (x .>= zeros(rows(x),1)) - 1; xx = abs(x); xa1 = 1 + a_ * xx; xa1a = xa1 ^ (-alpha); z = 1 - xa1a; zp = alpha * a_ * xa1a ./ xa1; zpp = -(alpha + 1) * a_ * zp ./ xa1; x11 = 1/xa1a; x1 = x11 - 1; x1p = x11 .* x11; x1pp = 2 * x11 .* x1p; x2a1 = 1/sqrt(xa1a); x2 = (x2a1 - 1) / a2_; x2p = x2a1 ./ (2*a2_ * xa1a); x2pp = 1.5 * x2p ./ xa1a; cd = cden (x1); cdp = - 2 * pi * x1 .* cd .* cd; gd = gden(x2); gdp = - x2 .* gd / 2; j1 = trunc(20 * z) + 1; rpz = poly(pd_[.,j1], z, 4); rppz = poly(pdd_[.,j1], z, 3); cgr = alf2_ * cd .* x1p + alf1_ * gd .* x2p - rpz; sden = cgr .* zp; sdenp = sign .* (cgr .* zpp + zp .* zp .* (alf2_ * (cd.*x1pp + x1p.*x1p.*cdp) + alf1_ * (gd .* x2pp + x2p .* x2p .* gdp) -rppz)); retp (sden, sdenp); endp; proc(3) = symstb3(x,alpha); @ June 1993: GAUSS version @ @ If you use this program, please cite @ @ J. Huston McCulloch, "Numerical Approximation of the Symmetric @ @ Stable Distribution and Density," OSU Econ Dept., Oct. 1994. @ @ x may be a column vector; alpha is a scalar @ @ returns {sdisc,sden,sdenp} @ @ sdisc = complemented symmetric stable distribution @ @ sden = symmetric stable density @ @ sdenp = derivative of density wrt x @ declare s_, sqpi_, a2_, cpxp0_, gpxp0_, cpxpp0_, gpxpp0_, cppp_, gppp_, c5i_; declare a_, alf2_, alf1_, pd_, p_, pdd_, sq2_; declare first_ = 1; declare oldalf_ = -999.99; local r, q, znot, zn4, zn5; local i, i1, j, j1; local pialf, sp0, sppp0, xp0, xpp0, xppp0, spzp1; local rp0, rpp0, rppp0, rp1, b, c; local xx, xa1, xa1a, z, x1, x2, zp, x1p, x2p, rpz, sden, pos, sdisc, rz; local sign, zpp, x11, x1pp, x2a1, x2pp, cd, cdp, gd, gdp, rppz, cgr, sdenp; fn afun(alpha) = 2^(1/alpha) - 1; fn cden(x) = 1/(pi*(1+x.*x)); fn gden(x) = exp(maxc(-700*ones(1,rows(x)) | -(x.*x)'/4)) / (2*sqpi_); fn cfun(x) = .5 - atan(x) / pi; fn gfun(x) = cdfnc (x/sq2_); fn zfun(x, alpha, a) = 1 - (1 + a*x) ^ (-alpha); fn zpfun(x, alpha, a) = alpha * a * (1+a*x) ^ (-alpha -1); fn xfun(z, alpha, a) = ((1-z)^(-1/alpha) - 1) / a; if(not first_); goto starta; endif; @ this s is the gij of the writeup @ s_ = {1.8514190959D2, -4.6769332663D2, 4.8424720302D2, -1.7639153404D2, -3.0236552164D2, 7.6351931975D2, -7.8560342101D2, 2.8426313374D2, 4.4078923600D2, -1.1181138121D3, 1.1548311335D3, -4.1969666223D2, -5.2448142165D2, 1.3224487717D3, -1.3555648053D3, 4.8834079950D2, 5.3530435018D2, -1.3374570340D3, 1.3660140118D3, -4.9286099583D2, -4.8988957866D2, 1.2091418165D3, -1.2285872257D3, 4.4063174114D2, 3.2905528742D2, -7.3211767697D2, 6.8183641829D2, -2.2824291084D2, -2.1495402244D2, 3.9694906604D2, -3.3695710692D2, 1.0905855709D2, 2.1112581866D2, -2.7921107017D2, 1.1717966020D2, 3.4394664342D0, -2.6486798043D2, 1.1999093707D2, 2.1044841328D2, -1.5110881541D2, 9.4105784123D2, -1.7221988478D3, 1.4087544698D3, -4.2472511892D2, -2.1990475933D3, 4.2637720422D3, -3.4723981786D3, 1.0174373627D3, 3.1047490290D3, -5.4204210990D3, 4.2221052925D3, -1.2345971177D3, -5.1408260668D3, 1.1090264364D4, -1.0270337246D4, 3.4243449595D3, 1.1215157876D4, -2.4243529825D4, 2.1536057267D4, -6.8490996103D3, -1.8120631586D4, 3.1430132257D4, -2.4164285641D4, 6.9126862826D3, 1.7388413126D4, -2.2108397686D4, 1.3397999271D4, -3.1246611987D3, -7.2435775303D3, 4.3545399418D3, 2.3616155949D2, -7.6571653073D2, -8.7376725439D3, 1.5510852129D4, -1.3789764138D4, 4.6387417712D3}; s_ = reshape (s_,19, 4); sqpi_ = sqrt(pi); sq2_ = sqrt(2); a2_ = afun(2); cpxp0_ = 1/pi; gpxp0_ = 1/(4*sqpi_*a2_); cpxpp0_ = 2/pi; gpxpp0_ = gpxp0_ * 1.5; cppp_ = 4/pi; gppp_ = gpxpp0_ * 2.5 - 1/(32*sqpi_*a2_^3); c5i_ = {1, 5, 10, 10, 5, 1}; first_ = 0; starta: if alpha > 2.000001; format /rdn 20,15; print "alpha too big: " alpha; end; endif; if alpha >= 2; sdisc = gfun(x); sden = gden(x); sdenp = -sden .* x / 2; retp(sdisc, sden, sdenp); endif; if alpha == oldalf_; goto startx; endif; znot = seqa(1,1,19) /20; zn4 = (1-znot)^4; zn5 = zn4 .* (1-znot); a_ = afun(alpha); alf2_ = 2 - alpha; alf1_ = alpha - 1; pialf = pi * alpha; sp0 = dgamma(1/alpha) / pialf; sppp0 = -dgamma(3/alpha) / pialf; xp0 = 1/(alpha * a_); xpp0 = xp0 * (1+alpha) / alpha; xppp0 = xpp0 * (1+2*alpha)/alpha; spzp1 = (a_^alpha) * dgamma(alpha) * sin(pialf / 2) / pi; rp0 = -sp0 * xp0 + alf2_ * cpxp0_ + alf1_ * gpxp0_; rpp0 = -sp0 * xpp0 + alf2_ * cpxpp0_ + alf1_ * gpxpp0_; rppp0 = -sp0 * xppp0 - sppp0 * xp0^3 + alf2_ * cppp_ + alf1_ * gppp_; rp1 = -spzp1 + alf2_ / pi; q = zeros(6,1); q[2] = rp0; q[3] = rpp0/2; q[4] = rppp0/6; r = alf2_ * (s_ * (alf2_^seqa(1,1,4) - 1)); b = -sumc(q[2:4]) - sumc(r.*zn5); c = rp1 - q[2:4]' * seqa(1,1,3) - 5 * sumc(r.*zn4); q[5] = 5*b-c; q[6] = c-4*b; p_ = zeros(6,20); i = 0; do until i > 5; i1 = i + 1; j = 1; do until j > 19; j1 = j + 1; p_[i1, j1] = p_[i1, j] + r[j] * (-znot[j])^(5-i); j = j1; endo; i = i1; endo; p_ = q + c5i_ .* p_; pd_ = p_[2:6,.] .* seqa (1,1,5); pdd_ = pd_[2:5,.] .* seqa (1,1,4); oldalf_ = alpha; startx: pos = x .>= zeros(rows(x),1); sign = 2 * pos - 1; xx = abs(x); xa1 = 1 + a_ * xx; xa1a = xa1 ^ (-alpha); z = 1 - xa1a; zp = alpha * a_ * xa1a ./ xa1; zpp = -(alpha + 1) * a_ * zp ./ xa1; x11 = 1/xa1a; x1 = x11 - 1; x1p = x11 .* x11; x1pp = 2 * x11 .* x1p; x2a1 = 1/sqrt(xa1a); x2 = (x2a1 - 1) / a2_; x2p = x2a1 ./ (2*a2_ * xa1a); x2pp = 1.5 * x2p ./ xa1a; cd = cden (x1); cdp = - 2 * pi * x1 .* cd .* cd; gd = gden(x2); gdp = - x2 .* gd / 2; j1 = trunc(20 * z) + 1; rz = poly(p_[.,j1], z, 5); rpz = poly(pd_[.,j1], z, 4); rppz = poly(pdd_[.,j1], z, 3); cgr = alf2_ * cd .* x1p + alf1_ * gd .* x2p - rpz; sden = cgr .* zp; sdenp = sign .* (cgr .* zpp + zp .* zp .* (alf2_ * (cd.*x1pp + x1p.*x1p.*cdp) + alf1_ * (gd .* x2pp + x2p .* x2p .* gdp) -rppz)); sdisc = alf2_*cfun(x1) + alf1_*gfun(x2) + rz; sdisc = sdisc .* pos + (1-sdisc) .* (1-pos); retp (sdisc, sden, sdenp); endp; proc poly(a, x, k); local sum, j; sum = a[k+1,.]'; j = k; do until j ==0; sum = sum .* x + a[j,.]'; j = j - 1; endo; retp(sum); endp; proc dgamma(x); @ Note -- GAUSS 3.2+ has adopted an improved gamma function similar to this one, making this proc redundant. @ local t, ser, c, stp; c = {76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, .001208650973866179, -.5395239384953d-5}; stp = 2.5066282746310005; t = x + 5.5; t = t^(x+.5)/exp(t); ser = 1.000000000190015 + sumc(c./(x+seqa(1,1,6))); retp(t * stp * ser / x); endp;