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