Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RooFFTConvPdf.cxx
Go to the documentation of this file.
1
2 /*****************************************************************************
3 * Project: RooFit *
4 * *
5 * Copyright (c) 2000-2005, Regents of the University of California *
6 * and Stanford University. All rights reserved. *
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/// \class RooFFTConvPdf
15/// \ingroup Roofitcore
16///
17/// This class implements a generic one-dimensional numeric convolution of two PDFs,
18/// and can convolve any two RooAbsPdfs. The class exploits the convolution theorem
19/// \f[
20/// f(x) * g(x) \rightarrow F(k_i) \cdot G(k_i)
21/// \f]
22/// to calculate the convolution by calculating a Real->Complex FFT of both input PDFs,
23/// multiplying the complex coefficients and performing the reverse Complex->Real FFT
24/// to get the result in the input space. This class uses the ROOT FFT interface to
25/// the (free) FFTW3 package (www.fftw.org), and requires that your ROOT installation is
26/// compiled with the `fftw3=ON` (default). Instructions for manually installing fftw below.
27///
28/// Note that the performance in terms of speed and stability of RooFFTConvPdf is
29/// vastly superior to that of RooNumConvPdf.
30///
31/// An important feature of FFT convolutions is that the observable is assumed to be
32/// cyclical. This is correct for cyclical observables such as angles,
33/// but does not hold in general. For non-cyclical variables, wrap-around artifacts may be
34/// encountered, *e.g.* if the PDF is zero at xMin and non-zero at xMax. A rising tail may appear at xMin.
35/// This is inevitable when using FFTs. A distribution with 3 bins therefore looks like:
36/// ```
37/// ... 0 1 2 0 1 2 0 1 2 ...
38/// ```
39///
40/// Therefore, if bins 0 and 2 are not equal, the FFT sees a cyclical function with a step at the 2|0 boundary, which causes
41/// artifacts in Fourier space.
42///
43/// The spillover or discontinuity can be reduced or eliminated by
44/// introducing a buffer zone in the FFT calculation. If this feature is activated (on by default),
45/// the sampling array for the FFT calculation is extended in both directions,
46/// and padded with the lowest/highest bin.
47/// Example:
48/// ```
49/// original: -5 -4 -3 -2 -1 0 +1 +2 +3 +4 +5
50/// add buffer zones: U U -5 -4 -3 -2 -1 0 +1 +2 +3 +4 +5 O O
51/// rotate: 0 +1 +2 +3 +4 +5 O O U U -5 -4 -3 -2 -1
52/// ```
53/// The buffer bins are stripped away when the FFT output values
54/// are transferred back to the p.d.f cache. The default buffer size is 10% of the
55/// observable domain size, and can be changed with the `setBufferFraction()` member function.
56///
57/// The RooFFTConvPdf uses caching inherited from a RooAbsCachedPdf. If it is
58/// evaluated for a particular value of x, the FFT and convolution is calculated
59/// for all bins in the observable space for the given choice of parameters,
60/// which are also stored in the cache. Subsequent evaluations for different values of the convolution observable and
61/// identical parameters will be retrieved from the cache. If one or more
62/// of the parameters change, the cache will be updated, *i.e.*, a new FFT runs.
63///
64/// The sampling density of the FFT is controlled by the binning of the
65/// the convolution observable, which can be changed using RooRealVar::setBins(N).
66/// For good results, N should be large (>=1000). Additional interpolation
67/// between the bins may improve the result if coarse binnings are chosen. These can be
68/// activated in the constructor or by calling `setInterpolationOrder()`.
69/// For N >> 1000, interpolation will not substantially improve the accuracy.
70///
71/// Additionial information on caching can be displayed by monitoring
72/// the message stream with topic "Caching" at the INFO level, *i.e.*
73/// by calling `RooMsgService::instance().addStream(RooMsgService::INFO,Topic("Caching"))`
74/// to see these message on stdout.
75///
76/// Multi-dimensional convolutions are not supported at the moment.
77///
78/// ---
79///
80/// Installing an external version of FFTW on Linux and compiling ROOT to use it
81/// -------
82///
83/// You have two options:
84/// * **Recommended**: ROOT can automatically install FFTW for itself, see `builtin_fftw3` at https://root.cern.ch/building-root
85/// * Install FFTW and let ROOT discover it. `fftw3` is on by default (see https://root.cern.ch/building-root)
86///
87/// 1) Go to www.fftw.org and download the latest stable version (a .tar.gz file)
88///
89/// If you have root access to your machine and want to make a system installation of FFTW
90///
91/// 2) Untar fftw-XXX.tar.gz in /tmp, cd into the untarred directory
92/// and type './configure' followed by 'make install'.
93/// This will install fftw in /usr/local/bin,lib etc...
94///
95/// 3) Start from a source installation of ROOT. ROOT should discover it. See https://root.cern.ch/building-root
96///
97///
98/// If you do not have root access and want to make a private installation of FFTW
99///
100/// 2) Make a private install area for FFTW, e.g. /home/myself/fftw
101///
102/// 3) Untar fftw-XXX.tar.gz in /tmp, cd into the untarred directory
103/// and type './configure --prefix=/home/myself/fftw' followed by 'make install'.
104/// Substitute /home/myself/fftw with a directory of your choice. This
105/// procedure will install FFTW in the location designated by you
106///
107/// 4) Start from a source installation of ROOT.
108/// Look up and set the proper paths for ROOT to discover FFTW. See https://root.cern.ch/building-root
109///
110
111#include "RooFFTConvPdf.h"
112
113#include "RooAbsReal.h"
114#include "RooMsgService.h"
115#include "RooDataHist.h"
116#include "RooHistPdf.h"
117#include "RooRealVar.h"
118#include "RooGenContext.h"
119#include "RooConvGenContext.h"
120#include "RooBinning.h"
121#include "RooLinearVar.h"
122#include "RooCustomizer.h"
123#include "RooGlobalFunc.h"
124#include "RooConstVar.h"
125#include "RooUniformBinning.h"
126
127#include "TClass.h"
128#include "TComplex.h"
129#include "TVirtualFFT.h"
130
131#include <iostream>
132#include <stdexcept>
133
134#ifndef ROOFIT_MATH_FFTW3
135#include "TInterpreter.h"
136
137namespace {
138
139auto declareDoFFT()
140{
141 static void (*doFFT)(int, double *, double *, double *) = nullptr;
142
143 if (doFFT)
144 return doFFT;
145
146 bool success = gInterpreter->Declare("#include \"fftw3.h\"");
147
148 if (!success) {
149 std::stringstream ss;
150 ss << "RooFFTConvPdf evaluation Failed! The interpreter could not find the fftw3.h header.\n";
151 ss << "You have three options to fix this problem:\n";
152 ss << " 1) Install fftw3 on your system so that the interpreter can find it\n";
153 ss << " 2) In case fftw3.h is installed somewhere else,\n"
154 << " tell ROOT with gInterpreter->AddIncludePath() where to find it\n";
155 ss << " 3) Compile ROOT with the -Dfftw3=ON in the CMake configuration,\n"
156 << " such that ROOT comes with built-in fftw3 convolution routines\n";
157 oocoutE(nullptr, Eval) << ss.str() << std::endl;
158 throw std::runtime_error("RooFFTConvPdf evaluation Failed! The interpreter could not find the fftw3.h header");
159 }
160
161 gInterpreter->Declare(R"(
162void RooFFTConvPdf_doFFT(int n, double *input1, double *input2, double *output)
163{
164 auto fftr2c1_Out = reinterpret_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex) * (n / 2 + 1)));
165 auto fftr2c2_Out = reinterpret_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex) * (n / 2 + 1)));
166 auto fftc2r_In = reinterpret_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex) * (n / 2 + 1)));
167
168 fftw_plan fftr2c1_plan = fftw_plan_dft_r2c(1, &n, input1, fftr2c1_Out, FFTW_ESTIMATE);
169 fftw_plan fftr2c2_plan = fftw_plan_dft_r2c(1, &n, input2, fftr2c2_Out, FFTW_ESTIMATE);
170 fftw_plan fftc2r_plan = fftw_plan_dft_c2r(1, &n, fftc2r_In, output, FFTW_ESTIMATE);
171
172 // Real->Complex FFT Transform on p.d.f. samplings
173 fftw_execute(fftr2c1_plan);
174 fftw_execute(fftr2c2_plan);
175
176 // Loop over first half +1 of complex output results, multiply
177 // and set as input of reverse transform
178 for (Int_t i = 0; i < n / 2 + 1; i++) {
179 double re1 = fftr2c1_Out[i][0];
180 double re2 = fftr2c2_Out[i][0];
181 double im1 = fftr2c1_Out[i][1];
182 double im2 = fftr2c2_Out[i][1];
183 fftc2r_In[i][0] = re1 * re2 - im1 * im2;
184 fftc2r_In[i][1] = re1 * im2 + re2 * im1;
185 }
186
187 // Reverse Complex->Real FFT transform product
188 fftw_execute(fftc2r_plan);
189
190 fftw_destroy_plan(fftr2c1_plan);
191 fftw_destroy_plan(fftr2c2_plan);
192 fftw_destroy_plan(fftc2r_plan);
193
194 fftw_free(fftr2c1_Out);
195 fftw_free(fftr2c2_Out);
196 fftw_free(fftc2r_In);
197}
198)");
199
200 doFFT = reinterpret_cast<void(*)(int, double*, double*, double*)>(gInterpreter->ProcessLine("RooFFTConvPdf_doFFT;"));
201 return doFFT;
202}
203
204} // namespace
205
206#endif
207
208using namespace std;
209
211
212////////////////////////////////////////////////////////////////////////////////
213/// Constructor for numerical (FFT) convolution of PDFs.
214/// \param[in] name Name of this PDF
215/// \param[in] title Title for plotting this PDF
216/// \param[in] convVar Observable to convolve the PDFs in \attention Use a high number of bins (>= 1000) for good accuracy.
217/// \param[in] pdf1 First PDF to be convolved
218/// \param[in] pdf2 Second PDF to be convolved
219/// \param[in] ipOrder Order for interpolation between bins (since FFT is discrete)
220/// The binning used for the FFT sampling is controlled by the binning named "cache" in the convolution observable `convVar`.
221/// If such a binning is not set, the same number of bins as for `convVar` will be used.
222
223RooFFTConvPdf::RooFFTConvPdf(const char *name, const char *title, RooRealVar& convVar, RooAbsPdf& pdf1, RooAbsPdf& pdf2, Int_t ipOrder) :
224 RooAbsCachedPdf(name,title,ipOrder),
225 _x("!x","Convolution Variable",this,convVar),
226 _xprime("!xprime","External Convolution Variable",this,false),
227 _pdf1("!pdf1","pdf1",this,pdf1,false),
228 _pdf2("!pdf2","pdf2",this,pdf2,false),
229 _params("!params","effective parameters",this),
230 _bufFrac(0.1),
231 _bufStrat(Extend),
232 _shift1(0),
233 _shift2(0),
234 _cacheObs("!cacheObs","Cached observables",this,false,false)
235{
236 prepareFFTBinning(convVar);
237
238 _shift2 = (convVar.getMax("cache")+convVar.getMin("cache"))/2 ;
239
240 calcParams() ;
241
242}
243
244////////////////////////////////////////////////////////////////////////////////
245/// \copydoc RooFFTConvPdf(const char*, const char*, RooRealVar&, RooAbsPdf&, RooAbsPdf&, Int_t)
246/// \param[in] pdfConvVar If the variable used for convolution is a PDF, itself, pass the PDF here, and pass the convolution variable to
247/// `convVar`. See also rf210_angularconv.C in the <a href="https://root.cern.ch/root/html/tutorials/roofit/index.html.">roofit tutorials</a>
248
249RooFFTConvPdf::RooFFTConvPdf(const char *name, const char *title, RooAbsReal& pdfConvVar, RooRealVar& convVar, RooAbsPdf& pdf1, RooAbsPdf& pdf2, Int_t ipOrder) :
250 RooAbsCachedPdf(name,title,ipOrder),
251 _x("!x","Convolution Variable",this,convVar,false,false),
252 _xprime("!xprime","External Convolution Variable",this,pdfConvVar),
253 _pdf1("!pdf1","pdf1",this,pdf1,false),
254 _pdf2("!pdf2","pdf2",this,pdf2,false),
255 _params("!params","effective parameters",this),
256 _bufFrac(0.1),
257 _bufStrat(Extend),
258 _shift1(0),
259 _shift2(0),
260 _cacheObs("!cacheObs","Cached observables",this,false,false)
261{
262 prepareFFTBinning(convVar);
263
264 _shift2 = (convVar.getMax("cache")+convVar.getMin("cache"))/2 ;
265
266 calcParams() ;
267}
268
269
270
271////////////////////////////////////////////////////////////////////////////////
272/// Copy constructor
273
275 RooAbsCachedPdf(other,name),
276 _x("!x",this,other._x),
277 _xprime("!xprime",this,other._xprime),
278 _pdf1("!pdf1",this,other._pdf1),
279 _pdf2("!pdf2",this,other._pdf2),
280 _params("!params",this,other._params),
281 _bufFrac(other._bufFrac),
282 _bufStrat(other._bufStrat),
283 _shift1(other._shift1),
284 _shift2(other._shift2),
285 _cacheObs("!cacheObs",this,other._cacheObs)
286 {
287 }
288
289
290
291////////////////////////////////////////////////////////////////////////////////
292/// Destructor
293
295{
296}
297
298
299////////////////////////////////////////////////////////////////////////////////
300/// Try to improve the binning and inform user if possible.
301/// With a 10% buffer fraction, 930 raw bins yield 1024 FFT bins,
302/// a sweet spot for the speed of FFTW.
303
305 if (!convVar.hasBinning("cache")) {
306 const RooAbsBinning& varBinning = convVar.getBinning();
307 const int optimal = static_cast<Int_t>(1024/(1.+_bufFrac));
308
309 //Can improve precision if binning is uniform
310 if (varBinning.numBins() < optimal && varBinning.isUniform()) {
311 coutI(Caching) << "Changing internal binning of variable '" << convVar.GetName()
312 << "' in FFT '" << fName << "'"
313 << " from " << varBinning.numBins()
314 << " to " << optimal << " to improve the precision of the numerical FFT."
315 << " This can be done manually by setting an additional binning named 'cache'." << std::endl;
316 convVar.setBinning(RooUniformBinning(varBinning.lowBound(), varBinning.highBound(), optimal, "cache"), "cache");
317 } else {
318 coutE(Caching) << "The internal binning of variable " << convVar.GetName()
319 << " is not uniform. The numerical FFT will likely yield wrong results." << std::endl;
320 convVar.setBinning(varBinning, "cache");
321 }
322 }
323}
324
325
326////////////////////////////////////////////////////////////////////////////////
327/// Return base name component for cache components in this case 'PDF1_CONV_PDF2'
328
330{
331 static TString name ;
332 name = _pdf1.arg().GetName() ;
333 name.Append("_CONV_") ;
334 name.Append(_pdf2.arg().GetName()) ;
335 return name.Data() ;
336}
337
338
339
340
341////////////////////////////////////////////////////////////////////////////////
342/// Return specialized cache subclass for FFT calculations
343
345{
346 return new FFTCacheElem(*this,nset) ;
347}
348
349
350
351
352////////////////////////////////////////////////////////////////////////////////
353/// Clone input pdf and attach to dataset
354
356 PdfCacheElem(self,nsetIn)
357{
358 RooAbsPdf* clonePdf1 = (RooAbsPdf*) self._pdf1.arg().cloneTree() ;
359 RooAbsPdf* clonePdf2 = (RooAbsPdf*) self._pdf2.arg().cloneTree() ;
360 clonePdf1->attachDataSet(*hist()) ;
361 clonePdf2->attachDataSet(*hist()) ;
362
363 // Shift observable
364 RooRealVar* convObs = (RooRealVar*) hist()->get()->find(self._x.arg().GetName()) ;
365
366 // Install FFT reference range
367 string refName = Form("refrange_fft_%s",self.GetName()) ;
368 convObs->setRange(refName.c_str(),convObs->getMin(),convObs->getMax()) ;
369
370 if (self._shift1!=0) {
371 RooLinearVar* shiftObs1 = new RooLinearVar(Form("%s_shifted_FFTBuffer1",convObs->GetName()),"shiftObs1",
372 *convObs,RooFit::RooConst(1),RooFit::RooConst(-1*self._shift1)) ;
373
374 RooArgSet clonedBranches1 ;
375 RooCustomizer cust(*clonePdf1,"fft") ;
376 cust.replaceArg(*convObs,*shiftObs1) ;
377
378 pdf1Clone.reset(static_cast<RooAbsPdf*>(cust.build()));
379
380 pdf1Clone->addOwnedComponents(*shiftObs1) ;
381 pdf1Clone->addOwnedComponents(*clonePdf1) ;
382
383 } else {
384 pdf1Clone.reset(clonePdf1) ;
385 }
386
387 if (self._shift2!=0) {
388 RooLinearVar* shiftObs2 = new RooLinearVar(Form("%s_shifted_FFTBuffer2",convObs->GetName()),"shiftObs2",
389 *convObs,RooFit::RooConst(1),RooFit::RooConst(-1*self._shift2)) ;
390
391 RooArgSet clonedBranches2 ;
392 RooCustomizer cust(*clonePdf2,"fft") ;
393 cust.replaceArg(*convObs,*shiftObs2) ;
394
395 pdf1Clone->addOwnedComponents(*shiftObs2) ;
396 pdf1Clone->addOwnedComponents(*clonePdf2) ;
397
398 pdf2Clone.reset(static_cast<RooAbsPdf*>(cust.build()));
399
400 } else {
401 pdf2Clone.reset(clonePdf2) ;
402 }
403
404
405 // Attach cloned pdf to all original parameters of self
406 RooArgSet convObsSet{*convObs};
407 RooArgSet fftParams;
408 self.getParameters(&convObsSet, fftParams) ;
409
410 // Remove all cache histogram from fftParams as these
411 // observable need to remain attached to the histogram
412 fftParams.remove(*hist()->get(),true,true) ;
413
414 pdf1Clone->recursiveRedirectServers(fftParams) ;
415 pdf2Clone->recursiveRedirectServers(fftParams) ;
416 pdf1Clone->fixAddCoefRange(refName.c_str(), true) ;
417 pdf2Clone->fixAddCoefRange(refName.c_str(), true) ;
418
419 // Ensure that coefficients for Add PDFs are only interpreted with respect to the convolution observable
420 RooArgSet convSet(self._x.arg());
421 pdf1Clone->fixAddCoefNormalization(convSet, true);
422 pdf2Clone->fixAddCoefNormalization(convSet, true);
423
424 // Save copy of original histX binning and make alternate binning
425 // for extended range scanning
426
427 const Int_t N = convObs->numBins();
428 if (N < 900) {
429 oocoutW(&self, Eval) << "The FFT convolution '" << self.GetName() << "' will run with " << N
430 << " bins. A decent accuracy for difficult convolutions is typically only reached with n >= 1000. Suggest to increase the number"
431 " of bins of the observable '" << convObs->GetName() << "'." << std::endl;
432 }
433 Int_t Nbuf = static_cast<Int_t>((N*self.bufferFraction())/2 + 0.5) ;
434 double obw = (convObs->getMax() - convObs->getMin())/N ;
435 Int_t N2 = N+2*Nbuf ;
436
437 scanBinning = std::make_unique<RooUniformBinning>(convObs->getMin()-Nbuf*obw,convObs->getMax()+Nbuf*obw,N2);
438 histBinning.reset(convObs->getBinning().clone());
439
440 // Deactivate dirty state propagation on datahist observables
441 // and set all nodes on both pdfs to operMode AlwaysDirty
442 hist()->setDirtyProp(false) ;
443 convObs->setOperMode(ADirty,true) ;
444}
445
446
447////////////////////////////////////////////////////////////////////////////////
448/// Suffix for cache histogram (added in addition to suffix for cache name)
449
451{
452 return TString(Form("_BufFrac%3.1f_BufStrat%d",_bufFrac,_bufStrat)) ;
453}
454
455
456
457////////////////////////////////////////////////////////////////////////////////
458/// Returns all RooAbsArg objects contained in the cache element
459
461{
462 RooArgList ret(PdfCacheElem::containedArgs(a)) ;
463
464 ret.add(*pdf1Clone) ;
465 ret.add(*pdf2Clone) ;
466 if (pdf1Clone->ownedComponents()) {
467 ret.add(*pdf1Clone->ownedComponents()) ;
468 }
469 if (pdf2Clone->ownedComponents()) {
470 ret.add(*pdf2Clone->ownedComponents()) ;
471 }
472
473 return ret ;
474}
475
476
477
478////////////////////////////////////////////////////////////////////////////////
479/// Fill the contents of the cache the FFT convolution output
480
482{
483 RooDataHist& cacheHist = *cache.hist() ;
484
485 ((FFTCacheElem&)cache).pdf1Clone->setOperMode(ADirty,true) ;
486 ((FFTCacheElem&)cache).pdf2Clone->setOperMode(ADirty,true) ;
487
488 // Determine if there other observables than the convolution observable in the cache
489 RooArgSet otherObs ;
490 RooArgSet(*cacheHist.get()).snapshot(otherObs) ;
491
492 RooAbsArg* histArg = otherObs.find(_x.arg().GetName()) ;
493 if (histArg) {
494 otherObs.remove(*histArg,true,true) ;
495 }
496
497 //cout << "RooFFTConvPdf::fillCacheObject() otherObs = " << otherObs << endl ;
498
499 // Handle trivial scenario -- no other observables
500 if (otherObs.empty()) {
502 return ;
503 }
504
505 // Handle cases where there are other cache slices
506 // Iterator over available slice positions and fill each
507
508 // Determine number of bins for each slice position observable
509 Int_t n = otherObs.getSize() ;
510 std::vector<int> binCur(n+1);
511 std::vector<int> binMax(n+1);
512 Int_t curObs = 0 ;
513
514 std::vector<RooAbsLValue*> obsLV(n);
515 Int_t i(0) ;
516 for (auto const& arg : otherObs) {
517 RooAbsLValue* lvarg = dynamic_cast<RooAbsLValue*>(arg) ;
518 obsLV[i] = lvarg ;
519 binCur[i] = 0 ;
520 // coverity[FORWARD_NULL]
521 binMax[i] = lvarg->numBins(binningName())-1 ;
522 i++ ;
523 }
524
525 bool loop(true) ;
526 while(loop) {
527 // Set current slice position
528 for (Int_t j=0 ; j<n ; j++) { obsLV[j]->setBin(binCur[j],binningName()) ; }
529
530// cout << "filling slice: bin of obsLV[0] = " << obsLV[0]->getBin() << endl ;
531
532 // Fill current slice
533 fillCacheSlice((FFTCacheElem&)cache,otherObs) ;
534
535 // Determine which iterator to increment
536 while(binCur[curObs]==binMax[curObs]) {
537
538 // Reset current iterator and consider next iterator ;
539 binCur[curObs]=0 ;
540 curObs++ ;
541
542 // master termination condition
543 if (curObs==n) {
544 loop=false ;
545 break ;
546 }
547 }
548
549 // Increment current iterator
550 binCur[curObs]++ ;
551 curObs=0 ;
552
553 }
554
555}
556
557
558////////////////////////////////////////////////////////////////////////////////
559/// Fill a slice of cachePdf with the output of the FFT convolution calculation
560
561void RooFFTConvPdf::fillCacheSlice(FFTCacheElem& aux, const RooArgSet& slicePos) const
562{
563 // Extract histogram that is the basis of the RooHistPdf
564 RooDataHist& cacheHist = *aux.hist() ;
565
566 // Sample array of input points from both pdfs
567 // Note that returned arrays have optional buffers zones below and above range ends
568 // to reduce cyclical effects and have been cyclically rotated so that bin containing
569 // zero value is at position zero. Example:
570 //
571 // original: -5 -4 -3 -2 -1 0 +1 +2 +3 +4 +5
572 // add buffer zones: U U -5 -4 -3 -2 -1 0 +1 +2 +3 +4 +5 O O
573 // rotate: 0 +1 +2 +3 +4 +5 O O U U -5 -4 -3 -2 -1
574 //
575 //
576
577 Int_t N,N2,binShift1,binShift2 ;
578
579 RooRealVar* histX = (RooRealVar*) cacheHist.get()->find(_x.arg().GetName()) ;
580 if (_bufStrat==Extend) histX->setBinning(*aux.scanBinning) ;
581 std::vector<double> input1 = scanPdf((RooRealVar&)_x.arg(),*aux.pdf1Clone,cacheHist,slicePos,N,N2,binShift1,_shift1) ;
582 std::vector<double> input2 = scanPdf((RooRealVar&)_x.arg(),*aux.pdf2Clone,cacheHist,slicePos,N,N2,binShift2,_shift2) ;
583 if (_bufStrat==Extend) histX->setBinning(*aux.histBinning) ;
584
585#ifndef ROOFIT_MATH_FFTW3
586 // If ROOT was NOT built with the fftw3 interface, we try to include fftw3.h
587 // with the interpreter and run the concolution in the interpreter.
588 std::vector<double> output(N2);
589
590 auto doFFT = declareDoFFT();
591 doFFT(N2, input1.data(), input2.data(), output.data());
592#else
593 // If ROOT was built with the fftw3 interface, we can use it as a TVirtualFFT
594 // plugin. The advantage here is that nothing can go wrong if fftw3.h wahs
595 // not istalled by the user separately.
596
597 // Retrieve previously defined FFT transformation plans
598 if (!aux.fftr2c1) {
599 aux.fftr2c1.reset(TVirtualFFT::FFT(1, &N2, "R2CK"));
600 aux.fftr2c2.reset(TVirtualFFT::FFT(1, &N2, "R2CK"));
601 aux.fftc2r.reset(TVirtualFFT::FFT(1, &N2, "C2RK"));
602
603 if (aux.fftr2c1 == nullptr || aux.fftr2c2 == nullptr || aux.fftc2r == nullptr) {
604 coutF(Eval) << "RooFFTConvPdf::fillCacheSlice(" << GetName() << "Cannot get a handle to fftw. Maybe ROOT was built without it?" << std::endl;
605 throw std::runtime_error("Cannot get a handle to fftw.");
606 }
607 }
608
609 // Real->Complex FFT Transform on p.d.f. 1 sampling
610 aux.fftr2c1->SetPoints(input1.data());
611 aux.fftr2c1->Transform();
612
613 // Real->Complex FFT Transform on p.d.f 2 sampling
614 aux.fftr2c2->SetPoints(input2.data());
615 aux.fftr2c2->Transform();
616
617 // Loop over first half +1 of complex output results, multiply
618 // and set as input of reverse transform
619 for (Int_t i=0 ; i<N2/2+1 ; i++) {
620 double re1,re2,im1,im2 ;
621 aux.fftr2c1->GetPointComplex(i,re1,im1) ;
622 aux.fftr2c2->GetPointComplex(i,re2,im2) ;
623 double re = re1*re2 - im1*im2 ;
624 double im = re1*im2 + re2*im1 ;
625 TComplex t(re,im) ;
626 aux.fftc2r->SetPointComplex(i,t) ;
627 }
628
629 // Reverse Complex->Real FFT transform product
630 aux.fftc2r->Transform() ;
631#endif
632
633 Int_t totalShift = binShift1 + (N2-N)/2 ;
634
635 // Store FFT result in cache
636
637 std::unique_ptr<TIterator> iter{const_cast<RooDataHist&>(cacheHist).sliceIterator(const_cast<RooAbsReal&>(_x.arg()),slicePos)};
638 for (Int_t i =0 ; i<N ; i++) {
639
640 // Cyclically shift array back so that bin containing zero is back in zeroBin
641 Int_t j = i + totalShift ;
642 while (j<0) j+= N2 ;
643 while (j>=N2) j-= N2 ;
644
645 iter->Next() ;
646#ifndef ROOFIT_MATH_FFTW3
647 cacheHist.set(output[j]);
648#else
649 cacheHist.set(aux.fftc2r->GetPointReal(j));
650#endif
651 }
652}
653
654
655////////////////////////////////////////////////////////////////////////////////
656/// Scan the values of 'pdf' in observable 'obs' using the bin values stored in 'hist' at slice position 'slicePos'
657/// N is filled with the number of bins defined in hist, N2 is filled with N plus the number of buffer bins
658/// The return value is an array of doubles of length N2 with the sampled values. The caller takes ownership
659/// of the array
660
661std::vector<double> RooFFTConvPdf::scanPdf(RooRealVar& obs, RooAbsPdf& pdf, const RooDataHist& hist, const RooArgSet& slicePos,
662 Int_t& N, Int_t& N2, Int_t& zeroBin, double shift) const
663{
664
665 RooRealVar* histX = (RooRealVar*) hist.get()->find(obs.GetName()) ;
666
667 // Calculate number of buffer bins on each size to avoid cyclical flow
668 N = histX->numBins(binningName()) ;
669 Int_t Nbuf = static_cast<Int_t>((N*bufferFraction())/2 + 0.5) ;
670 N2 = N+2*Nbuf ;
671
672
673 // Allocate array of sampling size plus optional buffer zones
674 std::vector<double> array(N2);
675
676 // Set position of non-convolution observable to that of the cache slice that were are processing now
677 hist.get(slicePos) ;
678
679 // Find bin ID that contains zero value
680 zeroBin = 0 ;
681 if (histX->getMax()>=0 && histX->getMin()<=0) {
682 zeroBin = histX->getBinning().binNumber(0) ;
683 } else if (histX->getMin()>0) {
684 double bw = (histX->getMax() - histX->getMin())/N2 ;
685 zeroBin = Int_t(-histX->getMin()/bw) ;
686 } else {
687 double bw = (histX->getMax() - histX->getMin())/N2 ;
688 zeroBin = Int_t(-1*histX->getMax()/bw) ;
689 }
690
691 Int_t binShift = Int_t((N2* shift) / (histX->getMax()-histX->getMin())) ;
692
693 zeroBin += binShift ;
694 while(zeroBin>=N2) zeroBin-= N2 ;
695 while(zeroBin<0) zeroBin+= N2 ;
696
697 // First scan hist into temp array
698 std::vector<double> tmp(N2);
699 Int_t k(0) ;
700 switch(_bufStrat) {
701
702 case Extend:
703 // Sample entire extended range (N2 samples)
704 for (k=0 ; k<N2 ; k++) {
705 histX->setBin(k) ;
706 tmp[k] = pdf.getVal(hist.get()) ;
707 }
708 break ;
709
710 case Flat:
711 // Sample original range (N samples) and fill lower and upper buffer
712 // bins with p.d.f. value at respective boundary
713 {
714 histX->setBin(0) ;
715 double val = pdf.getVal(hist.get()) ;
716 for (k=0 ; k<Nbuf ; k++) {
717 tmp[k] = val ;
718 }
719 for (k=0 ; k<N ; k++) {
720 histX->setBin(k) ;
721 tmp[k+Nbuf] = pdf.getVal(hist.get()) ;
722 }
723 histX->setBin(N-1) ;
724 val = pdf.getVal(hist.get()) ;
725 for (k=0 ; k<Nbuf ; k++) {
726 tmp[N+Nbuf+k] = val ;
727 }
728 }
729 break ;
730
731 case Mirror:
732 // Sample original range (N samples) and fill lower and upper buffer
733 // bins with mirror image of sampled range
734 for (k=0 ; k<N ; k++) {
735 histX->setBin(k) ;
736 tmp[k+Nbuf] = pdf.getVal(hist.get()) ;
737 }
738 for (k=1 ; k<=Nbuf ; k++) {
739 histX->setBin(k) ;
740 tmp[Nbuf-k] = pdf.getVal(hist.get()) ;
741 histX->setBin(N-k) ;
742 tmp[Nbuf+N+k-1] = pdf.getVal(hist.get()) ;
743 }
744 break ;
745 }
746
747 // Scan function and store values in array
748 for (Int_t i=0 ; i<N2 ; i++) {
749 // Cyclically shift writing location by zero bin position
750 Int_t j = i - (zeroBin) ;
751 if (j<0) j+= N2 ;
752 if (j>=N2) j-= N2 ;
753 array[i] = tmp[j] ;
754 }
755
756 return array ;
757}
758
759
760
761////////////////////////////////////////////////////////////////////////////////
762/// Return the observables to be cached given the normalization set nset.
763///
764/// If the cache observable is in nset then this is
765/// - the convolution observable plus
766/// - any member of nset that is either a RooCategory,
767/// - or was previously specified through setCacheObservables().
768///
769/// In case the cache observable is *not* in nset, then it is
770/// - the convolution observable plus
771/// - all member of nset that are observables of this p.d.f.
772///
773
775{
776 // Get complete list of observables
777 auto obs1 = new RooArgSet{};
778 RooArgSet obs2;
779 _pdf1.arg().getObservables(&nset, *obs1) ;
780 _pdf2.arg().getObservables(&nset, obs2) ;
781 obs1->add(obs2, true) ;
782
783 // Check if convolution observable is in nset
784 if (nset.contains(_x.arg())) {
785
786 // Now strip out all non-category observables
787 RooArgSet killList ;
788 for(auto const& arg : *obs1) {
789 if (arg->IsA()->InheritsFrom(RooAbsReal::Class()) && !_cacheObs.find(arg->GetName())) {
790 killList.add(*arg) ;
791 }
792 }
793 obs1->remove(killList) ;
794
795 // And add back the convolution observables
796 obs1->add(_x.arg(),true) ;
797
798 obs1->add(_cacheObs) ;
799
800 } else {
801
802 // If cacheObs was filled, cache only observables in there
803 if (!_cacheObs.empty()) {
804 RooArgSet killList ;
805 for(auto const& arg : *obs1) {
806 if (arg->IsA()->InheritsFrom(RooAbsReal::Class()) && !_cacheObs.find(arg->GetName())) {
807 killList.add(*arg) ;
808 }
809 }
810 obs1->remove(killList) ;
811 }
812
813
814 // Make sure convolution observable is always in there
815 obs1->add(_x.arg(),true) ;
816
817 }
818
819 return RooFit::OwningPtr<RooArgSet>{obs1};
820}
821
822
823
824////////////////////////////////////////////////////////////////////////////////
825/// Return the parameters on which the cache depends given normalization
826/// set nset. For this p.d.f these are the parameters of the input p.d.f.
827/// but never the convolution variable, in case it is not part of nset.
828
830{
831 auto vars = getVariables() ;
832 vars->remove(*std::unique_ptr<RooArgSet>{actualObservables(nset)});
833
834 return RooFit::OwningPtr<RooArgSet>{std::move(vars)};
835}
836
837
838
839////////////////////////////////////////////////////////////////////////////////
840/// Return p.d.f. observable (which can be a function) to substitute given
841/// p.d.f. observable. Substitutes x by xprime if xprime is set.
842
844{
845 if (_xprime.absArg() && string(histObservable.GetName())==_x.absArg()->GetName()) {
846 return (*_xprime.absArg()) ;
847 }
848 return histObservable ;
849}
850
851
852
853////////////////////////////////////////////////////////////////////////////////
854/// Create appropriate generator context for this convolution. If both input p.d.f.s support
855/// internal generation, if it is safe to use them and if no observables other than the convolution
856/// observable are requested for generation, use the specialized convolution generator context
857/// which implements a smearing strategy in the convolution observable. If not return the
858/// regular accept/reject generator context
859
861 const RooArgSet* auxProto, bool verbose) const
862{
863 RooArgSet vars2(vars) ;
864 vars2.remove(_x.arg(),true,true) ;
865 Int_t numAddDep = vars2.getSize() ;
866
867 RooArgSet dummy ;
868 bool pdfCanDir = (((RooAbsPdf&)_pdf1.arg()).getGenerator(_x.arg(),dummy) != 0 && \
870 bool resCanDir = (((RooAbsPdf&)_pdf2.arg()).getGenerator(_x.arg(),dummy) !=0 &&
872
873 if (pdfCanDir) {
874 cxcoutI(Generation) << "RooFFTConvPdf::genContext() input p.d.f " << _pdf1.arg().GetName()
875 << " has internal generator that is safe to use in current context" << endl ;
876 }
877 if (resCanDir) {
878 cxcoutI(Generation) << "RooFFTConvPdf::genContext() input p.d.f. " << _pdf2.arg().GetName()
879 << " has internal generator that is safe to use in current context" << endl ;
880 }
881 if (numAddDep>0) {
882 cxcoutI(Generation) << "RooFFTConvPdf::genContext() generation requested for observables other than the convolution observable " << _x.arg().GetName() << endl ;
883 }
884
885
886 if (numAddDep>0 || !pdfCanDir || !resCanDir) {
887 // Any resolution model with more dependents than the convolution variable
888 // or pdf or resmodel do not support direct generation
889 cxcoutI(Generation) << "RooFFTConvPdf::genContext() selecting accept/reject generator context because one or both of the input "
890 << "p.d.f.s cannot use internal generator and/or "
891 << "observables other than the convolution variable are requested for generation" << endl ;
892 return new RooGenContext(*this,vars,prototype,auxProto,verbose) ;
893 }
894
895 // Any other resolution model: use specialized generator context
896 cxcoutI(Generation) << "RooFFTConvPdf::genContext() selecting specialized convolution generator context as both input "
897 << "p.d.fs are safe for internal generator and only "
898 << "the convolution observables is requested for generation" << endl ;
899 return new RooConvGenContext(*this,vars,prototype,auxProto,verbose) ;
900}
901
902
903
904////////////////////////////////////////////////////////////////////////////////
905/// Change the size of the buffer on either side of the observable range to `frac` times the
906/// size of the range of the convolution observable.
907
909{
910 if (frac<0) {
911 coutE(InputArguments) << "RooFFTConvPdf::setBufferFraction(" << GetName() << ") fraction should be greater than or equal to zero" << endl ;
912 return ;
913 }
914 _bufFrac = frac ;
915
916 // Sterilize the cache as certain partial results depend on buffer fraction
918}
919
920
921////////////////////////////////////////////////////////////////////////////////
922/// Change strategy to fill the overflow buffer on either side of the convolution observable range.
923///
924/// - `Extend` means is that the input p.d.f convolution observable range is widened to include the buffer range
925/// - `Flat` means that the buffer is filled with the p.d.f. value at the boundary of the observable range
926/// - `Mirror` means that the buffer is filled with a mirror image of the p.d.f. around the convolution observable boundary
927///
928/// The default strategy is extend. If one of the input p.d.f.s is a RooAddPdf, it is configured so that the interpretation
929/// range of the fraction coefficients is kept at the nominal convolutions observable range (instead of interpreting coefficients
930/// in the widened range including the buffer).
931
933{
934 _bufStrat = bs ;
935}
936
937
938
939////////////////////////////////////////////////////////////////////////////////
940/// Customized printing of arguments of a RooNumConvPdf to more intuitively reflect the contents of the
941/// product operator construction
942
943void RooFFTConvPdf::printMetaArgs(ostream& os) const
944{
945 os << _pdf1.arg().GetName() << "(" << _x.arg().GetName() << ") (*) " << _pdf2.arg().GetName() << "(" << _x.arg().GetName() << ") " ;
946}
947
948
949
950////////////////////////////////////////////////////////////////////////////////
951/// (Re)calculate effective parameters of this p.d.f.
952
954{
955 RooArgSet argSet{_x.arg()};
956 RooArgSet params1;
957 RooArgSet params2;
958 _pdf1.arg().getParameters(&argSet, params1);
959 _pdf2.arg().getParameters(&argSet, params2);
961 _params.add(params1) ;
962 _params.add(params2,true) ;
963}
#define a(i)
Definition RSha256.hxx:99
#define coutI(a)
#define cxcoutI(a)
#define oocoutW(o, a)
#define coutF(a)
#define oocoutE(o, a)
#define coutE(a)
int Int_t
Definition RtypesCore.h:45
#define ClassImp(name)
Definition Rtypes.h:377
#define N
char name[80]
Definition TGX11.cxx:110
#define gInterpreter
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2467
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:79
void setOperMode(OperMode mode, bool recurseADirty=true)
Set the operation mode of this node.
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...
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.
RooFit::OwningPtr< RooArgSet > getVariables(bool stripDisconnected=true) const
Return RooArgSet with all variables (tree leaf nodes of expression tree)
virtual RooAbsArg * cloneTree(const char *newname=nullptr) const
Clone tree expression of objects.
void attachDataSet(const RooAbsData &set)
Replace server nodes with names matching the dataset variable names with those data set variables,...
RooAbsBinning is the abstract base class for RooRealVar binning definitions.
int binNumber(double x) const
Returns the bin number corresponding to the value x.
Int_t numBins() const
Return number of bins.
virtual bool isUniform() const
virtual double highBound() const =0
virtual double lowBound() const =0
virtual RooAbsBinning * clone(const char *name=nullptr) const =0
Abstract base class for p.d.f.s that need or want to cache their evaluate() output in a RooHistPdf de...
virtual const char * binningName() const
RooObjCacheManager _cacheMgr
virtual bool remove(const RooAbsArg &var, bool silent=false, bool matchByNameOnly=false)
Remove the specified argument from our list.
bool contains(const RooAbsArg &var) const
Check if collection contains an argument with the same name as var.
Int_t getSize() const
Return the number of elements in the collection.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
RooAbsArg * find(const char *name) const
Find object with given name in list.
void setDirtyProp(bool flag)
Control propagation of dirty flags from observables in dataset.
Abstract base class for generator contexts of RooAbsPdf objects.
Abstract base class for objects that are lvalues, i.e.
virtual Int_t numBins(const char *rangeName=nullptr) const =0
Abstract interface for all probability density functions.
Definition RooAbsPdf.h:40
virtual bool isDirectGenSafe(const RooAbsArg &arg) const
Check if given observable can be safely generated using the pdfs internal generator mechanism (if tha...
virtual Int_t getGenerator(const RooArgSet &directVars, RooArgSet &generateVars, bool staticInitOK=true) const
Load generatedVars with the subset of directVars that we can generate events for, and return a code t...
Int_t numBins(const char *rangeName=nullptr) const override
virtual double getMax(const char *name=nullptr) const
Get maximum of currently defined range.
void setBin(Int_t ibin, const char *rangeName=nullptr) override
Set value to center of bin 'ibin' of binning 'rangeName' (or of default binning if no range is specif...
virtual double getMin(const char *name=nullptr) const
Get minimum of currently defined range.
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:59
double getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
Definition RooAbsReal.h:103
static TClass * Class()
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooAbsArg * absArg() const
Return pointer to contained argument.
Definition RooArgProxy.h:46
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:55
RooArgSet * snapshot(bool deepCopy=true) const
Use RooAbsCollection::snapshot(), but return as RooArgSet.
Definition RooArgSet.h:178
void removeAll() override
Remove all argument inset using remove(const RooAbsArg&).
bool add(const RooAbsArg &var, bool valueServer, bool shapeServer, bool silent)
Overloaded RooCollection_t::add() method insert object into set and registers object as server to own...
RooCustomizer is a factory class to produce clones of a prototype composite PDF object with the same ...
void replaceArg(const RooAbsArg &orig, const RooAbsArg &subst)
Replace any occurrence of arg 'orig' with arg 'subst'.
RooAbsArg * build(const char *masterCatState, bool verbose=false)
Build a clone of the prototype executing all registered 'replace' rules and 'split' rules for the mas...
The RooDataHist is a container class to hold N-dimensional binned data.
Definition RooDataHist.h:39
void set(std::size_t binNumber, double weight, double wgtErr)
Set bin content of bin that was last loaded with get(std::size_t).
const RooArgSet * get() const override
Get bin centre of current bin.
Definition RooDataHist.h:76
RooDataSet is a container class to hold unbinned data.
Definition RooDataSet.h:57
std::unique_ptr< TVirtualFFT > fftr2c2
std::unique_ptr< TVirtualFFT > fftc2r
RooArgList containedArgs(Action) override
Returns all RooAbsArg objects contained in the cache element.
std::unique_ptr< RooAbsPdf > pdf2Clone
FFTCacheElem(const RooFFTConvPdf &self, const RooArgSet *nset)
Clone input pdf and attach to dataset.
std::unique_ptr< RooAbsBinning > histBinning
std::unique_ptr< RooAbsBinning > scanBinning
std::unique_ptr< RooAbsPdf > pdf1Clone
std::unique_ptr< TVirtualFFT > fftr2c1
PDF for the numerical (FFT) convolution of two PDFs.
friend class RooConvGenContext
RooSetProxy _params
Effective parameters of this p.d.f.
BufStrat _bufStrat
void calcParams()
(Re)calculate effective parameters of this p.d.f.
void setBufferFraction(double frac)
Change the size of the buffer on either side of the observable range to frac times the size of the ra...
double bufferFraction() const
Return value of buffer fraction applied in FFT calculation array beyond either end of the observable ...
TString histNameSuffix() const override
Suffix for cache histogram (added in addition to suffix for cache name)
void prepareFFTBinning(RooRealVar &convVar) const
Try to improve the binning and inform user if possible.
void fillCacheSlice(FFTCacheElem &cache, const RooArgSet &slicePosition) const
Fill a slice of cachePdf with the output of the FFT convolution calculation.
RooRealProxy _xprime
Input function representing value of convolution observable.
std::vector< double > scanPdf(RooRealVar &obs, RooAbsPdf &pdf, const RooDataHist &hist, const RooArgSet &slicePos, Int_t &N, Int_t &N2, Int_t &zeroBin, double shift) const
Scan the values of 'pdf' in observable 'obs' using the bin values stored in 'hist' at slice position ...
RooRealProxy _pdf1
First input p.d.f.
RooRealProxy _x
Convolution observable.
const char * inputBaseName() const override
Return base name component for cache components in this case 'PDF1_CONV_PDF2'.
RooAbsArg & pdfObservable(RooAbsArg &histObservable) const override
Return p.d.f.
~RooFFTConvPdf() override
Destructor.
RooFit::OwningPtr< RooArgSet > actualParameters(const RooArgSet &nset) const override
Return the parameters on which the cache depends given normalization set nset.
RooRealProxy _pdf2
Second input p.d.f.
void printMetaArgs(std::ostream &os) const override
Customized printing of arguments of a RooNumConvPdf to more intuitively reflect the contents of the p...
void setBufferStrategy(BufStrat bs)
Change strategy to fill the overflow buffer on either side of the convolution observable range.
PdfCacheElem * createCache(const RooArgSet *nset) const override
Return specialized cache subclass for FFT calculations.
RooAbsGenContext * genContext(const RooArgSet &vars, const RooDataSet *prototype=nullptr, const RooArgSet *auxProto=nullptr, bool verbose=false) const override
Create appropriate generator context for this convolution.
RooFit::OwningPtr< RooArgSet > actualObservables(const RooArgSet &nset) const override
Return the observables to be cached given the normalization set nset.
void fillCacheObject(PdfCacheElem &cache) const override
Fill the contents of the cache the FFT convolution output.
RooSetProxy _cacheObs
Non-convolution observables that are also cached.
Class RooGenContext implement a universal generator context for all RooAbsPdf classes that do not hav...
RooLinearVar is the most general form of a derived real-valued object that can be used by RooRealInte...
void sterilize() override
Clear the cache payload but retain slot mapping w.r.t to normalization and integration sets.
RooRealVar represents a variable that can be changed from the outside.
Definition RooRealVar.h:37
bool hasBinning(const char *name) const override
Returns true if variable has a binning named 'name'.
const RooAbsBinning & getBinning(const char *name=nullptr, bool verbose=true, bool createOnTheFly=false) const override
Return binning definition with name.
void setBinning(const RooAbsBinning &binning, const char *name=nullptr)
Add given binning under name 'name' with this variable.
void setRange(const char *name, double min, double max)
Set a fit or plotting range.
const T & arg() const
Return reference to object held in proxy.
RooUniformBinning is an implementation of RooAbsBinning that provides a uniform binning in 'n' bins b...
const char * GetName() const override
Returns name of object.
Definition TNamed.h:47
TString fName
Definition TNamed.h:32
Basic string class.
Definition TString.h:139
static TVirtualFFT * FFT(Int_t ndim, Int_t *n, Option_t *option)
Returns a pointer to the FFT of requested size and type.
RooConstVar & RooConst(double val)
const Int_t n
Definition legend1.C:16
void(off) SmallVectorTemplateBase< T
T * OwningPtr
An alias for raw pointers for indicating that the return type of a RooFit function is an owning point...
Definition Config.h:43
static void output()