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/building-root
85/// * Install FFTW and let ROOT discover it. `fftw3` is on by default (see https://root.cern/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/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/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,
224 Int_t ipOrder)
225 : RooAbsCachedPdf(name, title, ipOrder),
226 _x("!x", "Convolution Variable", this, convVar),
227 _xprime("!xprime", "External Convolution Variable", this, false),
228 _pdf1("!pdf1", "pdf1", this, pdf1, false),
229 _pdf2("!pdf2", "pdf2", this, pdf2, false),
230 _params("!params", "effective parameters", this),
231 _bufFrac(0.1),
232 _bufStrat(Extend),
233 _shift1(0),
234 _shift2((convVar.getMax("cache") + convVar.getMin("cache")) / 2),
235 _cacheObs("!cacheObs", "Cached observables", this, false, false)
236{
237 prepareFFTBinning(convVar);
238
239 calcParams() ;
240
241}
242
243////////////////////////////////////////////////////////////////////////////////
244/// \copydoc RooFFTConvPdf(const char*, const char*, RooRealVar&, RooAbsPdf&, RooAbsPdf&, Int_t)
245/// \param[in] pdfConvVar If the variable used for convolution is a PDF, itself, pass the PDF here, and pass the convolution variable to
246/// `convVar`. See also rf210_angularconv.C in the <a href="https://root.cern/root/html/tutorials/roofit/index.html.">roofit tutorials</a>
247
248RooFFTConvPdf::RooFFTConvPdf(const char *name, const char *title, RooAbsReal &pdfConvVar, RooRealVar &convVar,
249 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((convVar.getMax("cache") + convVar.getMin("cache")) / 2),
260 _cacheObs("!cacheObs", "Cached observables", this, false, false)
261{
262 prepareFFTBinning(convVar);
263
264 calcParams() ;
265}
266
267
268
269////////////////////////////////////////////////////////////////////////////////
270/// Copy constructor
271
273 RooAbsCachedPdf(other,name),
274 _x("!x",this,other._x),
275 _xprime("!xprime",this,other._xprime),
276 _pdf1("!pdf1",this,other._pdf1),
277 _pdf2("!pdf2",this,other._pdf2),
278 _params("!params",this,other._params),
279 _bufFrac(other._bufFrac),
280 _bufStrat(other._bufStrat),
281 _shift1(other._shift1),
282 _shift2(other._shift2),
283 _cacheObs("!cacheObs",this,other._cacheObs)
284 {
285 }
286
287
288
289////////////////////////////////////////////////////////////////////////////////
290/// Destructor
291
293{
294}
295
296
297////////////////////////////////////////////////////////////////////////////////
298/// Try to improve the binning and inform user if possible.
299/// With a 10% buffer fraction, 930 raw bins yield 1024 FFT bins,
300/// a sweet spot for the speed of FFTW.
301
303 if (!convVar.hasBinning("cache")) {
304 const RooAbsBinning& varBinning = convVar.getBinning();
305 const int optimal = static_cast<Int_t>(1024/(1.+_bufFrac));
306
307 //Can improve precision if binning is uniform
308 if (varBinning.numBins() < optimal && varBinning.isUniform()) {
309 coutI(Caching) << "Changing internal binning of variable '" << convVar.GetName()
310 << "' in FFT '" << fName << "'"
311 << " from " << varBinning.numBins()
312 << " to " << optimal << " to improve the precision of the numerical FFT."
313 << " This can be done manually by setting an additional binning named 'cache'." << std::endl;
314 convVar.setBinning(RooUniformBinning(varBinning.lowBound(), varBinning.highBound(), optimal, "cache"), "cache");
315 } else {
316 coutE(Caching) << "The internal binning of variable " << convVar.GetName()
317 << " is not uniform. The numerical FFT will likely yield wrong results." << std::endl;
318 convVar.setBinning(varBinning, "cache");
319 }
320 }
321}
322
323
324////////////////////////////////////////////////////////////////////////////////
325/// Return base name component for cache components in this case 'PDF1_CONV_PDF2'
326
328{
329 static TString name ;
330 name = _pdf1.arg().GetName() ;
331 name.Append("_CONV_") ;
332 name.Append(_pdf2.arg().GetName()) ;
333 return name.Data() ;
334}
335
336
337
338
339////////////////////////////////////////////////////////////////////////////////
340/// Return specialized cache subclass for FFT calculations
341
343{
344 return new FFTCacheElem(*this,nset) ;
345}
346
347
348
349
350////////////////////////////////////////////////////////////////////////////////
351/// Clone input pdf and attach to dataset
352
354 PdfCacheElem(self,nsetIn)
355{
356 RooAbsPdf* clonePdf1 = static_cast<RooAbsPdf*>(self._pdf1.arg().cloneTree()) ;
357 RooAbsPdf* clonePdf2 = static_cast<RooAbsPdf*>(self._pdf2.arg().cloneTree()) ;
358 clonePdf1->attachDataSet(*hist()) ;
359 clonePdf2->attachDataSet(*hist()) ;
360
361 // Shift observable
362 RooRealVar* convObs = static_cast<RooRealVar*>(hist()->get()->find(self._x.arg().GetName())) ;
363
364 // Install FFT reference range
365 string refName = Form("refrange_fft_%s",self.GetName()) ;
366 convObs->setRange(refName.c_str(),convObs->getMin(),convObs->getMax()) ;
367
368 if (self._shift1!=0) {
369 RooLinearVar* shiftObs1 = new RooLinearVar(Form("%s_shifted_FFTBuffer1",convObs->GetName()),"shiftObs1",
370 *convObs,RooFit::RooConst(1),RooFit::RooConst(-1*self._shift1)) ;
371
372 RooArgSet clonedBranches1 ;
373 RooCustomizer cust(*clonePdf1,"fft") ;
374 cust.replaceArg(*convObs,*shiftObs1) ;
375
376 pdf1Clone.reset(static_cast<RooAbsPdf*>(cust.build()));
377
378 pdf1Clone->addOwnedComponents(*shiftObs1) ;
379 pdf1Clone->addOwnedComponents(*clonePdf1) ;
380
381 } else {
382 pdf1Clone.reset(clonePdf1) ;
383 }
384
385 if (self._shift2!=0) {
386 RooLinearVar* shiftObs2 = new RooLinearVar(Form("%s_shifted_FFTBuffer2",convObs->GetName()),"shiftObs2",
387 *convObs,RooFit::RooConst(1),RooFit::RooConst(-1*self._shift2)) ;
388
389 RooArgSet clonedBranches2 ;
390 RooCustomizer cust(*clonePdf2,"fft") ;
391 cust.replaceArg(*convObs,*shiftObs2) ;
392
393 pdf1Clone->addOwnedComponents(*shiftObs2) ;
394 pdf1Clone->addOwnedComponents(*clonePdf2) ;
395
396 pdf2Clone.reset(static_cast<RooAbsPdf*>(cust.build()));
397
398 } else {
399 pdf2Clone.reset(clonePdf2) ;
400 }
401
402
403 // Attach cloned pdf to all original parameters of self
404 RooArgSet convObsSet{*convObs};
405 RooArgSet fftParams;
406 self.getParameters(&convObsSet, fftParams) ;
407
408 // Remove all cache histogram from fftParams as these
409 // observable need to remain attached to the histogram
410 fftParams.remove(*hist()->get(),true,true) ;
411
412 pdf1Clone->recursiveRedirectServers(fftParams) ;
413 pdf2Clone->recursiveRedirectServers(fftParams) ;
414 pdf1Clone->fixAddCoefRange(refName.c_str(), true) ;
415 pdf2Clone->fixAddCoefRange(refName.c_str(), true) ;
416
417 // Ensure that coefficients for Add PDFs are only interpreted with respect to the convolution observable
418 RooArgSet convSet(self._x.arg());
419 pdf1Clone->fixAddCoefNormalization(convSet, true);
420 pdf2Clone->fixAddCoefNormalization(convSet, true);
421
422 // Save copy of original histX binning and make alternate binning
423 // for extended range scanning
424
425 const Int_t N = convObs->numBins();
426 if (N < 900) {
427 oocoutW(&self, Eval) << "The FFT convolution '" << self.GetName() << "' will run with " << N
428 << " bins. A decent accuracy for difficult convolutions is typically only reached with n >= 1000. Suggest to increase the number"
429 " of bins of the observable '" << convObs->GetName() << "'." << std::endl;
430 }
431 Int_t Nbuf = static_cast<Int_t>((N*self.bufferFraction())/2 + 0.5) ;
432 double obw = (convObs->getMax() - convObs->getMin())/N ;
433 Int_t N2 = N+2*Nbuf ;
434
435 scanBinning = std::make_unique<RooUniformBinning>(convObs->getMin()-Nbuf*obw,convObs->getMax()+Nbuf*obw,N2);
436 histBinning.reset(convObs->getBinning().clone());
437
438 // Deactivate dirty state propagation on datahist observables
439 // and set all nodes on both pdfs to operMode AlwaysDirty
440 hist()->setDirtyProp(false) ;
441 convObs->setOperMode(ADirty,true) ;
442}
443
444
445////////////////////////////////////////////////////////////////////////////////
446/// Suffix for cache histogram (added in addition to suffix for cache name)
447
449{
450 return TString(Form("_BufFrac%3.1f_BufStrat%d",_bufFrac,_bufStrat)) ;
451}
452
453
454
455////////////////////////////////////////////////////////////////////////////////
456/// Returns all RooAbsArg objects contained in the cache element
457
459{
460 RooArgList ret(PdfCacheElem::containedArgs(a)) ;
461
462 ret.add(*pdf1Clone) ;
463 ret.add(*pdf2Clone) ;
464 if (pdf1Clone->ownedComponents()) {
465 ret.add(*pdf1Clone->ownedComponents()) ;
466 }
467 if (pdf2Clone->ownedComponents()) {
468 ret.add(*pdf2Clone->ownedComponents()) ;
469 }
470
471 return ret ;
472}
473
474
475
476////////////////////////////////////////////////////////////////////////////////
477/// Fill the contents of the cache the FFT convolution output
478
480{
481 RooDataHist& cacheHist = *cache.hist() ;
482
483 (static_cast<FFTCacheElem&>(cache)).pdf1Clone->setOperMode(ADirty,true) ;
484 (static_cast<FFTCacheElem&>(cache)).pdf2Clone->setOperMode(ADirty,true) ;
485
486 // Determine if there other observables than the convolution observable in the cache
487 RooArgSet otherObs ;
488 RooArgSet(*cacheHist.get()).snapshot(otherObs) ;
489
490 RooAbsArg* histArg = otherObs.find(_x.arg().GetName()) ;
491 if (histArg) {
492 otherObs.remove(*histArg,true,true) ;
493 }
494
495 //cout << "RooFFTConvPdf::fillCacheObject() otherObs = " << otherObs << endl ;
496
497 // Handle trivial scenario -- no other observables
498 if (otherObs.empty()) {
499 fillCacheSlice(static_cast<FFTCacheElem&>(cache),RooArgSet()) ;
500 return ;
501 }
502
503 // Handle cases where there are other cache slices
504 // Iterator over available slice positions and fill each
505
506 // Determine number of bins for each slice position observable
507 Int_t n = otherObs.size() ;
508 std::vector<int> binCur(n+1);
509 std::vector<int> binMax(n+1);
510 Int_t curObs = 0 ;
511
512 std::vector<RooAbsLValue*> obsLV(n);
513 Int_t i(0) ;
514 for (auto const& arg : otherObs) {
515 RooAbsLValue* lvarg = dynamic_cast<RooAbsLValue*>(arg) ;
516 obsLV[i] = lvarg ;
517 binCur[i] = 0 ;
518 // coverity[FORWARD_NULL]
519 binMax[i] = lvarg->numBins(binningName())-1 ;
520 i++ ;
521 }
522
523 bool loop(true) ;
524 while(loop) {
525 // Set current slice position
526 for (Int_t j=0 ; j<n ; j++) { obsLV[j]->setBin(binCur[j],binningName()) ; }
527
528// cout << "filling slice: bin of obsLV[0] = " << obsLV[0]->getBin() << endl ;
529
530 // Fill current slice
531 fillCacheSlice(static_cast<FFTCacheElem&>(cache),otherObs) ;
532
533 // Determine which iterator to increment
534 while(binCur[curObs]==binMax[curObs]) {
535
536 // Reset current iterator and consider next iterator ;
537 binCur[curObs]=0 ;
538 curObs++ ;
539
540 // master termination condition
541 if (curObs==n) {
542 loop=false ;
543 break ;
544 }
545 }
546
547 // Increment current iterator
548 binCur[curObs]++ ;
549 curObs=0 ;
550
551 }
552
553}
554
555
556////////////////////////////////////////////////////////////////////////////////
557/// Fill a slice of cachePdf with the output of the FFT convolution calculation
558
559void RooFFTConvPdf::fillCacheSlice(FFTCacheElem& aux, const RooArgSet& slicePos) const
560{
561 // Extract histogram that is the basis of the RooHistPdf
562 RooDataHist& cacheHist = *aux.hist() ;
563
564 // Sample array of input points from both pdfs
565 // Note that returned arrays have optional buffers zones below and above range ends
566 // to reduce cyclical effects and have been cyclically rotated so that bin containing
567 // zero value is at position zero. Example:
568 //
569 // original: -5 -4 -3 -2 -1 0 +1 +2 +3 +4 +5
570 // add buffer zones: U U -5 -4 -3 -2 -1 0 +1 +2 +3 +4 +5 O O
571 // rotate: 0 +1 +2 +3 +4 +5 O O U U -5 -4 -3 -2 -1
572 //
573 //
574
575 Int_t N;
576 Int_t N2;
577 Int_t binShift1;
578 Int_t binShift2;
579
580 RooRealVar* histX = static_cast<RooRealVar*>(cacheHist.get()->find(_x.arg().GetName())) ;
581 if (_bufStrat==Extend) histX->setBinning(*aux.scanBinning) ;
582 std::vector<double> input1 = scanPdf(const_cast<RooRealVar &>(static_cast<RooRealVar const&>(_x.arg())),*aux.pdf1Clone,cacheHist,slicePos,N,N2,binShift1,_shift1) ;
583 std::vector<double> input2 = scanPdf(const_cast<RooRealVar &>(static_cast<RooRealVar const&>(_x.arg())),*aux.pdf2Clone,cacheHist,slicePos,N,N2,binShift2,_shift2) ;
584 if (_bufStrat==Extend) histX->setBinning(*aux.histBinning) ;
585
586#ifndef ROOFIT_MATH_FFTW3
587 // If ROOT was NOT built with the fftw3 interface, we try to include fftw3.h
588 // with the interpreter and run the concolution in the interpreter.
589 std::vector<double> output(N2);
590
591 auto doFFT = declareDoFFT();
592 doFFT(N2, input1.data(), input2.data(), output.data());
593#else
594 // If ROOT was built with the fftw3 interface, we can use it as a TVirtualFFT
595 // plugin. The advantage here is that nothing can go wrong if fftw3.h wahs
596 // not istalled by the user separately.
597
598 // Retrieve previously defined FFT transformation plans
599 if (!aux.fftr2c1) {
600 aux.fftr2c1.reset(TVirtualFFT::FFT(1, &N2, "R2CK"));
601 aux.fftr2c2.reset(TVirtualFFT::FFT(1, &N2, "R2CK"));
602 aux.fftc2r.reset(TVirtualFFT::FFT(1, &N2, "C2RK"));
603
604 if (aux.fftr2c1 == nullptr || aux.fftr2c2 == nullptr || aux.fftc2r == nullptr) {
605 coutF(Eval) << "RooFFTConvPdf::fillCacheSlice(" << GetName() << "Cannot get a handle to fftw. Maybe ROOT was built without it?" << std::endl;
606 throw std::runtime_error("Cannot get a handle to fftw.");
607 }
608 }
609
610 // Real->Complex FFT Transform on p.d.f. 1 sampling
611 aux.fftr2c1->SetPoints(input1.data());
612 aux.fftr2c1->Transform();
613
614 // Real->Complex FFT Transform on p.d.f 2 sampling
615 aux.fftr2c2->SetPoints(input2.data());
616 aux.fftr2c2->Transform();
617
618 // Loop over first half +1 of complex output results, multiply
619 // and set as input of reverse transform
620 for (Int_t i=0 ; i<N2/2+1 ; i++) {
621 double re1;
622 double re2;
623 double im1;
624 double im2;
625 aux.fftr2c1->GetPointComplex(i,re1,im1) ;
626 aux.fftr2c2->GetPointComplex(i,re2,im2) ;
627 double re = re1*re2 - im1*im2 ;
628 double im = re1*im2 + re2*im1 ;
629 TComplex t(re,im) ;
630 aux.fftc2r->SetPointComplex(i,t) ;
631 }
632
633 // Reverse Complex->Real FFT transform product
634 aux.fftc2r->Transform() ;
635#endif
636
637 Int_t totalShift = binShift1 + (N2-N)/2 ;
638
639 // Store FFT result in cache
640
641 std::unique_ptr<TIterator> iter{const_cast<RooDataHist&>(cacheHist).sliceIterator(const_cast<RooAbsReal&>(_x.arg()),slicePos)};
642 for (Int_t i =0 ; i<N ; i++) {
643
644 // Cyclically shift array back so that bin containing zero is back in zeroBin
645 Int_t j = i + totalShift ;
646 while (j<0) j+= N2 ;
647 while (j>=N2) j-= N2 ;
648
649 iter->Next() ;
650#ifndef ROOFIT_MATH_FFTW3
651 cacheHist.set(output[j]);
652#else
653 cacheHist.set(aux.fftc2r->GetPointReal(j));
654#endif
655 }
656}
657
658
659////////////////////////////////////////////////////////////////////////////////
660/// Scan the values of 'pdf' in observable 'obs' using the bin values stored in 'hist' at slice position 'slicePos'
661/// N is filled with the number of bins defined in hist, N2 is filled with N plus the number of buffer bins
662/// The return value is an array of doubles of length N2 with the sampled values. The caller takes ownership
663/// of the array
664
665std::vector<double> RooFFTConvPdf::scanPdf(RooRealVar& obs, RooAbsPdf& pdf, const RooDataHist& hist, const RooArgSet& slicePos,
666 Int_t& N, Int_t& N2, Int_t& zeroBin, double shift) const
667{
668
669 RooRealVar* histX = static_cast<RooRealVar*>(hist.get()->find(obs.GetName())) ;
670
671 // Calculate number of buffer bins on each size to avoid cyclical flow
672 N = histX->numBins(binningName()) ;
673 Int_t Nbuf = static_cast<Int_t>((N*bufferFraction())/2 + 0.5) ;
674 N2 = N+2*Nbuf ;
675
676
677 // Allocate array of sampling size plus optional buffer zones
678 std::vector<double> array(N2);
679
680 // Set position of non-convolution observable to that of the cache slice that were are processing now
681 hist.get(slicePos) ;
682
683 // Find bin ID that contains zero value
684 zeroBin = 0 ;
685 if (histX->getMax()>=0 && histX->getMin()<=0) {
686 zeroBin = histX->getBinning().binNumber(0) ;
687 } else if (histX->getMin()>0) {
688 double bw = (histX->getMax() - histX->getMin())/N2 ;
689 zeroBin = Int_t(-histX->getMin()/bw) ;
690 } else {
691 double bw = (histX->getMax() - histX->getMin())/N2 ;
692 zeroBin = Int_t(-1*histX->getMax()/bw) ;
693 }
694
695 Int_t binShift = Int_t((N2* shift) / (histX->getMax()-histX->getMin())) ;
696
697 zeroBin += binShift ;
698 while(zeroBin>=N2) zeroBin-= N2 ;
699 while(zeroBin<0) zeroBin+= N2 ;
700
701 // First scan hist into temp array
702 std::vector<double> tmp(N2);
703 Int_t k(0) ;
704 switch(_bufStrat) {
705
706 case Extend:
707 // Sample entire extended range (N2 samples)
708 for (k=0 ; k<N2 ; k++) {
709 histX->setBin(k) ;
710 tmp[k] = pdf.getVal(hist.get()) ;
711 }
712 break ;
713
714 case Flat:
715 // Sample original range (N samples) and fill lower and upper buffer
716 // bins with p.d.f. value at respective boundary
717 {
718 histX->setBin(0) ;
719 double val = pdf.getVal(hist.get()) ;
720 for (k=0 ; k<Nbuf ; k++) {
721 tmp[k] = val ;
722 }
723 for (k=0 ; k<N ; k++) {
724 histX->setBin(k) ;
725 tmp[k+Nbuf] = pdf.getVal(hist.get()) ;
726 }
727 histX->setBin(N-1) ;
728 val = pdf.getVal(hist.get()) ;
729 for (k=0 ; k<Nbuf ; k++) {
730 tmp[N+Nbuf+k] = val ;
731 }
732 }
733 break ;
734
735 case Mirror:
736 // Sample original range (N samples) and fill lower and upper buffer
737 // bins with mirror image of sampled range
738 for (k=0 ; k<N ; k++) {
739 histX->setBin(k) ;
740 tmp[k+Nbuf] = pdf.getVal(hist.get()) ;
741 }
742 for (k=1 ; k<=Nbuf ; k++) {
743 histX->setBin(k) ;
744 tmp[Nbuf-k] = pdf.getVal(hist.get()) ;
745 histX->setBin(N-k) ;
746 tmp[Nbuf+N+k-1] = pdf.getVal(hist.get()) ;
747 }
748 break ;
749 }
750
751 // Scan function and store values in array
752 for (Int_t i=0 ; i<N2 ; i++) {
753 // Cyclically shift writing location by zero bin position
754 Int_t j = i - (zeroBin) ;
755 if (j<0) j+= N2 ;
756 if (j>=N2) j-= N2 ;
757 array[i] = tmp[j] ;
758 }
759
760 return array ;
761}
762
763
764
765////////////////////////////////////////////////////////////////////////////////
766/// Return the observables to be cached given the normalization set nset.
767///
768/// If the cache observable is in nset then this is
769/// - the convolution observable plus
770/// - any member of nset that is either a RooCategory,
771/// - or was previously specified through setCacheObservables().
772///
773/// In case the cache observable is *not* in nset, then it is
774/// - the convolution observable plus
775/// - all member of nset that are observables of this p.d.f.
776///
777
779{
780 // Get complete list of observables
781 auto obs1 = new RooArgSet{};
782 RooArgSet obs2;
783 _pdf1.arg().getObservables(&nset, *obs1) ;
784 _pdf2.arg().getObservables(&nset, obs2) ;
785 obs1->add(obs2, true) ;
786
787 // Check if convolution observable is in nset
788 if (nset.contains(_x.arg())) {
789
790 // Now strip out all non-category observables
791 RooArgSet killList ;
792 for(auto const& arg : *obs1) {
793 if (arg->IsA()->InheritsFrom(RooAbsReal::Class()) && !_cacheObs.find(arg->GetName())) {
794 killList.add(*arg) ;
795 }
796 }
797 obs1->remove(killList) ;
798
799 // And add back the convolution observables
800 obs1->add(_x.arg(),true) ;
801
802 obs1->add(_cacheObs) ;
803
804 } else {
805
806 // If cacheObs was filled, cache only observables in there
807 if (!_cacheObs.empty()) {
808 RooArgSet killList ;
809 for(auto const& arg : *obs1) {
810 if (arg->IsA()->InheritsFrom(RooAbsReal::Class()) && !_cacheObs.find(arg->GetName())) {
811 killList.add(*arg) ;
812 }
813 }
814 obs1->remove(killList) ;
815 }
816
817
818 // Make sure convolution observable is always in there
819 obs1->add(_x.arg(),true) ;
820
821 }
822
823 return RooFit::OwningPtr<RooArgSet>{obs1};
824}
825
826
827
828////////////////////////////////////////////////////////////////////////////////
829/// Return the parameters on which the cache depends given normalization
830/// set nset. For this p.d.f these are the parameters of the input p.d.f.
831/// but never the convolution variable, in case it is not part of nset.
832
834{
835 std::unique_ptr<RooArgSet> vars{getVariables()};
836 vars->remove(*std::unique_ptr<RooArgSet>{actualObservables(nset)});
837
838 return RooFit::makeOwningPtr(std::move(vars));
839}
840
841
842
843////////////////////////////////////////////////////////////////////////////////
844/// Return p.d.f. observable (which can be a function) to substitute given
845/// p.d.f. observable. Substitutes x by xprime if xprime is set.
846
848{
849 if (_xprime.absArg() && string(histObservable.GetName())==_x.absArg()->GetName()) {
850 return (*_xprime.absArg()) ;
851 }
852 return histObservable ;
853}
854
855
856
857////////////////////////////////////////////////////////////////////////////////
858/// Create appropriate generator context for this convolution. If both input p.d.f.s support
859/// internal generation, if it is safe to use them and if no observables other than the convolution
860/// observable are requested for generation, use the specialized convolution generator context
861/// which implements a smearing strategy in the convolution observable. If not return the
862/// regular accept/reject generator context
863
865 const RooArgSet* auxProto, bool verbose) const
866{
867 RooArgSet vars2(vars) ;
868 vars2.remove(_x.arg(),true,true) ;
869 Int_t numAddDep = vars2.size() ;
870
871 RooArgSet dummy ;
872 bool pdfCanDir = ((static_cast<RooAbsPdf const &>(_pdf1.arg())).getGenerator(_x.arg(), dummy) != 0 &&
873 (static_cast<RooAbsPdf const &>(_pdf1.arg())).isDirectGenSafe(_x.arg()));
874 bool resCanDir = ((static_cast<RooAbsPdf const &>(_pdf2.arg())).getGenerator(_x.arg(), dummy) != 0 &&
875 (static_cast<RooAbsPdf const &>(_pdf2.arg())).isDirectGenSafe(_x.arg()));
876
877 if (pdfCanDir) {
878 cxcoutI(Generation) << "RooFFTConvPdf::genContext() input p.d.f " << _pdf1.arg().GetName()
879 << " has internal generator that is safe to use in current context" << endl ;
880 }
881 if (resCanDir) {
882 cxcoutI(Generation) << "RooFFTConvPdf::genContext() input p.d.f. " << _pdf2.arg().GetName()
883 << " has internal generator that is safe to use in current context" << endl ;
884 }
885 if (numAddDep>0) {
886 cxcoutI(Generation) << "RooFFTConvPdf::genContext() generation requested for observables other than the convolution observable " << _x.arg().GetName() << endl ;
887 }
888
889
890 if (numAddDep>0 || !pdfCanDir || !resCanDir) {
891 // Any resolution model with more dependents than the convolution variable
892 // or pdf or resmodel do not support direct generation
893 cxcoutI(Generation) << "RooFFTConvPdf::genContext() selecting accept/reject generator context because one or both of the input "
894 << "p.d.f.s cannot use internal generator and/or "
895 << "observables other than the convolution variable are requested for generation" << endl ;
896 return new RooGenContext(*this,vars,prototype,auxProto,verbose) ;
897 }
898
899 // Any other resolution model: use specialized generator context
900 cxcoutI(Generation) << "RooFFTConvPdf::genContext() selecting specialized convolution generator context as both input "
901 << "p.d.fs are safe for internal generator and only "
902 << "the convolution observables is requested for generation" << endl ;
903 return new RooConvGenContext(*this,vars,prototype,auxProto,verbose) ;
904}
905
906
907
908////////////////////////////////////////////////////////////////////////////////
909/// Change the size of the buffer on either side of the observable range to `frac` times the
910/// size of the range of the convolution observable.
911
913{
914 if (frac<0) {
915 coutE(InputArguments) << "RooFFTConvPdf::setBufferFraction(" << GetName() << ") fraction should be greater than or equal to zero" << endl ;
916 return ;
917 }
918 _bufFrac = frac ;
919
920 // Sterilize the cache as certain partial results depend on buffer fraction
922}
923
924
925////////////////////////////////////////////////////////////////////////////////
926/// Change strategy to fill the overflow buffer on either side of the convolution observable range.
927///
928/// - `Extend` means is that the input p.d.f convolution observable range is widened to include the buffer range
929/// - `Flat` means that the buffer is filled with the p.d.f. value at the boundary of the observable range
930/// - `Mirror` means that the buffer is filled with a mirror image of the p.d.f. around the convolution observable boundary
931///
932/// The default strategy is extend. If one of the input p.d.f.s is a RooAddPdf, it is configured so that the interpretation
933/// range of the fraction coefficients is kept at the nominal convolutions observable range (instead of interpreting coefficients
934/// in the widened range including the buffer).
935
937{
938 _bufStrat = bs ;
939}
940
941
942
943////////////////////////////////////////////////////////////////////////////////
944/// Customized printing of arguments of a RooNumConvPdf to more intuitively reflect the contents of the
945/// product operator construction
946
947void RooFFTConvPdf::printMetaArgs(ostream& os) const
948{
949 os << _pdf1.arg().GetName() << "(" << _x.arg().GetName() << ") (*) " << _pdf2.arg().GetName() << "(" << _x.arg().GetName() << ") " ;
950}
951
952
953
954////////////////////////////////////////////////////////////////////////////////
955/// (Re)calculate effective parameters of this p.d.f.
956
958{
959 RooArgSet argSet{_x.arg()};
960 RooArgSet params1;
961 RooArgSet params2;
962 _pdf1.arg().getParameters(&argSet, params1);
963 _pdf2.arg().getParameters(&argSet, params2);
965 _params.add(params1) ;
966 _params.add(params2,true) ;
967}
#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:77
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,...
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.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
Storage_t::size_type size() const
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
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...
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
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.
Implements a universal generator context for all RooAbsPdf classes that do not have or need a special...
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.
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.
Implementation of RooAbsBinning that provides a uniform binning in 'n' bins between the range end poi...
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
OwningPtr< T > makeOwningPtr(std::unique_ptr< T > &&ptr)
Internal helper to turn a std::unique_ptr<T> into an OwningPtr.
Definition Config.h:40
T * OwningPtr
An alias for raw pointers for indicating that the return type of a RooFit function is an owning point...
Definition Config.h:35
static void output()