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 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
402/// Chi-squared contribution of one bin with "expected" errors:
403/// \f$ \sigma^2 = \mu \f$. Empty/no-prediction bins contribute zero; bins with
404/// non-positive \f$ \mu \f$ but non-empty data yield NaN (to let the minimizer
405/// recover).
406inline double chi2Expected(double mu, double weight)
407{
408 if (mu == 0.0 && weight == 0.0) {
409 return 0.0;
410 }
411 if (mu <= 0.0) {
412 return std::numeric_limits<double>::quiet_NaN();
413 }
414 const double diff = mu - weight;
415 return diff * diff / mu;
416}
417
418/// Chi-squared contribution of one bin with a user-supplied symmetric error
419/// squared (e.g. `SumW2` weights from the data).
420inline double chi2Symmetric(double mu, double weight, double sigma2)
421{
422 if (sigma2 == 0.0 && mu == 0.0 && weight == 0.0) {
423 return 0.0;
424 }
425 if (sigma2 <= 0.0) {
426 return std::numeric_limits<double>::quiet_NaN();
427 }
428 const double diff = mu - weight;
429 return diff * diff / sigma2;
430}
431
432/// Chi-squared contribution of one bin with asymmetric (Poisson-style) data
433/// errors. The side facing the prediction is used: `errHi` when
434/// \f$ \mu > \mathrm{weight} \f$, otherwise `errLo`.
435inline double chi2Asymmetric(double mu, double weight, double errLo, double errHi)
436{
437 const double diff = mu - weight;
438 const double err = diff > 0.0 ? errHi : errLo;
439 const double sigma2 = err * err;
440 if (sigma2 == 0.0 && mu == 0.0 && weight == 0.0) {
441 return 0.0;
442 }
443 if (sigma2 <= 0.0) {
444 return std::numeric_limits<double>::quiet_NaN();
445 }
446 return diff * diff / sigma2;
447}
448
449inline double nll(double pdf, double weight, int binnedL, int doBinOffset)
450{
451 if (binnedL) {
452 // Special handling of this case since std::log(Poisson(0,0)=0 but can't be
453 // calculated with usual log-formula since std::log(mu)=0. No update of result
454 // is required since term=0.
455 if (std::abs(pdf) < 1e-10 && std::abs(weight) < 1e-10) {
456 return 0.0;
457 }
458 if (doBinOffset) {
459 return pdf - weight - weight * (std::log(pdf) - std::log(weight));
460 }
461 return pdf - weight * std::log(pdf) + TMath::LnGamma(weight + 1);
462 } else {
463 return -weight * std::log(pdf);
464 }
465}
466
467template <typename DoubleArray>
468double recursiveFraction(DoubleArray a, unsigned int n)
469{
470 double prod = a[0];
471
472 for (unsigned int i = 1; i < n; ++i) {
473 prod *= 1.0 - a[i];
474 }
475
476 return prod;
477}
478
479inline double cbShape(double m, double m0, double sigma, double alpha, double n)
480{
481 double t = (m - m0) / sigma;
482 if (alpha < 0)
483 t = -t;
484
485 double absAlpha = std::abs(alpha);
486
487 if (t >= -absAlpha) {
488 return std::exp(-0.5 * t * t);
489 } else {
490 double r = n / absAlpha;
491 double a = std::exp(-0.5 * absAlpha * absAlpha);
492 double b = r - absAlpha;
493
494 return a * std::pow(r / (b - t), n);
495 }
496}
497
498// For RooCBShape
499inline double approxErf(double arg)
500{
501 if (arg > 5.0)
502 return 1.0;
503 if (arg < -5.0)
504 return -1.0;
505
506 return std::erf(arg);
507}
508
509/// @brief Function to calculate the integral of an un-normalized RooGaussian over x. To calculate the integral over
510/// mean, just interchange the respective values of x and mean.
511/// @param xMin Minimum value of variable to integrate wrt.
512/// @param xMax Maximum value of of variable to integrate wrt.
513/// @param mean Mean.
514/// @param sigma Sigma.
515/// @return The integral of an un-normalized RooGaussian over the value in x.
516inline double gaussianIntegral(double xMin, double xMax, double mean, double sigma)
517{
518 // The normalisation constant 1./sqrt(2*pi*sigma^2) is left out in evaluate().
519 // Therefore, the integral is scaled up by that amount to make RooFit normalise
520 // correctly.
521 double resultScale = 0.5 * std::sqrt(TMath::TwoPi()) * sigma;
522
523 // Here everything is scaled and shifted into a standard normal distribution:
524 double xscale = TMath::Sqrt2() * sigma;
525 double scaledMin = 0.;
526 double scaledMax = 0.;
527 scaledMin = (xMin - mean) / xscale;
528 scaledMax = (xMax - mean) / xscale;
529
530 // Here we go for maximum precision: We compute all integrals in the UPPER
531 // tail of the Gaussian, because erfc has the highest precision there.
532 // Therefore, the different cases for range limits in the negative hemisphere are mapped onto
533 // the equivalent points in the upper hemisphere using erfc(-x) = 2. - erfc(x)
534 double ecmin = std::erfc(std::abs(scaledMin));
535 double ecmax = std::erfc(std::abs(scaledMax));
536
537 double cond = 0.0;
538 // Don't put this "prd" inside the "if" because clad will not be able to differentiate the code correctly (as of
539 // v1.1)!
540 double prd = scaledMin * scaledMax;
541 if (prd < 0.0) {
542 cond = 2.0 - (ecmin + ecmax);
543 } else if (scaledMax <= 0.0) {
544 cond = ecmax - ecmin;
545 } else {
546 cond = ecmin - ecmax;
547 }
548 return resultScale * cond;
549}
550
551inline double bifurGaussIntegral(double xMin, double xMax, double mean, double sigmaL, double sigmaR)
552{
553 const double xscaleL = TMath::Sqrt2() * sigmaL;
554 const double xscaleR = TMath::Sqrt2() * sigmaR;
555
556 const double resultScale = 0.5 * std::sqrt(TMath::TwoPi());
557
558 if (xMax < mean) {
559 return resultScale * (sigmaL * (std::erf((xMax - mean) / xscaleL) - std::erf((xMin - mean) / xscaleL)));
560 } else if (xMin > mean) {
561 return resultScale * (sigmaR * (std::erf((xMax - mean) / xscaleR) - std::erf((xMin - mean) / xscaleR)));
562 } else {
563 return resultScale * (sigmaR * std::erf((xMax - mean) / xscaleR) - sigmaL * std::erf((xMin - mean) / xscaleL));
564 }
565}
566
567inline double exponentialIntegral(double xMin, double xMax, double constant)
568{
569 if (constant == 0.0) {
570 return xMax - xMin;
571 }
572
573 return (std::exp(constant * xMax) - std::exp(constant * xMin)) / constant;
574}
575
576/// In pdfMode, a coefficient for the constant term of 1.0 is implied if lowestOrder > 0.
577template <bool pdfMode = false, typename DoubleArray>
578double polynomialIntegral(DoubleArray coeffs, int nCoeffs, int lowestOrder, double xMin, double xMax)
579{
580 int denom = lowestOrder + nCoeffs;
581 double min = coeffs[nCoeffs - 1] / double(denom);
582 double max = coeffs[nCoeffs - 1] / double(denom);
583
584 for (int i = nCoeffs - 2; i >= 0; i--) {
585 denom--;
586 min = (coeffs[i] / double(denom)) + xMin * min;
587 max = (coeffs[i] / double(denom)) + xMax * max;
588 }
589
590 max = max * std::pow(xMax, 1 + lowestOrder);
591 min = min * std::pow(xMin, 1 + lowestOrder);
592
593 return max - min + (pdfMode && lowestOrder > 0.0 ? xMax - xMin : 0.0);
594}
595
596/// use fast FMA if available, fall back to normal arithmetic if not
597inline double fast_fma(double x, double y, double z) noexcept
598{
599#if defined(FP_FAST_FMA) // check if std::fma has fast hardware implementation
600 return std::fma(x, y, z);
601#else // defined(FP_FAST_FMA)
602 // std::fma might be slow, so use a more pedestrian implementation
603#if defined(__clang__)
604#pragma STDC FP_CONTRACT ON // hint clang that using an FMA is okay here
605#endif // defined(__clang__)
606 return (x * y) + z;
607#endif // defined(FP_FAST_FMA)
608}
609
610template <typename DoubleArray>
611double
612chebychevIntegral(DoubleArray coeffs, unsigned int nCoeffs, double xMin, double xMax, double xMinFull, double xMaxFull)
613{
614 const double halfrange = .5 * (xMax - xMin);
615 const double mid = .5 * (xMax + xMin);
616
617 // the full range of the function is mapped to the normalised [-1, 1] range
618 const double b = (xMaxFull - mid) / halfrange;
619 const double a = (xMinFull - mid) / halfrange;
620
621 // coefficient for integral(T_0(x)) is 1 (implicit), integrate by hand
622 // T_0(x) and T_1(x), and use for n > 1: integral(T_n(x) dx) =
623 // (T_n+1(x) / (n + 1) - T_n-1(x) / (n - 1)) / 2
624 double sum = b - a; // integrate T_0(x) by hand
625
626 const unsigned int iend = nCoeffs;
627 if (iend > 0) {
628 {
629 // integrate T_1(x) by hand...
630 const double c = coeffs[0];
631 sum = fast_fma(0.5 * (b + a) * (b - a), c, sum);
632 }
633 if (1 < iend) {
634 double bcurr = b;
635 double btwox = 2 * b;
636 double blast = 1;
637
638 double acurr = a;
639 double atwox = 2 * a;
640 double alast = 1;
641
642 double newval = atwox * acurr - alast;
643 alast = acurr;
644 acurr = newval;
645
646 newval = btwox * bcurr - blast;
647 blast = bcurr;
648 bcurr = newval;
649 double nminus1 = 1.;
650 for (unsigned int i = 1; iend != i; ++i) {
651 // integrate using recursion relation
652 const double c = coeffs[i];
653 const double term2 = (blast - alast) / nminus1;
654
655 newval = atwox * acurr - alast;
656 alast = acurr;
657 acurr = newval;
658
659 newval = btwox * bcurr - blast;
660 blast = bcurr;
661 bcurr = newval;
662
663 ++nminus1;
664 const double term1 = (bcurr - acurr) / (nminus1 + 1.);
665 const double intTn = 0.5 * (term1 - term2);
666 sum = fast_fma(intTn, c, sum);
667 }
668 }
669 }
670
671 // take care to multiply with the right factor to account for the mapping to
672 // normalised range [-1, 1]
673 return halfrange * sum;
674}
675
676// The last param should be of type bool but it is not as that causes some issues with Cling for some reason...
677inline double
678poissonIntegral(int code, double mu, double x, double integrandMin, double integrandMax, unsigned int protectNegative)
679{
680 if (protectNegative && mu < 0.0) {
681 return std::exp(-2.0 * mu); // make it fall quickly
682 }
683
684 if (code == 1) {
685 // Implement integral over x as summation. Add special handling in case
686 // range boundaries are not on integer values of x
687 integrandMin = std::max(0., integrandMin);
688
689 if (integrandMax < 0. || integrandMax < integrandMin) {
690 return 0;
691 }
692 const double delta = 100.0 * std::sqrt(mu);
693 // If the limits are more than many standard deviations away from the mean,
694 // we might as well return the integral of the full Poisson distribution to
695 // save computing time.
696 if (integrandMin < std::max(mu - delta, 0.0) && integrandMax > mu + delta) {
697 return 1.;
698 }
699
700 // The range as integers. ixMin is included, ixMax outside.
701 const unsigned int ixMin = integrandMin;
702 const unsigned int ixMax = std::min(integrandMax + 1, (double)std::numeric_limits<unsigned int>::max());
703
704 // Sum from 0 to just before the bin outside of the range.
705 if (ixMin == 0) {
706 return ROOT::Math::inc_gamma_c(ixMax, mu);
707 } else {
708 // If necessary, subtract from 0 to the beginning of the range
709 if (ixMin <= mu) {
711 } else {
712 // Avoid catastrophic cancellation in the high tails:
714 }
715 }
716 }
717
718 // the integral with respect to the mean is the integral of a gamma distribution
719 // negative ix does not need protection (gamma returns 0.0)
720 const double ix = 1 + x;
721
723}
724
725inline double logNormalIntegral(double xMin, double xMax, double m0, double k)
726{
727 const double root2 = std::sqrt(2.);
728
729 double ln_k = std::abs(std::log(k));
730 double ret = 0.5 * (std::erf(std::log(xMax / m0) / (root2 * ln_k)) - std::erf(std::log(xMin / m0) / (root2 * ln_k)));
731
732 return ret;
733}
734
735inline double logNormalIntegralStandard(double xMin, double xMax, double mu, double sigma)
736{
737 const double root2 = std::sqrt(2.);
738
739 double ln_k = std::abs(sigma);
740 double ret =
741 0.5 * (std::erf((std::log(xMax) - mu) / (root2 * ln_k)) - std::erf((std::log(xMin) - mu) / (root2 * ln_k)));
742
743 return ret;
744}
745
746inline double cbShapeIntegral(double mMin, double mMax, double m0, double sigma, double alpha, double n)
747{
748 const double sqrtPiOver2 = 1.2533141373;
749 const double sqrt2 = 1.4142135624;
750
751 double result = 0.0;
752 bool useLog = false;
753
754 if (std::abs(n - 1.0) < 1.0e-05)
755 useLog = true;
756
757 double sig = std::abs(sigma);
758
759 double tmin = (mMin - m0) / sig;
760 double tmax = (mMax - m0) / sig;
761
762 if (alpha < 0) {
763 double tmp = tmin;
764 tmin = -tmax;
765 tmax = -tmp;
766 }
767
768 double absAlpha = std::abs(alpha);
769
770 if (tmin >= -absAlpha) {
771 result += sig * sqrtPiOver2 * (approxErf(tmax / sqrt2) - approxErf(tmin / sqrt2));
772 } else if (tmax <= -absAlpha) {
773 double r = n / absAlpha;
774 double a = r * std::exp(-0.5 * absAlpha * absAlpha);
775 double b = r - absAlpha;
776
777 if (useLog) {
778 double log_b_tmin = std::log(b - tmin);
779 double log_b_tmax = std::log(b - tmax);
780 result += a * std::pow(r, n - 1) * sig *
781 (log_b_tmin - log_b_tmax + 0.5 * (1.0 - n) * (log_b_tmin * log_b_tmin - log_b_tmax * log_b_tmax));
782 } else {
783 result += a * sig / (1.0 - n) * (std::pow(r / (b - tmin), n - 1.0) - std::pow(r / (b - tmax), n - 1.0));
784 }
785 } else {
786 double r = n / absAlpha;
787 double a = r * std::exp(-0.5 * absAlpha * absAlpha);
788 double b = r - absAlpha;
789
790 double term1 = 0.0;
791 if (useLog) {
792 double log_b_tmin = std::log(b - tmin);
793 double log_r = std::log(r);
794 term1 = a * std::pow(r, n - 1) * sig *
795 (log_b_tmin - log_r + 0.5 * (1.0 - n) * (log_b_tmin * log_b_tmin - log_r * log_r));
796 } else {
797 term1 = a * sig / (1.0 - n) * (std::pow(r / (b - tmin), n - 1.0) - 1.0);
798 }
799
800 double term2 = sig * sqrtPiOver2 * (approxErf(tmax / sqrt2) - approxErf(-absAlpha / sqrt2));
801
802 result += term1 + term2;
803 }
804
805 if (result == 0)
806 return 1.E-300;
807 return result;
808}
809
810template <typename DoubleArray>
811double bernsteinIntegral(double xlo, double xhi, double xmin, double xmax, DoubleArray coefs, int nCoefs)
812{
813 double xloScaled = (xlo - xmin) / (xmax - xmin);
814 double xhiScaled = (xhi - xmin) / (xmax - xmin);
815
816 int degree = nCoefs - 1; // n+1 polys of degree n
817 double norm = 0.;
818
819 for (int i = 0; i <= degree; ++i) {
820 // for each of the i Bernstein basis polynomials
821 // represent it in the 'power basis' (the naive polynomial basis)
822 // where the integral is straight forward.
823 double temp = 0.;
824 for (int j = i; j <= degree; ++j) { // power basisŧ
825 double binCoefs = binomial(degree, j) * binomial(j, i);
826 double oneOverJPlusOne = 1. / (j + 1.);
827 double powDiff = std::pow(xhiScaled, j + 1.) - std::pow(xloScaled, j + 1.);
828 temp += std::pow(-1., j - i) * binCoefs * powDiff * oneOverJPlusOne;
829 }
830 temp *= coefs[i]; // include coeff
831 norm += temp; // add this basis's contribution to total
832 }
833
834 return norm * (xmax - xmin);
835}
836
837template <typename XArray, typename MuArray, typename CovArray>
839{
840 double result = 0.0;
841
842 // Compute the bilinear form (x-mu)^T * covI * (x-mu)
843 for (int i = 0; i < n; ++i) {
844 for (int j = 0; j < n; ++j) {
845 result += (x[i] - mu[i]) * covI[i * n + j] * (x[j] - mu[j]);
846 }
847 }
848 return std::exp(-0.5 * result);
849}
850
851// Integral of a step function defined by `nBins` intervals, where the
852// intervals have values `coefs` and the boundary on the interval `iBin` is
853// given by `[boundaries[i], boundaries[i+1])`.
854template <typename DoubleArray>
855double stepFunctionIntegral(double xmin, double xmax, std::size_t nBins, DoubleArray boundaries, DoubleArray coefs)
856{
857 double out = 0.0;
858 for (std::size_t i = 0; i < nBins; ++i) {
859 double a = boundaries[i];
860 double b = boundaries[i + 1];
861 out += coefs[i] * std::max(0.0, std::min(b, xmax) - std::max(a, xmin));
862 }
863 return out;
864}
865
866} // namespace RooFit::Detail::MathFuncs
867
868namespace clad::custom_derivatives {
870
871// Clad can't generate the pullback for binNumber because of the
872// std::lower_bound usage. But since binNumber returns an integer, and such
873// functions have mathematically no derivatives anyway, we just declare a
874// custom dummy pullback that does nothing.
875
876template <class... Types>
877void binNumber_pullback(Types...)
878{
879}
880
881} // namespace RooFit::Detail::MathFuncs
882} // namespace clad::custom_derivatives
883
884#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:578
double logNormalIntegral(double xMin, double xMax, double m0, double k)
Definition MathFuncs.h:725
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:516
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:551
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:479
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:612
double recursiveFraction(DoubleArray a, unsigned int n)
Definition MathFuncs.h:468
double cbShapeIntegral(double mMin, double mMax, double m0, double sigma, double alpha, double n)
Definition MathFuncs.h:746
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:597
double logNormalIntegralStandard(double xMin, double xMax, double mu, double sigma)
Definition MathFuncs.h:735
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:380
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:838
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:811
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:435
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:406
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:420
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:111
double ratio(double numerator, double denominator)
Definition MathFuncs.h:106
double approxErf(double arg)
Definition MathFuncs.h:499
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:678
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:855
double nll(double pdf, double weight, int binnedL, int doBinOffset)
Definition MathFuncs.h:449
double efficiency(double effFuncVal, int catIndex, int sigCatIndex)
Definition MathFuncs.h:120
double exponentialIntegral(double xMin, double xMax, double constant)
Definition MathFuncs.h:567
Double_t QuietNaN()
Returns a quiet NaN as defined by IEEE 754.
Definition TMath.h:915
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.
Definition TMath.h:923
constexpr Double_t TwoPi()
Definition TMath.h:47
TMarker m
Definition textangle.C:8
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2335