STK++ 1.0
STK_Funct_gammaRatio.cpp
Go to the documentation of this file.
00001 /*--------------------------------------------------------------------*/
00002 /*  Copyright (C) 2004-2007  Serge Iovleff
00003     
00004     This program is free software; you can redistribute it and/or modify
00005     it under the terms of the GNU Lesser General Public License as
00006     published by the Free Software Foundation; either version 2 of the
00007     License, or (at your option) any later version.
00008     
00009     This program is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012     GNU Lesser General Public License for more details.
00013     
00014     You should have received a copy of the GNU Lesser General Public
00015     License along with this program; if not, write to the
00016     Free Software Foundation, Inc.,
00017     59 Temple Place,
00018     Suite 330,
00019     Boston, MA 02111-1307
00020     USA
00021     
00022     Contact : Serge.Iovleff@stkpp.org
00023 */
00024 
00025 /*
00026  * Project:  Analysis
00027  * Purpose:  Implementation of functions around the gamma function ratio
00028  * Author:   Serge Iovleff, serge.iovleff@stkpp.org
00029  **/
00030 
00036 #include "../include/STK_Funct_gammaRatio.h"
00037 
00038 #include "../include/STK_ISerie.h"
00039 #include "../include/STK_Algo.h"
00040 #include "../include/STK_Funct_gamma.h"
00041 #include "../include/STK_Funct_util.h"
00042 #include "../include/STK_Funct_raw.h"
00043 
00044 namespace STK
00045 {
00046 
00047 namespace Funct
00048 {
00064 static Real apois(Real const& a, Real const& b)
00065 {
00066   // special value
00067   if (b==0.) return( 0. );
00068   // stirling approximation and deviance
00069   return( sqrt(a)*exp(-gammaLnStirlingError(a)-dev0(a, b))
00070         * (Const::_1_SQRT2PI_)
00071         );
00072 }
00073 
00080 class Seriedl : public ISerie<Seriedl>
00081 { 
00082   private:
00083     const   Real& a_;
00084     const   Real& x_;
00085     mutable Real n_;
00086     mutable Real xn_factn_;
00087     mutable Real aplusn_;
00088   
00089   public:
00090     inline Seriedl(Real const& a, Real const& x)
00091                     : a_(a), x_(x)
00092                     , n_(1), xn_factn_(x), aplusn_(a+1.)
00093     { ;}
00094     
00095     inline Real first() const
00096     { return x_/aplusn_;}
00097 
00098     inline Real next() const
00099     {
00100       // n and x^n/ n!
00101       xn_factn_ *= (x_/(++n_));
00102       // x^n/((a+n)n!)
00103       return xn_factn_/(++aplusn_);
00104     }
00105 };
00106 
00124 static Real gammaRatio_dl( Real const& a
00125                          , Real const& x
00126                          , const bool &lower_tail
00127                          )
00128 {
00129   Seriedl f(a, x);
00130   if (lower_tail)
00131      return (1.- a*sumAlternateSerie(f))*poisson_pdf_raw(a, x)*exp(x);
00132   else
00133     return apois(a, x) * sumAlternateSerie(f) * exp(x)
00134          - expm1(a*log(x)-gammaLn(a+1.));
00135 }
00136 
00154 static Real gammaRatio_cf( Real const& a
00155                          , Real const& x
00156                          , const bool lower_tail
00157                          )
00158 {
00159   // initialize b_n
00160   Real bn = x + 1. -a; // b_1
00161   // initialize numerator
00162   Real Nold = 0, Ncur = 1.; // = a_1 = 1
00163   // initialize denominator
00164   Real Dold = 1, Dcur = bn; // = b_1
00165 
00166   // normalize if necessary by 2^32
00167   if (abs(Dcur) > 4294967296.0)
00168   {
00169     Ncur /= Dcur;
00170     Dold /= Dcur;
00171     Dcur = 1.;
00172   }
00173 
00174   // auxiliary variables
00175   Real aminusn = a;
00176   // result
00177   Real cf = Ncur/Dcur;
00178   // iterations
00179   for (Integer n=1; ; n++)
00180   {
00181     // compute a_n
00182     Real an = n*(--aminusn);
00183     // check trivial case
00184     // (note a_n =0 => b_n != 0 for x>0 and thus cf!=N_n/D_n exists)
00185     if (an == 0.) break;
00186     // update b_n
00187     bn += 2.;
00188     // compute numerator and denominator
00189     Real anew = bn * Ncur + an * Nold;
00190     Nold = Ncur;
00191     Ncur = anew;
00192     anew = bn * Dcur + an * Dold;
00193     Dold = Dcur;
00194     Dcur = anew;
00195     // normalize if necessary with 2^32
00196     if (abs(Dcur) > 4294967296.0)
00197     {
00198       Ncur /= Dcur;
00199       Nold /= Dcur;
00200       Dold /= Dcur;
00201       Dcur = 1.;
00202     }
00203     // normalize if necessary with 2^32
00204     if (abs(Ncur) > 4294967296.0)
00205     {
00206       Ncur /= 4294967296.0;
00207       Nold /= 4294967296.0;
00208       Dold /= 4294967296.0;
00209       Dcur /= 4294967296.0;
00210     }
00211     // test D_n not too small
00212     if (abs(Dcur) != 0)
00213     {
00214       Real cfold = cf;
00215       cf = Ncur/Dcur;
00216       // check cv: relative; absolute for small cf
00217       if (abs(cf - cfold) < abs(cf)*Arithmetic<Real>::epsilon())
00218         break;
00219     }
00220   }
00221   // cv
00222   return (lower_tail) ? (1 - apois(a, x)*cf)
00223                       : (apois(a, x)*cf);
00224 }
00225  
00240 static Real gammaRatio_sr( Real const& a
00241                          , Real const& x
00242                          , const bool &lower_tail
00243                          )
00244 {
00245   Real y = a+1., term = x / (a+1.),  sum = term;
00246   /* sum =  1+\sum_{n=1}^\infty x^n / (a+1)*(a+2)*...*(a+n))
00247    */
00248   do
00249   {
00250     y++;
00251     sum += (term *= x / y);
00252   }
00253   while (term > sum * Arithmetic<Real>::epsilon());
00254   sum+=1;
00255 
00256   if (lower_tail)
00257     return poisson_pdf_raw(a, x)*sum;
00258   else
00259     return 1.-poisson_pdf_raw(a, x)*sum;
00260 }
00261 
00282 static Real gammaRatio_ae( Real const& a
00283                          , Real const& x
00284                          , const bool &lower_tail
00285                          )
00286 {
00287   Real term = 1, sum = 0, b=a-1;
00288   // sum =  1+\sum_{n=1}^\infty (a-1)*...*(a - n) / x^n
00289   while ((b > 1.) && (term > sum * Arithmetic<Real>::epsilon()))
00290   {
00291     sum += (term *= b / x);
00292     b--;
00293   }
00294   sum+=1.;
00295   // achieved cv
00296   if (b>1.)
00297     return lower_tail ? (1.-poisson_pdf_raw(a-1, x)*sum)
00298                       : (   poisson_pdf_raw(a-1, x)*sum);
00299   // case (b>x) and (b <=1)
00300   if (b>x)
00301     return lower_tail ? (gammaRatio_sr(b, x, lower_tail)-poisson_pdf_raw(a-1, x)*sum)
00302                       : (gammaRatio_sr(b, x, lower_tail)+poisson_pdf_raw(a-1, x)*sum);
00303   // case (b <x) and (b <=1)
00304   return lower_tail ? gammaRatio_cf(b, x, lower_tail)-poisson_pdf_raw(a-1, x)*sum
00305                     : gammaRatio_cf(b, x, lower_tail)+poisson_pdf_raw(a-1, x)*sum;
00306 }
00307  
00321 static inline Real poisson_ae( Real const& a1
00322                              , Real const& apd
00323                              , const bool &lower_tail = true
00324                              )
00325 {
00326   // odd coefficients
00327   static const Real coefs_i[8] =
00328   {
00329     0., /* placeholder used for 1-indexing */
00330           2/3.,
00331          -4/135.,
00332           8/2835.,
00333          16/8505.,
00334       -8992/12629925.,
00335     -334144/492567075.,
00336      698752/1477701225.
00337   };
00338   // stirling coefficients
00339   static const Real coefs_s[8] =
00340   {
00341     0., /* placeholder used for 1-indexing */
00342              1/12.,
00343              1/288.,
00344           -139/51840.,
00345           -571/2488320.,
00346         163879/209018880.,
00347        5246819/75246796800.,
00348     -534703531/902961561600.
00349   };
00350   // compute D = -x(log(1+d/x) - d/x) = xlog(x/(x+d))+(x+d)-x 
00351   Real D   = dev0(a1, apd);
00352   // compute sqrt(2D)
00353   Real sqrt2D = sqrt (2. * D);
00354   // compute D/x
00355   Real D_x = D/a1;
00356 
00357   // treat negative difference
00358   if (apd - a1 < 0) sqrt2D = -sqrt2D;
00359 
00360   // variables for the numerator
00361   Real num = 0;
00362   Real sum1, num1_term, sum2, num2_term;
00363   num1_term = sum1 = sqrt(a1);
00364   num2_term = sum2 = sqrt2D;
00365 
00366   // variables for the denominator
00367   Real den = a1;
00368   Real den_term = 1;
00369   // computation
00370   for (Integer i = 1; i < 8; i++)
00371   {
00372     // compute the numerator
00373     num += num1_term * coefs_i[i];
00374     num += num2_term * coefs_s[i];
00375     // first sum
00376     sum1 *= (D_x / i) ;  // sqrt(x) * D^n/(n! x^n)
00377     num1_term = num1_term/a1 + sum1;
00378     // second sum
00379     sum2 *= (D_x / (i + 0.5)); // sqrt(2D) * D^n/((1+0.5)*...(n+0.5)*x^n)
00380     num2_term = num2_term/a1 + sum2;
00381     // compute the denominator
00382     den += den_term * coefs_s[i];
00383     den_term /= a1;
00384   }
00385   // result (P or Q ?)
00386    return lower_tail ? normal_cdf_raw( sqrt2D)
00387                      - (num / den) * normal_pdf_raw(sqrt2D)
00388                      : normal_cdf_raw(-sqrt2D)
00389                      + (num / den) * normal_pdf_raw(sqrt2D);
00390 }
00391  
00399 Real gammaRatioQ(Real const& a, Real const& x)
00400 {
00401   return gammaRatio(a, x, false);
00402 } 
00403  
00411 Real gammaRatioP(Real const& a, Real const& x)
00412 {
00413   return gammaRatio(a, x, true);
00414 }
00415  
00424 Real gammaRatio(Real const& a, Real const& x, const bool &lower_tail)
00425 {
00426   // Check if a and x are available
00427   if (Arithmetic<Real>::isNA(a)||Arithmetic<Real>::isNA(x)) return a;
00428   // Negative parameter not allowed
00429   if (a<=0)
00430     throw domain_error("Funct::gammaRatio(a,x,lower_tail) "
00431                        "Negative parameter a");
00432   // trivial case
00433   if (x<=0) return lower_tail ? 0 : 1.;
00434   if (Arithmetic<Real>::isInfinite(x)) return lower_tail ? 1. : 0.;
00435 
00436   // small values of x
00437   if (x <= 1.) return gammaRatio_dl(a, x, lower_tail);
00438 
00439   // large a compared to x
00440   if (0.75*a> x) return gammaRatio_sr(a, x, lower_tail);
00441 
00442   // large x compared to a 
00443   if (0.75*x > a)
00444   { // if a>1 we can use the a.e. before computing
00445     if (a>1) return gammaRatio_ae(a, x, lower_tail);
00446     else     return gammaRatio_cf(a, x, lower_tail);
00447   }
00448 
00449   // (a<100) and (x ~ a)
00450   if (a<100.)
00451   {
00452     // a greater than x
00453     if (a>x) return gammaRatio_sr(a, x, lower_tail);
00454     else
00455     { // if a>1 we can use the a.e. before computing
00456       if (a>1) return gammaRatio_ae(a, x, lower_tail);
00457       else     return gammaRatio_cf(a, x, lower_tail);}
00458     }
00459   // (x ~ a) and (a>=100)
00460   return poisson_ae(a-1, x, lower_tail);
00461 }
00462 } // namespace Funct
00463 
00464 } // namespace STK