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