52constexpr const char *RooNLLVarNew::weightVarName;
53constexpr const char *RooNLLVarNew::weightVarNameSumW2;
54constexpr const char *RooNLLVarNew::binVolumeVarName;
55constexpr const char *RooNLLVarNew::weightErrorLoVarName;
56constexpr const char *RooNLLVarNew::weightErrorHiVarName;
61std::unique_ptr<RooConstVar> dummyVar(
const char *
name)
63 return std::make_unique<RooConstVar>(
name,
name, 1.0);
67class RooOffsetPdf :
public RooAbsPdf {
69 RooOffsetPdf(
const char *
name,
const char *title, RooArgSet
const &observables, RooAbsReal &weightVar)
70 : RooAbsPdf(
name, title),
71 _observables(
"!observables",
"List of observables", this),
72 _weightVar{
"!weightVar",
"weightVar", this, weightVar, true, false}
74 for (RooAbsArg *obs : observables) {
75 _observables.add(*obs);
78 RooOffsetPdf(
const RooOffsetPdf &other,
const char *
name =
nullptr)
79 : RooAbsPdf(other,
name),
80 _observables(
"!servers", this, other._observables),
81 _weightVar{
"!weightVar", this, other._weightVar}
84 TObject *clone(
const char *newname)
const override {
return new RooOffsetPdf(*
this, newname); }
86 void doEval(RooFit::EvalContext &ctx)
const override
88 std::span<double> output = ctx.
output();
89 std::size_t nEvents = output.size();
91 std::span<const double> weights = ctx.
at(_weightVar);
97 RooDataHist dataHist{
"data",
"data", _observables};
99 for (std::size_t i = 0; i < nEvents; ++i) {
101 var->setVal(ctx.
at(var)[i]);
103 dataHist.
add(_observables, weights[weights.size() == 1 ? 0 : i]);
107 RooHistPdf pdf{
"offsetPdf",
"offsetPdf", _observables, dataHist};
108 for (std::size_t i = 0; i < nEvents; ++i) {
110 var->setVal(ctx.
at(var)[i]);
112 output[i] = pdf.
getVal(_observables);
117 double evaluate()
const override {
return 0.0; }
120 RooTemplateProxy<RooAbsReal> _weightVar;
128RooNLLVarNew::RooNLLVarNew(
const char *
name,
const char *title, RooAbsReal &func, RooArgSet
const &observables,
130 : RooAbsReal(
name, title),
131 _func{
"func",
"func", this, func},
132 _weightVar{
"weightVar",
"weightVar", this, dummyVar(weightVarName)},
133 _weightSquaredVar{weightVarNameSumW2, weightVarNameSumW2, this, dummyVar(
"weightSquardVar")},
134 _statistic{cfg.statistic},
135 _chi2ErrorType{cfg.chi2ErrorType}
137 auto *pdf =
dynamic_cast<RooAbsPdf *
>(&func);
139 if (_statistic == Statistic::Chi2) {
143 setAttribute(
"Chi2EvaluationActive");
144 _funcMode = !pdf ? FuncMode::Function : (cfg.extended ? FuncMode::ExtendedPdf : FuncMode::Pdf);
146 _binnedL = pdf && pdf->
getAttribute(
"BinnedLikelihoodActive");
157 const bool wantsExpectedEvents = pdf && ((_statistic == Statistic::NLL && cfg.extended && !_binnedL) ||
158 (_statistic == Statistic::Chi2 && _funcMode == FuncMode::ExtendedPdf));
159 if (wantsExpectedEvents) {
161 if (expectedEvents) {
163 std::make_unique<RooTemplateProxy<RooAbsReal>>(
"expectedEvents",
"expectedEvents",
this, *expectedEvents);
164 addOwnedComponents(std::move(expectedEvents));
168 if (_statistic == Statistic::NLL) {
173 if (_binnedL && !pdf->
getAttribute(
"BinnedLikelihoodActiveYields")) {
174 fillBinWidthsFromPdfBoundaries(*pdf, obs);
182 if (!_binnedL && _doBinOffset) {
183 auto offsetPdf = std::make_unique<RooOffsetPdf>(
"_offset_func",
"_offset_func", obs, *_weightVar);
184 _offsetPdf = std::make_unique<RooTemplateProxy<RooAbsPdf>>(
"offsetPdf",
"offsetPdf",
this, *offsetPdf);
185 addOwnedComponents(std::move(offsetPdf));
190 auto binVolumeDummy = std::make_unique<RooConstVar>(binVolumeVarName, binVolumeVarName, 1.0);
191 _binVolumes = std::make_unique<RooTemplateProxy<RooAbsReal>>(binVolumeVarName, binVolumeVarName,
this,
192 *binVolumeDummy,
true,
false);
193 addOwnedComponents(std::move(binVolumeDummy));
196 auto errLoDummy = std::make_unique<RooConstVar>(weightErrorLoVarName, weightErrorLoVarName, 1.0);
197 auto errHiDummy = std::make_unique<RooConstVar>(weightErrorHiVarName, weightErrorHiVarName, 1.0);
198 _weightErrLo = std::make_unique<RooTemplateProxy<RooAbsReal>>(weightErrorLoVarName, weightErrorLoVarName,
this,
199 *errLoDummy,
true,
false);
200 _weightErrHi = std::make_unique<RooTemplateProxy<RooAbsReal>>(weightErrorHiVarName, weightErrorHiVarName,
this,
201 *errHiDummy,
true,
false);
202 addOwnedComponents(std::move(errLoDummy));
203 addOwnedComponents(std::move(errHiDummy));
207 resetWeightVarNames();
210RooNLLVarNew::RooNLLVarNew(
const RooNLLVarNew &other,
const char *
name)
212 _func{
"func", this, other._func},
213 _weightVar{
"weightVar", this, other._weightVar},
214 _weightSquaredVar{
"weightSquaredVar", this, other._weightSquaredVar},
215 _weightSquared{other._weightSquared},
216 _binnedL{other._binnedL},
217 _doOffset{other._doOffset},
218 _doBinOffset{other._doBinOffset},
219 _statistic{other._statistic},
220 _funcMode{other._funcMode},
221 _chi2ErrorType{other._chi2ErrorType},
222 _simCount{other._simCount},
223 _prefix{other._prefix},
226 if (other._expectedEvents) {
227 _expectedEvents = std::make_unique<RooTemplateProxy<RooAbsReal>>(
"expectedEvents", this, *other._expectedEvents);
229 if (other._binVolumes) {
230 _binVolumes = std::make_unique<RooTemplateProxy<RooAbsReal>>(binVolumeVarName, this, *other._binVolumes);
232 if (other._weightErrLo) {
233 _weightErrLo = std::make_unique<RooTemplateProxy<RooAbsReal>>(weightErrorLoVarName, this, *other._weightErrLo);
235 if (other._weightErrHi) {
236 _weightErrHi = std::make_unique<RooTemplateProxy<RooAbsReal>>(weightErrorHiVarName, this, *other._weightErrHi);
240void RooNLLVarNew::fillBinWidthsFromPdfBoundaries(
RooAbsReal const &pdf,
RooArgSet const &observables)
243 if (!_binw.empty()) {
247 if (observables.
size() != 1) {
248 throw std::runtime_error(
"BinnedPdf optimization only works with a 1D pdf.");
251 std::list<double> *boundaries = pdf.
binBoundaries(*var, var->getMin(), var->getMax());
252 std::list<double>::iterator biter = boundaries->begin();
253 _binw.resize(boundaries->size() - 1);
254 double lastBound = (*biter);
257 while (biter != boundaries->end()) {
258 _binw[ibin] = (*biter) - lastBound;
259 lastBound = (*biter);
267 std::span<const double> weights)
const
272 const bool predsAreYields = _binw.empty();
274 for (std::size_t i = 0; i < preds.size(); ++i) {
277 double N = weights[i];
278 double mu = preds[i];
279 if (!predsAreYields) {
283 if (mu <= 0 && N > 0) {
285 logEvalError(
Form(
"Observed %f events in bin %lu with zero event yield",
N, (
unsigned long)i));
288 sumWeightKahanSum +=
N;
292 finalizeResult(ctx, result, sumWeightKahanSum.
Sum());
295void RooNLLVarNew::doEvalChi2(
RooFit::EvalContext &ctx, std::span<const double> preds, std::span<const double> weights,
296 std::span<const double> weightsSumW2)
const
305 auto config = ctx.
config(
this);
306 std::span<const double> binVol = ctx.
at(*_binVolumes);
307 std::span<const double> errLo = _weightErrLo ? ctx.
at(*_weightErrLo) : std::span<const double>{};
308 std::span<const double> errHi = _weightErrHi ? ctx.
at(*_weightErrHi) : std::span<const double>{};
312 double normFactor = 1.0;
314 case FuncMode::Pdf: normFactor = sumWeight;
break;
315 case FuncMode::ExtendedPdf: normFactor = ctx.
at(*_expectedEvents)[0];
break;
316 case FuncMode::Function: normFactor = 1.0;
break;
322 for (std::size_t i = 0; i < preds.size(); ++i) {
323 const double N = weights[i];
324 const double mu = preds[i] * normFactor * binVol[i];
325 const double diff = mu -
N;
328 switch (_chi2ErrorType) {
332 const double err = diff > 0 ? errHi[i] : errLo[i];
336 default: sigma2 = mu;
break;
340 if (sigma2 == 0.0 &&
N == 0.0 && mu == 0.0) {
344 logEvalError(
Form(
"chi2 bin %lu has non-positive error; term replaced with NaN", (
unsigned long)i));
345 result += std::numeric_limits<double>::quiet_NaN();
349 result += diff * diff / sigma2;
350 sumWeightKahanSum +=
N;
353 finalizeResult(ctx, result, sumWeightKahanSum.
Sum());
358 std::span<const double> weights = ctx.
at(_weightVar);
359 std::span<const double> weightsSumW2 = ctx.
at(_weightSquaredVar);
361 if (_statistic == Statistic::Chi2) {
362 return doEvalChi2(ctx, ctx.
at(&*_func), weights, weightsSumW2);
366 return doEvalBinnedL(ctx, ctx.
at(&*_func), _weightSquared ? weightsSumW2 : weights);
369 auto config = ctx.
config(
this);
374 double sumWeight2 = 0.;
375 if (_expectedEvents && _weightSquared) {
380 _doBinOffset ? ctx.
at(*_offsetPdf) : std::span<const double>{});
382 if (nllOut.nInfiniteValues > 0) {
383 oocoutW(&*_func, Eval) <<
"RooAbsPdf::getLogVal(" << _func->GetName()
384 <<
") WARNING: top-level pdf has some infinite values" << std::endl;
386 for (std::size_t i = 0; i < nllOut.nNonPositiveValues; ++i) {
387 _func->logEvalError(
"getLogVal() top-level p.d.f not greater than zero");
389 for (std::size_t i = 0; i < nllOut.nNaNValues; ++i) {
390 _func->logEvalError(
"getLogVal() top-level p.d.f evaluates to NaN");
393 if (_expectedEvents) {
396 std::span<const double> expected = ctx.
at(*_expectedEvents);
397 nllOut.nllSum += pdf.extendedTerm(sumWeight, expected[0], _weightSquared ? sumWeight2 : 0.0, _doBinOffset);
400 finalizeResult(ctx, {nllOut.nllSum, nllOut.nllSumCarry}, sumWeight);
407void RooNLLVarNew::setPrefix(std::string
const &prefix)
411 resetWeightVarNames();
414void RooNLLVarNew::resetWeightVarNames()
416 _weightVar->SetName((_prefix + weightVarName).c_str());
417 _weightSquaredVar->SetName((_prefix + weightVarNameSumW2).c_str());
419 (*_offsetPdf)->SetName((_prefix +
"_offset_func").c_str());
422 (*_binVolumes)->SetName((_prefix + binVolumeVarName).c_str());
425 (*_weightErrLo)->SetName((_prefix + weightErrorLoVarName).c_str());
428 (*_weightErrHi)->SetName((_prefix + weightErrorHiVarName).c_str());
434void RooNLLVarNew::applyWeightSquared(
bool flag)
436 if (_statistic == Statistic::Chi2) {
438 coutW(Fitting) <<
"RooNLLVarNew::applyWeightSquared(" << GetName()
439 <<
") has no effect on a chi-squared evaluator; ignoring." << std::endl;
443 _weightSquared = flag;
446void RooNLLVarNew::enableOffsetting(
bool flag)
458 if (_statistic == Statistic::NLL && !_doBinOffset && _simCount > 1) {
459 result += weightSum * std::log(
static_cast<double>(_simCount));
466 if (_offset.Sum() == 0 && _offset.Carry() == 0 && (result.
Sum() != 0 || result.
Carry() != 0)) {
RooCollectionProxy< RooArgSet > RooSetProxy
ROOT::RRangeCast< T, false, Range_t > static_range_cast(Range_t &&coll)
double evaluate() const override
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
The Kahan summation is a compensated summation algorithm, which significantly reduces numerical error...
RooFit::OwningPtr< RooArgSet > getObservables(const RooArgSet &set, bool valueOnly=true) const
Given a set of possible observables, return the observables that this PDF depends on.
bool getAttribute(const Text_t *name) const
Check if a named attribute is set. By default, all attributes are unset.
Storage_t::size_type size() const
RooAbsArg * first() const
Abstract interface for all probability density functions.
virtual std::unique_ptr< RooAbsReal > createExpectedEventsFunc(const RooArgSet *nset) const
Returns an object that represents the expected number of events for a given normalization set,...
Abstract base class for objects that represent a real value and implements functionality common to al...
virtual std::list< double > * binBoundaries(RooAbsRealLValue &obs, double xlo, double xhi) const
Retrieve bin boundaries if this distribution is binned in obs.
double getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
RooArgSet is a container object that can hold multiple RooAbsArg objects.
void add(const RooArgSet &row, double wgt=1.0) override
Add wgt to the bin content enclosed by the coordinates passed in row.
std::span< const double > at(RooAbsArg const *arg, RooAbsArg const *caller=nullptr)
std::span< double > output()
RooBatchCompute::Config config(RooAbsArg const *arg) const
void setOutputWithOffset(RooAbsArg const *arg, ROOT::Math::KahanSum< double > val, ROOT::Math::KahanSum< double > const &offset)
Sets the output value with an offset.
Variable that can be changed from the outside.
double reduceSum(Config cfg, InputArr input, size_t n)
ReduceNLLOutput reduceNLL(Config cfg, std::span< const double > probas, std::span< const double > weights, std::span< const double > offsetProbas)
double nll(double pdf, double weight, int binnedL, int doBinOffset)
void probas(TString dataset, TString fin="TMVA.root", Bool_t useTMVAStyle=kTRUE)