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