The RooBinIntegrator gives wrong results in case the integral boundaries lie within the bin boundaries of the integrand.
Reproducer:
RooRealVar x{"x", "x", 0., 10.};
x.setBins(5); // five bins of width 2: edges at 0, 2, 4, 6, 8, 10
const double heights[5] = {1.0, 3.0, 2.0, 4.0, 1.5};
RooDataHist dh{"dh", "", x};
for (int i = 0; i < 5; ++i) {
dh.set(i, heights[i], 0.0);
}
// The sub-range [3, 7] deliberately cuts through the bins [2,4] and [6,8].
x.setRange("partial", 3.0, 7.0);
const double binWidth = 2.0;
const double expected = (heights[1] * 1.0 + heights[2] * 2.0 + heights[3] * 1.0) / binWidth; // = 5.5
RooHistPdf analytic{"analytic", "", x, dh, 0};
RooHistPdf numeric{"numeric", "", x, dh, 0};
numeric.forceNumInt(true);
std::unique_ptr<RooAbsReal> integAna{analytic.createIntegral(x, RooFit::Range("partial"))};
std::unique_ptr<RooAbsReal> integNum{numeric.createIntegral(x, RooFit::Range("partial"))};
const double valAna = integAna->getVal();
const double valNum = integNum->getVal();
std::cout << "\n=== integral over the sub-range [3, 7] ===" << std::endl;
std::cout << " expected (by hand) : " << expected << std::endl;
std::cout << " analytic (RooHistPdf) : " << valAna << std::endl;
std::cout << " numeric (RooBinIntegr.): " << valNum << std::endl;
Gives:
=== integral over the sub-range [3, 7] ===
expected (by hand) : 5.5
analytic (RooHistPdf) : 5.5
numeric (RooBinIntegr.): 2
It is expected that the RooBinIntegrator gives the correct result.
The RooBinIntegrator gives wrong results in case the integral boundaries lie within the bin boundaries of the integrand.
Reproducer:
RooRealVar x{"x", "x", 0., 10.}; x.setBins(5); // five bins of width 2: edges at 0, 2, 4, 6, 8, 10 const double heights[5] = {1.0, 3.0, 2.0, 4.0, 1.5}; RooDataHist dh{"dh", "", x}; for (int i = 0; i < 5; ++i) { dh.set(i, heights[i], 0.0); } // The sub-range [3, 7] deliberately cuts through the bins [2,4] and [6,8]. x.setRange("partial", 3.0, 7.0); const double binWidth = 2.0; const double expected = (heights[1] * 1.0 + heights[2] * 2.0 + heights[3] * 1.0) / binWidth; // = 5.5 RooHistPdf analytic{"analytic", "", x, dh, 0}; RooHistPdf numeric{"numeric", "", x, dh, 0}; numeric.forceNumInt(true); std::unique_ptr<RooAbsReal> integAna{analytic.createIntegral(x, RooFit::Range("partial"))}; std::unique_ptr<RooAbsReal> integNum{numeric.createIntegral(x, RooFit::Range("partial"))}; const double valAna = integAna->getVal(); const double valNum = integNum->getVal(); std::cout << "\n=== integral over the sub-range [3, 7] ===" << std::endl; std::cout << " expected (by hand) : " << expected << std::endl; std::cout << " analytic (RooHistPdf) : " << valAna << std::endl; std::cout << " numeric (RooBinIntegr.): " << valNum << std::endl;Gives:
It is expected that the RooBinIntegrator gives the correct result.