File indexing completed on 2026-08-16 09:21:00
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #ifndef RooFit_Detail_MathFuncs_h
0015 #define RooFit_Detail_MathFuncs_h
0016
0017 #include <ROOT/RConfig.hxx> // for R__HAS_CLAD
0018
0019 #include <TMath.h>
0020 #include <Math/PdfFuncMathCore.h>
0021 #include <Math/ProbFuncMathCore.h>
0022
0023 #include <algorithm>
0024 #include <cmath>
0025 #include <limits>
0026 #include <stdexcept>
0027
0028 namespace RooFit::Detail::MathFuncs {
0029
0030
0031
0032 inline double binomial(int n, int k)
0033 {
0034 if (n < 0 || k < 0 || n < k)
0035 return TMath::SignalingNaN();
0036 if (k == 0 || n == k)
0037 return 1;
0038
0039 int k1 = std::min(k, n - k);
0040 int k2 = n - k1;
0041 double fact = k2 + 1;
0042 for (double i = k1; i > 1.; --i) {
0043 fact *= (k2 + i) / i;
0044 }
0045 return fact;
0046 }
0047
0048
0049 template <typename DoubleArray>
0050 double bernstein(double x, double xmin, double xmax, DoubleArray coefs, int nCoefs)
0051 {
0052 double xScaled = (x - xmin) / (xmax - xmin);
0053 int degree = nCoefs - 1;
0054
0055
0056 if (degree < 0) {
0057 return TMath::SignalingNaN();
0058 } else if (degree == 0) {
0059 return coefs[0];
0060 } else if (degree == 1) {
0061
0062 double a0 = coefs[0];
0063 double a1 = coefs[1] - a0;
0064 return a1 * xScaled + a0;
0065
0066 } else if (degree == 2) {
0067
0068 double a0 = coefs[0];
0069 double a1 = 2 * (coefs[1] - a0);
0070 double a2 = coefs[2] - a1 - a0;
0071 return (a2 * xScaled + a1) * xScaled + a0;
0072 }
0073
0074 double t = xScaled;
0075 double s = 1. - xScaled;
0076
0077 double result = coefs[0] * s;
0078 for (int i = 1; i < degree; i++) {
0079 result = (result + t * binomial(degree, i) * coefs[i]) * s;
0080 t *= xScaled;
0081 }
0082 result += t * coefs[degree];
0083
0084 return result;
0085 }
0086
0087
0088 inline double gaussian(double x, double mean, double sigma)
0089 {
0090 const double arg = x - mean;
0091 const double sig = sigma;
0092 return std::exp(-0.5 * arg * arg / (sig * sig));
0093 }
0094
0095 template <typename DoubleArray>
0096 double product(DoubleArray factors, std::size_t nFactors)
0097 {
0098 double out = 1.0;
0099 for (std::size_t i = 0; i < nFactors; ++i) {
0100 out *= factors[i];
0101 }
0102 return out;
0103 }
0104
0105
0106 inline double ratio(double numerator, double denominator)
0107 {
0108 return numerator / denominator;
0109 }
0110
0111 inline double bifurGauss(double x, double mean, double sigmaL, double sigmaR)
0112 {
0113
0114
0115 if (x < mean)
0116 return gaussian(x, mean, sigmaL);
0117 return gaussian(x, mean, sigmaR);
0118 }
0119
0120 inline double efficiency(double effFuncVal, int catIndex, int sigCatIndex)
0121 {
0122
0123 effFuncVal = std::clamp(effFuncVal, 0.0, 1.0);
0124
0125 if (catIndex == sigCatIndex)
0126 return effFuncVal;
0127 else
0128 return 1 - effFuncVal;
0129 }
0130
0131
0132 template <bool pdfMode = false, typename DoubleArray>
0133 double polynomial(DoubleArray coeffs, int nCoeffs, int lowestOrder, double x)
0134 {
0135 double retVal = coeffs[nCoeffs - 1];
0136 for (int i = nCoeffs - 2; i >= 0; i--) {
0137 retVal = coeffs[i] + x * retVal;
0138 }
0139 retVal = retVal * std::pow(x, lowestOrder);
0140 return retVal + (pdfMode && lowestOrder > 0 ? 1.0 : 0.0);
0141 }
0142
0143 template <typename DoubleArray>
0144 double chebychev(DoubleArray coeffs, unsigned int nCoeffs, double x_in, double xMin, double xMax)
0145 {
0146
0147 const double xPrime = (x_in - 0.5 * (xMax + xMin)) / (0.5 * (xMax - xMin));
0148
0149
0150 double sum = 1.;
0151 if (nCoeffs > 0) {
0152 double curr = xPrime;
0153 double twox = 2 * xPrime;
0154 double last = 1;
0155 double newval = twox * curr - last;
0156 last = curr;
0157 curr = newval;
0158 for (unsigned int i = 0; nCoeffs != i; ++i) {
0159 sum += last * coeffs[i];
0160 newval = twox * curr - last;
0161 last = curr;
0162 curr = newval;
0163 }
0164 }
0165 return sum;
0166 }
0167
0168 template <typename DoubleArray>
0169 double multipdf(int idx, DoubleArray pdfs)
0170 {
0171
0172
0173
0174
0175
0176 return pdfs[idx];
0177 }
0178
0179 template <typename DoubleArray>
0180 double constraintSum(DoubleArray comp, unsigned int compSize)
0181 {
0182 double sum = 0;
0183 #if defined(__CLING__) && defined(R__HAS_CLAD)
0184 #pragma clad checkpoint loop
0185 #endif
0186 for (unsigned int i = 0; i < compSize; i++) {
0187 sum -= std::log(comp[i]);
0188 }
0189 return sum;
0190 }
0191
0192 inline unsigned int uniformBinNumber(double low, double high, double val, unsigned int numBins, double coef)
0193 {
0194 double binWidth = (high - low) / numBins;
0195 return coef * (val >= high ? numBins - 1 : std::abs((val - low) / binWidth));
0196 }
0197
0198 template <typename DoubleArray>
0199 unsigned int rawBinNumber(double x, DoubleArray boundaries, std::size_t nBoundaries)
0200 {
0201 DoubleArray end = boundaries + nBoundaries;
0202 DoubleArray it = std::lower_bound(boundaries, end, x);
0203
0204 while (boundaries != it && (end == it || end == it + 1 || x < *it)) {
0205 --it;
0206 }
0207 return it - boundaries;
0208 }
0209
0210 template <typename DoubleArray>
0211 unsigned int binNumber(double x, double coef, DoubleArray boundaries, unsigned int nBoundaries, int nbins, int blo)
0212 {
0213 const int rawBin = rawBinNumber(x, boundaries, nBoundaries);
0214 int tmp = std::min(nbins, rawBin - blo);
0215 return coef * std::max(0, tmp);
0216 }
0217
0218 template <typename DoubleArray>
0219 double interpolate1d(double low, double high, double val, unsigned int numBins, DoubleArray vals)
0220 {
0221 double binWidth = (high - low) / numBins;
0222 int idx = val >= high ? numBins - 1 : std::abs((val - low) / binWidth);
0223
0224
0225 double central = low + (idx + 0.5) * binWidth;
0226 if (val > low + 0.5 * binWidth && val < high - 0.5 * binWidth) {
0227 double slope;
0228 if (val < central) {
0229 slope = vals[idx] - vals[idx - 1];
0230 } else {
0231 slope = vals[idx + 1] - vals[idx];
0232 }
0233 return vals[idx] + slope * (val - central) / binWidth;
0234 }
0235
0236 return vals[idx];
0237 }
0238
0239 inline double poisson(double x, double par)
0240 {
0241 if (par < 0)
0242 return TMath::QuietNaN();
0243
0244 if (x < 0) {
0245 return 0;
0246 } else if (x == 0.0) {
0247 return std::exp(-par);
0248 } else {
0249 double out = x * std::log(par) - TMath::LnGamma(x + 1.) - par;
0250 return std::exp(out);
0251 }
0252 }
0253
0254 inline double flexibleInterpSingle(unsigned int code, double low, double high, double boundary, double nominal,
0255 double paramVal, double res)
0256 {
0257 if (code == 0) {
0258
0259 if (paramVal > 0) {
0260 return paramVal * (high - nominal);
0261 } else {
0262 return paramVal * (nominal - low);
0263 }
0264 } else if (code == 1) {
0265
0266 if (paramVal >= 0) {
0267 return res * (std::pow(high / nominal, +paramVal) - 1);
0268 } else {
0269 return res * (std::pow(low / nominal, -paramVal) - 1);
0270 }
0271 } else if (code == 2) {
0272
0273 double a = 0.5 * (high + low) - nominal;
0274 double b = 0.5 * (high - low);
0275 double c = 0;
0276 if (paramVal > 1) {
0277 return (2 * a + b) * (paramVal - 1) + high - nominal;
0278 } else if (paramVal < -1) {
0279 return -1 * (2 * a - b) * (paramVal + 1) + low - nominal;
0280 } else {
0281 return a * paramVal * paramVal + b * paramVal + c;
0282 }
0283
0284
0285
0286
0287 } else if (code == 4 || code == 6) {
0288 double x = paramVal;
0289 double mod = 1.0;
0290 if (code == 6) {
0291 high /= nominal;
0292 low /= nominal;
0293 nominal = 1;
0294 }
0295 if (x >= boundary) {
0296 mod = x * (high - nominal);
0297 } else if (x <= -boundary) {
0298 mod = x * (nominal - low);
0299 } else {
0300
0301 double t = x / boundary;
0302 double eps_plus = high - nominal;
0303 double eps_minus = nominal - low;
0304 double S = 0.5 * (eps_plus + eps_minus);
0305 double A = 0.0625 * (eps_plus - eps_minus);
0306
0307 mod = x * (S + t * A * (15 + t * t * (-10 + t * t * 3)));
0308 }
0309
0310
0311 if (code == 6) {
0312 mod *= res;
0313 }
0314 return mod;
0315
0316 } else if (code == 5) {
0317 double x = paramVal;
0318 double mod = 1.0;
0319 if (x >= boundary) {
0320 mod = std::pow(high / nominal, +paramVal);
0321 } else if (x <= -boundary) {
0322 mod = std::pow(low / nominal, -paramVal);
0323 } else {
0324
0325 double x0 = boundary;
0326
0327 high /= nominal;
0328 low /= nominal;
0329
0330
0331 double logHi = std::log(high);
0332 double logLo = std::log(low);
0333 double powUp = std::exp(x0 * logHi);
0334 double powDown = std::exp(x0 * logLo);
0335 double powUpLog = high <= 0.0 ? 0.0 : powUp * logHi;
0336 double powDownLog = low <= 0.0 ? 0.0 : -powDown * logLo;
0337 double powUpLog2 = high <= 0.0 ? 0.0 : powUpLog * logHi;
0338 double powDownLog2 = low <= 0.0 ? 0.0 : -powDownLog * logLo;
0339
0340 double S0 = 0.5 * (powUp + powDown);
0341 double A0 = 0.5 * (powUp - powDown);
0342 double S1 = 0.5 * (powUpLog + powDownLog);
0343 double A1 = 0.5 * (powUpLog - powDownLog);
0344 double S2 = 0.5 * (powUpLog2 + powDownLog2);
0345 double A2 = 0.5 * (powUpLog2 - powDownLog2);
0346
0347
0348
0349 double x0Sq = x0 * x0;
0350
0351 double a = 1. / (8 * x0) * (15 * A0 - 7 * x0 * S1 + x0 * x0 * A2);
0352 double b = 1. / (8 * x0Sq) * (-24 + 24 * S0 - 9 * x0 * A1 + x0 * x0 * S2);
0353 double c = 1. / (4 * x0Sq * x0) * (-5 * A0 + 5 * x0 * S1 - x0 * x0 * A2);
0354 double d = 1. / (4 * x0Sq * x0Sq) * (12 - 12 * S0 + 7 * x0 * A1 - x0 * x0 * S2);
0355 double e = 1. / (8 * x0Sq * x0Sq * x0) * (+3 * A0 - 3 * x0 * S1 + x0 * x0 * A2);
0356 double f = 1. / (8 * x0Sq * x0Sq * x0Sq) * (-8 + 8 * S0 - 5 * x0 * A1 + x0 * x0 * S2);
0357
0358
0359 double value = 1. + x * (a + x * (b + x * (c + x * (d + x * (e + x * f)))));
0360 mod = value;
0361 }
0362 return res * (mod - 1.0);
0363 }
0364
0365 return 0.0;
0366 }
0367
0368 template <typename ParamsArray, typename DoubleArray>
0369 double flexibleInterp(unsigned int code, ParamsArray params, unsigned int n, DoubleArray low, DoubleArray high,
0370 double boundary, double nominal, int doCutoff)
0371 {
0372 double total = nominal;
0373 #if defined(__CLING__) && defined(R__HAS_CLAD)
0374 #pragma clad checkpoint loop
0375 #endif
0376 for (std::size_t i = 0; i < n; ++i) {
0377 total += flexibleInterpSingle(code, low[i], high[i], boundary, nominal, params[i], total);
0378 }
0379
0380 return doCutoff && total <= 0 ? TMath::Limits<double>::Min() : total;
0381 }
0382
0383 inline double landau(double x, double mu, double sigma)
0384 {
0385 if (sigma <= 0.)
0386 return 0.;
0387 return ROOT::Math::landau_pdf((x - mu) / sigma);
0388 }
0389
0390 inline double logNormal(double x, double k, double m0)
0391 {
0392 return ROOT::Math::lognormal_pdf(x, std::log(m0), std::abs(std::log(k)));
0393 }
0394
0395 inline double logNormalStandard(double x, double sigma, double mu)
0396 {
0397 return ROOT::Math::lognormal_pdf(x, mu, std::abs(sigma));
0398 }
0399
0400 inline double effProd(double eff, double pdf)
0401 {
0402 return eff * pdf;
0403 }
0404
0405
0406
0407
0408
0409 inline double chi2Expected(double mu, double weight)
0410 {
0411 if (mu == 0.0 && weight == 0.0) {
0412 return 0.0;
0413 }
0414 if (mu <= 0.0) {
0415 return std::numeric_limits<double>::quiet_NaN();
0416 }
0417 const double diff = mu - weight;
0418 return diff * diff / mu;
0419 }
0420
0421
0422
0423 inline double chi2Symmetric(double mu, double weight, double sigma2)
0424 {
0425 if (sigma2 == 0.0 && mu == 0.0 && weight == 0.0) {
0426 return 0.0;
0427 }
0428 if (sigma2 <= 0.0) {
0429 return std::numeric_limits<double>::quiet_NaN();
0430 }
0431 const double diff = mu - weight;
0432 return diff * diff / sigma2;
0433 }
0434
0435
0436
0437
0438 inline double chi2Asymmetric(double mu, double weight, double errLo, double errHi)
0439 {
0440 const double diff = mu - weight;
0441 const double err = diff > 0.0 ? errHi : errLo;
0442 const double sigma2 = err * err;
0443 if (sigma2 == 0.0 && mu == 0.0 && weight == 0.0) {
0444 return 0.0;
0445 }
0446 if (sigma2 <= 0.0) {
0447 return std::numeric_limits<double>::quiet_NaN();
0448 }
0449 return diff * diff / sigma2;
0450 }
0451
0452 inline double nll(double pdf, double weight, int binnedL, int doBinOffset)
0453 {
0454 if (binnedL) {
0455
0456
0457
0458 if (std::abs(pdf) < 1e-10 && std::abs(weight) < 1e-10) {
0459 return 0.0;
0460 }
0461 if (doBinOffset) {
0462 return pdf - weight - weight * (std::log(pdf) - std::log(weight));
0463 }
0464 return pdf - weight * std::log(pdf) + TMath::LnGamma(weight + 1);
0465 } else {
0466 return -weight * std::log(pdf);
0467 }
0468 }
0469
0470 template <typename DoubleArray>
0471 double recursiveFraction(DoubleArray a, unsigned int n)
0472 {
0473 double prod = a[0];
0474
0475 for (unsigned int i = 1; i < n; ++i) {
0476 prod *= 1.0 - a[i];
0477 }
0478
0479 return prod;
0480 }
0481
0482 inline double cbShape(double m, double m0, double sigma, double alpha, double n)
0483 {
0484 double t = (m - m0) / sigma;
0485 if (alpha < 0)
0486 t = -t;
0487
0488 double absAlpha = std::abs(alpha);
0489
0490 if (t >= -absAlpha) {
0491 return std::exp(-0.5 * t * t);
0492 } else {
0493 double r = n / absAlpha;
0494 double a = std::exp(-0.5 * absAlpha * absAlpha);
0495 double b = r - absAlpha;
0496
0497 return a * std::pow(r / (b - t), n);
0498 }
0499 }
0500
0501
0502 inline double approxErf(double arg)
0503 {
0504 if (arg > 5.0)
0505 return 1.0;
0506 if (arg < -5.0)
0507 return -1.0;
0508
0509 return std::erf(arg);
0510 }
0511
0512
0513
0514
0515
0516
0517
0518
0519 inline double gaussianIntegral(double xMin, double xMax, double mean, double sigma)
0520 {
0521
0522
0523
0524 double resultScale = 0.5 * std::sqrt(TMath::TwoPi()) * sigma;
0525
0526
0527 double xscale = TMath::Sqrt2() * sigma;
0528 double scaledMin = 0.;
0529 double scaledMax = 0.;
0530 scaledMin = (xMin - mean) / xscale;
0531 scaledMax = (xMax - mean) / xscale;
0532
0533
0534
0535
0536
0537 double ecmin = std::erfc(std::abs(scaledMin));
0538 double ecmax = std::erfc(std::abs(scaledMax));
0539
0540 double cond = 0.0;
0541
0542
0543 double prd = scaledMin * scaledMax;
0544 if (prd < 0.0) {
0545 cond = 2.0 - (ecmin + ecmax);
0546 } else if (scaledMax <= 0.0) {
0547 cond = ecmax - ecmin;
0548 } else {
0549 cond = ecmin - ecmax;
0550 }
0551 return resultScale * cond;
0552 }
0553
0554 inline double bifurGaussIntegral(double xMin, double xMax, double mean, double sigmaL, double sigmaR)
0555 {
0556 const double xscaleL = TMath::Sqrt2() * sigmaL;
0557 const double xscaleR = TMath::Sqrt2() * sigmaR;
0558
0559 const double resultScale = 0.5 * std::sqrt(TMath::TwoPi());
0560
0561 if (xMax < mean) {
0562 return resultScale * (sigmaL * (std::erf((xMax - mean) / xscaleL) - std::erf((xMin - mean) / xscaleL)));
0563 } else if (xMin > mean) {
0564 return resultScale * (sigmaR * (std::erf((xMax - mean) / xscaleR) - std::erf((xMin - mean) / xscaleR)));
0565 } else {
0566 return resultScale * (sigmaR * std::erf((xMax - mean) / xscaleR) - sigmaL * std::erf((xMin - mean) / xscaleL));
0567 }
0568 }
0569
0570 inline double exponentialIntegral(double xMin, double xMax, double constant)
0571 {
0572 if (constant == 0.0) {
0573 return xMax - xMin;
0574 }
0575
0576 return (std::exp(constant * xMax) - std::exp(constant * xMin)) / constant;
0577 }
0578
0579
0580 template <bool pdfMode = false, typename DoubleArray>
0581 double polynomialIntegral(DoubleArray coeffs, int nCoeffs, int lowestOrder, double xMin, double xMax)
0582 {
0583 int denom = lowestOrder + nCoeffs;
0584 double min = coeffs[nCoeffs - 1] / double(denom);
0585 double max = coeffs[nCoeffs - 1] / double(denom);
0586
0587 for (int i = nCoeffs - 2; i >= 0; i--) {
0588 denom--;
0589 min = (coeffs[i] / double(denom)) + xMin * min;
0590 max = (coeffs[i] / double(denom)) + xMax * max;
0591 }
0592
0593 max = max * std::pow(xMax, 1 + lowestOrder);
0594 min = min * std::pow(xMin, 1 + lowestOrder);
0595
0596 return max - min + (pdfMode && lowestOrder > 0.0 ? xMax - xMin : 0.0);
0597 }
0598
0599
0600 inline double fast_fma(double x, double y, double z) noexcept
0601 {
0602 #if defined(FP_FAST_FMA)
0603 return std::fma(x, y, z);
0604 #else
0605
0606 #if defined(__clang__)
0607 #pragma STDC FP_CONTRACT ON
0608 #endif
0609 return (x * y) + z;
0610 #endif
0611 }
0612
0613 template <typename DoubleArray>
0614 double
0615 chebychevIntegral(DoubleArray coeffs, unsigned int nCoeffs, double xMin, double xMax, double xMinFull, double xMaxFull)
0616 {
0617 const double halfrange = .5 * (xMax - xMin);
0618 const double mid = .5 * (xMax + xMin);
0619
0620
0621 const double b = (xMaxFull - mid) / halfrange;
0622 const double a = (xMinFull - mid) / halfrange;
0623
0624
0625
0626
0627 double sum = b - a;
0628
0629 const unsigned int iend = nCoeffs;
0630 if (iend > 0) {
0631 {
0632
0633 const double c = coeffs[0];
0634 sum = fast_fma(0.5 * (b + a) * (b - a), c, sum);
0635 }
0636 if (1 < iend) {
0637 double bcurr = b;
0638 double btwox = 2 * b;
0639 double blast = 1;
0640
0641 double acurr = a;
0642 double atwox = 2 * a;
0643 double alast = 1;
0644
0645 double newval = atwox * acurr - alast;
0646 alast = acurr;
0647 acurr = newval;
0648
0649 newval = btwox * bcurr - blast;
0650 blast = bcurr;
0651 bcurr = newval;
0652 double nminus1 = 1.;
0653 for (unsigned int i = 1; iend != i; ++i) {
0654
0655 const double c = coeffs[i];
0656 const double term2 = (blast - alast) / nminus1;
0657
0658 newval = atwox * acurr - alast;
0659 alast = acurr;
0660 acurr = newval;
0661
0662 newval = btwox * bcurr - blast;
0663 blast = bcurr;
0664 bcurr = newval;
0665
0666 ++nminus1;
0667 const double term1 = (bcurr - acurr) / (nminus1 + 1.);
0668 const double intTn = 0.5 * (term1 - term2);
0669 sum = fast_fma(intTn, c, sum);
0670 }
0671 }
0672 }
0673
0674
0675
0676 return halfrange * sum;
0677 }
0678
0679
0680 inline double
0681 poissonIntegral(int code, double mu, double x, double integrandMin, double integrandMax, unsigned int protectNegative)
0682 {
0683 if (protectNegative && mu < 0.0) {
0684 return std::exp(-2.0 * mu);
0685 }
0686
0687 if (code == 1) {
0688
0689
0690 integrandMin = std::max(0., integrandMin);
0691
0692 if (integrandMax < 0. || integrandMax < integrandMin) {
0693 return 0;
0694 }
0695 const double delta = 100.0 * std::sqrt(mu);
0696
0697
0698
0699 if (integrandMin < std::max(mu - delta, 0.0) && integrandMax > mu + delta) {
0700 return 1.;
0701 }
0702
0703
0704 const unsigned int ixMin = integrandMin;
0705 const unsigned int ixMax = std::min(integrandMax + 1, (double)std::numeric_limits<unsigned int>::max());
0706
0707
0708 if (ixMin == 0) {
0709 return ROOT::Math::inc_gamma_c(ixMax, mu);
0710 } else {
0711
0712 if (ixMin <= mu) {
0713 return ROOT::Math::inc_gamma_c(ixMax, mu) - ROOT::Math::inc_gamma_c(ixMin, mu);
0714 } else {
0715
0716 return ROOT::Math::inc_gamma(ixMin, mu) - ROOT::Math::inc_gamma(ixMax, mu);
0717 }
0718 }
0719 }
0720
0721
0722
0723 const double ix = 1 + x;
0724
0725 return ROOT::Math::inc_gamma(ix, integrandMax) - ROOT::Math::inc_gamma(ix, integrandMin);
0726 }
0727
0728 inline double logNormalIntegral(double xMin, double xMax, double m0, double k)
0729 {
0730 const double root2 = std::sqrt(2.);
0731
0732 double ln_k = std::abs(std::log(k));
0733 double ret = 0.5 * (std::erf(std::log(xMax / m0) / (root2 * ln_k)) - std::erf(std::log(xMin / m0) / (root2 * ln_k)));
0734
0735 return ret;
0736 }
0737
0738 inline double logNormalIntegralStandard(double xMin, double xMax, double mu, double sigma)
0739 {
0740 const double root2 = std::sqrt(2.);
0741
0742 double ln_k = std::abs(sigma);
0743 double ret =
0744 0.5 * (std::erf((std::log(xMax) - mu) / (root2 * ln_k)) - std::erf((std::log(xMin) - mu) / (root2 * ln_k)));
0745
0746 return ret;
0747 }
0748
0749 inline double cbShapeIntegral(double mMin, double mMax, double m0, double sigma, double alpha, double n)
0750 {
0751 const double sqrtPiOver2 = 1.2533141373;
0752 const double sqrt2 = 1.4142135624;
0753
0754 double result = 0.0;
0755 bool useLog = false;
0756
0757 if (std::abs(n - 1.0) < 1.0e-05)
0758 useLog = true;
0759
0760 double sig = std::abs(sigma);
0761
0762 double tmin = (mMin - m0) / sig;
0763 double tmax = (mMax - m0) / sig;
0764
0765 if (alpha < 0) {
0766 double tmp = tmin;
0767 tmin = -tmax;
0768 tmax = -tmp;
0769 }
0770
0771 double absAlpha = std::abs(alpha);
0772
0773 if (tmin >= -absAlpha) {
0774 result += sig * sqrtPiOver2 * (approxErf(tmax / sqrt2) - approxErf(tmin / sqrt2));
0775 } else if (tmax <= -absAlpha) {
0776 double r = n / absAlpha;
0777 double a = r * std::exp(-0.5 * absAlpha * absAlpha);
0778 double b = r - absAlpha;
0779
0780 if (useLog) {
0781 double log_b_tmin = std::log(b - tmin);
0782 double log_b_tmax = std::log(b - tmax);
0783 result += a * std::pow(r, n - 1) * sig *
0784 (log_b_tmin - log_b_tmax + 0.5 * (1.0 - n) * (log_b_tmin * log_b_tmin - log_b_tmax * log_b_tmax));
0785 } else {
0786 result += a * sig / (1.0 - n) * (std::pow(r / (b - tmin), n - 1.0) - std::pow(r / (b - tmax), n - 1.0));
0787 }
0788 } else {
0789 double r = n / absAlpha;
0790 double a = r * std::exp(-0.5 * absAlpha * absAlpha);
0791 double b = r - absAlpha;
0792
0793 double term1 = 0.0;
0794 if (useLog) {
0795 double log_b_tmin = std::log(b - tmin);
0796 double log_r = std::log(r);
0797 term1 = a * std::pow(r, n - 1) * sig *
0798 (log_b_tmin - log_r + 0.5 * (1.0 - n) * (log_b_tmin * log_b_tmin - log_r * log_r));
0799 } else {
0800 term1 = a * sig / (1.0 - n) * (std::pow(r / (b - tmin), n - 1.0) - 1.0);
0801 }
0802
0803 double term2 = sig * sqrtPiOver2 * (approxErf(tmax / sqrt2) - approxErf(-absAlpha / sqrt2));
0804
0805 result += term1 + term2;
0806 }
0807
0808 if (result == 0)
0809 return 1.E-300;
0810 return result;
0811 }
0812
0813 template <typename DoubleArray>
0814 double bernsteinIntegral(double xlo, double xhi, double xmin, double xmax, DoubleArray coefs, int nCoefs)
0815 {
0816 double xloScaled = (xlo - xmin) / (xmax - xmin);
0817 double xhiScaled = (xhi - xmin) / (xmax - xmin);
0818
0819 int degree = nCoefs - 1;
0820 double norm = 0.;
0821
0822 for (int i = 0; i <= degree; ++i) {
0823
0824
0825
0826 double temp = 0.;
0827 for (int j = i; j <= degree; ++j) {
0828 double binCoefs = binomial(degree, j) * binomial(j, i);
0829 double oneOverJPlusOne = 1. / (j + 1.);
0830 double powDiff = std::pow(xhiScaled, j + 1.) - std::pow(xloScaled, j + 1.);
0831 temp += std::pow(-1., j - i) * binCoefs * powDiff * oneOverJPlusOne;
0832 }
0833 temp *= coefs[i];
0834 norm += temp;
0835 }
0836
0837 return norm * (xmax - xmin);
0838 }
0839
0840 template <typename XArray, typename MuArray, typename CovArray>
0841 double multiVarGaussian(int n, XArray x, MuArray mu, CovArray covI)
0842 {
0843 double result = 0.0;
0844
0845
0846 for (int i = 0; i < n; ++i) {
0847 for (int j = 0; j < n; ++j) {
0848 result += (x[i] - mu[i]) * covI[i * n + j] * (x[j] - mu[j]);
0849 }
0850 }
0851 return std::exp(-0.5 * result);
0852 }
0853
0854
0855
0856
0857 template <typename DoubleArray>
0858 double stepFunctionIntegral(double xmin, double xmax, std::size_t nBins, DoubleArray boundaries, DoubleArray coefs)
0859 {
0860 double out = 0.0;
0861 for (std::size_t i = 0; i < nBins; ++i) {
0862 double a = boundaries[i];
0863 double b = boundaries[i + 1];
0864 out += coefs[i] * std::max(0.0, std::min(b, xmax) - std::max(a, xmin));
0865 }
0866 return out;
0867 }
0868
0869 }
0870
0871 namespace clad::custom_derivatives {
0872 namespace RooFit::Detail::MathFuncs {
0873
0874
0875
0876
0877
0878
0879 template <class... Types>
0880 void binNumber_pullback(Types...)
0881 {
0882 }
0883
0884 }
0885 }
0886
0887 #endif