Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RooNLLVarNew.cxx
Go to the documentation of this file.
1/*
2 * Project: RooFit
3 * Authors:
4 * Jonas Rembser, CERN 2021
5 * Emmanouil Michalainas, CERN 2021
6 *
7 * Copyright (c) 2021, CERN
8 *
9 * Redistribution and use in source and binary forms,
10 * with or without modification, are permitted according to the terms
11 * listed in LICENSE (http://roofit.sourceforge.net/license.txt)
12 */
13
14/**
15\file RooNLLVarNew.cxx
16\class RooNLLVarNew
17\ingroup Roofitcore
18
19This is a simple class designed to produce the nll values needed by the fitter.
20In contrast to the `RooNLLVar` class, any logic except the bare minimum has been
21transfered away to other classes, like the `RooFitDriver`. This class also calls
22functions from `RooBatchCompute` library to provide faster computation times.
23**/
24
25#include <RooNLLVarNew.h>
26
27#include <RooAddition.h>
28#include <RooFormulaVar.h>
29#include <RooNaNPacker.h>
30#include <RooRealSumPdf.h>
31#include <RooProdPdf.h>
32#include <RooRealVar.h>
34
35#include <ROOT/StringUtils.hxx>
36
37#include <TClass.h>
38#include <TMath.h>
39#include <Math/Util.h>
40#include <TMath.h>
41
42#include <numeric>
43#include <stdexcept>
44#include <vector>
45
46using namespace ROOT::Experimental;
47
48// Declare constexpr static members to make them available if odr-used in C++14.
49constexpr const char *RooNLLVarNew::weightVarName;
50constexpr const char *RooNLLVarNew::weightVarNameSumW2;
51
52namespace {
53
54std::unique_ptr<RooAbsReal>
55createFractionInRange(RooAbsPdf const &pdf, RooArgSet const &observables, std::string const &rangeNames)
56{
57 return std::unique_ptr<RooAbsReal>{
58 pdf.createIntegral(observables, &observables, pdf.getIntegratorConfig(), rangeNames.c_str())};
59}
60
61template <class Input>
62double kahanSum(Input const &input)
63{
64 return ROOT::Math::KahanSum<double, 4u>::Accumulate(input.begin(), input.end()).Sum();
65}
66
67RooArgSet getObservablesInPdf(RooAbsPdf const &pdf, RooArgSet const &observables)
68{
69 RooArgSet observablesInPdf;
70 pdf.getObservables(&observables, observablesInPdf);
71 return observablesInPdf;
72}
73
74} // namespace
75
76/** Contstruct a RooNLLVarNew
77
78\param pdf The pdf for which the nll is computed for
79\param observables The observabes of the pdf
80\param isExtended Set to true if this is an extended fit
81**/
82RooNLLVarNew::RooNLLVarNew(const char *name, const char *title, RooAbsPdf &pdf, RooArgSet const &observables,
83 bool isExtended, std::string const &rangeName, bool doOffset)
84 : RooAbsReal(name, title), _pdf{"pdf", "pdf", this, pdf}, _observables{getObservablesInPdf(pdf, observables)},
85 _isExtended{isExtended}, _doOffset{doOffset},
86 _weightVar{"weightVar", "weightVar", this, *new RooRealVar(weightVarName, weightVarName, 1.0), true, false, true},
87 _weightSquaredVar{weightVarNameSumW2,
88 weightVarNameSumW2,
89 this,
90 *new RooRealVar("weightSquardVar", "weightSquaredVar", 1.0),
91 true,
92 false,
93 true}
94{
95 RooAbsPdf *actualPdf = &pdf;
96
97 if (pdf.getAttribute("BinnedLikelihood") && pdf.IsA()->InheritsFrom(RooRealSumPdf::Class())) {
98 // Simplest case: top-level of component is a RooRealSumPdf
99 _binnedL = true;
100 } else if (pdf.IsA()->InheritsFrom(RooProdPdf::Class())) {
101 // Default case: top-level pdf is a product of RooRealSumPdf and other pdfs
102 for (RooAbsArg *component : static_cast<RooProdPdf &>(pdf).pdfList()) {
103 if (component->getAttribute("BinnedLikelihood") && component->IsA()->InheritsFrom(RooRealSumPdf::Class())) {
104 actualPdf = static_cast<RooAbsPdf *>(component);
105 _binnedL = true;
106 }
107 }
108 }
109
110 if (actualPdf != &pdf) {
111 _pdf.setArg(*actualPdf);
112 }
113
114 if (_binnedL) {
115 if (_observables.size() != 1) {
116 throw std::runtime_error("BinnedPdf optimization only works with a 1D pdf.");
117 } else {
118 auto *var = static_cast<RooRealVar *>(_observables.first());
119 std::list<double> *boundaries = actualPdf->binBoundaries(*var, var->getMin(), var->getMax());
120 std::list<double>::iterator biter = boundaries->begin();
121 _binw.resize(boundaries->size() - 1);
122 double lastBound = (*biter);
123 ++biter;
124 int ibin = 0;
125 while (biter != boundaries->end()) {
126 _binw[ibin] = (*biter) - lastBound;
127 lastBound = (*biter);
128 ibin++;
129 ++biter;
130 }
131 }
132 }
133
134 if (!rangeName.empty()) {
135 auto term = createFractionInRange(*actualPdf, _observables, rangeName);
137 std::make_unique<RooTemplateProxy<RooAbsReal>>("_fractionInRange", "_fractionInRange", this, *term);
138 addOwnedComponents(std::move(term));
139 }
140
142}
143
145 : RooAbsReal(other, name), _pdf{"pdf", this, other._pdf}, _observables{other._observables},
146 _isExtended{other._isExtended}, _weightSquared{other._weightSquared}, _binnedL{other._binnedL},
147 _prefix{other._prefix}, _weightVar{"weightVar", this, other._weightVar}, _weightSquaredVar{"weightSquaredVar",
148 this,
149 other._weightSquaredVar}
150{
151 if (other._fractionInRange)
153 std::make_unique<RooTemplateProxy<RooAbsReal>>("_fractionInRange", this, *other._fractionInRange);
154}
155
156/** Compute multiple negative logs of propabilities
157
158\param dispatch A pointer to the RooBatchCompute library interface used for this computation
159\param output An array of doubles where the computation results will be stored
160\param nEvents The number of events to be processed
161\param dataMap A map containing spans with the input data for the computation
162**/
163void RooNLLVarNew::computeBatch(cudaStream_t * /*stream*/, double *output, size_t /*nOut*/,
164 RooFit::Detail::DataMap const &dataMap) const
165{
166 std::size_t nEvents = dataMap.at(_pdf).size();
167
168 auto weights = dataMap.at(_weightVar);
169 auto weightsSumW2 = dataMap.at(_weightSquaredVar);
170 auto weightSpan = _weightSquared ? weightsSumW2 : weights;
171
172 if (_binnedL) {
174 ROOT::Math::KahanSum<double> sumWeightKahanSum{0.0};
175 auto preds = dataMap.at(&*_pdf);
176
177 for (std::size_t i = 0; i < nEvents; ++i) {
178
179 double eventWeight = weightSpan[i];
180
181 // Calculate log(Poisson(N|mu) for this bin
182 double N = eventWeight;
183 double mu = preds[i] * _binw[i];
184
185 if (mu <= 0 && N > 0) {
186
187 // Catch error condition: data present where zero events are predicted
188 logEvalError(Form("Observed %f events in bin %lu with zero event yield", N, (unsigned long)i));
189
190 } else if (std::abs(mu) < 1e-10 && std::abs(N) < 1e-10) {
191
192 // Special handling of this case since log(Poisson(0,0)=0 but can't be calculated with usual log-formula
193 // since log(mu)=0. No update of result is required since term=0.
194
195 } else {
196
197 result += -1 * (-mu + N * log(mu) - TMath::LnGamma(N + 1));
198 sumWeightKahanSum += eventWeight;
199 }
200 }
201
202 result += sumWeightKahanSum.Sum();
203
204 // Check if value offset flag is set.
205 if (_doOffset) {
206
207 // If no offset is stored enable this feature now
208 if (_offset == 0 && result != 0) {
209 _offset = result;
210 }
211
212 // Subtract offset
213 result -= _offset;
214 }
215
216 output[0] = result.Sum();
217
218 return;
219 }
220
221 auto probas = dataMap.at(_pdf);
222
223 _logProbasBuffer.resize(nEvents);
224 (*_pdf).getLogProbabilities(probas, _logProbasBuffer.data());
225
226 if ((_isExtended || _fractionInRange) && _sumWeight == 0.0) {
227 _sumWeight = weights.size() == 1 ? weights[0] * nEvents : kahanSum(weights);
228 }
230 _sumWeight2 = weights.size() == 1 ? weightsSumW2[0] * nEvents : kahanSum(weightsSumW2);
231 }
232 double sumCorrectionTerm = 0;
233 if (_fractionInRange) {
234 auto fractionInRangeSpan = dataMap.at(*_fractionInRange);
235 if (fractionInRangeSpan.size() == 1) {
236 sumCorrectionTerm = (_weightSquared ? _sumWeight2 : _sumWeight) * std::log(fractionInRangeSpan[0]);
237 } else {
238 if (weightSpan.size() == 1) {
239 double fractionInRangeLogSum = 0.0;
240 for (std::size_t i = 0; i < fractionInRangeSpan.size(); ++i) {
241 fractionInRangeLogSum += std::log(fractionInRangeSpan[i]);
242 }
243 sumCorrectionTerm = weightSpan[0] * fractionInRangeLogSum;
244 } else {
245 // We don't need to use the library for now because the weights and
246 // correction term integrals are always in the CPU map.
247 sumCorrectionTerm = 0.0;
248 for (std::size_t i = 0; i < nEvents; ++i) {
249 sumCorrectionTerm += weightSpan[i] * std::log(fractionInRangeSpan[i]);
250 }
251 }
252 }
253 }
254
256 RooNaNPacker packedNaN(0.f);
257
258 for (std::size_t i = 0; i < nEvents; ++i) {
259
260 double eventWeight = weightSpan.size() > 1 ? weightSpan[i] : weightSpan[0];
261 if (0. == eventWeight * eventWeight)
262 continue;
263
264 const double term = -eventWeight * _logProbasBuffer[i];
265
266 kahanProb.Add(term);
267 packedNaN.accumulate(term);
268 }
269
270 if (packedNaN.getPayload() != 0.) {
271 // Some events with evaluation errors. Return "badness" of errors.
272 kahanProb = packedNaN.getNaNWithPayload();
273 }
274
275 if (_isExtended) {
276 assert(_sumWeight != 0.0);
277 double expected = _pdf->expectedEvents(&_observables);
278 if (_fractionInRange) {
279 expected *= dataMap.at(*_fractionInRange)[0];
280 }
281 kahanProb += _pdf->extendedTerm(_sumWeight, expected, _weightSquared ? _sumWeight2 : 0.0);
282 }
283 if (_fractionInRange) {
284 kahanProb += sumCorrectionTerm;
285 }
286
287 // Check if value offset flag is set.
288 if (_doOffset) {
289
290 // If no offset is stored enable this feature now
291 if (_offset == 0 && kahanProb != 0) {
292 _offset = kahanProb;
293 }
294
295 // Subtract offset
296 kahanProb -= _offset;
297 }
298
299 output[0] = kahanProb.Sum();
300}
301
303{
304 return _value;
305}
306
307void RooNLLVarNew::getParametersHook(const RooArgSet * /*nset*/, RooArgSet *params, Bool_t /*stripDisconnected*/) const
308{
309 // strip away the observables and weights
310 params->remove(_observables, true, true);
311 params->remove(RooArgList{*_weightVar, *_weightSquaredVar}, true, true);
312}
313
314////////////////////////////////////////////////////////////////////////////////
315/// Replaces all observables and the weight variable of this NLL with clones
316/// that only differ by a prefix added to the names. Used for simultaneous fits.
317/// \return A RooArgSet with the new observable args.
318/// \param[in] prefix The prefix to add to the observables and weight names.
320{
321 _prefix = prefix;
322
323 RooArgSet obsSet{_observables};
324 RooArgSet obsClones;
325 obsSet.snapshot(obsClones);
326 for (auto *arg : static_range_cast<RooRealVar *>(obsClones)) {
327 arg->setAttribute((std::string("ORIGNAME:") + arg->GetName()).c_str());
328 arg->SetName((prefix + arg->GetName()).c_str());
329 arg->setConstant();
330 }
331 recursiveRedirectServers(obsClones, false, true);
332
333 RooArgSet newObservables{obsClones};
334
336 _observables.add(obsClones);
337
338 addOwnedComponents(std::move(obsClones));
339
341
342 return newObservables;
343}
344
346{
349}
350
351////////////////////////////////////////////////////////////////////////////////
352/// Toggles the weight square correction.
354{
355 _weightSquared = flag;
356}
357
358std::unique_ptr<RooArgSet>
359RooNLLVarNew::fillNormSetForServer(RooArgSet const & /*normSet*/, RooAbsArg const & /*server*/) const
360{
361 if (_binnedL) {
362 return std::make_unique<RooArgSet>();
363 }
364 return nullptr;
365}
#define e(i)
Definition RSha256.hxx:103
#define N
char name[80]
Definition TGX11.cxx:110
char * Form(const char *fmt,...)
std::unique_ptr< RooTemplateProxy< RooAbsReal > > _fractionInRange
RooTemplateProxy< RooAbsReal > _weightVar
void computeBatch(cudaStream_t *, double *output, size_t nOut, RooFit::Detail::DataMap const &) const override
Compute multiple negative logs of propabilities.
void applyWeightSquared(bool flag) override
Toggles the weight square correction.
ROOT::Math::KahanSum< double > _offset
! Offset as KahanSum to avoid loss of precision
RooTemplateProxy< RooAbsPdf > _pdf
std::vector< double > _logProbasBuffer
!
RooTemplateProxy< RooAbsReal > _weightSquaredVar
void getParametersHook(const RooArgSet *nset, RooArgSet *list, Bool_t stripDisconnected) const override
std::vector< double > _binw
!
static constexpr const char * weightVarName
std::unique_ptr< RooArgSet > fillNormSetForServer(RooArgSet const &normSet, RooAbsArg const &server) const override
Fills a RooArgSet to be used as the normalization set for a server, given a normalization set for thi...
RooArgSet prefixObservableAndWeightNames(std::string const &prefix)
Replaces all observables and the weight variable of this NLL with clones that only differ by a prefix...
static constexpr const char * weightVarNameSumW2
double evaluate() const override
Evaluate this PDF / function / constant. Needs to be overridden by all derived classes.
The Kahan summation is a compensated summation algorithm, which significantly reduces numerical error...
Definition Util.h:122
T Sum() const
Definition Util.h:240
static KahanSum< T, N > Accumulate(Iterator begin, Iterator end, T initialValue=T{})
Iterate over a range and return an instance of a KahanSum.
Definition Util.h:211
void Add(T x)
Single-element accumulation. Will not vectorise.
Definition Util.h:165
RooAbsArg is the common abstract base class for objects that represent a value and a "shape" in RooFi...
Definition RooAbsArg.h:69
RooArgSet * getObservables(const RooArgSet &set, Bool_t valueOnly=kTRUE) const
Given a set of possible observables, return the observables that this PDF depends on.
Definition RooAbsArg.h:309
bool addOwnedComponents(const RooAbsCollection &comps)
Take ownership of the contents of 'comps'.
Bool_t getAttribute(const Text_t *name) const
Check if a named attribute is set. By default, all attributes are unset.
Bool_t recursiveRedirectServers(const RooAbsCollection &newServerList, Bool_t mustReplaceAll=kFALSE, Bool_t nameChange=kFALSE, Bool_t recurseInNewSet=kTRUE)
Recursively replace all servers with the new servers in newSet.
void SetName(const char *name)
Set the name of the TNamed.
virtual Bool_t add(const RooAbsArg &var, Bool_t silent=kFALSE)
Add the specified argument to list.
Storage_t::size_type size() const
RooAbsArg * first() const
void clear()
Clear contents. If the collection is owning, it will also delete the contents.
virtual Bool_t remove(const RooAbsArg &var, Bool_t silent=kFALSE, Bool_t matchByNameOnly=kFALSE)
Remove the specified argument from our list.
virtual Double_t expectedEvents(const RooArgSet *nset) const
Return expected number of events to be used in calculation of extended likelihood.
double extendedTerm(double sumEntries, double expected, double sumEntriesW2=0.0) const
RooAbsReal is the common abstract base class for objects that represent a real value and implements f...
Definition RooAbsReal.h:64
RooAbsReal * createIntegral(const RooArgSet &iset, const RooCmdArg &arg1, const RooCmdArg &arg2=RooCmdArg::none(), const RooCmdArg &arg3=RooCmdArg::none(), const RooCmdArg &arg4=RooCmdArg::none(), const RooCmdArg &arg5=RooCmdArg::none(), const RooCmdArg &arg6=RooCmdArg::none(), const RooCmdArg &arg7=RooCmdArg::none(), const RooCmdArg &arg8=RooCmdArg::none()) const
Create an object that represents the integral of the function over one or more observables listed in ...
virtual std::list< Double_t > * binBoundaries(RooAbsRealLValue &obs, Double_t xlo, Double_t xhi) const
Retrieve bin boundaries if this distribution is binned in obs.
Double_t _value
Definition RooAbsReal.h:484
const RooNumIntConfig * getIntegratorConfig() const
Return the numeric integration configuration used for this object.
void logEvalError(const char *message, const char *serverValueString=0) const
Log evaluation error message.
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:35
RooArgSet * snapshot(bool deepCopy=true) const
Use RooAbsCollection::snapshot(), but return as RooArgSet.
Definition RooArgSet.h:158
auto & at(RooAbsArg const *arg, RooAbsArg const *=nullptr)
Definition DataMap.h:88
RooProdPdf is an efficient implementation of a product of PDFs of the form.
Definition RooProdPdf.h:33
RooRealVar represents a variable that can be changed from the outside.
Definition RooRealVar.h:39
bool setArg(T &newRef)
Change object held in proxy into newRef.
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:515
Double_t LnGamma(Double_t z)
Computation of ln[gamma(z)] for all z.
Definition TMath.cxx:486
Little struct that can pack a float into the unused bits of the mantissa of a NaN double.
float getPayload() const
Retrieve packed float.
double getNaNWithPayload() const
Retrieve a NaN with the current float payload packed into the mantissa.
void accumulate(double val)
Accumulate a packed float from another NaN into this.
static void output(int code)
Definition gifencode.c:226