Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RooUnbinnedL.cxx
Go to the documentation of this file.
1/*
2 * Project: RooFit
3 * Authors:
4 * PB, Patrick Bos, Netherlands eScience Center, p.bos@esciencecenter.nl
5 *
6 * Copyright (c) 2021, CERN
7 *
8 * Redistribution and use in source and binary forms,
9 * with or without modification, are permitted according to the terms
10 * listed in LICENSE (http://roofit.sourceforge.net/license.txt)
11 */
12
13/**
14\file RooUnbinnedL.cxx
15\class RooUnbinnedL
16\ingroup Roofitcore
17
18A -log(likelihood) calculation from a dataset
19(assumed to be unbinned) and a PDF. The NLL is calculated as
20\f[
21 \sum_\mathrm{data} -\log( \mathrm{pdf}(x_\mathrm{data}))
22\f]
23In extended mode, a
24\f$ N_\mathrm{expect} - N_\mathrm{observed}*log(N_\mathrm{expect}) \f$ term is added.
25**/
26
28
29#include <RooAbsData.h>
30#include <RooAbsPdf.h>
31#include <RooAbsDataStore.h>
32#include <RooChangeTracker.h>
33#include <RooNaNPacker.h>
34#include <RooFit/Evaluator.h>
35
37
38namespace RooFit {
39namespace TestStatistics {
40
41namespace {
42
44{
46 return {&pdf, &data};
47 }
48 // For the evaluation with the BatchMode, the pdf needs to be "compiled" for
49 // a given normalization set.
50 return {RooFit::Detail::compileForNormSet(pdf, *data.get()), &data};
51}
52
53} // namespace
54
57 : RooAbsL(clonePdfData(*pdf, *data, evalBackend), data->numEntries(), 1, extended)
58{
59 std::unique_ptr<RooArgSet> params(pdf->getParameters(data));
60 paramTracker_ = std::make_unique<RooChangeTracker>("chtracker", "change tracker", *params, true);
61
63 evaluator_ = std::make_unique<RooFit::Evaluator>(*pdf_, evalBackend.value() == RooFit::EvalBackend::Value::Cuda);
64 std::stack<std::vector<double>>{}.swap(_vectorBuffers);
65 auto dataSpans =
66 RooFit::BatchModeDataHelpers::getDataSpans(*data, "", nullptr, /*skipZeroWeights=*/true,
67 /*takeGlobalObservablesFromData=*/false, _vectorBuffers);
68 for (auto const &item : dataSpans) {
69 evaluator_->setInput(item.first->GetName(), item.second, false);
70 }
71 }
72}
73
75 : RooAbsL(other),
76 apply_weight_squared(other.apply_weight_squared),
77 _first(other._first),
78 lastSection_(other.lastSection_),
79 cachedResult_(other.cachedResult_),
80 evaluator_(other.evaluator_)
81{
82 paramTracker_ = std::make_unique<RooChangeTracker>(*other.paramTracker_);
83}
84
86
87//////////////////////////////////////////////////////////////////////////////////
88
89/// Returns true if value was changed, false otherwise.
91{
94 return true;
95 }
96 // setValueDirty();
97 return false;
98}
99
100//////////////////////////////////////////////////////////////////////////////////
101
102/// With the vectorizing evaluation backends, the pdf is compiled for a fixed
103/// normalization set and evaluated outside of the RooFit data store, so the
104/// constant term optimization is not applicable. Attempting it anyway is not
105/// only useless: caching constant branches means snapshotting parts of the
106/// compiled computation graph into the data store, which classes like
107/// RooFit::Detail::RooNormalizedPdf don't support. Refuse the request instead.
109{
110 if (evaluator_) {
111 oocoutW((TObject *)nullptr, Optimization)
112 << "RooUnbinnedL::constOptimizeTestStatistic(" << GetName()
113 << ") the constant term optimization only applies to likelihoods evaluated with EvalBackend::Legacy(), "
114 "ignoring the request"
115 << std::endl;
116 return;
117 }
119}
120
121namespace {
122
123using ComputeResult = std::pair<ROOT::Math::KahanSum<double>, double>;
124
125// Copy of RooNLLVar::computeScalarFunc.
127 std::size_t stepSize, std::size_t firstEvent, std::size_t lastEvent,
128 RooAbsPdf const *offsetPdf = nullptr)
129{
133
134 for (auto i = firstEvent; i < lastEvent; i += stepSize) {
135 dataClone->get(i);
136
137 double weight = dataClone->weight(); // FIXME
138
139 if (0. == weight * weight)
140 continue;
141 if (weightSq)
142 weight = dataClone->weightSquared();
143
144 double logProba = pdfClone->getLogVal(normSet);
145
146 if (offsetPdf) {
147 logProba -= offsetPdf->getLogVal(normSet);
148 }
149
150 const double term = -weight * logProba;
151
152 kahanWeight.Add(weight);
153 kahanProb.Add(term);
154 packedNaN.accumulate(term);
155 }
156
157 if (packedNaN.getPayload() != 0.) {
158 // Some events with evaluation errors. Return "badness" of errors.
159 return {ROOT::Math::KahanSum<double>{packedNaN.getNaNWithPayload()}, kahanWeight.Sum()};
160 }
161
162 return {kahanProb, kahanWeight.Sum()};
163}
164
165// For now, almost exact copy of computeScalarFunc.
166ComputeResult computeBatchFunc(std::span<const double> probas, RooAbsData *dataClone, bool weightSq,
167 std::size_t stepSize, std::size_t firstEvent, std::size_t lastEvent)
168{
172
173 for (auto i = firstEvent; i < lastEvent; i += stepSize) {
174 dataClone->get(i);
175
176 double weight = dataClone->weight();
177
178 if (0. == weight * weight)
179 continue;
180 if (weightSq)
181 weight = dataClone->weightSquared();
182
183 double logProba = std::log(probas[i]);
184 const double term = -weight * logProba;
185
186 kahanWeight.Add(weight);
187 kahanProb.Add(term);
188 packedNaN.accumulate(term);
189 }
190
191 if (packedNaN.getPayload() != 0.) {
192 // Some events with evaluation errors. Return "badness" of errors.
193 return {ROOT::Math::KahanSum<double>{packedNaN.getNaNWithPayload()}, kahanWeight.Sum()};
194 }
195
196 return {kahanProb, kahanWeight.Sum()};
197}
198
199} // namespace
200
201//////////////////////////////////////////////////////////////////////////////////
202/// Calculate and return likelihood on subset of data from firstEvent to lastEvent
203/// processed with a step size of 'stepSize'. If this an extended likelihood and
204/// and the zero event is processed the extended term is added to the return
205/// likelihood.
206///
208RooUnbinnedL::evaluatePartition(Section events, std::size_t /*components_begin*/, std::size_t /*components_end*/)
209{
210 // Throughout the calculation, we use Kahan's algorithm for summing to
211 // prevent loss of precision - this is a factor four more expensive than
212 // straight addition, but since evaluating the PDF is usually much more
213 // expensive than that, we tolerate the additional cost...
215 double sumWeight;
217
218 // Do not reevaluate likelihood if parameters nor event range have changed
219 if (!paramTracker_->hasChanged(true) && events == lastSection_ &&
220 (cachedResult_.Sum() != 0 || cachedResult_.Carry() != 0))
221 return cachedResult_;
222
223 if (evaluator_) {
224 // Here, we have a memory allocation that should be avoided when this
225 // code needs to be optimized.
226 std::span<const double> probas = evaluator_->run();
227 std::tie(result, sumWeight) =
228 computeBatchFunc(probas, data_.get(), apply_weight_squared, 1, events.begin(N_events_), events.end(N_events_));
229 } else {
230 // The cache-and-track optimization tracks staleness of the cached
231 // branches globally, but recalculateCache() only refreshes the requested
232 // event range. A cache that was refreshed for one event section hence
233 // reports itself as up-to-date for all other sections as well, even
234 // though their rows may still hold values from an older parameter point.
235 // This happens when event-range tasks migrate between workers in
236 // RooFit::MultiProcess likelihood splitting. Force a full update of the
237 // cached branches whenever the evaluated section changes.
238 if (!(events == lastCacheSection_)) {
239 data_->store()->forceCacheUpdate();
240 lastCacheSection_ = events;
241 }
242 data_->store()->recalculateCache(nullptr, events.begin(N_events_), events.end(N_events_), 1, true);
243 std::tie(result, sumWeight) = computeScalarFunc(pdf_.get(), data_.get(), normSet_.get(), apply_weight_squared, 1,
244 events.begin(N_events_), events.end(N_events_));
245 }
246
247 // include the extended maximum likelihood term, if requested
248 if (extended_ && events.begin_fraction == 0) {
249 result += pdf_->extendedTerm(*data_, apply_weight_squared);
250 }
251
252 // If part of simultaneous PDF normalize probability over
253 // number of simultaneous PDFs: -sum(log(p/n)) = -sum(log(p)) + N*log(n)
254 if (sim_count_ > 1) {
255 result += sumWeight * log(1.0 * sim_count_);
256 }
257
258 // At the end of the first full calculation, wire the caches. This doesn't
259 // need to be done in BatchMode with the RooFit driver.
260 if (_first && !evaluator_) {
261 _first = false;
262 pdf_->wireAllCaches();
263 }
264
269 lastSection_ = events;
270 }
271 return result;
272}
273
274} // namespace TestStatistics
275} // namespace RooFit
#define oocoutW(o, a)
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
The Kahan summation is a compensated summation algorithm, which significantly reduces numerical error...
Definition Util.h:141
T Sum() const
Definition Util.h:259
T Carry() const
Definition Util.h:269
RooFit::OwningPtr< RooArgSet > getParameters(const RooAbsData *data, bool stripDisconnected=true) const
Create a list of leaf nodes in the arg tree starting with ourself as top node that don't match any of...
Abstract base class for binned and unbinned datasets.
Definition RooAbsData.h:56
Abstract interface for all probability density functions.
Definition RooAbsPdf.h:32
static ErrorLoggingMode evalErrorLoggingMode()
Return current evaluation error logging mode.
static Int_t numEvalErrors()
Return the number of logged evaluation errors since the last clearing.
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:24
std::shared_ptr< RooAbsData > data_
Definition RooAbsL.h:143
virtual std::string GetName() const
Definition RooAbsL.cxx:287
std::unique_ptr< RooArgSet > normSet_
Pointer to set with observables used for normalization.
Definition RooAbsL.h:144
virtual void constOptimizeTestStatistic(RooAbsArg::ConstOpCode opcode, bool doAlsoTrackingOpt)
Interface function signaling a request to perform constant term optimization.
Definition RooAbsL.cxx:243
std::shared_ptr< RooAbsPdf > pdf_
Definition RooAbsL.h:142
ROOT::Math::KahanSum< double > cachedResult_
void constOptimizeTestStatistic(RooAbsArg::ConstOpCode opcode, bool doAlsoTrackingOpt) override
With the vectorizing evaluation backends, the pdf is compiled for a fixed normalization set and evalu...
bool setApplyWeightSquared(bool flag)
Returns true if value was changed, false otherwise.
ROOT::Math::KahanSum< double > evaluatePartition(Section events, std::size_t components_begin, std::size_t components_end) override
Calculate and return likelihood on subset of data from firstEvent to lastEvent processed with a step ...
std::unique_ptr< RooChangeTracker > paramTracker_
bool apply_weight_squared
Apply weights squared?
RooUnbinnedL(RooAbsPdf *pdf, RooAbsData *data, RooAbsL::Extended extended=RooAbsL::Extended::Auto, RooFit::EvalBackend evalBackend=RooFit::EvalBackend::Legacy())
std::stack< std::vector< double > > _vectorBuffers
std::shared_ptr< RooFit::Evaluator > evaluator_
! For batched evaluation
Mother of all ROOT objects.
Definition TObject.h:42
std::unique_ptr< T > compileForNormSet(T const &arg, RooArgSet const &normSet)
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition CodegenImpl.h:72
A part of some range delimited by two fractional points between 0 and 1 (inclusive).
Definition RooAbsL.h:65
std::size_t begin(std::size_t N_total) const
Definition RooAbsL.h:73
std::size_t end(std::size_t N_total) const
Definition RooAbsL.h:75
Little struct that can pack a float into the unused bits of the mantissa of a NaN double.