[RF] Values of RooProdPdfs in a RooAbsPdf should not depend on its factors `normRange()`
Describe the bug
If a RooProdPdf is in a RooAddPdf, its values change if one changes the normalization range of its factors with RooAbsPdf::normRange().
Expected behavior
The values of a RooProdPdf should not depend on the normRange() of its factors, because the RooProdPdf is responsible for normalizing itself.
To Reproduce
Enable the new unit test in testRooProdPdf shipped with https://github.com/root-project/root/pull/11485.
Setup
ROOT master on Arch Linux.
Additional context
It is important now to fix the issues related to the RooAbsPdf::normRange() feature, because as of https://github.com/root-project/root/pull/11455 it is used in multi-range fits. Thus, we need to make sure it's less fragile.
After this issue is fixed, it should be verified with stressRooFit that things would still work if one were to set the normalization ranges of all PDFs in the computation graph in RooAbsOptTestStatistic::initSlave() not just the top-level PDF:
For debugging, it could be helpful to replace the RooProdPdfs with RooFixedProdPdf objects, which explicitly represent the conputation graph of a RooProdPdf for a given normalization set without internal caching:
if(auto prodPdf = dynamic_cast<RooProdPdf *>(pdf)) {
auto normalizedPdf = std::make_unique<RooFixedProdPdf>(*prodPdf, currNormSet);
replaceArg(*normalizedPdf, *pdf);
newNodes.emplace_back(std::move(normalizedPdf));
continue;
}
Related forum post: https://root-forum.cern.ch/t/slow-performance-in-limited-range-fit-with-roofit/52834/2
Another related forum post: https://root-forum.cern.ch/t/plot-normalization-after-rooaddpdf-fixaddcoefrange/54480
The underlying problem is that multi-range integrals in the RooProdPdf don't work:
// Define observables x,y
RooRealVar x("x","x",-10,10) ;
RooRealVar y("y","y",-10,10) ;
// Construct the background pdf (flat in x,y)
RooUniform px("px","px",x) ;
RooUniform py("py","py",y) ;
RooProdPdf bkg("bkg","bkg",px,py) ;
// Construct the SideBand1,SideBand2,Signal regions
//
// |
// +-------------+-----------+
// | | |
// | Side | Sig |
// | Band1 | nal |
// | | |
// --+-------------+-----------+--
// | |
// | Side |
// | Band2 |
// | |
// +-------------+-----------+
// |
x.setRange("SB1",-10,+10) ;
y.setRange("SB1",-10,0) ;
x.setRange("SB2",-10,0) ;
y.setRange("SB2",0,+10) ;
x.setRange("SIG",0,+10) ;
y.setRange("SIG",0,+10) ;
x.setRange("FULL",-10,+10) ;
y.setRange("FULL",-10,+10) ;
RooArgSet deps{x, y};
bkg.setNormRange("SB1,SB2");
RooArgSet normSet{x, y};
// Doesn't work
std::cout << bkg.getVal(normSet) << std::endl;