From 4e61a6400fe7eae171fec954f22f1c5127580074 Mon Sep 17 00:00:00 2001 From: Jonas Rembser Date: Sun, 25 Sep 2022 12:23:44 +0200 Subject: [PATCH] [RF] New unit test for `SplitRange()` feature in `RooAbsPdf::fitTo()` The recent commit 922dec5157 fixed GitHub issue #11396, but there was still no unit test implemented with the reproducer code from that issue. This commit is implementing such a unit test. --- .../roofitcore/test/testRooSimultaneous.cxx | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/roofit/roofitcore/test/testRooSimultaneous.cxx b/roofit/roofitcore/test/testRooSimultaneous.cxx index bfe6a37b67500..10816c982afdd 100644 --- a/roofit/roofitcore/test/testRooSimultaneous.cxx +++ b/roofit/roofitcore/test/testRooSimultaneous.cxx @@ -129,3 +129,71 @@ TEST(RooSimultaneous, CategoriesWithNoPdf) m1.setError(0.0); sim.fitTo(*ds, BatchMode(true), PrintLevel(-1)); } + +/// GitHub issue #11396. +/// Test whether the RooFit::SplitRange() command argument for simultaneous +/// fits is correctly considered in multi-range fits. +TEST(RooSimultaneous, MultiRangeFitWithSplitRange) +{ + using namespace RooFit; + + int nEventsInCat = 11000; + + RooWorkspace wsCat1{"wsCat1"}; + wsCat1.factory("Gaussian::pdf_cat1(x_cat1[0,10],muCat1[4,0,10],sigma_cat1[1.0,0.1,10.0])"); + RooAbsPdf &pdfCat1 = *wsCat1.pdf("pdf_cat1"); + RooRealVar &xCat1 = *wsCat1.var("x_cat1"); + xCat1.setRange("SideBandLo_cat1", 0, 2); + xCat1.setRange("SideBandHi_cat1", 6, 10); + std::unique_ptr dsCat1{pdfCat1.generate(xCat1, nEventsInCat)}; + + RooWorkspace wsCat2{"wsCat2"}; + wsCat2.factory("Gaussian::pdf_cat2(x_cat2[0,10],muCat2[6,0,10],sigma_cat2[1.0,0.1,10.0])"); + RooAbsPdf &pdfCat2 = *wsCat2.pdf("pdf_cat2"); + RooRealVar &xCat2 = *wsCat2.var("x_cat2"); + xCat2.setRange("SideBandLo_cat2", 0, 4); + xCat2.setRange("SideBandHi_cat2", 8, 10); + std::unique_ptr dsCat2{pdfCat1.generate(xCat2, nEventsInCat)}; + + RooCategory indexCat{"cat", "cat"}; + indexCat.defineType("cat1"); + indexCat.defineType("cat2"); + + RooSimultaneous simPdf{"simPdf", "", indexCat}; + simPdf.addPdf(pdfCat1, "cat1"); + simPdf.addPdf(pdfCat2, "cat2"); + + std::map dsmap{{"cat1", dsCat1.get()}, {"cat2", dsCat2.get()}}; + + RooDataSet combData{"combData", "", {xCat1, xCat2}, Index(indexCat), Import(dsmap)}; + + std::unique_ptr nll1{pdfCat1.createNLL(*dsCat1, Range("SideBandLo_cat1,SideBandHi_cat1"))}; + std::unique_ptr nll2{pdfCat2.createNLL(*dsCat2, Range("SideBandLo_cat2,SideBandHi_cat2"))}; + std::unique_ptr nllSim{simPdf.createNLL(combData, Range("SideBandLo,SideBandHi"), SplitRange())}; + + const double nll1Val = nll1->getVal(); + const double nll2Val = nll2->getVal(); + // In simultaneous PDFs, the probability is normalized over the categories, + // so we have to do that as well when computing the reference value. Since + // we do a ranged fit, we have to consider the ranges when calculating the + // number of events in data. + double nCat1InRange = std::unique_ptr + { + dsCat1->reduce(CutRange("SideBandLo_cat1,SideBandHi_cat1")) + } -> sumEntries(); + double nCat2InRange = std::unique_ptr + { + dsCat2->reduce(CutRange("SideBandLo_cat2,SideBandHi_cat2")) + } -> sumEntries(); + const double nllSimRefVal = (nCat1InRange + nCat2InRange) * std::log(2) + nll1Val + nll2Val; + + const double nllSimVal = nllSim->getVal(); + + // For now, we can't cross check the likelihood value and the unit test + // only checks whether the simultaneous likelihood can be crated with the + // SplitRange() argument without the range overlap checks failing. Once the + // issue with multi-range simultaneous likelihood values is fixed, this can + // be commented out: + // + // EXPECT_FLOAT_EQ(nllSimVal, nllSimRefVal); +}