00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
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
00055
00056 bool debug = true;
00057 bool removeFiles = false;
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
00073
00074
00075
00076
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
00088 else {
00089 d = v1;
00090
00091 if ( v1 < 0) d = -d;
00092
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
00104 }
00105 }
00106
00107
00108
00109 return iret;
00110 }
00111
00112
00113
00114 typedef double (*FreeFunc3)(double, double, double );
00115 typedef double (*FreeFunc4)(double, double, double, double );
00116
00117
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
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;
00156 fScaleDer = 1;
00157 fScaleInv = 100;
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
00193 int TestIntegral(IntegrationOneDim::Type algotype);
00194
00195
00196 int TestDerivative();
00197
00198
00199 int TestInverse1(RootFinder::EType algotype);
00200
00201
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
00215 return (*fPdf)(x, *fParams, *(fParams+1));
00216 }
00217
00218
00219
00220
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
00239
00240 int StatFunction::TestIntegral(IntegrationOneDim::Type algoType = IntegrationOneDim::kADAPTIVESINGULAR) {
00241
00242 int iret = 0;
00243
00244
00245 double dx = (xmax-xmin)/NFuncTest;
00246
00247
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;
00253 double q1 = Cdf(v1);
00254
00255
00256 double q2 = 0;
00257
00258
00259 if (!fHasLowRange)
00260 q2 = ig.IntegralLow(v1);
00261 else
00262 q2 = ig.Integral(xlow,v1);
00263
00264 int r = ig.Status();
00265
00266 double err = ig.Error();
00267
00268
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
00289 double dx = (xmax-xmin)/NFuncTest;
00290
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;
00296 double q1 = Pdf(v1);
00297
00298
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
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
00345
00346
00347
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
00354 RootFinder rf1(algoType);
00355 for (int i = 1; i < NFuncTest; ++i) {
00356 double v1 = x1 + dx*i;
00357 InvFunc finv(this,v1);
00358 Functor1D func(finv);
00359 rf1.SetFunction(func, vmin, vmax);
00360
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
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
00387 double abstol = 1.E-12;
00388 double reltol = 1.E-12;
00389
00390
00391
00392 double x1 = 0.05; double x2 = 0.95;
00393 double dx = (x2-x1)/NFuncTest;
00394
00395
00396 double vstart = fStartRoot;
00397
00398 RootFinder rf1(algoType);
00399
00400
00401 for (int i = 1; i < NFuncTest; ++i) {
00402 double v1 = x1 + dx*i;
00403
00404 InvFunc finv(this,v1);
00405
00406 GradFunctor1D func(finv,*this);
00407
00408
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
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
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);
00443
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);
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
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
00507 for (int i = 0; i < 5; ++i) {
00508
00509 double alpha = i+2;
00510 double beta = 6-i;
00511 dist.SetParameters(alpha,beta);
00512 dist.SetStartRoot(alpha/(alpha+beta));
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);
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) {
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
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;
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
00595 }
00596 return stressMathMore(nscale);
00597 }