00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #include "Math/VavilovAccurate.h"
00035 #include "Math/SpecFuncMathCore.h"
00036 #include "Math/SpecFuncMathMore.h"
00037 #include "Math/QuantFuncMathCore.h"
00038
00039 #include <cassert>
00040 #include <iostream>
00041 #include <iomanip>
00042 #include <cmath>
00043 #include <limits>
00044
00045
00046 namespace ROOT {
00047 namespace Math {
00048
00049 VavilovAccurate *VavilovAccurate::fgInstance = 0;
00050
00051
00052 VavilovAccurate::VavilovAccurate(double kappa, double beta2, double epsilonPM, double epsilon)
00053 {
00054 Set (kappa, beta2, epsilonPM, epsilon);
00055 }
00056
00057
00058 VavilovAccurate::~VavilovAccurate()
00059 {
00060
00061 }
00062
00063 void VavilovAccurate::SetKappaBeta2 (double kappa, double beta2) {
00064 Set (kappa, beta2);
00065 }
00066
00067 void VavilovAccurate::Set(double kappa, double beta2, double epsilonPM, double epsilon) {
00068
00069
00070
00071 fQuantileInit = false;
00072
00073 fKappa = kappa;
00074 fBeta2 = beta2;
00075 fEpsilonPM = epsilonPM;
00076 fEpsilon = epsilon;
00077
00078 static const double eu = 0.577215664901532860606;
00079 static const double pi2 = 6.28318530717958647693,
00080 rpi = 0.318309886183790671538,
00081 pih = 1.57079632679489661923;
00082 double h1 = -std::log(fEpsilon)-1.59631259113885503887;
00083 double deltaEpsilon = 0.001;
00084 static const double logdeltaEpsilon = -std::log(deltaEpsilon);
00085 double logEpsilonPM = std::log(fEpsilonPM);
00086 static const double eps = 1e-5;
00087
00088 double xp[9] = {0,
00089 9.29, 2.47, 0.89, 0.36, 0.15, 0.07, 0.03, 0.02};
00090 double xq[7] = {0,
00091 0.012, 0.03, 0.08, 0.26, 0.87, 3.83};
00092
00093 if (kappa < 0.001) {
00094 std::cerr << "VavilovAccurate::Set: kappa = " << kappa << " - out of range" << std::endl;
00095 if (kappa < 0.001) kappa = 0.001;
00096 }
00097 if (beta2 < 0 || beta2 > 1) {
00098 std::cerr << "VavilovAccurate::Set: beta2 = " << beta2 << " - out of range" << std::endl;
00099 if (beta2 < 0) beta2 = -beta2;
00100 if (beta2 > 1) beta2 = 1;
00101 }
00102
00103
00104 fH[5] = 1-beta2*(1-eu)-logEpsilonPM/kappa;
00105 fH[6] = beta2;
00106 fH[7] = 1-beta2;
00107 double h4 = logEpsilonPM/kappa-(1+beta2*eu);
00108 double logKappa = std::log(kappa);
00109 double kappaInv = 1/kappa;
00110
00111
00112
00113 fT0 = (h4-fH[5]*logKappa-(fH[5]+beta2)*E1plLog(fH[5])+std::exp(-fH[5]))/fH[5];
00114 int lp = 1;
00115 while (lp < 9 && kappa < xp[lp]) ++lp;
00116 int lq = 1;
00117 while (lq < 7 && kappa >= xq[lq]) ++lq;
00118
00119 double delta = 0;
00120 int ifail = 0;
00121 do {
00122 ifail = Rzero(-lp-0.5-delta,lq-7.5+delta,fH[0],eps,1000,&ROOT::Math::VavilovAccurate::G116f2);
00123 delta += 0.5;
00124 } while (ifail == 2);
00125
00126 double q = 1/fH[0];
00127
00128
00129
00130 fT1 = h4*q-logKappa-(1+beta2*q)*E1plLog(fH[0])+std::exp(-fH[0])*q;
00131
00132 fT = fT1-fT0;
00133 fOmega = pi2/fT;
00134 fH[1] = kappa*(2+beta2*eu)+h1;
00135 if(kappa >= 0.07) fH[1] += logdeltaEpsilon;
00136 fH[2] = beta2*kappa;
00137 fH[3] = kappaInv*fOmega;
00138 fH[4] = pih*fOmega;
00139
00140
00141 ifail = Rzero(5.,MAXTERMS,fX0,eps,1000,&ROOT::Math::VavilovAccurate::G116f1);
00142
00143
00144
00145
00146
00147
00148
00149
00150 if (ifail == 2) {
00151 fX0 = (G116f1(5) > G116f1(MAXTERMS)) ? MAXTERMS : 5;
00152 }
00153 if (fX0 < 5) fX0 = 5;
00154 else if (fX0 > MAXTERMS) fX0 = MAXTERMS;
00155 int n = int(fX0+1);
00156
00157 double d = rpi*std::exp(kappa*(1+beta2*(eu-logKappa)));
00158 fA_pdf[n] = rpi*fOmega;
00159 fA_cdf[n] = 0;
00160 q = -1;
00161 double q2 = 2;
00162 for (int k = 1; k < n; ++k) {
00163 int l = n-k;
00164 double x = fOmega*k;
00165 double x1 = kappaInv*x;
00166 double c1 = std::log(x)-ROOT::Math::cosint(x1);
00167 double c2 = ROOT::Math::sinint(x1);
00168 double c3 = std::sin(x1);
00169 double c4 = std::cos(x1);
00170 double xf1 = kappa*(beta2*c1-c4)-x*c2;
00171 double xf2 = x*(c1 + fT0) + kappa*(c3+beta2*c2);
00172 double d1 = q*d*fOmega*std::exp(xf1);
00173 double s = std::sin(xf2);
00174 double c = std::cos(xf2);
00175 fA_pdf[l] = d1*c;
00176 fB_pdf[l] = -d1*s;
00177 d1 = q*d*std::exp(xf1)/k;
00178 fA_cdf[l] = d1*s;
00179 fB_cdf[l] = d1*c;
00180 fA_cdf[n] += q2*fA_cdf[l];
00181 q = -q;
00182 q2 = -q2;
00183 }
00184 }
00185
00186 void VavilovAccurate::InitQuantile() const {
00187 fQuantileInit = true;
00188
00189 fNQuant = 16;
00190
00191 if (fKappa < 0.02) return;
00192 else if (fKappa < 0.05) fNQuant = 32;
00193
00194
00195
00196 double estmedian = -4.22784335098467134e-01-std::log(fKappa)-fBeta2;
00197 if (estmedian>1.3) estmedian = 1.3;
00198
00199
00200 for (int i = 1; i < fNQuant/2; ++i) {
00201 double x = fT0 + i*(estmedian-fT0)/(fNQuant/2);
00202 fQuant[i] = Cdf(x);
00203 fLambda[i] = x;
00204 }
00205 for (int i = fNQuant/2; i < fNQuant-1; ++i) {
00206 double x = estmedian + (i-fNQuant/2)*(fT1-estmedian)/(fNQuant/2-1);
00207 fQuant[i] = Cdf(x);
00208 fLambda[i] = x;
00209 }
00210
00211 fQuant[0] = 0;
00212 fLambda[0] = fT0;
00213 fQuant[fNQuant-1] = 1;
00214 fLambda[fNQuant-1] = fT1;
00215
00216 }
00217
00218 double VavilovAccurate::Pdf (double x) const {
00219
00220 static const double pi = 3.14159265358979323846;
00221
00222 int n = int(fX0);
00223 double f;
00224 if (x < fT0) {
00225 f = 0;
00226 } else if (x <= fT1) {
00227 double y = x-fT0;
00228 double u = fOmega*y-pi;
00229 double cof = 2*cos(u);
00230 double a1 = 0;
00231 double a0 = fA_pdf[1];
00232 double a2 = 0;
00233 for (int k = 2; k <= n+1; ++k) {
00234 a2 = a1;
00235 a1 = a0;
00236 a0 = fA_pdf[k]+cof*a1-a2;
00237 }
00238 double b1 = 0;
00239 double b0 = fB_pdf[1];
00240 for (int k = 2; k <= n; ++k) {
00241 double b2 = b1;
00242 b1 = b0;
00243 b0 = fB_pdf[k]+cof*b1-b2;
00244 }
00245 f = 0.5*(a0-a2)+b0*sin(u);
00246 } else {
00247 f = 0;
00248 }
00249 return f;
00250 }
00251
00252 double VavilovAccurate::Pdf (double x, double kappa, double beta2) {
00253 if (kappa != fKappa || beta2 != fBeta2) Set (kappa, beta2);
00254 return Pdf (x);
00255 }
00256
00257 double VavilovAccurate::Cdf (double x) const {
00258
00259 static const double pi = 3.14159265358979323846;
00260
00261 int n = int(fX0);
00262 double f;
00263 if (x < fT0) {
00264 f = 0;
00265 } else if (x <= fT1) {
00266 double y = x-fT0;
00267 double u = fOmega*y-pi;
00268 double cof = 2*cos(u);
00269 double a1 = 0;
00270 double a0 = fA_cdf[1];
00271 double a2 = 0;
00272 for (int k = 2; k <= n+1; ++k) {
00273 a2 = a1;
00274 a1 = a0;
00275 a0 = fA_cdf[k]+cof*a1-a2;
00276 }
00277 double b1 = 0;
00278 double b0 = fB_cdf[1];
00279 for (int k = 2; k <= n; ++k) {
00280 double b2 = b1;
00281 b1 = b0;
00282 b0 = fB_cdf[k]+cof*b1-b2;
00283 }
00284 f = 0.5*(a0-a2)+b0*sin(u);
00285 f += y/fT;
00286 } else {
00287 f = 1;
00288 }
00289 return f;
00290 }
00291
00292 double VavilovAccurate::Cdf (double x, double kappa, double beta2) {
00293 if (kappa != fKappa || beta2 != fBeta2) Set (kappa, beta2);
00294 return Cdf (x);
00295 }
00296
00297 double VavilovAccurate::Cdf_c (double x) const {
00298
00299 static const double pi = 3.14159265358979323846;
00300
00301 int n = int(fX0);
00302 double f;
00303 if (x < fT0) {
00304 f = 1;
00305 } else if (x <= fT1) {
00306 double y = fT1-x;
00307 double u = fOmega*y-pi;
00308 double cof = 2*cos(u);
00309 double a1 = 0;
00310 double a0 = fA_cdf[1];
00311 double a2 = 0;
00312 for (int k = 2; k <= n+1; ++k) {
00313 a2 = a1;
00314 a1 = a0;
00315 a0 = fA_cdf[k]+cof*a1-a2;
00316 }
00317 double b1 = 0;
00318 double b0 = fB_cdf[1];
00319 for (int k = 2; k <= n; ++k) {
00320 double b2 = b1;
00321 b1 = b0;
00322 b0 = fB_cdf[k]+cof*b1-b2;
00323 }
00324 f = -0.5*(a0-a2)+b0*sin(u);
00325 f += y/fT;
00326 } else {
00327 f = 0;
00328 }
00329 return f;
00330 }
00331
00332 double VavilovAccurate::Cdf_c (double x, double kappa, double beta2) {
00333 if (kappa != fKappa || beta2 != fBeta2) Set (kappa, beta2);
00334 return Cdf_c (x);
00335 }
00336
00337 double VavilovAccurate::Quantile (double z) const {
00338 if (z < 0 || z > 1) return std::numeric_limits<double>::signaling_NaN();
00339
00340 if (!fQuantileInit) InitQuantile();
00341
00342 double x;
00343 if (fKappa < 0.02) {
00344 x = ROOT::Math::landau_quantile (z*(1-2*fEpsilonPM) + fEpsilonPM);
00345 if (x < fT0+5*fEpsilon) x = fT0+5*fEpsilon;
00346 else if (x > fT1-10*fEpsilon) x = fT1-10*fEpsilon;
00347 }
00348 else {
00349
00350 int i = 1;
00351 while (z > fQuant[i]) ++i;
00352 assert (i < fNQuant);
00353
00354 assert (i >= 1);
00355 assert (i < fNQuant);
00356
00357
00358 double f = (z-fQuant[i-1])/(fQuant[i]-fQuant[i-1]);
00359 assert (f >= 0);
00360 assert (f <= 1);
00361 assert (fQuant[i] > fQuant[i-1]);
00362
00363 x = f*fLambda[i] + (1-f)*fLambda[i-1];
00364 }
00365 if (fabs(x-fT0) < fEpsilon || fabs(x-fT1) < fEpsilon) return x;
00366
00367 assert (x > fT0 && x < fT1);
00368 double dx;
00369 int n = 0;
00370 do {
00371 ++n;
00372 double y = Cdf(x)-z;
00373 double y1 = Pdf (x);
00374 dx = - y/y1;
00375 x = x + dx;
00376
00377 if (x < fT0) x = 0.5*(fT0+x-dx);
00378 else if (x > fT1) x = 0.5*(fT1+x-dx);
00379 assert (x > fT0 && x < fT1);
00380 } while (fabs(dx) > fEpsilon && n < 100);
00381 return x;
00382 }
00383
00384 double VavilovAccurate::Quantile (double z, double kappa, double beta2) {
00385 if (kappa != fKappa || beta2 != fBeta2) Set (kappa, beta2);
00386 return Quantile (z);
00387 }
00388
00389 double VavilovAccurate::Quantile_c (double z) const {
00390 if (z < 0 || z > 1) return std::numeric_limits<double>::signaling_NaN();
00391
00392 if (!fQuantileInit) InitQuantile();
00393
00394 double z1 = 1-z;
00395
00396 double x;
00397 if (fKappa < 0.02) {
00398 x = ROOT::Math::landau_quantile (z1*(1-2*fEpsilonPM) + fEpsilonPM);
00399 if (x < fT0+5*fEpsilon) x = fT0+5*fEpsilon;
00400 else if (x > fT1-10*fEpsilon) x = fT1-10*fEpsilon;
00401 }
00402 else {
00403
00404 int i = 1;
00405 while (z1 > fQuant[i]) ++i;
00406 assert (i < fNQuant);
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416 assert (i >= 1);
00417 assert (i < fNQuant);
00418
00419
00420 double f = (z1-fQuant[i-1])/(fQuant[i]-fQuant[i-1]);
00421 assert (f >= 0);
00422 assert (f <= 1);
00423 assert (fQuant[i] > fQuant[i-1]);
00424
00425 x = f*fLambda[i] + (1-f)*fLambda[i-1];
00426 }
00427 if (fabs(x-fT0) < fEpsilon || fabs(x-fT1) < fEpsilon) return x;
00428
00429 assert (x > fT0 && x < fT1);
00430 double dx;
00431 int n = 0;
00432 do {
00433 ++n;
00434 double y = Cdf_c(x)-z;
00435 double y1 = -Pdf (x);
00436 dx = - y/y1;
00437 x = x + dx;
00438
00439 if (x < fT0) x = 0.5*(fT0+x-dx);
00440 else if (x > fT1) x = 0.5*(fT1+x-dx);
00441 assert (x > fT0 && x < fT1);
00442 } while (fabs(dx) > fEpsilon && n < 100);
00443 return x;
00444 }
00445
00446 double VavilovAccurate::Quantile_c (double z, double kappa, double beta2) {
00447 if (kappa != fKappa || beta2 != fBeta2) Set (kappa, beta2);
00448 return Quantile_c (z);
00449 }
00450
00451 VavilovAccurate *VavilovAccurate::GetInstance() {
00452 if (!fgInstance) fgInstance = new VavilovAccurate (1, 1);
00453 return fgInstance;
00454 }
00455
00456 VavilovAccurate *VavilovAccurate::GetInstance(double kappa, double beta2) {
00457 if (!fgInstance) fgInstance = new VavilovAccurate (kappa, beta2);
00458 else if (kappa != fgInstance->fKappa || beta2 != fgInstance->fBeta2) fgInstance->Set (kappa, beta2);
00459 return fgInstance;
00460 }
00461
00462 double vavilov_accurate_pdf (double x, double kappa, double beta2) {
00463 VavilovAccurate *vavilov = VavilovAccurate::GetInstance (kappa, beta2);
00464 return vavilov->Pdf (x);
00465 }
00466
00467 double vavilov_accurate_cdf_c (double x, double kappa, double beta2) {
00468 VavilovAccurate *vavilov = VavilovAccurate::GetInstance (kappa, beta2);
00469 return vavilov->Cdf_c (x);
00470 }
00471
00472 double vavilov_accurate_cdf (double x, double kappa, double beta2) {
00473 VavilovAccurate *vavilov = VavilovAccurate::GetInstance (kappa, beta2);
00474 return vavilov->Cdf (x);
00475 }
00476
00477 double vavilov_accurate_quantile (double z, double kappa, double beta2) {
00478 VavilovAccurate *vavilov = VavilovAccurate::GetInstance (kappa, beta2);
00479 return vavilov->Quantile (z);
00480 }
00481
00482 double vavilov_accurate_quantile_c (double z, double kappa, double beta2) {
00483 VavilovAccurate *vavilov = VavilovAccurate::GetInstance (kappa, beta2);
00484 return vavilov->Quantile_c (z);
00485 }
00486
00487 double VavilovAccurate::G116f1 (double x) const {
00488
00489
00490
00491
00492
00493 return fH[1]+fH[2]*std::log(fH[3]*x)-fH[4]*x;
00494 }
00495
00496 double VavilovAccurate::G116f2 (double x) const {
00497
00498
00499
00500
00501
00502 return fH[5]-x+fH[6]*E1plLog(x)-fH[7]*std::exp(-x);
00503 }
00504
00505 int VavilovAccurate::Rzero (double a, double b, double& x0,
00506 double eps, int mxf, double (VavilovAccurate::*f)(double)const) const {
00507
00508 double xa, xb, fa, fb, r;
00509
00510 if (a <= b) {
00511 xa = a;
00512 xb = b;
00513 } else {
00514 xa = b;
00515 xb = a;
00516 }
00517 fa = (this->*f)(xa);
00518 fb = (this->*f)(xb);
00519
00520 if(fa*fb > 0) {
00521 r = -2*(xb-xa);
00522 x0 = 0;
00523
00524
00525 return 2;
00526 }
00527 int mc = 0;
00528
00529 bool recalcF12 = true;
00530 bool recalcFab = true;
00531 bool fail = false;
00532
00533
00534 double x1=0, x2=0, f1=0, f2=0, fx=0, ee=0;
00535 do {
00536 if (recalcF12) {
00537 x0 = 0.5*(xa+xb);
00538 r = x0-xa;
00539 ee = eps*(std::abs(x0)+1);
00540 if(r <= ee) break;
00541 f1 = fa;
00542 x1 = xa;
00543 f2 = fb;
00544 x2 = xb;
00545 }
00546 if (recalcFab) {
00547 fx = (this->*f)(x0);
00548 ++mc;
00549 if(mc > mxf) {
00550 fail = true;
00551 break;
00552 }
00553 if(fx*fa > 0) {
00554 xa = x0;
00555 fa = fx;
00556 } else {
00557 xb = x0;
00558 fb = fx;
00559 }
00560 }
00561 recalcF12 = true;
00562 recalcFab = true;
00563
00564 double u1 = f1-f2;
00565 double u2 = x1-x2;
00566 double u3 = f2-fx;
00567 double u4 = x2-x0;
00568 if(u2 == 0 || u4 == 0) continue;
00569 double f3 = fx;
00570 double x3 = x0;
00571 u1 = u1/u2;
00572 u2 = u3/u4;
00573 double ca = u1-u2;
00574 double cb = (x1+x2)*u2-(x2+x0)*u1;
00575 double cc = (x1-x0)*f1-x1*(ca*x1+cb);
00576 if(ca == 0) {
00577 if(cb == 0) continue;
00578 x0 = -cc/cb;
00579 } else {
00580 u3 = cb/(2*ca);
00581 u4 = u3*u3-cc/ca;
00582 if(u4 < 0) continue;
00583 x0 = -u3 + (x0+u3 >= 0 ? +1 : -1)*std::sqrt(u4);
00584 }
00585 if(x0 < xa || x0 > xb) continue;
00586
00587 recalcF12 = false;
00588
00589 r = std::abs(x0-x3) < std::abs(x0-x2) ? std::abs(x0-x3) : std::abs(x0-x2);
00590 ee = eps*(std::abs(x0)+1);
00591 if (r > ee) {
00592 f1 = f2;
00593 x1 = x2;
00594 f2 = f3;
00595 x2 = x3;
00596 continue;
00597 }
00598
00599 recalcFab = false;
00600
00601 fx = (this->*f) (x0);
00602 if (fx == 0) break;
00603 double xx, ff;
00604 if (fx*fa < 0) {
00605 xx = x0-ee;
00606 if (xx <= xa) break;
00607 ff = (this->*f)(xx);
00608 fb = ff;
00609 xb = xx;
00610 } else {
00611 xx = x0+ee;
00612 if(xx >= xb) break;
00613 ff = (this->*f)(xx);
00614 fa = ff;
00615 xa = xx;
00616 }
00617 if (fx*ff <= 0) break;
00618 mc += 2;
00619 if (mc > mxf) {
00620 fail = true;
00621 break;
00622 }
00623 f1 = f3;
00624 x1 = x3;
00625 f2 = fx;
00626 x2 = x0;
00627 x0 = xx;
00628 fx = ff;
00629 }
00630 while (true);
00631
00632 if (fail) {
00633 r = -0.5*std::abs(xb-xa);
00634 x0 = 0;
00635 std::cerr << "VavilovAccurate::Rzero: fail=" << fail << ", f(" << a << ")=" << (this->*f) (a)
00636 << ", f(" << b << ")=" << (this->*f) (b) << std::endl;
00637 return 1;
00638 }
00639
00640 r = ee;
00641 return 0;
00642 }
00643
00644
00645 double VavilovAccurate::E1plLog (double x) {
00646 static const double eu = 0.577215664901532860606;
00647 double absx = std::fabs(x);
00648 if (absx < 1E-4) {
00649 return (x-0.25*x)*x-eu;
00650 }
00651 else if (x > 35) {
00652 return log (x);
00653 }
00654 else if (x < -50) {
00655 return -ROOT::Math::expint (-x);
00656 }
00657 return log(absx) -ROOT::Math::expint (-x);
00658 }
00659
00660 double VavilovAccurate::GetLambdaMin() const {
00661 return fT0;
00662 }
00663
00664 double VavilovAccurate::GetLambdaMax() const {
00665 return fT1;
00666 }
00667
00668 double VavilovAccurate::GetKappa() const {
00669 return fKappa;
00670 }
00671
00672 double VavilovAccurate::GetBeta2() const {
00673 return fBeta2;
00674 }
00675
00676 double VavilovAccurate::Mode() const {
00677 double x = -4.22784335098467134e-01-std::log(fKappa)-fBeta2;
00678 if (x>-0.223172) x = -0.223172;
00679 double eps = 0.01;
00680 double dx;
00681
00682 do {
00683 double p0 = Pdf (x - eps);
00684 double p1 = Pdf (x);
00685 double p2 = Pdf (x + eps);
00686 double y1 = 0.5*(p2-p0)/eps;
00687 double y2 = (p2-2*p1+p0)/(eps*eps);
00688 dx = - y1/y2;
00689 x += dx;
00690 if (fabs(dx) < eps) eps = 0.1*fabs(dx);
00691 } while (fabs(dx) > 1E-5);
00692 return x;
00693 }
00694
00695 double VavilovAccurate::Mode(double kappa, double beta2) {
00696 if (kappa != fKappa || beta2 != fBeta2) Set (kappa, beta2);
00697 return Mode();
00698 }
00699
00700 double VavilovAccurate::GetEpsilonPM() const {
00701 return fEpsilonPM;
00702 }
00703
00704 double VavilovAccurate::GetEpsilon() const {
00705 return fEpsilon;
00706 }
00707
00708 double VavilovAccurate::GetNTerms() const {
00709 return fX0;
00710 }
00711
00712
00713
00714 }
00715 }