Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
MathFuncs.h
Go to the documentation of this file.
1/*
2 * Project: RooFit
3 * Authors:
4 * Jonas Rembser, CERN 2024
5 * Garima Singh, CERN 2023
6 *
7 * Copyright (c) 2024, CERN
8 *
9 * Redistribution and use in source and binary forms,
10 * with or without modification, are permitted according to the terms
11 * listed in LICENSE (http://roofit.sourceforge.net/license.txt)
12 */
13
14#ifndef RooFit_Detail_MathFuncs_h
15#define RooFit_Detail_MathFuncs_h
16
17#include <TMath.h>
20
21#include <algorithm>
22#include <cmath>
23
24namespace RooFit {
25namespace Detail {
26namespace MathFuncs {
27
28/// Calculates the binomial coefficient n over k.
29/// Equivalent to TMath::Binomial, but inlined.
30inline double binomial(int n, int k)
31{
32 if (n < 0 || k < 0 || n < k)
33 return TMath::SignalingNaN();
34 if (k == 0 || n == k)
35 return 1;
36
37 int k1 = std::min(k, n - k);
38 int k2 = n - k1;
39 double fact = k2 + 1;
40 for (double i = k1; i > 1.; --i) {
41 fact *= (k2 + i) / i;
42 }
43 return fact;
44}
45
46/// The caller needs to make sure that there is at least one coefficient.
47inline double bernstein(double x, double xmin, double xmax, double *coefs, int nCoefs)
48{
49 double xScaled = (x - xmin) / (xmax - xmin); // rescale to [0,1]
50 int degree = nCoefs - 1; // n+1 polys of degree n
51
52 // in case list of arguments passed is empty
53 if (degree < 0) {
54 return TMath::SignalingNaN();
55 } else if (degree == 0) {
56 return coefs[0];
57 } else if (degree == 1) {
58
59 double a0 = coefs[0]; // c0
60 double a1 = coefs[1] - a0; // c1 - c0
61 return a1 * xScaled + a0;
62
63 } else if (degree == 2) {
64
65 double a0 = coefs[0]; // c0
66 double a1 = 2 * (coefs[1] - a0); // 2 * (c1 - c0)
67 double a2 = coefs[2] - a1 - a0; // c0 - 2 * c1 + c2
68 return (a2 * xScaled + a1) * xScaled + a0;
69 }
70
71 double t = xScaled;
72 double s = 1. - xScaled;
73
74 double result = coefs[0] * s;
75 for (int i = 1; i < degree; i++) {
76 result = (result + t * binomial(degree, i) * coefs[i]) * s;
77 t *= xScaled;
78 }
79 result += t * coefs[degree];
80
81 return result;
82}
83
84/// @brief Function to evaluate an un-normalized RooGaussian.
85inline double gaussian(double x, double mean, double sigma)
86{
87 const double arg = x - mean;
88 const double sig = sigma;
89 return std::exp(-0.5 * arg * arg / (sig * sig));
90}
91
92inline double product(double const *factors, std::size_t nFactors)
93{
94 double out = 1.0;
95 for (std::size_t i = 0; i < nFactors; ++i) {
96 out *= factors[i];
97 }
98 return out;
99}
100
101// RooRatio evaluate function.
102inline double ratio(double numerator, double denominator)
103{
104 return numerator / denominator;
105}
106
107inline double bifurGauss(double x, double mean, double sigmaL, double sigmaR)
108{
109 // Note: this simplification does not work with Clad as of v1.1!
110 // return gaussian(x, mean, x < mean ? sigmaL : sigmaR);
111 if (x < mean)
112 return gaussian(x, mean, sigmaL);
113 return gaussian(x, mean, sigmaR);
114}
115
116inline double efficiency(double effFuncVal, int catIndex, int sigCatIndex)
117{
118 // Truncate efficiency function in range 0.0-1.0
119 effFuncVal = std::clamp(effFuncVal, 0.0, 1.0);
120
121 if (catIndex == sigCatIndex)
122 return effFuncVal; // Accept case
123 else
124 return 1 - effFuncVal; // Reject case
125}
126
127/// In pdfMode, a coefficient for the constant term of 1.0 is implied if lowestOrder > 0.
128template <bool pdfMode = false>
129inline double polynomial(double const *coeffs, int nCoeffs, int lowestOrder, double x)
130{
131 double retVal = coeffs[nCoeffs - 1];
132 for (int i = nCoeffs - 2; i >= 0; i--) {
133 retVal = coeffs[i] + x * retVal;
134 }
135 retVal = retVal * std::pow(x, lowestOrder);
136 return retVal + (pdfMode && lowestOrder > 0 ? 1.0 : 0.0);
137}
138
139inline double chebychev(double *coeffs, unsigned int nCoeffs, double x_in, double xMin, double xMax)
140{
141 // transform to range [-1, +1]
142 const double xPrime = (x_in - 0.5 * (xMax + xMin)) / (0.5 * (xMax - xMin));
143
144 // extract current values of coefficients
145 double sum = 1.;
146 if (nCoeffs > 0) {
147 double curr = xPrime;
148 double twox = 2 * xPrime;
149 double last = 1;
150 double newval = twox * curr - last;
151 last = curr;
152 curr = newval;
153 for (unsigned int i = 0; nCoeffs != i; ++i) {
154 sum += last * coeffs[i];
155 newval = twox * curr - last;
156 last = curr;
157 curr = newval;
158 }
159 }
160 return sum;
161}
162
163inline double constraintSum(double const *comp, unsigned int compSize)
164{
165 double sum = 0;
166 for (unsigned int i = 0; i < compSize; i++) {
167 sum -= std::log(comp[i]);
168 }
169 return sum;
170}
171
172inline unsigned int uniformBinNumber(double low, double high, double val, unsigned int numBins, double coef)
173{
174 double binWidth = (high - low) / numBins;
175 return coef * (val >= high ? numBins - 1 : std::abs((val - low) / binWidth));
176}
177
178inline unsigned int rawBinNumber(double x, double const *boundaries, std::size_t nBoundaries)
179{
180 double const *end = boundaries + nBoundaries;
181 double const *it = std::lower_bound(boundaries, end, x);
182 // always return valid bin number
183 while (boundaries != it && (end == it || end == it + 1 || x < *it)) {
184 --it;
185 }
186 return it - boundaries;
187}
188
189inline unsigned int
190binNumber(double x, double coef, double const *boundaries, unsigned int nBoundaries, int nbins, int blo)
191{
192 const int rawBin = rawBinNumber(x, boundaries, nBoundaries);
193 int tmp = std::min(nbins, rawBin - blo);
194 return coef * std::max(0, tmp);
195}
196
197inline double interpolate1d(double low, double high, double val, unsigned int numBins, double const *vals)
198{
199 double binWidth = (high - low) / numBins;
200 int idx = val >= high ? numBins - 1 : std::abs((val - low) / binWidth);
201
202 // interpolation
203 double central = low + (idx + 0.5) * binWidth;
204 if (val > low + 0.5 * binWidth && val < high - 0.5 * binWidth) {
205 double slope;
206 if (val < central) {
207 slope = vals[idx] - vals[idx - 1];
208 } else {
209 slope = vals[idx + 1] - vals[idx];
210 }
211 return vals[idx] + slope * (val - central) / binWidth;
212 }
213
214 return vals[idx];
215}
216
217inline double poisson(double x, double par)
218{
219 if (par < 0)
220 return TMath::QuietNaN();
221
222 if (x < 0) {
223 return 0;
224 } else if (x == 0.0) {
225 return std::exp(-par);
226 } else {
227 double out = x * std::log(par) - TMath::LnGamma(x + 1.) - par;
228 return std::exp(out);
229 }
230}
231
232inline double flexibleInterpSingle(unsigned int code, double low, double high, double boundary, double nominal,
233 double paramVal, double res)
234{
235 if (code == 0) {
236 // piece-wise linear
237 if (paramVal > 0) {
238 return paramVal * (high - nominal);
239 } else {
240 return paramVal * (nominal - low);
241 }
242 } else if (code == 1) {
243 // piece-wise log
244 if (paramVal >= 0) {
245 return res * (std::pow(high / nominal, +paramVal) - 1);
246 } else {
247 return res * (std::pow(low / nominal, -paramVal) - 1);
248 }
249 } else if (code == 2) {
250 // parabolic with linear
251 double a = 0.5 * (high + low) - nominal;
252 double b = 0.5 * (high - low);
253 double c = 0;
254 if (paramVal > 1) {
255 return (2 * a + b) * (paramVal - 1) + high - nominal;
256 } else if (paramVal < -1) {
257 return -1 * (2 * a - b) * (paramVal + 1) + low - nominal;
258 } else {
259 return a * paramVal * paramVal + b * paramVal + c;
260 }
261 // According to an old comment in the source code, code 3 was apparently
262 // meant to be a "parabolic version of log-normal", but it never got
263 // implemented. If someone would need it, it could be implemented as doing
264 // code 2 in log space.
265 } else if (code == 4 || code == 6) {
266 double x = paramVal;
267 double mod = 1.0;
268 if (code == 6) {
269 high /= nominal;
270 low /= nominal;
271 nominal = 1;
272 }
273 if (x >= boundary) {
274 mod = x * (high - nominal);
275 } else if (x <= -boundary) {
276 mod = x * (nominal - low);
277 } else {
278 // interpolate 6th degree
279 double t = x / boundary;
280 double eps_plus = high - nominal;
281 double eps_minus = nominal - low;
282 double S = 0.5 * (eps_plus + eps_minus);
283 double A = 0.0625 * (eps_plus - eps_minus);
284
285 mod = x * (S + t * A * (15 + t * t * (-10 + t * t * 3)));
286 }
287
288 // code 6 is multiplicative version of code 4
289 if (code == 6) {
290 mod *= res;
291 }
292 return mod;
293
294 } else if (code == 5) {
295 double x = paramVal;
296 double mod = 1.0;
297 if (x >= boundary) {
298 mod = std::pow(high / nominal, +paramVal);
299 } else if (x <= -boundary) {
300 mod = std::pow(low / nominal, -paramVal);
301 } else {
302 // interpolate 6th degree exp
303 double x0 = boundary;
304
305 high /= nominal;
306 low /= nominal;
307
308 // GHL: Swagato's suggestions
309 double logHi = std::log(high);
310 double logLo = std::log(low);
311 double powUp = std::exp(x0 * logHi);
312 double powDown = std::exp(x0 * logLo);
313 double powUpLog = high <= 0.0 ? 0.0 : powUp * logHi;
314 double powDownLog = low <= 0.0 ? 0.0 : -powDown * logLo;
315 double powUpLog2 = high <= 0.0 ? 0.0 : powUpLog * logHi;
316 double powDownLog2 = low <= 0.0 ? 0.0 : -powDownLog * logLo;
317
318 double S0 = 0.5 * (powUp + powDown);
319 double A0 = 0.5 * (powUp - powDown);
320 double S1 = 0.5 * (powUpLog + powDownLog);
321 double A1 = 0.5 * (powUpLog - powDownLog);
322 double S2 = 0.5 * (powUpLog2 + powDownLog2);
323 double A2 = 0.5 * (powUpLog2 - powDownLog2);
324
325 // fcns+der+2nd_der are eq at bd
326
327 double x0Sq = x0 * x0;
328
329 double a = 1. / (8 * x0) * (15 * A0 - 7 * x0 * S1 + x0 * x0 * A2);
330 double b = 1. / (8 * x0Sq) * (-24 + 24 * S0 - 9 * x0 * A1 + x0 * x0 * S2);
331 double c = 1. / (4 * x0Sq * x0) * (-5 * A0 + 5 * x0 * S1 - x0 * x0 * A2);
332 double d = 1. / (4 * x0Sq * x0Sq) * (12 - 12 * S0 + 7 * x0 * A1 - x0 * x0 * S2);
333 double e = 1. / (8 * x0Sq * x0Sq * x0) * (+3 * A0 - 3 * x0 * S1 + x0 * x0 * A2);
334 double f = 1. / (8 * x0Sq * x0Sq * x0Sq) * (-8 + 8 * S0 - 5 * x0 * A1 + x0 * x0 * S2);
335
336 // evaluate the 6-th degree polynomial using Horner's method
337 double value = 1. + x * (a + x * (b + x * (c + x * (d + x * (e + x * f)))));
338 mod = value;
339 }
340 return res * (mod - 1.0);
341 }
342
343 return 0.0;
344}
345
346inline double flexibleInterp(unsigned int code, double const *params, unsigned int n, double const *low,
347 double const *high, double boundary, double nominal, int doCutoff)
348{
349 double total = nominal;
350 for (std::size_t i = 0; i < n; ++i) {
351 total += flexibleInterpSingle(code, low[i], high[i], boundary, nominal, params[i], total);
352 }
353
355}
356
357inline double landau(double x, double mu, double sigma)
358{
359 if (sigma <= 0.)
360 return 0.;
361 return ROOT::Math::landau_pdf((x - mu) / sigma);
362}
363
364inline double logNormal(double x, double k, double m0)
365{
366 return ROOT::Math::lognormal_pdf(x, std::log(m0), std::abs(std::log(k)));
367}
368
369inline double logNormalStandard(double x, double sigma, double mu)
370{
371 return ROOT::Math::lognormal_pdf(x, mu, std::abs(sigma));
372}
373
374inline double effProd(double eff, double pdf)
375{
376 return eff * pdf;
377}
378
379inline double nll(double pdf, double weight, int binnedL, int doBinOffset)
380{
381 if (binnedL) {
382 // Special handling of this case since std::log(Poisson(0,0)=0 but can't be
383 // calculated with usual log-formula since std::log(mu)=0. No update of result
384 // is required since term=0.
385 if (std::abs(pdf) < 1e-10 && std::abs(weight) < 1e-10) {
386 return 0.0;
387 }
388 if (doBinOffset) {
389 return pdf - weight - weight * (std::log(pdf) - std::log(weight));
390 }
391 return pdf - weight * std::log(pdf) + TMath::LnGamma(weight + 1);
392 } else {
393 return -weight * std::log(pdf);
394 }
395}
396
397inline double recursiveFraction(double *a, unsigned int n)
398{
399 double prod = a[0];
400
401 for (unsigned int i = 1; i < n; ++i) {
402 prod *= 1.0 - a[i];
403 }
404
405 return prod;
406}
407
408inline double cbShape(double m, double m0, double sigma, double alpha, double n)
409{
410 double t = (m - m0) / sigma;
411 if (alpha < 0)
412 t = -t;
413
414 double absAlpha = std::abs(alpha);
415
416 if (t >= -absAlpha) {
417 return std::exp(-0.5 * t * t);
418 } else {
419 double r = n / absAlpha;
420 double a = std::exp(-0.5 * absAlpha * absAlpha);
421 double b = r - absAlpha;
422
423 return a * std::pow(r / (b - t), n);
424 }
425}
426
427// For RooCBShape
428inline double approxErf(double arg)
429{
430 if (arg > 5.0)
431 return 1.0;
432 if (arg < -5.0)
433 return -1.0;
434
435 return TMath::Erf(arg);
436}
437
438/// @brief Function to calculate the integral of an un-normalized RooGaussian over x. To calculate the integral over
439/// mean, just interchange the respective values of x and mean.
440/// @param xMin Minimum value of variable to integrate wrt.
441/// @param xMax Maximum value of of variable to integrate wrt.
442/// @param mean Mean.
443/// @param sigma Sigma.
444/// @return The integral of an un-normalized RooGaussian over the value in x.
445inline double gaussianIntegral(double xMin, double xMax, double mean, double sigma)
446{
447 // The normalisation constant 1./sqrt(2*pi*sigma^2) is left out in evaluate().
448 // Therefore, the integral is scaled up by that amount to make RooFit normalise
449 // correctly.
450 double resultScale = 0.5 * std::sqrt(TMath::TwoPi()) * sigma;
451
452 // Here everything is scaled and shifted into a standard normal distribution:
453 double xscale = TMath::Sqrt2() * sigma;
454 double scaledMin = 0.;
455 double scaledMax = 0.;
456 scaledMin = (xMin - mean) / xscale;
457 scaledMax = (xMax - mean) / xscale;
458
459 // Here we go for maximum precision: We compute all integrals in the UPPER
460 // tail of the Gaussian, because erfc has the highest precision there.
461 // Therefore, the different cases for range limits in the negative hemisphere are mapped onto
462 // the equivalent points in the upper hemisphere using erfc(-x) = 2. - erfc(x)
463 double ecmin = TMath::Erfc(std::abs(scaledMin));
464 double ecmax = TMath::Erfc(std::abs(scaledMax));
465
466 double cond = 0.0;
467 // Don't put this "prd" inside the "if" because clad will not be able to differentiate the code correctly (as of
468 // v1.1)!
469 double prd = scaledMin * scaledMax;
470 if (prd < 0.0) {
471 cond = 2.0 - (ecmin + ecmax);
472 } else if (scaledMax <= 0.0) {
473 cond = ecmax - ecmin;
474 } else {
475 cond = ecmin - ecmax;
476 }
477 return resultScale * cond;
478}
479
480inline double bifurGaussIntegral(double xMin, double xMax, double mean, double sigmaL, double sigmaR)
481{
482 const double xscaleL = TMath::Sqrt2() * sigmaL;
483 const double xscaleR = TMath::Sqrt2() * sigmaR;
484
485 const double resultScale = 0.5 * std::sqrt(TMath::TwoPi());
486
487 if (xMax < mean) {
488 return resultScale * (sigmaL * (TMath::Erf((xMax - mean) / xscaleL) - TMath::Erf((xMin - mean) / xscaleL)));
489 } else if (xMin > mean) {
490 return resultScale * (sigmaR * (TMath::Erf((xMax - mean) / xscaleR) - TMath::Erf((xMin - mean) / xscaleR)));
491 } else {
492 return resultScale *
493 (sigmaR * TMath::Erf((xMax - mean) / xscaleR) - sigmaL * TMath::Erf((xMin - mean) / xscaleL));
494 }
495}
496
497inline double exponentialIntegral(double xMin, double xMax, double constant)
498{
499 if (constant == 0.0) {
500 return xMax - xMin;
501 }
502
503 return (std::exp(constant * xMax) - std::exp(constant * xMin)) / constant;
504}
505
506/// In pdfMode, a coefficient for the constant term of 1.0 is implied if lowestOrder > 0.
507template <bool pdfMode = false>
508inline double polynomialIntegral(double const *coeffs, int nCoeffs, int lowestOrder, double xMin, double xMax)
509{
510 int denom = lowestOrder + nCoeffs;
511 double min = coeffs[nCoeffs - 1] / double(denom);
512 double max = coeffs[nCoeffs - 1] / double(denom);
513
514 for (int i = nCoeffs - 2; i >= 0; i--) {
515 denom--;
516 min = (coeffs[i] / double(denom)) + xMin * min;
517 max = (coeffs[i] / double(denom)) + xMax * max;
518 }
519
520 max = max * std::pow(xMax, 1 + lowestOrder);
521 min = min * std::pow(xMin, 1 + lowestOrder);
522
523 return max - min + (pdfMode && lowestOrder > 0.0 ? xMax - xMin : 0.0);
524}
525
526/// use fast FMA if available, fall back to normal arithmetic if not
527inline double fast_fma(double x, double y, double z) noexcept
528{
529#if defined(FP_FAST_FMA) // check if std::fma has fast hardware implementation
530 return std::fma(x, y, z);
531#else // defined(FP_FAST_FMA)
532 // std::fma might be slow, so use a more pedestrian implementation
533#if defined(__clang__)
534#pragma STDC FP_CONTRACT ON // hint clang that using an FMA is okay here
535#endif // defined(__clang__)
536 return (x * y) + z;
537#endif // defined(FP_FAST_FMA)
538}
539
540inline double chebychevIntegral(double const *coeffs, unsigned int nCoeffs, double xMin, double xMax, double xMinFull,
541 double xMaxFull)
542{
543 const double halfrange = .5 * (xMax - xMin);
544 const double mid = .5 * (xMax + xMin);
545
546 // the full range of the function is mapped to the normalised [-1, 1] range
547 const double b = (xMaxFull - mid) / halfrange;
548 const double a = (xMinFull - mid) / halfrange;
549
550 // coefficient for integral(T_0(x)) is 1 (implicit), integrate by hand
551 // T_0(x) and T_1(x), and use for n > 1: integral(T_n(x) dx) =
552 // (T_n+1(x) / (n + 1) - T_n-1(x) / (n - 1)) / 2
553 double sum = b - a; // integrate T_0(x) by hand
554
555 const unsigned int iend = nCoeffs;
556 if (iend > 0) {
557 {
558 // integrate T_1(x) by hand...
559 const double c = coeffs[0];
560 sum = fast_fma(0.5 * (b + a) * (b - a), c, sum);
561 }
562 if (1 < iend) {
563 double bcurr = b;
564 double btwox = 2 * b;
565 double blast = 1;
566
567 double acurr = a;
568 double atwox = 2 * a;
569 double alast = 1;
570
571 double newval = atwox * acurr - alast;
572 alast = acurr;
573 acurr = newval;
574
575 newval = btwox * bcurr - blast;
576 blast = bcurr;
577 bcurr = newval;
578 double nminus1 = 1.;
579 for (unsigned int i = 1; iend != i; ++i) {
580 // integrate using recursion relation
581 const double c = coeffs[i];
582 const double term2 = (blast - alast) / nminus1;
583
584 newval = atwox * acurr - alast;
585 alast = acurr;
586 acurr = newval;
587
588 newval = btwox * bcurr - blast;
589 blast = bcurr;
590 bcurr = newval;
591
592 ++nminus1;
593 const double term1 = (bcurr - acurr) / (nminus1 + 1.);
594 const double intTn = 0.5 * (term1 - term2);
595 sum = fast_fma(intTn, c, sum);
596 }
597 }
598 }
599
600 // take care to multiply with the right factor to account for the mapping to
601 // normalised range [-1, 1]
602 return halfrange * sum;
603}
604
605// The last param should be of type bool but it is not as that causes some issues with Cling for some reason...
606inline double
607poissonIntegral(int code, double mu, double x, double integrandMin, double integrandMax, unsigned int protectNegative)
608{
609 if (protectNegative && mu < 0.0) {
610 return std::exp(-2.0 * mu); // make it fall quickly
611 }
612
613 if (code == 1) {
614 // Implement integral over x as summation. Add special handling in case
615 // range boundaries are not on integer values of x
616 integrandMin = std::max(0., integrandMin);
617
618 if (integrandMax < 0. || integrandMax < integrandMin) {
619 return 0;
620 }
621 const double delta = 100.0 * std::sqrt(mu);
622 // If the limits are more than many standard deviations away from the mean,
623 // we might as well return the integral of the full Poisson distribution to
624 // save computing time.
625 if (integrandMin < std::max(mu - delta, 0.0) && integrandMax > mu + delta) {
626 return 1.;
627 }
628
629 // The range as integers. ixMin is included, ixMax outside.
630 const unsigned int ixMin = integrandMin;
631 const unsigned int ixMax = std::min(integrandMax + 1, (double)std::numeric_limits<unsigned int>::max());
632
633 // Sum from 0 to just before the bin outside of the range.
634 if (ixMin == 0) {
635 return ROOT::Math::inc_gamma_c(ixMax, mu);
636 } else {
637 // If necessary, subtract from 0 to the beginning of the range
638 if (ixMin <= mu) {
640 } else {
641 // Avoid catastrophic cancellation in the high tails:
643 }
644 }
645 }
646
647 // the integral with respect to the mean is the integral of a gamma distribution
648 // negative ix does not need protection (gamma returns 0.0)
649 const double ix = 1 + x;
650
652}
653
654inline double logNormalIntegral(double xMin, double xMax, double m0, double k)
655{
656 const double root2 = std::sqrt(2.);
657
658 double ln_k = std::abs(std::log(k));
659 double ret =
660 0.5 * (TMath::Erf(std::log(xMax / m0) / (root2 * ln_k)) - TMath::Erf(std::log(xMin / m0) / (root2 * ln_k)));
661
662 return ret;
663}
664
665inline double logNormalIntegralStandard(double xMin, double xMax, double mu, double sigma)
666{
667 const double root2 = std::sqrt(2.);
668
669 double ln_k = std::abs(sigma);
670 double ret =
671 0.5 * (TMath::Erf((std::log(xMax) - mu) / (root2 * ln_k)) - TMath::Erf((std::log(xMin) - mu) / (root2 * ln_k)));
672
673 return ret;
674}
675
676inline double cbShapeIntegral(double mMin, double mMax, double m0, double sigma, double alpha, double n)
677{
678 const double sqrtPiOver2 = 1.2533141373;
679 const double sqrt2 = 1.4142135624;
680
681 double result = 0.0;
682 bool useLog = false;
683
684 if (std::abs(n - 1.0) < 1.0e-05)
685 useLog = true;
686
687 double sig = std::abs(sigma);
688
689 double tmin = (mMin - m0) / sig;
690 double tmax = (mMax - m0) / sig;
691
692 if (alpha < 0) {
693 double tmp = tmin;
694 tmin = -tmax;
695 tmax = -tmp;
696 }
697
698 double absAlpha = std::abs(alpha);
699
700 if (tmin >= -absAlpha) {
701 result += sig * sqrtPiOver2 * (approxErf(tmax / sqrt2) - approxErf(tmin / sqrt2));
702 } else if (tmax <= -absAlpha) {
703 double r = n / absAlpha;
704 double a = r * std::exp(-0.5 * absAlpha * absAlpha);
705 double b = r - absAlpha;
706
707 if (useLog) {
708 result += a * std::pow(r, n-1) * sig * (std::log(b - tmin) - std::log(b - tmax));
709 } else {
710 result += a * sig / (1.0 - n) * (std::pow(r / (b - tmin), n - 1.0) - std::pow(r / (b - tmax), n - 1.0));
711 }
712 } else {
713 double r = n / absAlpha;
714 double a = r * std::exp(-0.5 * absAlpha * absAlpha);
715 double b = r - absAlpha;
716
717 double term1 = 0.0;
718 if (useLog) {
719 term1 = a * std::pow(r, n-1) * sig * (std::log(b - tmin) - std::log(r));
720 } else {
721 term1 = a * sig / (1.0 - n) * (std::pow(r / (b - tmin), n - 1.0) - 1.0);
722 }
723
724 double term2 = sig * sqrtPiOver2 * (approxErf(tmax / sqrt2) - approxErf(-absAlpha / sqrt2));
725
726 result += term1 + term2;
727 }
728
729 if (result == 0)
730 return 1.E-300;
731 return result;
732}
733
734inline double bernsteinIntegral(double xlo, double xhi, double xmin, double xmax, double *coefs, int nCoefs)
735{
736 double xloScaled = (xlo - xmin) / (xmax - xmin);
737 double xhiScaled = (xhi - xmin) / (xmax - xmin);
738
739 int degree = nCoefs - 1; // n+1 polys of degree n
740 double norm = 0.;
741
742 for (int i = 0; i <= degree; ++i) {
743 // for each of the i Bernstein basis polynomials
744 // represent it in the 'power basis' (the naive polynomial basis)
745 // where the integral is straight forward.
746 double temp = 0.;
747 for (int j = i; j <= degree; ++j) { // power basisŧ
748 double binCoefs = binomial(degree, j) * binomial(j, i);
749 double oneOverJPlusOne = 1. / (j + 1.);
750 double powDiff = std::pow(xhiScaled, j + 1.) - std::pow(xloScaled, j + 1.);
751 temp += std::pow(-1., j - i) * binCoefs * powDiff * oneOverJPlusOne;
752 }
753 temp *= coefs[i]; // include coeff
754 norm += temp; // add this basis's contribution to total
755 }
756
757 return norm * (xmax - xmin);
758}
759
760inline double multiVarGaussian(int n, const double *x, const double *mu, const double *covI)
761{
762 double result = 0.0;
763
764 // Compute the bilinear form (x-mu)^T * covI * (x-mu)
765 for (int i = 0; i < n; ++i) {
766 for (int j = 0; j < n; ++j) {
767 result += (x[i] - mu[i]) * covI[i * n + j] * (x[j] - mu[j]);
768 }
769 }
770 return std::exp(-0.5 * result);
771}
772
773// Integral of a step function defined by `nBins` intervals, where the
774// intervals have values `coefs` and the boundary on the interval `iBin` is
775// given by `[boundaries[i], boundaries[i+1])`.
776inline double stepFunctionIntegral(double xmin, double xmax, std::size_t nBins, double const *boundaries, double const *coefs)
777{
778 double out = 0.0;
779 for (std::size_t i = 0; i < nBins; ++i) {
780 double a = boundaries[i];
781 double b = boundaries[i + 1];
782 out += coefs[i] * std::max(0.0, std::min(b, xmax) - std::max(a, xmin));
783 }
784 return out;
785}
786
787} // namespace MathFuncs
788} // namespace Detail
789} // namespace RooFit
790
791namespace clad {
792namespace custom_derivatives {
793namespace RooFit {
794namespace Detail {
795namespace MathFuncs {
796
797// Clad can't generate the pullback for binNumber because of the
798// std::lower_bound usage. But since binNumber returns an integer, and such
799// functions have mathematically no derivatives anyway, we just declare a
800// custom dummy pullback that does nothing.
801
802template <class... Types>
803void binNumber_pullback(Types...)
804{
805}
806
807} // namespace MathFuncs
808} // namespace Detail
809} // namespace RooFit
810} // namespace custom_derivatives
811} // namespace clad
812
813#endif
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define S0(x)
Definition RSha256.hxx:88
#define S1(x)
Definition RSha256.hxx:89
#define c(i)
Definition RSha256.hxx:101
#define a(i)
Definition RSha256.hxx:99
#define e(i)
Definition RSha256.hxx:103
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
static unsigned int total
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
float xmin
float xmax
double lognormal_pdf(double x, double m, double s, double x0=0)
Probability density function of the lognormal distribution.
double landau_pdf(double x, double xi=1, double x0=0)
Probability density function of the Landau distribution:
double inc_gamma_c(double a, double x)
Calculates the normalized (regularized) upper incomplete gamma function (upper integral)
double inc_gamma(double a, double x)
Calculates the normalized (regularized) lower incomplete gamma function (lower integral)
const Double_t sigma
Double_t y[n]
Definition legend1.C:17
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
double stepFunctionIntegral(double xmin, double xmax, std::size_t nBins, double const *boundaries, double const *coefs)
Definition MathFuncs.h:776
double logNormalIntegral(double xMin, double xMax, double m0, double k)
Definition MathFuncs.h:654
double gaussianIntegral(double xMin, double xMax, double mean, double sigma)
Function to calculate the integral of an un-normalized RooGaussian over x.
Definition MathFuncs.h:445
double chebychevIntegral(double const *coeffs, unsigned int nCoeffs, double xMin, double xMax, double xMinFull, double xMaxFull)
Definition MathFuncs.h:540
double bifurGaussIntegral(double xMin, double xMax, double mean, double sigmaL, double sigmaR)
Definition MathFuncs.h:480
double cbShape(double m, double m0, double sigma, double alpha, double n)
Definition MathFuncs.h:408
double polynomial(double const *coeffs, int nCoeffs, int lowestOrder, double x)
In pdfMode, a coefficient for the constant term of 1.0 is implied if lowestOrder > 0.
Definition MathFuncs.h:129
double recursiveFraction(double *a, unsigned int n)
Definition MathFuncs.h:397
unsigned int binNumber(double x, double coef, double const *boundaries, unsigned int nBoundaries, int nbins, int blo)
Definition MathFuncs.h:190
double constraintSum(double const *comp, unsigned int compSize)
Definition MathFuncs.h:163
double cbShapeIntegral(double mMin, double mMax, double m0, double sigma, double alpha, double n)
Definition MathFuncs.h:676
double fast_fma(double x, double y, double z) noexcept
use fast FMA if available, fall back to normal arithmetic if not
Definition MathFuncs.h:527
double logNormalIntegralStandard(double xMin, double xMax, double mu, double sigma)
Definition MathFuncs.h:665
double landau(double x, double mu, double sigma)
Definition MathFuncs.h:357
double gaussian(double x, double mean, double sigma)
Function to evaluate an un-normalized RooGaussian.
Definition MathFuncs.h:85
double product(double const *factors, std::size_t nFactors)
Definition MathFuncs.h:92
double chebychev(double *coeffs, unsigned int nCoeffs, double x_in, double xMin, double xMax)
Definition MathFuncs.h:139
double poisson(double x, double par)
Definition MathFuncs.h:217
double binomial(int n, int k)
Calculates the binomial coefficient n over k.
Definition MathFuncs.h:30
unsigned int uniformBinNumber(double low, double high, double val, unsigned int numBins, double coef)
Definition MathFuncs.h:172
double flexibleInterpSingle(unsigned int code, double low, double high, double boundary, double nominal, double paramVal, double res)
Definition MathFuncs.h:232
unsigned int rawBinNumber(double x, double const *boundaries, std::size_t nBoundaries)
Definition MathFuncs.h:178
double interpolate1d(double low, double high, double val, unsigned int numBins, double const *vals)
Definition MathFuncs.h:197
double logNormalStandard(double x, double sigma, double mu)
Definition MathFuncs.h:369
double bifurGauss(double x, double mean, double sigmaL, double sigmaR)
Definition MathFuncs.h:107
double ratio(double numerator, double denominator)
Definition MathFuncs.h:102
double polynomialIntegral(double const *coeffs, int nCoeffs, int lowestOrder, double xMin, double xMax)
In pdfMode, a coefficient for the constant term of 1.0 is implied if lowestOrder > 0.
Definition MathFuncs.h:508
double bernsteinIntegral(double xlo, double xhi, double xmin, double xmax, double *coefs, int nCoefs)
Definition MathFuncs.h:734
double approxErf(double arg)
Definition MathFuncs.h:428
double effProd(double eff, double pdf)
Definition MathFuncs.h:374
double poissonIntegral(int code, double mu, double x, double integrandMin, double integrandMax, unsigned int protectNegative)
Definition MathFuncs.h:607
double logNormal(double x, double k, double m0)
Definition MathFuncs.h:364
double multiVarGaussian(int n, const double *x, const double *mu, const double *covI)
Definition MathFuncs.h:760
double nll(double pdf, double weight, int binnedL, int doBinOffset)
Definition MathFuncs.h:379
double bernstein(double x, double xmin, double xmax, double *coefs, int nCoefs)
The caller needs to make sure that there is at least one coefficient.
Definition MathFuncs.h:47
double efficiency(double effFuncVal, int catIndex, int sigCatIndex)
Definition MathFuncs.h:116
double flexibleInterp(unsigned int code, double const *params, unsigned int n, double const *low, double const *high, double boundary, double nominal, int doCutoff)
Definition MathFuncs.h:346
double exponentialIntegral(double xMin, double xMax, double constant)
Definition MathFuncs.h:497
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition CodegenImpl.h:64
Double_t Erf(Double_t x)
Computation of the error function erf(x).
Definition TMath.cxx:190
Double_t QuietNaN()
Returns a quiet NaN as defined by IEEE 754.
Definition TMath.h:906
constexpr Double_t Sqrt2()
Definition TMath.h:86
Double_t Erfc(Double_t x)
Computes the complementary error function erfc(x).
Definition TMath.cxx:199
Double_t LnGamma(Double_t z)
Computation of ln[gamma(z)] for all z.
Definition TMath.cxx:509
Double_t SignalingNaN()
Returns a signaling NaN as defined by IEEE 754](http://en.wikipedia.org/wiki/NaN#Signaling_NaN).
Definition TMath.h:914
constexpr Double_t TwoPi()
Definition TMath.h:44
TMarker m
Definition textangle.C:8
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2345