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