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>
180double constraintSum(DoubleArray comp, unsigned int compSize)
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
377 return doCutoff && total <= 0 ? TMath::Limits<double>::Min() : total;
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
567/// (exp(x) - 1) / x with the removable singularity at x = 0 filled in. The
568/// derivative is expm1OverXDerivative(), also registered with clad at the end
569/// of this header: differentiating the branches would give zero at x = 0.
570inline double expm1OverX(double x)
571{
572 return x == 0.0 ? 1.0 : std::expm1(x) / x;
573}
574
575/// Derivative of expm1OverX(). The closed form (x * exp(x) - expm1(x)) / x^2
576/// cancels for small x, so a Taylor expansion is used there. Both branches are
577/// accurate to about 1e-15 at the switching point.
578inline double expm1OverXDerivative(double x)
579{
580 if (std::abs(x) < 0.25) {
581 // Sum over n of (n + 1) x^n / (n + 2)!, up to n = 10 in Horner form.
582 // clang-format off
583 return 1. / 2 + x * (1. / 3 + x * (1. / 8 + x * (1. / 30 + x * (1. / 144 + x * (1. / 840
584 + x * (1. / 5760 + x * (1. / 45360 + x * (1. / 403200 + x * (1. / 3991680 + x * (1. / 43545600))))))))));
585 // clang-format on
586 }
587 return (std::exp(x) - expm1OverX(x)) / x;
588}
589
590/// Integral of exp(constant * x) from xMin to xMax. The naive form
591/// (exp(constant * xMax) - exp(constant * xMin)) / constant cancels when
592/// constant * (xMax - xMin) is small. Factoring out the larger exponential and
593/// using std::expm1 for the rest is accurate for any constant and only
594/// overflows if the integral itself does. The derivative with respect to the
595/// constant is exponentialIntegralDConstant(), also registered with clad at
596/// the end of this header (https://github.com/root-project/root/issues/23724).
597inline double exponentialIntegral(double xMin, double xMax, double constant)
598{
599 if (constant == 0.0) {
600 return xMax - xMin;
601 }
602
603 const double z = constant * (xMax - xMin);
604 if (z > 0.0) {
605 return -std::exp(constant * xMax) * std::expm1(-z) / constant;
606 }
607 return std::exp(constant * xMin) * std::expm1(z) / constant;
608}
609
610/// Derivative of exponentialIntegral() with respect to the constant, i.e. the
611/// integral of x * exp(constant * x). With a the endpoint where constant * x is
612/// larger and d the signed distance to the other one, substituting x = a + s
613/// gives exp(constant * a) * d * (a * expm1OverX(w) + d * expm1OverXDerivative(w))
614/// with w = constant * d <= 0, so the helpers only see bounded arguments.
615inline double exponentialIntegralDConstant(double xMin, double xMax, double constant)
616{
617 const bool fromMin = constant * (xMax - xMin) <= 0.0;
618 const double a = fromMin ? xMin : xMax;
619 const double d = fromMin ? xMax - xMin : xMin - xMax;
620 const double w = constant * d;
621 // The prefactor (xMax - xMin) instead of d absorbs the sign flip for a == xMax.
622 return std::exp(constant * a) * (xMax - xMin) * (a * expm1OverX(w) + d * expm1OverXDerivative(w));
623}
624
625/// In pdfMode, a coefficient for the constant term of 1.0 is implied if lowestOrder > 0.
626template <bool pdfMode = false, typename DoubleArray>
627double polynomialIntegral(DoubleArray coeffs, int nCoeffs, int lowestOrder, double xMin, double xMax)
628{
629 int denom = lowestOrder + nCoeffs;
630 double min = coeffs[nCoeffs - 1] / double(denom);
631 double max = coeffs[nCoeffs - 1] / double(denom);
632
633 for (int i = nCoeffs - 2; i >= 0; i--) {
634 denom--;
635 min = (coeffs[i] / double(denom)) + xMin * min;
636 max = (coeffs[i] / double(denom)) + xMax * max;
637 }
638
639 max = max * std::pow(xMax, 1 + lowestOrder);
640 min = min * std::pow(xMin, 1 + lowestOrder);
641
642 return max - min + (pdfMode && lowestOrder > 0.0 ? xMax - xMin : 0.0);
643}
644
645/// use fast FMA if available, fall back to normal arithmetic if not
646inline double fast_fma(double x, double y, double z) noexcept
647{
648#if defined(FP_FAST_FMA) // check if std::fma has fast hardware implementation
649 return std::fma(x, y, z);
650#else // defined(FP_FAST_FMA)
651 // std::fma might be slow, so use a more pedestrian implementation
652#if defined(__clang__)
653#pragma STDC FP_CONTRACT ON // hint clang that using an FMA is okay here
654#endif // defined(__clang__)
655 return (x * y) + z;
656#endif // defined(FP_FAST_FMA)
657}
658
659template <typename DoubleArray>
660double
661chebychevIntegral(DoubleArray coeffs, unsigned int nCoeffs, double xMin, double xMax, double xMinFull, double xMaxFull)
662{
663 const double halfrange = .5 * (xMax - xMin);
664 const double mid = .5 * (xMax + xMin);
665
666 // the full range of the function is mapped to the normalised [-1, 1] range
667 const double b = (xMaxFull - mid) / halfrange;
668 const double a = (xMinFull - mid) / halfrange;
669
670 // coefficient for integral(T_0(x)) is 1 (implicit), integrate by hand
671 // T_0(x) and T_1(x), and use for n > 1: integral(T_n(x) dx) =
672 // (T_n+1(x) / (n + 1) - T_n-1(x) / (n - 1)) / 2
673 double sum = b - a; // integrate T_0(x) by hand
674
675 const unsigned int iend = nCoeffs;
676 if (iend > 0) {
677 {
678 // integrate T_1(x) by hand...
679 const double c = coeffs[0];
680 sum = fast_fma(0.5 * (b + a) * (b - a), c, sum);
681 }
682 if (1 < iend) {
683 double bcurr = b;
684 double btwox = 2 * b;
685 double blast = 1;
686
687 double acurr = a;
688 double atwox = 2 * a;
689 double alast = 1;
690
691 double newval = atwox * acurr - alast;
692 alast = acurr;
693 acurr = newval;
694
695 newval = btwox * bcurr - blast;
696 blast = bcurr;
697 bcurr = newval;
698 double nminus1 = 1.;
699 for (unsigned int i = 1; iend != i; ++i) {
700 // integrate using recursion relation
701 const double c = coeffs[i];
702 const double term2 = (blast - alast) / nminus1;
703
704 newval = atwox * acurr - alast;
705 alast = acurr;
706 acurr = newval;
707
708 newval = btwox * bcurr - blast;
709 blast = bcurr;
710 bcurr = newval;
711
712 ++nminus1;
713 const double term1 = (bcurr - acurr) / (nminus1 + 1.);
714 const double intTn = 0.5 * (term1 - term2);
715 sum = fast_fma(intTn, c, sum);
716 }
717 }
718 }
719
720 // take care to multiply with the right factor to account for the mapping to
721 // normalised range [-1, 1]
722 return halfrange * sum;
723}
724
725// The last param should be of type bool but it is not as that causes some issues with Cling for some reason...
726inline double
727poissonIntegral(int code, double mu, double x, double integrandMin, double integrandMax, unsigned int protectNegative)
728{
729 if (protectNegative && mu < 0.0) {
730 return std::exp(-2.0 * mu); // make it fall quickly
731 }
732
733 if (code == 1) {
734 // Implement integral over x as summation. Add special handling in case
735 // range boundaries are not on integer values of x
736 integrandMin = std::max(0., integrandMin);
737
738 if (integrandMax < 0. || integrandMax < integrandMin) {
739 return 0;
740 }
741 const double delta = 100.0 * std::sqrt(mu);
742 // If the limits are more than many standard deviations away from the mean,
743 // we might as well return the integral of the full Poisson distribution to
744 // save computing time.
745 if (integrandMin < std::max(mu - delta, 0.0) && integrandMax > mu + delta) {
746 return 1.;
747 }
748
749 // The range as integers. ixMin is included, ixMax outside.
750 const unsigned int ixMin = integrandMin;
751 const unsigned int ixMax = std::min(integrandMax + 1, (double)std::numeric_limits<unsigned int>::max());
752
753 // Sum from 0 to just before the bin outside of the range.
754 if (ixMin == 0) {
755 return ROOT::Math::inc_gamma_c(ixMax, mu);
756 } else {
757 // If necessary, subtract from 0 to the beginning of the range
758 if (ixMin <= mu) {
759 return ROOT::Math::inc_gamma_c(ixMax, mu) - ROOT::Math::inc_gamma_c(ixMin, mu);
760 } else {
761 // Avoid catastrophic cancellation in the high tails:
762 return ROOT::Math::inc_gamma(ixMin, mu) - ROOT::Math::inc_gamma(ixMax, mu);
763 }
764 }
765 }
766
767 // the integral with respect to the mean is the integral of a gamma distribution
768 // negative ix does not need protection (gamma returns 0.0)
769 const double ix = 1 + x;
770
771 return ROOT::Math::inc_gamma(ix, integrandMax) - ROOT::Math::inc_gamma(ix, integrandMin);
772}
773
774inline double logNormalIntegral(double xMin, double xMax, double m0, double k)
775{
776 const double root2 = std::sqrt(2.);
777
778 double ln_k = std::abs(std::log(k));
779 double ret = 0.5 * (std::erf(std::log(xMax / m0) / (root2 * ln_k)) - std::erf(std::log(xMin / m0) / (root2 * ln_k)));
780
781 return ret;
782}
783
784inline double logNormalIntegralStandard(double xMin, double xMax, double mu, double sigma)
785{
786 const double root2 = std::sqrt(2.);
787
788 double ln_k = std::abs(sigma);
789 double ret =
790 0.5 * (std::erf((std::log(xMax) - mu) / (root2 * ln_k)) - std::erf((std::log(xMin) - mu) / (root2 * ln_k)));
791
792 return ret;
793}
794
795inline double cbShapeIntegral(double mMin, double mMax, double m0, double sigma, double alpha, double n)
796{
797 const double sqrtPiOver2 = 1.2533141373;
798 const double sqrt2 = 1.4142135624;
799
800 double result = 0.0;
801 bool useLog = false;
802
803 if (std::abs(n - 1.0) < 1.0e-05)
804 useLog = true;
805
806 double sig = std::abs(sigma);
807
808 double tmin = (mMin - m0) / sig;
809 double tmax = (mMax - m0) / sig;
810
811 if (alpha < 0) {
812 double tmp = tmin;
813 tmin = -tmax;
814 tmax = -tmp;
815 }
816
817 double absAlpha = std::abs(alpha);
818
819 if (tmin >= -absAlpha) {
820 result += sig * sqrtPiOver2 * (approxErf(tmax / sqrt2) - approxErf(tmin / sqrt2));
821 } else if (tmax <= -absAlpha) {
822 double r = n / absAlpha;
823 double a = r * std::exp(-0.5 * absAlpha * absAlpha);
824 double b = r - absAlpha;
825
826 if (useLog) {
827 double log_b_tmin = std::log(b - tmin);
828 double log_b_tmax = std::log(b - tmax);
829 result += a * std::pow(r, n - 1) * sig *
830 (log_b_tmin - log_b_tmax + 0.5 * (1.0 - n) * (log_b_tmin * log_b_tmin - log_b_tmax * log_b_tmax));
831 } else {
832 result += a * sig / (1.0 - n) * (std::pow(r / (b - tmin), n - 1.0) - std::pow(r / (b - tmax), n - 1.0));
833 }
834 } else {
835 double r = n / absAlpha;
836 double a = r * std::exp(-0.5 * absAlpha * absAlpha);
837 double b = r - absAlpha;
838
839 double term1 = 0.0;
840 if (useLog) {
841 double log_b_tmin = std::log(b - tmin);
842 double log_r = std::log(r);
843 term1 = a * std::pow(r, n - 1) * sig *
844 (log_b_tmin - log_r + 0.5 * (1.0 - n) * (log_b_tmin * log_b_tmin - log_r * log_r));
845 } else {
846 term1 = a * sig / (1.0 - n) * (std::pow(r / (b - tmin), n - 1.0) - 1.0);
847 }
848
849 double term2 = sig * sqrtPiOver2 * (approxErf(tmax / sqrt2) - approxErf(-absAlpha / sqrt2));
850
851 result += term1 + term2;
852 }
853
854 if (result == 0)
855 return 1.E-300;
856 return result;
857}
858
859template <typename DoubleArray>
860double bernsteinIntegral(double xlo, double xhi, double xmin, double xmax, DoubleArray coefs, int nCoefs)
861{
862 double xloScaled = (xlo - xmin) / (xmax - xmin);
863 double xhiScaled = (xhi - xmin) / (xmax - xmin);
864
865 int degree = nCoefs - 1; // n+1 polys of degree n
866 double norm = 0.;
867
868 for (int i = 0; i <= degree; ++i) {
869 // for each of the i Bernstein basis polynomials
870 // represent it in the 'power basis' (the naive polynomial basis)
871 // where the integral is straight forward.
872 double temp = 0.;
873 for (int j = i; j <= degree; ++j) { // power basisŧ
874 double binCoefs = binomial(degree, j) * binomial(j, i);
875 double oneOverJPlusOne = 1. / (j + 1.);
876 double powDiff = std::pow(xhiScaled, j + 1.) - std::pow(xloScaled, j + 1.);
877 temp += std::pow(-1., j - i) * binCoefs * powDiff * oneOverJPlusOne;
878 }
879 temp *= coefs[i]; // include coeff
880 norm += temp; // add this basis's contribution to total
881 }
882
883 return norm * (xmax - xmin);
884}
885
886template <typename XArray, typename MuArray, typename CovArray>
887double multiVarGaussian(int n, XArray x, MuArray mu, CovArray covI)
888{
889 double result = 0.0;
890
891 // Compute the bilinear form (x-mu)^T * covI * (x-mu)
892 for (int i = 0; i < n; ++i) {
893 for (int j = 0; j < n; ++j) {
894 result += (x[i] - mu[i]) * covI[i * n + j] * (x[j] - mu[j]);
895 }
896 }
897 return std::exp(-0.5 * result);
898}
899
900// Integral of a step function defined by `nBins` intervals, where the
901// intervals have values `coefs` and the boundary on the interval `iBin` is
902// given by `[boundaries[i], boundaries[i+1])`.
903template <typename DoubleArray>
904double stepFunctionIntegral(double xmin, double xmax, std::size_t nBins, DoubleArray boundaries, DoubleArray coefs)
905{
906 double out = 0.0;
907 for (std::size_t i = 0; i < nBins; ++i) {
908 double a = boundaries[i];
909 double b = boundaries[i + 1];
910 out += coefs[i] * std::max(0.0, std::min(b, xmax) - std::max(a, xmin));
911 }
912 return out;
913}
914
915} // namespace RooFit::Detail::MathFuncs
916
917namespace clad {
918// Only declared, never defined here: clad's own headers exist exclusively
919// inside the interpreter, but this header is also compiled normally.
920template <typename T, typename U>
922} // namespace clad
923
924namespace clad::custom_derivatives {
926
927// Clad can't generate the derivatives for binNumber because of the
928// std::lower_bound usage. But since binNumber returns an integer, and such
929// functions have mathematically no derivatives anyway, we just declare custom
930// dummy derivatives that do nothing.
931//
932// Both directions have to be covered: the pullback alone is enough for
933// gradients, but Hessians also run the forward pass over binNumber, and
934// without a pushforward clad descends into std::lower_bound.
935
936template <class... Types>
937void binNumber_pullback(Types...)
938{
939}
940
941// The second parameter is unused. It is what keeps the return type of the
942// pushforward below dependent, so that clad::ValueAndPushforward only has to be
943// complete once that template is instantiated. That happens under clad and
944// nowhere else, while parsing the declaration happens in every build that
945// includes this header. A class template is needed here: an alias template
946// would be expanded eagerly and defeat the purpose.
947template <typename T, typename>
951
952template <typename DoubleArray, class... Types>
954binNumber_pushforward(double x, double coef, DoubleArray boundaries, unsigned int nBoundaries, int nbins, int blo,
955 Types...)
956{
957 return {::RooFit::Detail::MathFuncs::binNumber(x, coef, boundaries, nBoundaries, nbins, blo), 0};
958}
959
960// Custom derivatives of exponentialIntegral and its helper expm1OverX, as
961// pullbacks for gradients and pushforwards for Hessians. Clad's derivatives of
962// the function bodies would be inaccurate for small constant * (xMax - xMin)
963// and identically zero in the == 0 branches
964// (https://github.com/root-project/root/issues/23724). The helper needs its
965// own because clad builds the Hessian by differentiating the pushforward body.
966//
967// The template parameters only keep the return types dependent, see
968// ValueAndPushforwardOf above. They are deliberately not deduced: clad passes
969// integer literals as derivatives of the integer literals in generated code.
970
971inline void expm1OverX_pullback(double x, double d_out, double *d_x)
972{
974}
975
976template <typename T = double>
981
982inline void exponentialIntegral_pullback(double xMin, double xMax, double constant, double d_out, double *d_xMin,
983 double *d_xMax, double *d_constant)
984{
985 *d_xMin -= d_out * std::exp(constant * xMin);
986 *d_xMax += d_out * std::exp(constant * xMax);
987 *d_constant += d_out * ::RooFit::Detail::MathFuncs::exponentialIntegralDConstant(xMin, xMax, constant);
988}
989
990template <typename T = double>
992exponentialIntegral_pushforward(double xMin, double xMax, double constant, double d_xMin, double d_xMax,
993 double d_constant)
994{
995 return {::RooFit::Detail::MathFuncs::exponentialIntegral(xMin, xMax, constant),
996 -std::exp(constant * xMin) * d_xMin + std::exp(constant * xMax) * d_xMax +
997 ::RooFit::Detail::MathFuncs::exponentialIntegralDConstant(xMin, xMax, constant) * d_constant};
998}
999
1000} // namespace RooFit::Detail::MathFuncs
1001} // namespace clad::custom_derivatives
1002
1003#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
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 exponentialIntegralDConstant(double xMin, double xMax, double constant)
Derivative of exponentialIntegral() with respect to the constant, i.e.
Definition MathFuncs.h:615
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:627
double logNormalIntegral(double xMin, double xMax, double m0, double k)
Definition MathFuncs.h:774
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:661
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:795
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:646
double logNormalIntegralStandard(double xMin, double xMax, double mu, double sigma)
Definition MathFuncs.h:784
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:887
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:860
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 expm1OverX(double x)
(exp(x) - 1) / x with the removable singularity at x = 0 filled in.
Definition MathFuncs.h:570
double approxErf(double arg)
Definition MathFuncs.h:499
double expm1OverXDerivative(double x)
Derivative of expm1OverX().
Definition MathFuncs.h:578
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:727
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:904
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)
Integral of exp(constant * x) from xMin to xMax.
Definition MathFuncs.h:597
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
ValueAndPushforwardOf< unsignedint, DoubleArray >::type binNumber_pushforward(double x, double coef, DoubleArray boundaries, unsigned int nBoundaries, int nbins, int blo, Types...)
Definition MathFuncs.h:954
void exponentialIntegral_pullback(double xMin, double xMax, double constant, double d_out, double *d_xMin, double *d_xMax, double *d_constant)
Definition MathFuncs.h:982
ValueAndPushforwardOf< double, T >::type expm1OverX_pushforward(double x, double d_x)
Definition MathFuncs.h:977
ValueAndPushforwardOf< double, T >::type exponentialIntegral_pushforward(double xMin, double xMax, double constant, double d_xMin, double d_xMax, double d_constant)
Definition MathFuncs.h:992
void expm1OverX_pullback(double x, double d_out, double *d_x)
Definition MathFuncs.h:971
TMarker m
Definition textangle.C:8
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2335