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