Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion roofit/roofitcore/src/FitHelpers.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,7 @@ std::unique_ptr<RooAbsReal> createChi2(RooAbsReal &real, RooAbsData &data, const

pc.defineInt("numcpu", "NumCPU", 0, 1);
pc.defineInt("verbose", "Verbose", 0, 0);
pc.defineString("rangeName", "RangeWithName", 0, "", true);

RooAbsTestStatistic::Configuration cfg;

Expand Down Expand Up @@ -980,7 +981,6 @@ std::unique_ptr<RooAbsReal> createChi2(RooAbsReal &real, RooAbsData &data, const
} else {
pc.defineInt("integrate", "Integrate", 0, 0);
pc.defineObject("yvar", "YVar", 0, nullptr);
pc.defineString("rangeName", "RangeWithName", 0, "", true);
pc.defineInt("interleave", "NumCPU", 1, 0);

// Process and check varargs
Expand Down
31 changes: 30 additions & 1 deletion roofit/roofitcore/src/RooChi2Var.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
#include "RooRealVar.h"
#include "RooAbsDataStore.h"

#include <ROOT/StringUtils.hxx>

RooChi2Var::RooChi2Var(const char *name, const char *title, RooAbsReal &func, RooDataHist &data, bool extended,
RooDataHist::ErrorType etype, RooAbsTestStatistic::Configuration const &cfg)
: RooAbsOptTestStatistic(name, title, func, data, RooArgSet{}, cfg),
Expand Down Expand Up @@ -99,6 +101,12 @@ double RooChi2Var::evaluatePartition(std::size_t firstEvent, std::size_t lastEve
double result(0);
double carry(0);

// Also consider the composite case of multiple ranges
std::vector<std::string> rangeTokens;
if (!_rangeName.empty()) {
rangeTokens = ROOT::Split(_rangeName, ",");
}

// Determine normalization factor depending on type of input function
double normFactor(1) ;
switch (_funcMode) {
Expand All @@ -112,7 +120,28 @@ double RooChi2Var::evaluatePartition(std::size_t firstEvent, std::size_t lastEve
for (auto i=firstEvent ; i<lastEvent ; i+=stepSize) {

// get the data values for this event
hdata->get(i);
RooArgSet const *row = hdata->get(i);

// Skip bins that are outside of the selected range
bool doSelect(true) ;
if (!_rangeName.empty()) {
doSelect = false;
// A row is selected if it is inside at least one complete named range.
for (const auto &rangeName : rangeTokens) {
bool inThisRange = true;
for (const auto arg : *row) {
if (!arg->inRange(rangeName.c_str())) {
inThisRange = false;
break;
}
}
if (inThisRange) {
doSelect = true;
break;
}
}
}
if (!doSelect) continue ;

const double nData = hdata->weight() ;

Expand Down
88 changes: 88 additions & 0 deletions roofit/roofitcore/test/testTestStatistics.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#include <RooRealVar.h>
#include <RooSimultaneous.h>
#include <RooWorkspace.h>
#include <RooFormulaVar.h>
#include <RooGenericPdf.h>

#include <TH1D.h>
#include <TMath.h>
Expand Down Expand Up @@ -891,3 +893,89 @@ TEST(CreateNLL, ResetDataCodegen)

EXPECT_FLOAT_EQ(nll2Val, 2 * nll1Val);
}

TEST(RooChi2Var, BinnedRangeAdditivityAndNormalization)
{
RooHelpers::LocalChangeMsgLevel changeMsgLvl(RooFit::WARNING);

using namespace RooFit;

RooRealVar x("x", "x", 0., 1.);
x.setRange("lo", 0., 0.5);
x.setRange("hi", 0.5, 1.);
x.setRange("full", 0., 1.);

RooRealVar nbkg("nbkg", "", 11.);

// Flat function that evaluates to the expected number of events per unit x.
RooFormulaVar flat("flat", "nbkg + x - x", {nbkg, x});

RooGenericPdf uniform("uniform", "1 + x - x", x);
RooExtendPdf pdf("pdf", "", uniform, nbkg);

constexpr int nBins = 10;
TH1D h("h", "h", nBins, 0., 1.);

for (int i = 1; i <= nBins; ++i) {
h.SetBinContent(i, 1.);
}

// Histogram integral is 10, while nbkg = 11, so extended/function chi2 should be > 0.
RooDataHist dh("dh", "dh", x, &h);

auto makeChi2 = [&](RooAbsReal &func, const char *rangeName = nullptr, bool extended = false) {
std::unique_ptr<RooAbsReal> chi2{
rangeName ? func.createChi2(dh, DataError(RooAbsData::Poisson), Extended(extended), Range(rangeName))
: func.createChi2(dh, DataError(RooAbsData::Poisson), Extended(extended))};
return chi2->getVal();
};

// Extended PDF
const double chi2ExtDefault = makeChi2(pdf, nullptr, true);
const double chi2ExtFull = makeChi2(pdf, "full", true);
const double chi2ExtLo = makeChi2(pdf, "lo", true);
const double chi2ExtHi = makeChi2(pdf, "hi", true);
const double chi2ExtLoHi = makeChi2(pdf, "lo,hi", true);

// Non-extended PDF
const double chi2NonExtDefault = makeChi2(uniform);
const double chi2NonExtFull = makeChi2(uniform, "full");
const double chi2NonExtLo = makeChi2(uniform, "lo");
const double chi2NonExtHi = makeChi2(uniform, "hi");
const double chi2NonExtLoHi = makeChi2(uniform, "lo,hi");

// Function
const double chi2FuncDefault = makeChi2(flat);
const double chi2FuncFull = makeChi2(flat, "full");
const double chi2FuncLo = makeChi2(flat, "lo");
const double chi2FuncHi = makeChi2(flat, "hi");
const double chi2FuncLoHi = makeChi2(flat, "lo,hi");

constexpr double tol = 1e-10;

// Extended PDF: range decomposition should be additive and positive.
EXPECT_GT(chi2ExtDefault, 0.);
EXPECT_NEAR(chi2ExtDefault, chi2ExtFull, tol);
EXPECT_NEAR(chi2ExtDefault, chi2ExtLoHi, tol);
EXPECT_NEAR(chi2ExtFull, chi2ExtLo + chi2ExtHi, tol);

// Non-extended uniform PDF against uniform data should be exactly compatible.
EXPECT_NEAR(chi2NonExtDefault, 0., tol);
EXPECT_NEAR(chi2NonExtFull, 0., tol);
EXPECT_NEAR(chi2NonExtLo, 0., tol);
EXPECT_NEAR(chi2NonExtHi, 0., tol);
EXPECT_NEAR(chi2NonExtLoHi, 0., tol);

// Flat function normalized to 11 should behave like the extended PDF.
EXPECT_GT(chi2FuncDefault, 0.);
EXPECT_NEAR(chi2FuncDefault, chi2FuncFull, tol);
EXPECT_NEAR(chi2FuncDefault, chi2FuncLoHi, tol);
EXPECT_NEAR(chi2FuncFull, chi2FuncLo + chi2FuncHi, tol);

// Function and extended PDF should give the same chi2 values.
EXPECT_NEAR(chi2ExtDefault, chi2FuncDefault, tol);
EXPECT_NEAR(chi2ExtFull, chi2FuncFull, tol);
EXPECT_NEAR(chi2ExtLo, chi2FuncLo, tol);
EXPECT_NEAR(chi2ExtHi, chi2FuncHi, tol);
EXPECT_NEAR(chi2ExtLoHi, chi2FuncLoHi, tol);
}
Loading