Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RooAcceptReject.cxx
Go to the documentation of this file.
1/// \cond ROOFIT_INTERNAL
2
3/*****************************************************************************
4 * Project: RooFit *
5 * Package: RooFitCore *
6 * @(#)root/roofitcore:$Id$
7 * Authors: *
8 * WV, Wouter Verkerke, UC Santa Barbara, verkerke@slac.stanford.edu *
9 * DK, David Kirkby, UC Irvine, dkirkby@uci.edu *
10 * *
11 * Copyright (c) 2000-2005, Regents of the University of California *
12 * and Stanford University. All rights reserved. *
13 * *
14 * Redistribution and use in source and binary forms, *
15 * with or without modification, are permitted according to the terms *
16 * listed in LICENSE (http://roofit.sourceforge.net/license.txt) *
17 *****************************************************************************/
18
19/**
20\file RooAcceptReject.cxx
21\class RooAcceptReject
22\ingroup Roofitcore
23
24Generic Monte Carlo toy generator implement
25the accept/reject sampling technique on any positively valued function.
26The RooAcceptReject generator is used by the various generator context
27classes to take care of generation of observables for which p.d.fs
28do not define internal methods
29**/
30
31#include "RooAcceptReject.h"
32#include "RooAbsReal.h"
33#include "RooCategory.h"
34#include "RooRealVar.h"
35#include "RooDataSet.h"
36#include "RooRandom.h"
37#include "RooPrintable.h"
38#include "RooMsgService.h"
39#include "RooRealBinding.h"
40#include "RooNumGenFactory.h"
41#include "RooNumGenConfig.h"
42
43#include <cassert>
44
45
46////////////////////////////////////////////////////////////////////////////////
47/// Register RooIntegrator1D, is parameters and capabilities with RooNumIntFactory
48
49void RooAcceptReject::registerSampler(RooNumGenFactory& fact)
50{
51 RooRealVar nTrial0D("nTrial0D","Number of trial samples for cat-only generation",100,0,1e9) ;
52 RooRealVar nTrial1D("nTrial1D","Number of trial samples for 1-dim generation",1000,0,1e9) ;
53 RooRealVar nTrial2D("nTrial2D","Number of trial samples for 2-dim generation",100000,0,1e9) ;
54 RooRealVar nTrial3D("nTrial3D","Number of trial samples for N-dim generation",10000000,0,1e9) ;
55
58}
59
60
61
62////////////////////////////////////////////////////////////////////////////////
63/// Initialize an accept-reject generator for the specified distribution function,
64/// which must be non-negative but does not need to be normalized over the
65/// variables to be generated, genVars. The function and its dependents are
66/// cloned and so will not be disturbed during the generation process.
67
68RooAcceptReject::RooAcceptReject(const RooAbsReal &func, const RooArgSet &genVars, const RooNumGenConfig &config,
69 bool verbose, const RooAbsReal *maxFuncVal)
71{
72 _minTrialsArray[0] = static_cast<Int_t>(config.getConfigSection("RooAcceptReject").getRealValue("nTrial0D")) ;
73 _minTrialsArray[1] = static_cast<Int_t>(config.getConfigSection("RooAcceptReject").getRealValue("nTrial1D")) ;
74 _minTrialsArray[2] = static_cast<Int_t>(config.getConfigSection("RooAcceptReject").getRealValue("nTrial2D")) ;
75 _minTrialsArray[3] = static_cast<Int_t>(config.getConfigSection("RooAcceptReject").getRealValue("nTrial3D")) ;
76
77 for (auto * cat : static_range_cast<RooAbsCategory*>(_catVars)) {
78 _catSampleMult *= cat->numTypes() ;
79 }
80
81
82 // calculate the minimum number of trials needed to estimate our integral and max value
83 if (!_funcMaxVal) {
84
85 if(_realSampleDim > 3) {
87 oocoutW(nullptr, Generation) << _funcClone->GetName() << "::RooAcceptReject" << ": WARNING: generating " << _realSampleDim
88 << " variables with accept-reject may not be accurate" << std::endl;
89 }
90 else {
92 }
93 if (_realSampleDim > 1) {
94 oocoutW(nullptr, Generation) << "RooAcceptReject::ctor(" << _funcClone->GetName()
95 << ") WARNING: performing accept/reject sampling on a p.d.f in "
96 << _realSampleDim << " dimensions without prior knowledge on maximum value "
97 << "of p.d.f. Determining maximum value by taking " << _minTrials
98 << " trial samples. If p.d.f contains sharp peaks smaller than average "
99 << "distance between trial sampling points these may be missed and p.d.f. "
100 << "may be sampled incorrectly." << std::endl ;
101 }
102 } else {
103 // No trials needed if we know the maximum a priori
104 _minTrials=0 ;
105 }
106
107 // Need to fix some things here
108 if (_minTrials>10000) {
109 oocoutW(nullptr, Generation) << "RooAcceptReject::ctor(" << func.GetName() << "): WARNING: " << _minTrials << " trial samples requested by p.d.f for "
110 << _realSampleDim << "-dimensional accept/reject sampling, this may take some time" << std::endl ;
111 }
112
113 // print a verbose summary of our configuration, if requested
114 if(_verbose) {
115 oocoutI(nullptr, Generation) << func.GetName() << "::RooAcceptReject" << ":" << std::endl
116 << " Initializing accept-reject generator for" << std::endl << " ";
117 _funcClone->printStream(ooccoutI(nullptr, Generation),RooPrintable::kName,RooPrintable::kSingleLine);
118 if (_funcMaxVal) {
119 ooccoutI(nullptr, Generation) << " Function maximum provided, no trial sampling performed" << std::endl ;
120 } else {
121 ooccoutI(nullptr, Generation) << " Real sampling dimension is " << _realSampleDim << std::endl;
122 ooccoutI(nullptr, Generation) << " Category sampling multiplier is " << _catSampleMult << std::endl ;
123 ooccoutI(nullptr, Generation) << " Min sampling trials is " << _minTrials << std::endl;
124 }
125 if (!_catVars.empty()) {
126 ooccoutI(nullptr, Generation) << " Will generate category vars "<< _catVars << std::endl ;
127 }
128 if (!_realVars.empty()) {
129 ooccoutI(nullptr, Generation) << " Will generate real vars " << _realVars << std::endl ;
130 }
131 }
132
133 // initialize our statistics
134 _maxFuncVal= 0;
135 _funcSum= 0;
136 _totalEvents= 0;
137 _eventsUsed= 0;
138}
139
140
141////////////////////////////////////////////////////////////////////////////////
142/// Return a pointer to a generated event. The caller does not own the event and it
143/// will be overwritten by a subsequent call. The input parameter 'remaining' should
144/// contain your best guess at the total number of subsequent events you will request.
145
146const RooArgSet *RooAcceptReject::generateEvent(UInt_t remaining, double& resampleRatio)
147{
148 // are we actually generating anything? (the cache always contains at least our function value)
149 const RooArgSet *event= _cache->get();
150 if(event->size() == 1) return event;
151
152 if (!_funcMaxVal) {
153 // Generation with empirical maximum determination
154
155 // first generate enough events to get reasonable estimates for the integral and
156 // maximum function value
157
158 while(_totalEvents < _minTrials) {
160
161 // Limit cache size to 1M events
162 if (_cache->numEntries()>1000000) {
163 oocoutI(nullptr, Generation) << "RooAcceptReject::generateEvent: resetting event cache" << std::endl;
164 _cache->reset() ;
165 _eventsUsed = 0 ;
166 }
167 }
168
169 event= nullptr;
170 double oldMax2(_maxFuncVal);
171 while(nullptr == event) {
172 // Use any cached events first
173 if (_maxFuncVal>oldMax2) {
174 oocxcoutD(nullptr, Generation) << "RooAcceptReject::generateEvent maxFuncVal has changed, need to resample already accepted events by factor"
175 << oldMax2 << "/" << _maxFuncVal << "=" << oldMax2/_maxFuncVal << std::endl ;
177 }
178 event= nextAcceptedEvent();
179 if(event) break;
180 // When we have used up the cache, start a new cache and add
181 // some more events to it.
182 _cache->reset();
183 _eventsUsed= 0;
184 // Calculate how many more events to generate using our best estimate of our efficiency.
185 // Always generate at least one more event so we don't get stuck.
186 if(_totalEvents*_maxFuncVal <= 0) {
187 oocoutE(nullptr, Generation) << "RooAcceptReject::generateEvent: cannot estimate efficiency...giving up" << std::endl;
188 return nullptr;
189 }
190
191 double eff= _funcSum/(_totalEvents*_maxFuncVal);
192 Long64_t extra= 1 + (Long64_t)(1.05*remaining/eff);
193 oocxcoutD(nullptr, Generation) << "RooAcceptReject::generateEvent: adding " << extra << " events to the cache, eff = " << eff << std::endl;
194 double oldMax(_maxFuncVal);
195 while(extra--) {
197 if((_maxFuncVal > oldMax)) {
198 oocxcoutD(nullptr, Generation) << "RooAcceptReject::generateEvent: estimated function maximum increased from "
199 << oldMax << " to " << _maxFuncVal << std::endl;
201 // Trim cache here
202 }
203 }
204 }
205
206 // Limit cache size to 1M events
207 if (_eventsUsed>1000000) {
208 _cache->reset() ;
209 _eventsUsed = 0 ;
210 }
211
212 } else {
213 // Generation with a priori maximum knowledge
214 _maxFuncVal = _funcMaxVal->getVal() ;
215
216 // Generate enough trials to produce a single accepted event
217 event = nullptr ;
218 while(nullptr==event) {
220 event = nextAcceptedEvent() ;
221 }
222
223 }
224 return event;
225}
226
227
228
229////////////////////////////////////////////////////////////////////////////////
230/// Scan through events in the cache which have not been used yet,
231/// looking for the first accepted one which is added to the specified
232/// container. Return a pointer to the accepted event, or else zero
233/// if we use up the cache before we accept an event. The caller does
234/// not own the event and it will be overwritten by a subsequent call.
235
236const RooArgSet *RooAcceptReject::nextAcceptedEvent()
237{
238 const RooArgSet *event = nullptr;
239 while((event= _cache->get(_eventsUsed))) {
240 _eventsUsed++ ;
241 // accept this cached event?
242 double r= RooRandom::uniform();
243 if(r*_maxFuncVal > _funcValPtr->getVal()) {
244 //cout << " event number " << _eventsUsed << " has been rejected" << std::endl ;
245 continue;
246 }
247 //cout << " event number " << _eventsUsed << " has been accepted" << std::endl ;
248 // copy this event into the output container
249 if(_verbose && (_eventsUsed%1000==0)) {
250 std::cerr << "RooAcceptReject: accepted event (used " << _eventsUsed << " of "
251 << _cache->numEntries() << " so far)" << std::endl;
252 }
253 break;
254 }
255 //cout << "accepted event " << _eventsUsed << " of " << _cache->numEntries() << std::endl ;
256 return event;
257}
258
259
260
261////////////////////////////////////////////////////////////////////////////////
262/// Add a trial event to our cache and update our estimates
263/// of the function maximum value and integral.
264
265void RooAcceptReject::addEventToCache()
266{
267 // randomize each discrete argument
268 for(auto * cat : static_range_cast<RooCategory*>(_catVars)) cat->randomize();
269
270 // randomize each real argument
271 for(auto * real : static_range_cast<RooRealVar*>(_realVars)) real->randomize();
272
273 // calculate and store our function value at this new point
274 double val= _funcClone->getVal();
275 _funcValPtr->setVal(val);
276
277 // Update the estimated integral and maximum value. Increase our
278 // maximum estimate slightly to give a safety margin with a
279 // corresponding loss of efficiency.
280 if(val > _maxFuncVal) _maxFuncVal= 1.05*val;
281 _funcSum+= val;
282
283 // fill a new entry in our cache dataset for this point
284 _cache->fill();
285 _totalEvents++;
286
287 if (_verbose &&_totalEvents%10000==0) {
288 std::cerr << "RooAcceptReject: generated " << _totalEvents << " events so far." << std::endl ;
289 }
290
291}
292
293double RooAcceptReject::getFuncMax()
294{
295 // Empirically determine maximum value of function by taking a large number
296 // of samples. The actual number depends on the number of dimensions in which
297 // the sampling occurs
298
299 // Generate the minimum required number of samples for a reliable maximum estimate
300 while(_totalEvents < _minTrials) {
302
303 // Limit cache size to 1M events
304 if (_cache->numEntries()>1000000) {
305 oocoutI(nullptr, Generation) << "RooAcceptReject::getFuncMax: resetting event cache" << std::endl ;
306 _cache->reset() ;
307 _eventsUsed = 0 ;
308 }
309 }
310
311 return _maxFuncVal ;
312}
313
314std::string const& RooAcceptReject::generatorName() const {
315 static const std::string name = "RooAcceptReject";
316 return name;
317}
318
319/// \endcond
ROOT::RRangeCast< T, false, Range_t > static_range_cast(Range_t &&coll)
cudaEvent_t event
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
#define oocoutW(o, a)
#define oocxcoutD(o, a)
#define oocoutE(o, a)
#define oocoutI(o, a)
#define ooccoutI(o, a)
int Int_t
Signed integer 4 bytes (int)
Definition RtypesCore.h:60
unsigned int UInt_t
Unsigned integer 4 bytes (unsigned int)
Definition RtypesCore.h:61
long long Long64_t
Portable signed long integer 8 bytes.
Definition RtypesCore.h:84
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 char Point_t Rectangle_t WindowAttributes_t Float_t r
char name[80]
Definition TGX11.cxx:142
A space to attach TBranches.
Storage_t const & get() const
Const access to the underlying stl container.
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:63
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:24
Object to represent discrete states.
Definition RooCategory.h:28
Holds the configuration parameters of the various numeric integrators used by RooRealIntegral.
const RooArgSet & getConfigSection(const char *name) const
Retrieve configuration information specific to integrator with given name.
static double uniform(TRandom *generator=randomGenerator())
Return a number uniformly distributed from (0,1)
Definition RooRandom.cxx:77
Variable that can be changed from the outside.
Definition RooRealVar.h:37
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49