Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
RooAcceptReject.cxx
Go to the documentation of this file.
1 /*****************************************************************************
2  * Project: RooFit *
3  * Package: RooFitCore *
4  * @(#)root/roofitcore:$Id$
5  * Authors: *
6  * WV, Wouter Verkerke, UC Santa Barbara, verkerke@slac.stanford.edu *
7  * DK, David Kirkby, UC Irvine, dkirkby@uci.edu *
8  * *
9  * Copyright (c) 2000-2005, Regents of the University of California *
10  * and Stanford University. All rights reserved. *
11  * *
12  * Redistribution and use in source and binary forms, *
13  * with or without modification, are permitted according to the terms *
14  * listed in LICENSE (http://roofit.sourceforge.net/license.txt) *
15  *****************************************************************************/
16 
17 /**
18 \file RooAcceptReject.cxx
19 \class RooAcceptReject
20 \ingroup Roofitcore
21 
22 Class RooAcceptReject is a generic toy monte carlo generator implement
23 the accept/reject sampling technique on any positively valued function.
24 The RooAcceptReject generator is used by the various generator context
25 classes to take care of generation of observables for which p.d.fs
26 do not define internal methods
27 **/
28 
29 
30 #include "RooFit.h"
31 #include "Riostream.h"
32 
33 #include "RooAcceptReject.h"
34 #include "RooAcceptReject.h"
35 #include "RooAbsReal.h"
36 #include "RooCategory.h"
37 #include "RooRealVar.h"
38 #include "RooDataSet.h"
39 #include "RooRandom.h"
40 #include "RooErrorHandler.h"
41 
42 #include "TString.h"
43 #include "TIterator.h"
44 #include "RooMsgService.h"
45 #include "TClass.h"
46 #include "TFoam.h"
47 #include "RooRealBinding.h"
48 #include "RooNumGenFactory.h"
49 #include "RooNumGenConfig.h"
50 
51 #include <assert.h>
52 
53 using namespace std;
54 
55 ClassImp(RooAcceptReject);
56  ;
57 
58 
59 ////////////////////////////////////////////////////////////////////////////////
60 /// Register RooIntegrator1D, is parameters and capabilities with RooNumIntFactory
61 
62 void RooAcceptReject::registerSampler(RooNumGenFactory& fact)
63 {
64  RooRealVar nTrial0D("nTrial0D","Number of trial samples for cat-only generation",100,0,1e9) ;
65  RooRealVar nTrial1D("nTrial1D","Number of trial samples for 1-dim generation",1000,0,1e9) ;
66  RooRealVar nTrial2D("nTrial2D","Number of trial samples for 2-dim generation",100000,0,1e9) ;
67  RooRealVar nTrial3D("nTrial3D","Number of trial samples for N-dim generation",10000000,0,1e9) ;
68 
69  RooAcceptReject* proto = new RooAcceptReject ;
70  fact.storeProtoSampler(proto,RooArgSet(nTrial0D,nTrial1D,nTrial2D,nTrial3D)) ;
71 }
72 
73 
74 
75 ////////////////////////////////////////////////////////////////////////////////
76 /// Initialize an accept-reject generator for the specified distribution function,
77 /// which must be non-negative but does not need to be normalized over the
78 /// variables to be generated, genVars. The function and its dependents are
79 /// cloned and so will not be disturbed during the generation process.
80 
81 RooAcceptReject::RooAcceptReject(const RooAbsReal &func, const RooArgSet &genVars, const RooNumGenConfig& config, Bool_t verbose, const RooAbsReal* maxFuncVal) :
82  RooAbsNumGenerator(func,genVars,verbose,maxFuncVal), _nextCatVar(0), _nextRealVar(0)
83 {
84  _minTrialsArray[0] = static_cast<Int_t>(config.getConfigSection("RooAcceptReject").getRealValue("nTrial0D")) ;
85  _minTrialsArray[1] = static_cast<Int_t>(config.getConfigSection("RooAcceptReject").getRealValue("nTrial1D")) ;
86  _minTrialsArray[2] = static_cast<Int_t>(config.getConfigSection("RooAcceptReject").getRealValue("nTrial2D")) ;
87  _minTrialsArray[3] = static_cast<Int_t>(config.getConfigSection("RooAcceptReject").getRealValue("nTrial3D")) ;
88 
89  _realSampleDim = _realVars.getSize() ;
90  TIterator* iter = _catVars.createIterator() ;
91  RooAbsCategory* cat ;
92  _catSampleMult = 1 ;
93  while((cat=(RooAbsCategory*)iter->Next())) {
94  _catSampleMult *= cat->numTypes() ;
95  }
96  delete iter ;
97 
98 
99  // calculate the minimum number of trials needed to estimate our integral and max value
100  if (!_funcMaxVal) {
101 
102  if(_realSampleDim > 3) {
103  _minTrials= _minTrialsArray[3]*_catSampleMult;
104  coutW(Generation) << fName << "::" << ClassName() << ": WARNING: generating " << _realSampleDim
105  << " variables with accept-reject may not be accurate" << endl;
106  }
107  else {
108  _minTrials= _minTrialsArray[_realSampleDim]*_catSampleMult;
109  }
110  if (_realSampleDim > 1) {
111  coutW(Generation) << "RooAcceptReject::ctor(" << fName
112  << ") WARNING: performing accept/reject sampling on a p.d.f in "
113  << _realSampleDim << " dimensions without prior knowledge on maximum value "
114  << "of p.d.f. Determining maximum value by taking " << _minTrials
115  << " trial samples. If p.d.f contains sharp peaks smaller than average "
116  << "distance between trial sampling points these may be missed and p.d.f. "
117  << "may be sampled incorrectly." << endl ;
118  }
119  } else {
120  // No trials needed if we know the maximum a priori
121  _minTrials=0 ;
122  }
123 
124  // Need to fix some things here
125  if (_minTrials>10000) {
126  coutW(Generation) << "RooAcceptReject::ctor(" << fName << "): WARNING: " << _minTrials << " trial samples requested by p.d.f for "
127  << _realSampleDim << "-dimensional accept/reject sampling, this may take some time" << endl ;
128  }
129 
130  // print a verbose summary of our configuration, if requested
131  if(_verbose) {
132  coutI(Generation) << fName << "::" << ClassName() << ":" << endl
133  << " Initializing accept-reject generator for" << endl << " ";
134  _funcClone->printStream(ccoutI(Generation),kName,kSingleLine);
135  if (_funcMaxVal) {
136  ccoutI(Generation) << " Function maximum provided, no trial sampling performed" << endl ;
137  } else {
138  ccoutI(Generation) << " Real sampling dimension is " << _realSampleDim << endl;
139  ccoutI(Generation) << " Category sampling multiplier is " << _catSampleMult << endl ;
140  ccoutI(Generation) << " Min sampling trials is " << _minTrials << endl;
141  }
142  if (_catVars.getSize()>0) {
143  ccoutI(Generation) << " Will generate category vars "<< _catVars << endl ;
144  }
145  if (_realVars.getSize()>0) {
146  ccoutI(Generation) << " Will generate real vars " << _realVars << endl ;
147  }
148  }
149  // create iterators for the new sets
150  _nextCatVar= _catVars.createIterator();
151  _nextRealVar= _realVars.createIterator();
152  assert(0 != _nextCatVar && 0 != _nextRealVar);
153 
154  // initialize our statistics
155  _maxFuncVal= 0;
156  _funcSum= 0;
157  _totalEvents= 0;
158  _eventsUsed= 0;
159 }
160 
161 
162 
163 ////////////////////////////////////////////////////////////////////////////////
164 /// Destructor
165 
166 RooAcceptReject::~RooAcceptReject()
167 {
168  delete _nextCatVar;
169  delete _nextRealVar;
170 }
171 
172 
173 
174 ////////////////////////////////////////////////////////////////////////////////
175 /// Return a pointer to a generated event. The caller does not own the event and it
176 /// will be overwritten by a subsequent call. The input parameter 'remaining' should
177 /// contain your best guess at the total number of subsequent events you will request.
178 
179 const RooArgSet *RooAcceptReject::generateEvent(UInt_t remaining, Double_t& resampleRatio)
180 {
181  // are we actually generating anything? (the cache always contains at least our function value)
182  const RooArgSet *event= _cache->get();
183  if(event->getSize() == 1) return event;
184 
185  if (!_funcMaxVal) {
186  // Generation with empirical maximum determination
187 
188  // first generate enough events to get reasonable estimates for the integral and
189  // maximum function value
190 
191  while(_totalEvents < _minTrials) {
192  addEventToCache();
193 
194  // Limit cache size to 1M events
195  if (_cache->numEntries()>1000000) {
196  coutI(Generation) << "RooAcceptReject::generateEvent: resetting event cache" << endl ;
197  _cache->reset() ;
198  _eventsUsed = 0 ;
199  }
200  }
201 
202  event= 0;
203  Double_t oldMax2(_maxFuncVal);
204  while(0 == event) {
205  // Use any cached events first
206  if (_maxFuncVal>oldMax2) {
207  cxcoutD(Generation) << "RooAcceptReject::generateEvent maxFuncVal has changed, need to resample already accepted events by factor"
208  << oldMax2 << "/" << _maxFuncVal << "=" << oldMax2/_maxFuncVal << endl ;
209  resampleRatio=oldMax2/_maxFuncVal ;
210  }
211  event= nextAcceptedEvent();
212  if(event) break;
213  // When we have used up the cache, start a new cache and add
214  // some more events to it.
215  _cache->reset();
216  _eventsUsed= 0;
217  // Calculate how many more events to generate using our best estimate of our efficiency.
218  // Always generate at least one more event so we don't get stuck.
219  if(_totalEvents*_maxFuncVal <= 0) {
220  coutE(Generation) << "RooAcceptReject::generateEvent: cannot estimate efficiency...giving up" << endl;
221  return 0;
222  }
223 
224  Double_t eff= _funcSum/(_totalEvents*_maxFuncVal);
225  Long64_t extra= 1 + (Long64_t)(1.05*remaining/eff);
226  cxcoutD(Generation) << "RooAcceptReject::generateEvent: adding " << extra << " events to the cache, eff = " << eff << endl;
227  Double_t oldMax(_maxFuncVal);
228  while(extra--) {
229  addEventToCache();
230  if((_maxFuncVal > oldMax)) {
231  cxcoutD(Generation) << "RooAcceptReject::generateEvent: estimated function maximum increased from "
232  << oldMax << " to " << _maxFuncVal << endl;
233  oldMax = _maxFuncVal ;
234  // Trim cache here
235  }
236  }
237  }
238 
239  // Limit cache size to 1M events
240  if (_eventsUsed>1000000) {
241  _cache->reset() ;
242  _eventsUsed = 0 ;
243  }
244 
245  } else {
246  // Generation with a priori maximum knowledge
247  _maxFuncVal = _funcMaxVal->getVal() ;
248 
249  // Generate enough trials to produce a single accepted event
250  event = 0 ;
251  while(0==event) {
252  addEventToCache() ;
253  event = nextAcceptedEvent() ;
254  }
255 
256  }
257  return event;
258 }
259 
260 
261 
262 ////////////////////////////////////////////////////////////////////////////////
263 /// Scan through events in the cache which have not been used yet,
264 /// looking for the first accepted one which is added to the specified
265 /// container. Return a pointer to the accepted event, or else zero
266 /// if we use up the cache before we accept an event. The caller does
267 /// not own the event and it will be overwritten by a subsequent call.
268 
269 const RooArgSet *RooAcceptReject::nextAcceptedEvent()
270 {
271  const RooArgSet *event = 0;
272  while((event= _cache->get(_eventsUsed))) {
273  _eventsUsed++ ;
274  // accept this cached event?
275  Double_t r= RooRandom::uniform();
276  if(r*_maxFuncVal > _funcValPtr->getVal()) {
277  //cout << " event number " << _eventsUsed << " has been rejected" << endl ;
278  continue;
279  }
280  //cout << " event number " << _eventsUsed << " has been accepted" << endl ;
281  // copy this event into the output container
282  if(_verbose && (_eventsUsed%1000==0)) {
283  cerr << "RooAcceptReject: accepted event (used " << _eventsUsed << " of "
284  << _cache->numEntries() << " so far)" << endl;
285  }
286  break;
287  }
288  //cout << "accepted event " << _eventsUsed << " of " << _cache->numEntries() << endl ;
289  return event;
290 }
291 
292 
293 
294 ////////////////////////////////////////////////////////////////////////////////
295 /// Add a trial event to our cache and update our estimates
296 /// of the function maximum value and integral.
297 
298 void RooAcceptReject::addEventToCache()
299 {
300  // randomize each discrete argument
301  _nextCatVar->Reset();
302  RooCategory *cat = 0;
303  while((cat= (RooCategory*)_nextCatVar->Next())) cat->randomize();
304 
305  // randomize each real argument
306  _nextRealVar->Reset();
307  RooRealVar *real = 0;
308  while((real= (RooRealVar*)_nextRealVar->Next())) real->randomize();
309 
310  // calculate and store our function value at this new point
311  Double_t val= _funcClone->getVal();
312  _funcValPtr->setVal(val);
313 
314  // Update the estimated integral and maximum value. Increase our
315  // maximum estimate slightly to give a safety margin with a
316  // corresponding loss of efficiency.
317  if(val > _maxFuncVal) _maxFuncVal= 1.05*val;
318  _funcSum+= val;
319 
320  // fill a new entry in our cache dataset for this point
321  _cache->fill();
322  _totalEvents++;
323 
324  if (_verbose &&_totalEvents%10000==0) {
325  cerr << "RooAcceptReject: generated " << _totalEvents << " events so far." << endl ;
326  }
327 
328 }
329 
330 Double_t RooAcceptReject::getFuncMax()
331 {
332  // Empirically determine maximum value of function by taking a large number
333  // of samples. The actual number depends on the number of dimensions in which
334  // the sampling occurs
335 
336  // Generate the minimum required number of samples for a reliable maximum estimate
337  while(_totalEvents < _minTrials) {
338  addEventToCache();
339 
340  // Limit cache size to 1M events
341  if (_cache->numEntries()>1000000) {
342  coutI(Generation) << "RooAcceptReject::getFuncMax: resetting event cache" << endl ;
343  _cache->reset() ;
344  _eventsUsed = 0 ;
345  }
346  }
347 
348  return _maxFuncVal ;
349 }
350