SpecFuncCephesInv.cxx

Go to the documentation of this file.
00001 // inverse of gamma and beta from Cephes library
00002 // see:  http://www.netlib.org/cephes
00003 // 
00004 // Copyright 1985, 1987, 2000 by Stephen L. Moshier
00005 
00006 
00007 
00008 #include "Math/Error.h"
00009 
00010 #include "SpecFuncCephes.h"
00011 
00012 #include <cmath>
00013 
00014 #include <limits> 
00015 
00016 namespace ROOT { 
00017 namespace Math { 
00018 
00019 namespace Cephes { 
00020 
00021 
00022 
00023 /*                                                      
00024  *
00025  *      Inverse of Normal distribution function
00026  *
00027  *
00028  *
00029  * SYNOPSIS:
00030  *
00031  * double x, y, ndtri();
00032  *
00033  * x = ndtri( y );
00034  *
00035  *
00036  *
00037  * DESCRIPTION:
00038  *
00039  * Returns the argument, x, for which the area under the
00040  * Gaussian probability density function (integrated from
00041  * minus infinity to x) is equal to y.
00042  *
00043  *
00044  * For small arguments 0 < y < exp(-2), the program computes
00045  * z = sqrt( -2.0 * log(y) );  then the approximation is
00046  * x = z - log(z)/z  - (1/z) P(1/z) / Q(1/z).
00047  * There are two rational functions P/Q, one for 0 < y < exp(-32)
00048  * and the other for y up to exp(-2).  For larger arguments,
00049  * w = y - 0.5, and  x/sqrt(2pi) = w + w**3 R(w**2)/S(w**2)).
00050  *
00051  *
00052  * ACCURACY:
00053  *
00054  *                      Relative error:
00055  * arithmetic   domain        # trials      peak         rms
00056  *    DEC      0.125, 1         5500       9.5e-17     2.1e-17
00057  *    DEC      6e-39, 0.135     3500       5.7e-17     1.3e-17
00058  *    IEEE     0.125, 1        20000       7.2e-16     1.3e-16
00059  *    IEEE     3e-308, 0.135   50000       4.6e-16     9.8e-17
00060  *
00061  *
00062  * ERROR MESSAGES:
00063  *
00064  *   message         condition    value returned
00065  * ndtri domain       x <= 0        -MAXNUM
00066  * ndtri domain       x >= 1         MAXNUM
00067  *
00068  */
00069 
00070 /*
00071 Cephes Math Library Release 2.8:  June, 2000
00072 Copyright 1984, 1987, 1989, 2000 by Stephen L. Moshier
00073 */
00074 
00075 
00076 static double s2pi = 2.50662827463100050242E0;
00077   
00078 static double P0[5] = {
00079 -5.99633501014107895267E1,
00080  9.80010754185999661536E1,
00081 -5.66762857469070293439E1,
00082  1.39312609387279679503E1,
00083 -1.23916583867381258016E0,
00084 };
00085 static double Q0[8] = {
00086  1.95448858338141759834E0,
00087  4.67627912898881538453E0,
00088  8.63602421390890590575E1,
00089 -2.25462687854119370527E2,
00090  2.00260212380060660359E2,
00091 -8.20372256168333339912E1,
00092  1.59056225126211695515E1,
00093 -1.18331621121330003142E0,
00094 };
00095 static double P1[9] = {
00096  4.05544892305962419923E0,
00097  3.15251094599893866154E1,
00098  5.71628192246421288162E1,
00099  4.40805073893200834700E1,
00100  1.46849561928858024014E1,
00101  2.18663306850790267539E0,
00102 -1.40256079171354495875E-1,
00103 -3.50424626827848203418E-2,
00104 -8.57456785154685413611E-4,
00105 };
00106 static double Q1[8] = {
00107  1.57799883256466749731E1,
00108  4.53907635128879210584E1,
00109  4.13172038254672030440E1,
00110  1.50425385692907503408E1,
00111  2.50464946208309415979E0,
00112 -1.42182922854787788574E-1,
00113 -3.80806407691578277194E-2,
00114 -9.33259480895457427372E-4,
00115 };
00116 static double P2[9] = {
00117   3.23774891776946035970E0,
00118   6.91522889068984211695E0,
00119   3.93881025292474443415E0,
00120   1.33303460815807542389E0,
00121   2.01485389549179081538E-1,
00122   1.23716634817820021358E-2,
00123   3.01581553508235416007E-4,
00124   2.65806974686737550832E-6,
00125   6.23974539184983293730E-9,
00126 };
00127 static double Q2[8] = {
00128   6.02427039364742014255E0,
00129   3.67983563856160859403E0,
00130   1.37702099489081330271E0,
00131   2.16236993594496635890E-1,
00132   1.34204006088543189037E-2,
00133   3.28014464682127739104E-4,
00134   2.89247864745380683936E-6,
00135   6.79019408009981274425E-9,
00136 };
00137 double ndtri( double y0 )
00138 {
00139    double x, y, z, y2, x0, x1;
00140    int code;
00141    if( y0 <= 0.0 )
00142       return( - std::numeric_limits<double>::infinity() );
00143    if( y0 >= 1.0 )
00144       return( + std::numeric_limits<double>::infinity() );
00145    code = 1;
00146    y = y0;
00147    if( y > (1.0 - 0.13533528323661269189) ) 
00148    {
00149       y = 1.0 - y;
00150       code = 0;
00151    }
00152    if( y > 0.13533528323661269189 )
00153    {
00154       y = y - 0.5;
00155       y2 = y * y;
00156       x = y + y * (y2 * Polynomialeval( y2, P0, 4)/ Polynomial1eval( y2, Q0, 8 ));
00157       x = x * s2pi; 
00158       return(x);
00159    }
00160    x = std::sqrt( -2.0 * std::log(y) );
00161    x0 = x - std::log(x)/x;
00162    z = 1.0/x;
00163    if( x < 8.0 ) 
00164       x1 = z * Polynomialeval( z, P1, 8 )/ Polynomial1eval ( z, Q1, 8 );
00165    else
00166       x1 = z * Polynomialeval( z, P2, 8 )/ Polynomial1eval( z, Q2, 8 );
00167    x = x0 - x1;
00168    if( code != 0 )
00169       x = -x;
00170    return( x );
00171 }
00172 
00173 
00174 
00175 
00176 /*                                                      
00177  *
00178  *      Inverse of complemented imcomplete gamma integral
00179  *
00180  *
00181  *
00182  * SYNOPSIS:
00183  *
00184  * double a, x, p, igami();
00185  *
00186  * x = igami( a, p );
00187  *
00188  * DESCRIPTION:
00189  *
00190  * Given p, the function finds x such that
00191  *
00192  *  igamc( a, x ) = p.
00193  *
00194  * Starting with the approximate value
00195  *
00196  *         3
00197  *  x = a t
00198  *
00199  *  where
00200  *
00201  *  t = 1 - d - ndtri(p) sqrt(d)
00202  * 
00203  * and
00204  *
00205  *  d = 1/9a,
00206  *
00207  * the routine performs up to 10 Newton iterations to find the
00208  * root of igamc(a,x) - p = 0.
00209  *
00210  * ACCURACY:
00211  *
00212  * Tested at random a, p in the intervals indicated.
00213  *
00214  *                a        p                      Relative error:
00215  * arithmetic   domain   domain     # trials      peak         rms
00216  *    IEEE     0.5,100   0,0.5       100000       1.0e-14     1.7e-15
00217  *    IEEE     0.01,0.5  0,0.5       100000       9.0e-14     3.4e-15
00218  *    IEEE    0.5,10000  0,0.5        20000       2.3e-13     3.8e-14
00219  */
00220 /*
00221 Cephes Math Library Release 2.8:  June, 2000
00222 Copyright 1984, 1987, 1995, 2000 by Stephen L. Moshier
00223 */
00224 
00225 double igami( double a, double y0 )
00226 {
00227    double x0, x1, x, yl, yh, y, d, lgm, dithresh;
00228    int i, dir;
00229 
00230    // check the domain
00231    if (a<= 0) { 
00232       MATH_ERROR_MSG("Cephes::igami","Wrong domain for parameter a (must be > 0)"); 
00233       return 0; 
00234    }
00235    if (y0 <= 0) { 
00236       //if (y0<0) MATH_ERROR_MSG("Cephes::igami","Wrong domain for y (must be in [0,1])"); 
00237       return std::numeric_limits<double>::infinity();
00238    }
00239    if (y0 >= 1) { 
00240       //if (y0>1) MATH_ERROR_MSG("Cephes::igami","Wrong domain for y (must be in [0,1])"); 
00241       return 0; 
00242    }
00243       
00244 
00245 /* bound the solution */
00246    static double kMAXNUM = std::numeric_limits<double>::max(); 
00247    x0 = kMAXNUM;
00248    yl = 0;
00249    x1 = 0;
00250    yh = 1.0;
00251    dithresh = 5.0 * kMACHEP;
00252 
00253 /* approximation to inverse function */
00254    d = 1.0/(9.0*a);
00255    y = ( 1.0 - d - ndtri(y0) * std::sqrt(d) );
00256    x = a * y * y * y;
00257 
00258    lgm = lgam(a);
00259 
00260    for( i=0; i<10; i++ )
00261    {
00262       if( x > x0 || x < x1 )
00263          goto ihalve;
00264       y = igamc(a,x);
00265       if( y < yl || y > yh )
00266          goto ihalve;
00267       if( y < y0 )
00268       {
00269          x0 = x;
00270          yl = y;
00271       }
00272       else
00273       {
00274          x1 = x;
00275          yh = y;
00276       }
00277 /* compute the derivative of the function at this point */
00278       d = (a - 1.0) * std::log(x) - x - lgm;
00279       if( d < -kMAXLOG )
00280          goto ihalve;
00281       d = -std::exp(d);
00282 /* compute the step to the next approximation of x */
00283       d = (y - y0)/d;
00284       if( std::fabs(d/x) < kMACHEP )
00285          goto done;
00286       x = x - d;
00287    }
00288 
00289 /* Resort to interval halving if Newton iteration did not converge. */
00290 ihalve:
00291 
00292    d = 0.0625;
00293    if( x0 == kMAXNUM )
00294    {
00295       if( x <= 0.0 )
00296          x = 1.0;
00297       while( x0 == kMAXNUM )
00298       {
00299          x = (1.0 + d) * x;
00300          y = igamc( a, x );
00301          if( y < y0 )
00302          {
00303             x0 = x;
00304             yl = y;
00305             break;
00306          }
00307          d = d + d;
00308       }
00309    }
00310    d = 0.5;
00311    dir = 0;
00312 
00313    for( i=0; i<400; i++ )
00314    {
00315       x = x1  +  d * (x0 - x1);
00316       y = igamc( a, x );
00317       lgm = (x0 - x1)/(x1 + x0);
00318       if( std::fabs(lgm) < dithresh )
00319          break;
00320       lgm = (y - y0)/y0;
00321       if( std::fabs(lgm) < dithresh )
00322          break;
00323       if( x <= 0.0 )
00324          break;
00325       if( y >= y0 )
00326       {
00327          x1 = x;
00328          yh = y;
00329          if( dir < 0 )
00330          {
00331             dir = 0;
00332             d = 0.5;
00333          }
00334          else if( dir > 1 )
00335             d = 0.5 * d + 0.5; 
00336          else
00337             d = (y0 - yl)/(yh - yl);
00338          dir += 1;
00339       }
00340       else
00341       {
00342          x0 = x;
00343          yl = y;
00344          if( dir > 0 )
00345          {
00346             dir = 0;
00347             d = 0.5;
00348          }
00349          else if( dir < -1 )
00350             d = 0.5 * d;
00351          else
00352             d = (y0 - yl)/(yh - yl);
00353          dir -= 1;
00354       }
00355    }
00356 
00357 //    if( x == 0.0 )
00358 //       mtherr( "igami", UNDERFLOW );
00359 
00360 done:
00361    return( x );
00362 }
00363 
00364 
00365 /*                                                      
00366  *
00367  *      Inverse of imcomplete beta integral
00368  *
00369  *
00370  *
00371  * SYNOPSIS:
00372  *
00373  * double a, b, x, y, incbi();
00374  *
00375  * x = incbi( a, b, y );
00376  *
00377  *
00378  *
00379  * DESCRIPTION:
00380  *
00381  * Given y, the function finds x such that
00382  *
00383  *  incbet( a, b, x ) = y .
00384  *
00385  * The routine performs interval halving or Newton iterations to find the
00386  * root of incbet(a,b,x) - y = 0.
00387  *
00388  *
00389  * ACCURACY:
00390  *
00391  *                      Relative error:
00392  *                x     a,b
00393  * arithmetic   domain  domain  # trials    peak       rms
00394  *    IEEE      0,1    .5,10000   50000    5.8e-12   1.3e-13
00395  *    IEEE      0,1   .25,100    100000    1.8e-13   3.9e-15
00396  *    IEEE      0,1     0,5       50000    1.1e-12   5.5e-15
00397  *    VAX       0,1    .5,100     25000    3.5e-14   1.1e-15
00398  * With a and b constrained to half-integer or integer values:
00399  *    IEEE      0,1    .5,10000   50000    5.8e-12   1.1e-13
00400  *    IEEE      0,1    .5,100    100000    1.7e-14   7.9e-16
00401  * With a = .5, b constrained to half-integer or integer values:
00402  *    IEEE      0,1    .5,10000   10000    8.3e-11   1.0e-11
00403  */
00404 
00405 /*
00406 Cephes Math Library Release 2.8:  June, 2000
00407 Copyright 1984, 1996, 2000 by Stephen L. Moshier
00408 */
00409 
00410 
00411 double incbi( double aa, double bb, double yy0 )
00412 {
00413    double a, b, y0, d, y, x, x0, x1, lgm, yp, di, dithresh, yl, yh, xt;
00414    int i, rflg, dir, nflg;
00415 
00416    // check the domain
00417    if (aa<= 0) { 
00418       MATH_ERROR_MSG("Cephes::incbi","Wrong domain for parameter a (must be > 0)"); 
00419       return 0; 
00420    }
00421    if (bb<= 0) { 
00422       MATH_ERROR_MSG("Cephes::incbi","Wrong domain for parameter b (must be > 0)"); 
00423       return 0; 
00424    }
00425 
00426 
00427    i = 0;
00428    if( yy0 <= 0 )
00429       return(0.0);
00430    if( yy0 >= 1.0 )
00431       return(1.0);
00432    x0 = 0.0;
00433    yl = 0.0;
00434    x1 = 1.0;
00435    yh = 1.0;
00436    nflg = 0;
00437 
00438    if( aa <= 1.0 || bb <= 1.0 )
00439    {
00440       dithresh = 1.0e-6;
00441       rflg = 0;
00442       a = aa;
00443       b = bb;
00444       y0 = yy0;
00445       x = a/(a+b);
00446       y = incbet( a, b, x );
00447       goto ihalve;
00448    }
00449    else
00450    {
00451       dithresh = 1.0e-4;
00452    }
00453 /* approximation to inverse function */
00454 
00455    yp = -ndtri(yy0);
00456 
00457    if( yy0 > 0.5 )
00458    {
00459       rflg = 1;
00460       a = bb;
00461       b = aa;
00462       y0 = 1.0 - yy0;
00463       yp = -yp;
00464    }
00465    else
00466    {
00467       rflg = 0;
00468       a = aa;
00469       b = bb;
00470       y0 = yy0;
00471    }
00472 
00473    lgm = (yp * yp - 3.0)/6.0;
00474    x = 2.0/( 1.0/(2.0*a-1.0)  +  1.0/(2.0*b-1.0) );
00475    d = yp * std::sqrt( x + lgm ) / x
00476       - ( 1.0/(2.0*b-1.0) - 1.0/(2.0*a-1.0) )
00477       * (lgm + 5.0/6.0 - 2.0/(3.0*x));
00478    d = 2.0 * d;
00479    if( d < kMINLOG )
00480    {
00481       x = 1.0;
00482       goto under;
00483    }
00484    x = a/( a + b * std::exp(d) );
00485    y = incbet( a, b, x );
00486    yp = (y - y0)/y0;
00487    if( std::fabs(yp) < 0.2 )
00488       goto newt;
00489 
00490 /* Resort to interval halving if not close enough. */
00491 ihalve:
00492 
00493    dir = 0;
00494    di = 0.5;
00495    for( i=0; i<100; i++ )
00496    {
00497       if( i != 0 )
00498       {
00499          x = x0  +  di * (x1 - x0);
00500          if( x == 1.0 )
00501             x = 1.0 - kMACHEP;
00502          if( x == 0.0 )
00503          {
00504             di = 0.5;
00505             x = x0  +  di * (x1 - x0);
00506             if( x == 0.0 )
00507                goto under;
00508          }
00509          y = incbet( a, b, x );
00510          yp = (x1 - x0)/(x1 + x0);
00511          if( std::fabs(yp) < dithresh )
00512             goto newt;
00513          yp = (y-y0)/y0;
00514          if( std::fabs(yp) < dithresh )
00515             goto newt;
00516       }
00517       if( y < y0 )
00518       {
00519          x0 = x;
00520          yl = y;
00521          if( dir < 0 )
00522          {
00523             dir = 0;
00524             di = 0.5;
00525          }
00526          else if( dir > 3 )
00527             di = 1.0 - (1.0 - di) * (1.0 - di);
00528          else if( dir > 1 )
00529             di = 0.5 * di + 0.5; 
00530          else
00531             di = (y0 - y)/(yh - yl);
00532          dir += 1;
00533          if( x0 > 0.75 )
00534          {
00535             if( rflg == 1 )
00536             {
00537                rflg = 0;
00538                a = aa;
00539                b = bb;
00540                y0 = yy0;
00541             }
00542             else
00543             {
00544                rflg = 1;
00545                a = bb;
00546                b = aa;
00547                y0 = 1.0 - yy0;
00548             }
00549             x = 1.0 - x;
00550             y = incbet( a, b, x );
00551             x0 = 0.0;
00552             yl = 0.0;
00553             x1 = 1.0;
00554             yh = 1.0;
00555             goto ihalve;
00556          }
00557       }
00558       else
00559       {
00560          x1 = x;
00561          if( rflg == 1 && x1 < kMACHEP )
00562          {
00563             x = 0.0;
00564             goto done;
00565          }
00566          yh = y;
00567          if( dir > 0 )
00568          {
00569             dir = 0;
00570             di = 0.5;
00571          }
00572          else if( dir < -3 )
00573             di = di * di;
00574          else if( dir < -1 )
00575             di = 0.5 * di;
00576          else
00577             di = (y - y0)/(yh - yl);
00578          dir -= 1;
00579       }
00580    }
00581    //mtherr( "incbi", PLOSS );
00582    if( x0 >= 1.0 )
00583    {
00584       x = 1.0 - kMACHEP;
00585       goto done;
00586    }
00587    if( x <= 0.0 )
00588    {
00589    under:
00590       //mtherr( "incbi", UNDERFLOW );
00591       x = 0.0;
00592       goto done;
00593    }
00594 
00595 newt:
00596 
00597    if( nflg )
00598       goto done;
00599    nflg = 1;
00600    lgm = lgam(a+b) - lgam(a) - lgam(b);
00601 
00602    for( i=0; i<8; i++ )
00603    {
00604       /* Compute the function at this point. */
00605       if( i != 0 )
00606          y = incbet(a,b,x);
00607       if( y < yl )
00608       {
00609          x = x0;
00610          y = yl;
00611       }
00612       else if( y > yh )
00613       {
00614          x = x1;
00615          y = yh;
00616       }
00617       else if( y < y0 )
00618       {
00619          x0 = x;
00620          yl = y;
00621       }
00622       else
00623       {
00624          x1 = x;
00625          yh = y;
00626       }
00627       if( x == 1.0 || x == 0.0 )
00628          break;
00629       /* Compute the derivative of the function at this point. */
00630       d = (a - 1.0) * std::log(x) + (b - 1.0) * std::log(1.0-x) + lgm;
00631       if( d < kMINLOG )
00632          goto done;
00633       if( d > kMAXLOG )
00634          break;
00635       d = std::exp(d);
00636       /* Compute the step to the next approximation of x. */
00637       d = (y - y0)/d;
00638       xt = x - d;
00639       if( xt <= x0 )
00640       {
00641          y = (x - x0) / (x1 - x0);
00642          xt = x0 + 0.5 * y * (x - x0);
00643          if( xt <= 0.0 )
00644             break;
00645       }
00646       if( xt >= x1 )
00647       {
00648          y = (x1 - x) / (x1 - x0);
00649          xt = x1 - 0.5 * y * (x1 - x);
00650          if( xt >= 1.0 )
00651             break;
00652       }
00653       x = xt;
00654       if( std::fabs(d/x) < 128.0 * kMACHEP )
00655          goto done;
00656    }
00657 /* Did not converge.  */
00658    dithresh = 256.0 * kMACHEP;
00659    goto ihalve;
00660 
00661 done:
00662 
00663    if( rflg )
00664    {
00665       if( x <= kMACHEP )
00666          x = 1.0 - kMACHEP;
00667       else
00668          x = 1.0 - x;
00669    }
00670    return( x );
00671 }
00672 
00673 } // end namespace Cephes
00674 
00675 } // end namespace Math
00676 } // end namespace ROOT

Generated on Tue Jul 5 14:34:26 2011 for ROOT_528-00b_version by  doxygen 1.5.1