49 VavilovAccurate *VavilovAccurate::fgInstance = 0;
52 VavilovAccurate::VavilovAccurate(
double kappa,
double beta2,
double epsilonPM,
double epsilon)
54 Set (kappa, beta2, epsilonPM, epsilon);
58 VavilovAccurate::~VavilovAccurate()
63 void VavilovAccurate::SetKappaBeta2 (
double kappa,
double beta2) {
67 void VavilovAccurate::Set(
double kappa,
double beta2,
double epsilonPM,
double epsilon) {
71 fQuantileInit =
false;
75 fEpsilonPM = epsilonPM;
78 static const double eu = 0.577215664901532860606;
79 static const double pi2 = 6.28318530717958647693,
80 rpi = 0.318309886183790671538,
81 pih = 1.57079632679489661923;
82 double h1 = -std::log(fEpsilon)-1.59631259113885503887;
83 double deltaEpsilon = 0.001;
84 static const double logdeltaEpsilon = -std::log(deltaEpsilon);
85 double logEpsilonPM = std::log(fEpsilonPM);
86 static const double eps = 1e-5;
89 9.29, 2.47, 0.89, 0.36, 0.15, 0.07, 0.03, 0.02};
91 0.012, 0.03, 0.08, 0.26, 0.87, 3.83};
94 std::cerr <<
"VavilovAccurate::Set: kappa = " << kappa <<
" - out of range" << std::endl;
95 if (kappa < 0.001) kappa = 0.001;
97 if (beta2 < 0 || beta2 > 1) {
98 std::cerr <<
"VavilovAccurate::Set: beta2 = " << beta2 <<
" - out of range" << std::endl;
99 if (beta2 < 0) beta2 = -beta2;
100 if (beta2 > 1) beta2 = 1;
104 fH[5] = 1-beta2*(1-eu)-logEpsilonPM/kappa;
107 double h4 = logEpsilonPM/kappa-(1+beta2*eu);
108 double logKappa = std::log(kappa);
109 double kappaInv = 1/kappa;
113 fT0 = (h4-fH[5]*logKappa-(fH[5]+beta2)*E1plLog(fH[5])+std::exp(-fH[5]))/fH[5];
115 while (lp < 9 && kappa < xp[lp]) ++lp;
117 while (lq < 7 && kappa >= xq[lq]) ++lq;
122 ifail = Rzero(-lp-0.5-delta,lq-7.5+delta,fH[0],eps,1000,&ROOT::Math::VavilovAccurate::G116f2);
124 }
while (ifail == 2);
130 fT1 = h4*q-logKappa-(1+beta2*q)*E1plLog(fH[0])+std::exp(-fH[0])*q;
134 fH[1] = kappa*(2+beta2*eu)+h1;
135 if(kappa >= 0.07) fH[1] += logdeltaEpsilon;
137 fH[3] = kappaInv*fOmega;
141 ifail = Rzero(5.,MAXTERMS,fX0,eps,1000,&ROOT::Math::VavilovAccurate::G116f1);
151 fX0 = (G116f1(5) > G116f1(MAXTERMS)) ? MAXTERMS : 5;
153 if (fX0 < 5) fX0 = 5;
154 else if (fX0 > MAXTERMS) fX0 = MAXTERMS;
157 double d = rpi*std::exp(kappa*(1+beta2*(eu-logKappa)));
158 fA_pdf[n] = rpi*fOmega;
162 for (
int k = 1; k < n; ++k) {
165 double x1 = kappaInv*x;
166 double c1 = std::log(x)-ROOT::Math::cosint(x1);
167 double c2 = ROOT::Math::sinint(x1);
168 double c3 = std::sin(x1);
169 double c4 = std::cos(x1);
170 double xf1 = kappa*(beta2*c1-c4)-x*c2;
171 double xf2 = x*(c1 + fT0) + kappa*(c3+beta2*c2);
172 double d1 = q*d*fOmega*std::exp(xf1);
173 double s = std::sin(xf2);
174 double c = std::cos(xf2);
177 d1 = q*d*std::exp(xf1)/k;
180 fA_cdf[n] += q2*fA_cdf[l];
186 void VavilovAccurate::InitQuantile()
const {
187 fQuantileInit =
true;
191 if (fKappa < 0.02)
return;
192 else if (fKappa < 0.05) fNQuant = 32;
196 double estmedian = -4.22784335098467134e-01-std::log(fKappa)-fBeta2;
197 if (estmedian>1.3) estmedian = 1.3;
200 for (
int i = 1; i < fNQuant/2; ++i) {
201 double x = fT0 + i*(estmedian-fT0)/(fNQuant/2);
205 for (
int i = fNQuant/2; i < fNQuant-1; ++i) {
206 double x = estmedian + (i-fNQuant/2)*(fT1-estmedian)/(fNQuant/2-1);
213 fQuant[fNQuant-1] = 1;
214 fLambda[fNQuant-1] = fT1;
218 double VavilovAccurate::Pdf (
double x)
const {
220 static const double pi = 3.14159265358979323846;
226 }
else if (x <= fT1) {
228 double u = fOmega*y-pi;
229 double cof = 2*cos(u);
231 double a0 = fA_pdf[1];
233 for (
int k = 2; k <= n+1; ++k) {
236 a0 = fA_pdf[k]+cof*a1-a2;
239 double b0 = fB_pdf[1];
240 for (
int k = 2; k <= n; ++k) {
243 b0 = fB_pdf[k]+cof*b1-b2;
245 f = 0.5*(a0-a2)+b0*sin(u);
252 double VavilovAccurate::Pdf (
double x,
double kappa,
double beta2) {
253 if (kappa != fKappa || beta2 != fBeta2) Set (kappa, beta2);
257 double VavilovAccurate::Cdf (
double x)
const {
259 static const double pi = 3.14159265358979323846;
265 }
else if (x <= fT1) {
267 double u = fOmega*y-pi;
268 double cof = 2*cos(u);
270 double a0 = fA_cdf[1];
272 for (
int k = 2; k <= n+1; ++k) {
275 a0 = fA_cdf[k]+cof*a1-a2;
278 double b0 = fB_cdf[1];
279 for (
int k = 2; k <= n; ++k) {
282 b0 = fB_cdf[k]+cof*b1-b2;
284 f = 0.5*(a0-a2)+b0*sin(u);
292 double VavilovAccurate::Cdf (
double x,
double kappa,
double beta2) {
293 if (kappa != fKappa || beta2 != fBeta2) Set (kappa, beta2);
297 double VavilovAccurate::Cdf_c (
double x)
const {
299 static const double pi = 3.14159265358979323846;
305 }
else if (x <= fT1) {
307 double u = fOmega*y-pi;
308 double cof = 2*cos(u);
310 double a0 = fA_cdf[1];
312 for (
int k = 2; k <= n+1; ++k) {
315 a0 = fA_cdf[k]+cof*a1-a2;
318 double b0 = fB_cdf[1];
319 for (
int k = 2; k <= n; ++k) {
322 b0 = fB_cdf[k]+cof*b1-b2;
324 f = -0.5*(a0-a2)+b0*sin(u);
332 double VavilovAccurate::Cdf_c (
double x,
double kappa,
double beta2) {
333 if (kappa != fKappa || beta2 != fBeta2) Set (kappa, beta2);
337 double VavilovAccurate::Quantile (
double z)
const {
338 if (z < 0 || z > 1)
return std::numeric_limits<double>::signaling_NaN();
340 if (!fQuantileInit) InitQuantile();
344 x = ROOT::Math::landau_quantile (z*(1-2*fEpsilonPM) + fEpsilonPM);
345 if (x < fT0+5*fEpsilon) x = fT0+5*fEpsilon;
346 else if (x > fT1-10*fEpsilon) x = fT1-10*fEpsilon;
351 while (z > fQuant[i]) ++i;
352 assert (i < fNQuant);
355 assert (i < fNQuant);
358 double f = (z-fQuant[i-1])/(fQuant[i]-fQuant[i-1]);
361 assert (fQuant[i] > fQuant[i-1]);
363 x = f*fLambda[i] + (1-f)*fLambda[i-1];
365 if (fabs(x-fT0) < fEpsilon || fabs(x-fT1) < fEpsilon)
return x;
367 assert (x > fT0 && x < fT1);
377 if (x < fT0) x = 0.5*(fT0+x-dx);
378 else if (x > fT1) x = 0.5*(fT1+x-dx);
379 assert (x > fT0 && x < fT1);
380 }
while (fabs(dx) > fEpsilon && n < 100);
384 double VavilovAccurate::Quantile (
double z,
double kappa,
double beta2) {
385 if (kappa != fKappa || beta2 != fBeta2) Set (kappa, beta2);
389 double VavilovAccurate::Quantile_c (
double z)
const {
390 if (z < 0 || z > 1)
return std::numeric_limits<double>::signaling_NaN();
392 if (!fQuantileInit) InitQuantile();
398 x = ROOT::Math::landau_quantile (z1*(1-2*fEpsilonPM) + fEpsilonPM);
399 if (x < fT0+5*fEpsilon) x = fT0+5*fEpsilon;
400 else if (x > fT1-10*fEpsilon) x = fT1-10*fEpsilon;
405 while (z1 > fQuant[i]) ++i;
406 assert (i < fNQuant);
417 assert (i < fNQuant);
420 double f = (z1-fQuant[i-1])/(fQuant[i]-fQuant[i-1]);
423 assert (fQuant[i] > fQuant[i-1]);
425 x = f*fLambda[i] + (1-f)*fLambda[i-1];
427 if (fabs(x-fT0) < fEpsilon || fabs(x-fT1) < fEpsilon)
return x;
429 assert (x > fT0 && x < fT1);
434 double y = Cdf_c(x)-z;
435 double y1 = -Pdf (x);
439 if (x < fT0) x = 0.5*(fT0+x-dx);
440 else if (x > fT1) x = 0.5*(fT1+x-dx);
441 assert (x > fT0 && x < fT1);
442 }
while (fabs(dx) > fEpsilon && n < 100);
446 double VavilovAccurate::Quantile_c (
double z,
double kappa,
double beta2) {
447 if (kappa != fKappa || beta2 != fBeta2) Set (kappa, beta2);
448 return Quantile_c (z);
451 VavilovAccurate *VavilovAccurate::GetInstance() {
452 if (!fgInstance) fgInstance =
new VavilovAccurate (1, 1);
456 VavilovAccurate *VavilovAccurate::GetInstance(
double kappa,
double beta2) {
457 if (!fgInstance) fgInstance =
new VavilovAccurate (kappa, beta2);
458 else if (kappa != fgInstance->fKappa || beta2 != fgInstance->fBeta2) fgInstance->Set (kappa, beta2);
462 double vavilov_accurate_pdf (
double x,
double kappa,
double beta2) {
463 VavilovAccurate *vavilov = VavilovAccurate::GetInstance (kappa, beta2);
464 return vavilov->Pdf (x);
467 double vavilov_accurate_cdf_c (
double x,
double kappa,
double beta2) {
468 VavilovAccurate *vavilov = VavilovAccurate::GetInstance (kappa, beta2);
469 return vavilov->Cdf_c (x);
472 double vavilov_accurate_cdf (
double x,
double kappa,
double beta2) {
473 VavilovAccurate *vavilov = VavilovAccurate::GetInstance (kappa, beta2);
474 return vavilov->Cdf (x);
477 double vavilov_accurate_quantile (
double z,
double kappa,
double beta2) {
478 VavilovAccurate *vavilov = VavilovAccurate::GetInstance (kappa, beta2);
479 return vavilov->Quantile (z);
482 double vavilov_accurate_quantile_c (
double z,
double kappa,
double beta2) {
483 VavilovAccurate *vavilov = VavilovAccurate::GetInstance (kappa, beta2);
484 return vavilov->Quantile_c (z);
487 double VavilovAccurate::G116f1 (
double x)
const {
493 return fH[1]+fH[2]*std::log(fH[3]*x)-fH[4]*x;
496 double VavilovAccurate::G116f2 (
double x)
const {
502 return fH[5]-x+fH[6]*E1plLog(x)-fH[7]*std::exp(-x);
505 int VavilovAccurate::Rzero (
double a,
double b,
double& x0,
506 double eps,
int mxf,
double (VavilovAccurate::*f)(
double)
const)
const {
508 double xa, xb, fa, fb, r;
529 bool recalcF12 =
true;
530 bool recalcFab =
true;
534 double x1=0, x2=0, f1=0, f2=0, fx=0, ee=0;
539 ee = eps*(std::abs(x0)+1);
568 if(u2 == 0 || u4 == 0)
continue;
574 double cb = (x1+x2)*u2-(x2+x0)*u1;
575 double cc = (x1-x0)*f1-x1*(ca*x1+cb);
577 if(cb == 0)
continue;
583 x0 = -u3 + (x0+u3 >= 0 ? +1 : -1)*std::sqrt(u4);
585 if(x0 < xa || x0 > xb)
continue;
589 r = std::abs(x0-x3) < std::abs(x0-x2) ? std::abs(x0-x3) : std::abs(x0-x2);
590 ee = eps*(std::abs(x0)+1);
601 fx = (this->*f) (x0);
617 if (fx*ff <= 0)
break;
633 r = -0.5*std::abs(xb-xa);
635 std::cerr <<
"VavilovAccurate::Rzero: fail=" << fail <<
", f(" << a <<
")=" << (this->*f) (a)
636 <<
", f(" << b <<
")=" << (this->*f) (b) << std::endl;
645 double VavilovAccurate::E1plLog (
double x) {
646 static const double eu = 0.577215664901532860606;
647 double absx = std::fabs(x);
649 return (x-0.25*x)*x-eu;
655 return -ROOT::Math::expint (-x);
657 return log(absx) -ROOT::Math::expint (-x);
660 double VavilovAccurate::GetLambdaMin()
const {
664 double VavilovAccurate::GetLambdaMax()
const {
668 double VavilovAccurate::GetKappa()
const {
672 double VavilovAccurate::GetBeta2()
const {
676 double VavilovAccurate::Mode()
const {
677 double x = -4.22784335098467134e-01-std::log(fKappa)-fBeta2;
678 if (x>-0.223172) x = -0.223172;
683 double p0 = Pdf (x - eps);
685 double p2 = Pdf (x + eps);
686 double y1 = 0.5*(p2-p0)/eps;
687 double y2 = (p2-2*p1+p0)/(eps*eps);
690 if (fabs(dx) < eps) eps = 0.1*fabs(dx);
691 }
while (fabs(dx) > 1E-5);
695 double VavilovAccurate::Mode(
double kappa,
double beta2) {
696 if (kappa != fKappa || beta2 != fBeta2) Set (kappa, beta2);
700 double VavilovAccurate::GetEpsilonPM()
const {
704 double VavilovAccurate::GetEpsilon()
const {
708 double VavilovAccurate::GetNTerms()
const {