stressMathMore.cxx

Go to the documentation of this file.
00001 // @(#)root/test:$Id: stressMathMore.cxx 33348 2010-05-03 15:20:49Z moneta $
00002 // Author: Lorenzo Moneta   06/2005 
00003 ///////////////////////////////////////////////////////////////////////////////////
00004 //
00005 //  MathMore Benchmark test suite
00006 //  ==============================
00007 //
00008 //  This program performs tests : 
00009 //     - numerical integration, derivation and root finders
00010 //    - it compares for various values of the gamma and beta distribution) 
00011 //          - the numerical calculated integral of pdf  with cdf function, 
00012 //           - the calculated derivative of cdf with pdf 
00013 //           - the inverse (using root finder) of cdf with quantile
00014 //
00015 //     to run the program outside ROOT do: 
00016 //        > make stressMathMore
00017 //        > ./stressMathMore
00018 // 
00019 //     to run the program in ROOT
00020 //       root> gSystem->Load("libMathMore") 
00021 //       root> .x stressMathMore.cxx+
00022 //
00023 
00024 #include "Math/DistFunc.h"
00025 #include "Math/IParamFunction.h"
00026 #include "Math/Integrator.h"
00027 #include "Math/Derivator.h"
00028 #include "Math/Functor.h"
00029 #include "Math/RootFinderAlgorithms.h"
00030 #include "Math/RootFinder.h"
00031 
00032 #include <iostream>
00033 #include <iomanip>
00034 #include <limits>
00035 #include <cmath>
00036 #include <stdlib.h>
00037 #include "TBenchmark.h"
00038 #include "TROOT.h"
00039 #include "TRandom3.h"
00040 #include "TSystem.h"
00041 #include "TF1.h"
00042 
00043 using namespace ROOT::Math; 
00044 
00045 
00046 
00047 
00048 #ifdef __CINT__
00049 #define INF 1.7E308
00050 #else  
00051 #define INF std::numeric_limits<double>::infinity() 
00052 #endif 
00053 
00054 //#define DEBUG
00055 
00056 bool debug = true;  // print out reason of test failures
00057 bool removeFiles = false; // remove Output root files 
00058 
00059 void PrintTest(std::string name) { 
00060    std::cout << std::left << std::setw(40) << name; 
00061 }
00062 
00063 void PrintStatus(int iret) { 
00064    if (iret == 0) 
00065       std::cout <<"\t\t................ OK" << std::endl;
00066    else  
00067       std::cout <<"\t\t............ FAILED " << std::endl;
00068 }
00069 
00070 
00071 int compare( std::string name, double v1, double v2, double scale = 2.0) {
00072   //  ntest = ntest + 1; 
00073 
00074    //std::cout << std::setw(50) << std::left << name << ":\t";   
00075    
00076   // numerical double limit for epsilon 
00077    double eps = scale* std::numeric_limits<double>::epsilon();
00078    int iret = 0; 
00079    double delta = v2 - v1;
00080    double d = 0;
00081    if (delta < 0 ) delta = - delta; 
00082    if (v1 == 0 || v2 == 0) { 
00083       if  (delta > eps ) { 
00084          iret = 1; 
00085       }
00086    }
00087    // skip case v1 or v2 is infinity
00088    else { 
00089       d = v1; 
00090 
00091       if ( v1 < 0) d = -d; 
00092       // add also case when delta is small by default
00093       if ( delta/d  > eps && delta > eps ) 
00094          iret =  1; 
00095    }
00096 
00097    if (iret) { 
00098       if (debug) { 
00099          int pr = std::cout.precision (18);
00100          std::cout << "\nDiscrepancy in " << name.c_str() << "() :\n  " << v1 << " != " << v2 << " discr = " << int(delta/d/eps) 
00101                    << "   (Allowed discrepancy is " << eps  << ")\n\n";
00102          std::cout.precision (pr);
00103       //nfail = nfail + 1;
00104       }
00105    }
00106    //else  
00107       //  std::cout <<".";
00108    
00109    return iret; 
00110 }
00111 
00112 // typedef for a free function like gamma(double x, double a, double b)
00113 // (dont have blank spaces between for not confusing CINT parser) 
00114 typedef double (*FreeFunc3)(double, double, double ); 
00115 typedef double (*FreeFunc4)(double, double, double, double ); 
00116 
00117 //implement simple functor
00118 struct Func { 
00119    virtual ~Func() {}
00120    virtual double operator() (double , double, double) const = 0; 
00121    
00122 };
00123 struct Func3 : public Func { 
00124    Func3(FreeFunc3 f) : fFunc(f) {};
00125    double operator() (double x, double a, double b) const {
00126       return fFunc(x,a,b);
00127    }  
00128    FreeFunc3 fFunc; 
00129 };
00130 
00131 struct Func4 : public Func { 
00132    Func4(FreeFunc4 f) : fFunc(f) {};
00133    double operator() (double x, double a, double b) const {
00134       return fFunc(x,a,b,0.);
00135    }  
00136    FreeFunc4 fFunc; 
00137 };
00138 
00139 
00140 
00141 // statistical function class 
00142 const int NPAR = 2; 
00143 
00144 class StatFunction : public ROOT::Math::IParamFunction { 
00145 
00146 public: 
00147 
00148    StatFunction(Func & pdf, Func & cdf, Func & quant, 
00149                 double x1 = -INF, 
00150                 double x2 = INF ) : 
00151       fPdf(&pdf), fCdf(&cdf), fQuant(&quant),
00152       xlow(x1), xup(x2), 
00153       fHasLowRange(false), fHasUpRange(false)
00154    {
00155       fScaleIg = 10; //scale for integral test
00156       fScaleDer = 1;  //scale for der test
00157       fScaleInv = 100;  //scale for inverse test
00158       for(int i = 0; i< NPAR; ++i) fParams[i]=0;
00159       NFuncTest = 100; 
00160       if (xlow > -INF) fHasLowRange = true;
00161       if (xup < INF) fHasUpRange = true;
00162    } 
00163 
00164 
00165 
00166    unsigned int NPar() const { return NPAR; } 
00167    const double * Parameters() const { return fParams; }
00168    ROOT::Math::IGenFunction * Clone() const { return new StatFunction(*fPdf,*fCdf,*fQuant); }
00169 
00170    void SetParameters(const double * p) { std::copy(p,p+NPAR,fParams); }
00171 
00172    void SetParameters(double p0, double p1) { *fParams = p0; *(fParams+1) = p1; }
00173 
00174    void SetTestRange(double x1, double x2) { xmin = x1; xmax = x2; }
00175    void SetNTest(int n) { NFuncTest = n; }
00176    void SetStartRoot(double x) { fStartRoot =x; }
00177 
00178 
00179    double Pdf(double x) const { 
00180       return (*this)(x); 
00181    }
00182 
00183    double Cdf(double x) const { 
00184       return  (*fCdf) ( x, fParams[0], fParams[1] ); 
00185    }
00186 
00187    double Quantile(double x) const { 
00188       return (*fQuant)( x, fParams[0], fParams[1]  ); 
00189    }
00190 
00191 
00192    // test integral with cdf function
00193    int TestIntegral(IntegrationOneDim::Type algotype); 
00194 
00195    // test derivative from cdf to pdf function
00196    int TestDerivative(); 
00197 
00198    // test root finding algorithm for finding inverse of cdf 
00199    int TestInverse1(RootFinder::EType algotype); 
00200 
00201    // test root finding algorithm for finding inverse of cdf using drivatives
00202    int TestInverse2(RootFinder::EType algotype); 
00203 
00204    
00205    void SetScaleIg(double s) { fScaleIg = s; }  
00206    void SetScaleDer(double s) { fScaleDer = s; }
00207    void SetScaleInv(double s) { fScaleInv = s; }
00208 
00209 
00210 private: 
00211 
00212 
00213    double DoEvalPar(double x, const double * ) const { 
00214       // use esplicity cached param values
00215       return (*fPdf)(x, *fParams, *(fParams+1)); 
00216    }
00217 
00218 //    std::auto_ptr<Func>  fPdf; 
00219 //    std::auto_ptr<Func>  fCdf; 
00220 //    std::auto_ptr<Func>  fQuant; 
00221    Func * fPdf; 
00222    Func *  fCdf; 
00223    Func *  fQuant; 
00224    double fParams[NPAR];
00225    double fScaleIg;
00226    double fScaleDer;
00227    double fScaleInv;
00228    int NFuncTest; 
00229    double xmin; 
00230    double xmax; 
00231    double xlow; 
00232    double xup; 
00233    bool fHasLowRange; 
00234    bool fHasUpRange; 
00235    double fStartRoot;
00236 };
00237 
00238 // test integral of function
00239 
00240 int StatFunction::TestIntegral(IntegrationOneDim::Type algoType = IntegrationOneDim::kADAPTIVESINGULAR) {
00241 
00242    int iret = 0; 
00243 
00244    // scan all values from xmin to xmax
00245    double dx = (xmax-xmin)/NFuncTest; 
00246 
00247    // create Integrator 
00248    Integrator ig(algoType, 1.E-12,1.E-12,100000);
00249    ig.SetFunction(*this);
00250 
00251    for (int i = 0; i < NFuncTest; ++i) { 
00252       double v1 = xmin + dx*i;  // value used  for testing
00253       double q1 = Cdf(v1);
00254       //std::cout << "v1 " << v1 << " pdf " << (*this)(v1) << " cdf " << q1 << " quantile " << Quantile(q1) << std::endl;  
00255       // calculate integral of pdf
00256       double q2 = 0; 
00257 
00258       // lower integral (cdf) 
00259       if (!fHasLowRange) 
00260          q2 = ig.IntegralLow(v1); 
00261       else 
00262          q2 = ig.Integral(xlow,v1); 
00263       
00264       int r  = ig.Status(); 
00265       // use a larger scale (integral error is 10-9)
00266       double err = ig.Error(); 
00267       //std::cout << "integral result is = " << q2 << " error is " << err << std::endl;
00268       // Gauss integral sometimes returns an error of 0
00269       err = std::max(err,  std::numeric_limits<double>::epsilon() );  
00270       double scale = std::max( fScaleIg * err / std::numeric_limits<double>::epsilon(), 1.);
00271       r |= compare("test integral", q1, q2, scale );
00272       if (r && debug)  { 
00273          std::cout << "Failed test for x = " << v1 << " q1= " << q1 << " q2= " << q2 << " p = "; 
00274          for (int j = 0; j < NPAR; ++j) std::cout << fParams[j] << "\t"; 
00275          std::cout << "ig error is " << err << " status " << ig.Status() << std::endl;
00276       } 
00277       iret |= r;
00278 
00279    }
00280    return iret; 
00281 
00282 }
00283 
00284 int StatFunction::TestDerivative() {
00285 
00286    int iret = 0; 
00287 
00288    // scan all values from xmin to xmax
00289    double dx = (xmax-xmin)/NFuncTest; 
00290    // create CDF function
00291    Functor1D func(this, &StatFunction::Cdf);
00292    Derivator d(func); 
00293 
00294    for (int i = 0; i < NFuncTest; ++i) { 
00295       double v1 = xmin + dx*i;  // value used  for testing
00296       double q1 = Pdf(v1);
00297       //std::cout << "v1 " << v1 << " pdf " << (*this)(v1) << " cdf " << q1 << " quantile " << Quantile(q1) << std::endl;  
00298       // calculate derivative of cdf
00299       double q2 = 0;
00300       if (fHasLowRange  && v1 == xlow) 
00301          q2 = d.EvalForward(v1);
00302       else if (fHasUpRange && v1 == xup) 
00303          q2 = d.EvalBackward(v1);
00304       else 
00305          q2 = d.Eval(v1); 
00306 
00307       int r = d.Status();
00308       double err = d.Error(); 
00309 
00310       double scale = std::max(1.,fScaleDer * err / std::numeric_limits<double>::epsilon() );
00311       
00312 
00313       r |= compare("test Derivative", q1, q2, scale );
00314       if (r && debug)  { 
00315          std::cout << "Failed test for x = " << v1 << " p = "; 
00316          for (int j = 0; j < NPAR; ++j) std::cout << fParams[j] << "\t"; 
00317          std::cout << "der error is " << err << std::endl;
00318          std::cout << d.Eval(v1) << "\t" << d.EvalForward(v1) << std::endl;
00319       } 
00320       iret |= r;
00321 
00322    }
00323    return iret; 
00324 
00325 }
00326 
00327 // function to be used in ROOT finding algorithm
00328 struct InvFunc { 
00329    InvFunc(const StatFunction * f, double y) : fFunc(f), fY(y)  {}
00330    double operator() (double x) { 
00331       return fFunc->Cdf(x) - fY; 
00332    }
00333    const StatFunction * fFunc; 
00334    double fY; 
00335 };
00336 
00337 
00338 int StatFunction::TestInverse1(RootFinder::EType algoType) {
00339 
00340    int iret = 0; 
00341    int maxitr = 2000;
00342    double abstol = 1.E-15;
00343    double reltol = 1.E-15;
00344    //NFuncTest = 4;
00345 
00346 
00347    // scan all values from 0.05 to 0.95  to avoid problem at the border of definitions
00348    double x1 = 0.05; double x2 = 0.95; 
00349    double dx = (x2-x1)/NFuncTest; 
00350    double vmin = Quantile(dx/2);
00351    double vmax = Quantile(1.-dx/2);
00352 
00353    // test ROOT finder algorithm function without derivative
00354    RootFinder  rf1(algoType); 
00355    for (int i = 1; i < NFuncTest; ++i) { 
00356       double v1 = x1 + dx*i;  // value used  for testing
00357       InvFunc finv(this,v1); 
00358       Functor1D func(finv); 
00359       rf1.SetFunction(func, vmin, vmax); 
00360       //std::cout << "\nfun values for :" << v1 << " f:  " << func(0.0) << "  " << func(1.0) << std::endl;
00361       int ret = ! rf1.Solve(maxitr,abstol,reltol); 
00362       if (ret && debug) { 
00363          std::cout << "\nError in solving for inverse, niter = " << rf1.Iterations() << std::endl;
00364       }
00365       double q1 = rf1.Root(); 
00366       // test that quantile value correspond: 
00367       double q2 = Quantile(v1); 
00368       
00369       ret |= compare("test Inverse1", q1, q2, fScaleInv );
00370       if (ret && debug)  { 
00371          std::cout << "\nFailed test for x = " << v1 << " p = "; 
00372          for (int j = 0; j < NPAR; ++j) std::cout << fParams[j] << "\t"; 
00373          std::cout << std::endl;
00374       } 
00375       iret |= ret;
00376 
00377    }
00378    return iret; 
00379 
00380 }
00381 
00382 int StatFunction::TestInverse2(RootFinder::EType algoType) {
00383 
00384    int iret = 0; 
00385    int maxitr = 2000;
00386    // put lower tolerance 
00387    double abstol = 1.E-12;
00388    double reltol = 1.E-12;
00389    //NFuncTest = 10;
00390 
00391    // scan all values from 0.05 to 0.95  to avoid problem at the border of definitions
00392    double x1 = 0.05; double x2 = 0.95; 
00393    double dx = (x2-x1)/NFuncTest; 
00394    // starting root is always on the left to avoid to go negative
00395    // it is very sensible at the starting point
00396    double vstart = fStartRoot; //depends on function shape
00397    // test ROOT finder algorithm function with derivative
00398    RootFinder rf1(algoType); 
00399    //RootFinder<Roots::Secant> rf1; 
00400 
00401    for (int i = 1; i < NFuncTest; ++i) { 
00402       double v1 = x1 + dx*i;  // value used  for testing
00403       
00404       InvFunc finv(this,v1); 
00405       //make a gradient function using inv function and derivative (which is pdf)
00406       GradFunctor1D func(finv,*this); 
00407       // use as estimate the quantile at 0.5
00408       //std::cout << "\nvstart : " << vstart << " fun/der values" << func(vstart) << "  " << func.Derivative(vstart) << std::endl;
00409       rf1.SetFunction(func,vstart ); 
00410       int ret = !rf1.Solve(maxitr,abstol,reltol); 
00411       if (ret && debug) { 
00412          std::cout << "\nError in solving for inverse using derivatives,  niter = " << rf1.Iterations() << std::endl;
00413       }
00414       double q1 = rf1.Root(); 
00415       // test that quantile value correspond: 
00416       double q2 = Quantile(v1); 
00417       
00418       ret |= compare("test InverseDeriv", q1, q2, fScaleInv );
00419       if (ret && debug)  { 
00420          std::cout << "Failed test for x = " << v1 << " p = "; 
00421          for (int j = 0; j < NPAR; ++j) std::cout << fParams[j] << "\t"; 
00422          std::cout << std::endl;
00423       } 
00424       iret |= ret;
00425 
00426    }
00427    return iret; 
00428 
00429 }
00430 
00431 // test intergal. derivative and inverse(Rootfinder) 
00432 int testGammaFunction(int n = 100) { 
00433 
00434    int iret = 0; 
00435 
00436    Func4 pdf(gamma_pdf);
00437    Func4 cdf(gamma_cdf);
00438    Func3 quantile(gamma_quantile);
00439    StatFunction dist(pdf, cdf, quantile, 0.); 
00440    dist.SetNTest(n);
00441    dist.SetTestRange(0.,10.);
00442    dist.SetScaleDer(10); // few tests fail here
00443    // vary shape of gamma parameter
00444    for (int i =1; i <= 5; ++i) { 
00445       double k = std::pow(2.,double(i-1)); 
00446       double theta = 2./double(i);
00447       dist.SetParameters(k,theta);
00448       if (k <=1 )
00449          dist.SetStartRoot(0.1);
00450       else
00451          dist.SetStartRoot(k*theta-1.);
00452 
00453       std::string name = "Gamma("+Util::ToString(int(k))+","+Util::ToString(theta)+") ";
00454       std::cout << "\nTest " << name << " distribution\n";
00455       int ret = 0;
00456 
00457       PrintTest("\t test integral GSL adaptive");
00458       ret = dist.TestIntegral(IntegrationOneDim::kADAPTIVESINGULAR);
00459       PrintStatus(ret);
00460       iret |= ret;
00461 
00462       PrintTest("\t test integral Gauss");
00463       dist.SetScaleIg(100); // relax for Gauss integral
00464       ret = dist.TestIntegral(IntegrationOneDim::kGAUSS);
00465       PrintStatus(ret);
00466       iret |= ret;
00467 
00468       PrintTest("\t test derivative");
00469       ret = dist.TestDerivative();
00470       PrintStatus(ret);
00471       iret |= ret;
00472 
00473       PrintTest("\t test inverse with GSL Brent method");
00474       ret = dist.TestInverse1(RootFinder::kGSL_BRENT);
00475       PrintStatus(ret);
00476       iret |= ret;
00477 
00478       PrintTest("\t test inverse with Steffenson algo");
00479       ret = dist.TestInverse2(RootFinder::kGSL_STEFFENSON);
00480       PrintStatus(ret);
00481       iret |= ret;
00482 
00483       PrintTest("\t test inverse with Brent method");
00484       dist.SetNTest(10);
00485       ret = dist.TestInverse1(RootFinder::kBRENT);
00486       PrintStatus(ret);
00487       iret |= ret;
00488 
00489    }
00490 
00491    return iret;
00492 }
00493 
00494 // test intergal. derivative and inverse(Rootfinder) 
00495 int testBetaFunction(int n = 100) { 
00496 
00497    int iret = 0; 
00498 
00499    Func3 pdf(beta_pdf);
00500    Func3 cdf(beta_cdf);
00501    Func3 quantile(beta_quantile);
00502    StatFunction dist(pdf, cdf, quantile, 0.,1.); 
00503 
00504    dist.SetNTest(n);
00505    dist.SetTestRange(0.,1.);
00506    // vary shape of beta function parameters
00507    for (int i = 0; i < 5; ++i) { 
00508       // avoid case alpha or beta = 1
00509       double alpha = i+2;
00510       double beta = 6-i; 
00511       dist.SetParameters(alpha,beta);
00512       dist.SetStartRoot(alpha/(alpha+beta)); // use mean value
00513 
00514       std::string name = "Beta("+Util::ToString(int(alpha))+","+Util::ToString(beta)+") ";
00515       std::cout << "\nTest " << name << " distribution\n";
00516       int ret = 0;
00517 
00518       PrintTest("\t test integral GSL adaptive");
00519       ret = dist.TestIntegral(IntegrationOneDim::kADAPTIVESINGULAR);
00520       PrintStatus(ret);
00521       iret |= ret;
00522 
00523       PrintTest("\t test integral Gauss");
00524       dist.SetScaleIg(100); // relax for Gauss integral
00525       ret = dist.TestIntegral(IntegrationOneDim::kGAUSS);
00526       PrintStatus(ret);
00527       iret |= ret;
00528 
00529       PrintTest("\t test derivative");
00530       ret = dist.TestDerivative();
00531       PrintStatus(ret);
00532       iret |= ret;
00533 
00534       PrintTest("\t test inverse with Brent method");
00535       ret = dist.TestInverse1(RootFinder::kBRENT);
00536       PrintStatus(ret);
00537       iret |= ret;
00538 
00539       PrintTest("\t test inverse with GSL Brent method");
00540       ret = dist.TestInverse1(RootFinder::kGSL_BRENT);
00541       PrintStatus(ret);
00542       iret |= ret;
00543 
00544       if (i < 5) {  // test failed for k=5
00545          PrintTest("\t test inverse with Steffenson algo");
00546          ret = dist.TestInverse2(RootFinder::kGSL_STEFFENSON);
00547          PrintStatus(ret);
00548          iret |= ret;
00549       }
00550    }
00551 
00552    return iret;
00553 }
00554 
00555 
00556 int stressMathMore(double nscale = 1) { 
00557 
00558    int iret = 0; 
00559 
00560 #ifdef __CINT__
00561    std::cout << "Test must be run in compile mode - please use ACLIC !!" << std::endl; 
00562    return 0; 
00563 #endif
00564 
00565    TBenchmark bm;
00566    bm.Start("stressMathMore");
00567    
00568    const int ntest = 10000; 
00569    int n = int(nscale*ntest);
00570    //std::cout << "StressMathMore: test number  n = " << n << std::endl;
00571 
00572    iret |= testGammaFunction(n);
00573    iret |= testBetaFunction(n);
00574 
00575    bm.Stop("stressMathMore");
00576    std::cout <<"******************************************************************************\n";
00577    bm.Print("stressMathMore");
00578    const double reftime = 7.24; //to be updated  // ref time on  pcbrun4
00579    double rootmarks = 860 * reftime / bm.GetCpuTime("stressMathMore");
00580    std::cout << " ROOTMARKS = " << rootmarks << " ROOT version: " << gROOT->GetVersion() << "\t" 
00581              << gROOT->GetSvnBranch() << "@" << gROOT->GetSvnRevision() << std::endl;
00582    std::cout <<"*******************************************************************************\n";
00583 
00584 
00585    if (iret !=0) std::cerr << "stressMathMore Test Failed !!" << std::endl;
00586    return iret; 
00587 }
00588 
00589 
00590 int main(int argc,const char *argv[]) { 
00591    double nscale = 1;
00592    if (argc > 1) { 
00593       nscale = atof(argv[1]);
00594       //nscale = std::pow(10.0,double(scale));
00595    } 
00596    return stressMathMore(nscale);
00597 }

Generated on Tue Jul 5 15:15:35 2011 for ROOT_528-00b_version by  doxygen 1.5.1