135#ifndef ROOFIT_MATH_FFTW3
142 static void (*doFFT)(
int,
double *,
double *,
double *) =
nullptr;
147 bool success =
gInterpreter->Declare(
"#include \"fftw3.h\"");
150 std::stringstream ss;
151 ss <<
"RooFFTConvPdf evaluation Failed! The interpreter could not find the fftw3.h header.\n";
152 ss <<
"You have three options to fix this problem:\n";
153 ss <<
" 1) Install fftw3 on your system so that the interpreter can find it\n";
154 ss <<
" 2) In case fftw3.h is installed somewhere else,\n"
155 <<
" tell ROOT with gInterpreter->AddIncludePath() where to find it\n";
156 ss <<
" 3) Compile ROOT with the -Dfftw3=ON in the CMake configuration,\n"
157 <<
" such that ROOT comes with built-in fftw3 convolution routines\n";
158 oocoutE(
nullptr, Eval) << ss.str() << std::endl;
159 throw std::runtime_error(
"RooFFTConvPdf evaluation Failed! The interpreter could not find the fftw3.h header");
163void RooFFTConvPdf_doFFT(int n, double *input1, double *input2, double *output)
165 auto fftr2c1_Out = reinterpret_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex) * (n / 2 + 1)));
166 auto fftr2c2_Out = reinterpret_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex) * (n / 2 + 1)));
167 auto fftc2r_In = reinterpret_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex) * (n / 2 + 1)));
169 fftw_plan fftr2c1_plan = fftw_plan_dft_r2c(1, &n, input1, fftr2c1_Out, FFTW_ESTIMATE);
170 fftw_plan fftr2c2_plan = fftw_plan_dft_r2c(1, &n, input2, fftr2c2_Out, FFTW_ESTIMATE);
171 fftw_plan fftc2r_plan = fftw_plan_dft_c2r(1, &n, fftc2r_In, output, FFTW_ESTIMATE);
173 // Real->Complex FFT Transform on p.d.f. samplings
174 fftw_execute(fftr2c1_plan);
175 fftw_execute(fftr2c2_plan);
177 // Loop over first half +1 of complex output results, multiply
178 // and set as input of reverse transform
179 for (Int_t i = 0; i < n / 2 + 1; i++) {
180 double re1 = fftr2c1_Out[i][0];
181 double re2 = fftr2c2_Out[i][0];
182 double im1 = fftr2c1_Out[i][1];
183 double im2 = fftr2c2_Out[i][1];
184 fftc2r_In[i][0] = re1 * re2 - im1 * im2;
185 fftc2r_In[i][1] = re1 * im2 + re2 * im1;
188 // Reverse Complex->Real FFT transform product
189 fftw_execute(fftc2r_plan);
191 fftw_destroy_plan(fftr2c1_plan);
192 fftw_destroy_plan(fftr2c2_plan);
193 fftw_destroy_plan(fftc2r_plan);
195 fftw_free(fftr2c1_Out);
196 fftw_free(fftr2c2_Out);
197 fftw_free(fftc2r_In);
201 doFFT = reinterpret_cast<void(*)(
int,
double*,
double*,
double*)
>(
gInterpreter->ProcessLine(
"RooFFTConvPdf_doFFT;"));
209using std::endl, std::string, std::ostream;
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),
234 _shift2((convVar.getMax(
"cache") + convVar.getMin(
"cache")) / 2),
235 _cacheObs(
"!cacheObs",
"Cached observables", this, false, false)
252 _x(
"!x",
"Convolution Variable", this, convVar, false, false),
253 _xprime(
"!xprime",
"External Convolution Variable", this, pdfConvVar),
254 _pdf1(
"!pdf1",
"pdf1", this, pdf1, false),
255 _pdf2(
"!pdf2",
"pdf2", this, pdf2, false),
256 _params(
"!params",
"effective parameters", this),
260 _shift2((convVar.getMax(
"cache") + convVar.getMin(
"cache")) / 2),
261 _cacheObs(
"!cacheObs",
"Cached observables", this, false, false)
275 _x(
"!x",this,other.
_x),
310 coutI(Caching) <<
"Changing internal binning of variable '" << convVar.
GetName()
311 <<
"' in FFT '" <<
fName <<
"'"
312 <<
" from " << varBinning.
numBins()
313 <<
" to " << optimal <<
" to improve the precision of the numerical FFT."
314 <<
" This can be done manually by setting an additional binning named 'cache'." << std::endl;
317 coutE(Caching) <<
"The internal binning of variable " << convVar.
GetName()
318 <<
" is not uniform. The numerical FFT will likely yield wrong results." << std::endl;
332 name.Append(
"_CONV_") ;
366 string refName =
Form(
"refrange_fft_%s",self.
GetName()) ;
379 pdf1Clone->addOwnedComponents(*shiftObs1) ;
380 pdf1Clone->addOwnedComponents(*clonePdf1) ;
394 pdf1Clone->addOwnedComponents(*shiftObs2) ;
395 pdf1Clone->addOwnedComponents(*clonePdf2) ;
421 pdf1Clone->recursiveRedirectServers(fftParams) ;
422 pdf2Clone->recursiveRedirectServers(fftParams) ;
423 pdf1Clone->fixAddCoefRange(refName.c_str(),
true) ;
424 pdf2Clone->fixAddCoefRange(refName.c_str(),
true) ;
428 pdf1Clone->fixAddCoefNormalization(convSet,
true);
429 pdf2Clone->fixAddCoefNormalization(convSet,
true);
436 oocoutW(&self, Eval) <<
"The FFT convolution '" << self.
GetName() <<
"' will run with " <<
N
437 <<
" bins. A decent accuracy for difficult convolutions is typically only reached with n >= 1000. Suggest to increase the number"
438 " of bins of the observable '" << convObs->
GetName() <<
"'." << std::endl;
501 otherObs.
remove(*histArg,
true,
true) ;
507 if (otherObs.
empty()) {
517 std::vector<int> binCur(
n+1);
518 std::vector<int> binMax(
n+1);
521 std::vector<RooAbsLValue*> obsLV(
n);
523 for (
auto const& arg : otherObs) {
543 while(binCur[curObs]==binMax[curObs]) {
596#ifndef ROOFIT_MATH_FFTW3
599 std::vector<double> output(N2);
601 auto doFFT = declareDoFFT();
602 doFFT(N2, input1.data(), input2.data(), output.data());
615 coutF(Eval) <<
"RooFFTConvPdf::fillCacheSlice(" <<
GetName() <<
"Cannot get a handle to fftw. Maybe ROOT was built without it?" << std::endl;
616 throw std::runtime_error(
"Cannot get a handle to fftw.");
621 aux.
fftr2c1->SetPoints(input1.data());
625 aux.
fftr2c2->SetPoints(input2.data());
630 for (
Int_t i=0 ; i<N2/2+1 ; i++) {
635 aux.
fftr2c1->GetPointComplex(i,re1,im1) ;
636 aux.
fftr2c2->GetPointComplex(i,re2,im2) ;
637 double re = re1*re2 - im1*im2 ;
638 double im = re1*im2 + re2*im1 ;
640 aux.
fftc2r->SetPointComplex(i,t) ;
647 Int_t totalShift = binShift1 + (N2-
N)/2 ;
651 std::unique_ptr<TIterator> iter{
const_cast<RooDataHist&
>(cacheHist).sliceIterator(
const_cast<RooAbsReal&
>(
_x.arg()),slicePos)};
652 for (
Int_t i =0 ; i<
N ; i++) {
655 Int_t j = i + totalShift ;
657 while (j>=N2) j-= N2 ;
660 const std::size_t binIdx = cacheHist.
getIndex(*cacheHist.
get(),
true);
661#ifndef ROOFIT_MATH_FFTW3
662 cacheHist.
set(binIdx, output[j], -1.);
664 cacheHist.
set(binIdx, aux.
fftc2r->GetPointReal(j), -1.);
690 std::vector<double> array(N2);
699 }
else if (histX->
getMin()>0) {
709 zeroBin += binShift ;
710 while(zeroBin>=N2) zeroBin-= N2 ;
711 while(zeroBin<0) zeroBin+= N2 ;
714 auto getPdfVal = [&]() {
715 double rawVal = pdf.
getVal();
720 std::vector<double> tmp(N2);
726 for (k=0 ; k<N2 ; k++) {
728 tmp[k] = getPdfVal();
737 double val = getPdfVal();
738 for (k=0 ; k<Nbuf ; k++) {
741 for (k=0 ; k<
N ; k++) {
743 tmp[k+Nbuf] = getPdfVal();
747 for (k=0 ; k<Nbuf ; k++) {
748 tmp[
N+Nbuf+k] = val ;
756 for (k=0 ; k<
N ; k++) {
758 tmp[k+Nbuf] = getPdfVal();
760 for (k=1 ; k<=Nbuf ; k++) {
762 tmp[Nbuf-k] = getPdfVal();
764 tmp[Nbuf+
N+k-1] = getPdfVal();
770 for (
Int_t i=0 ; i<N2 ; i++) {
772 Int_t j = i - (zeroBin) ;
801 _pdf1.arg().getObservables(&nset, *obs1) ;
802 _pdf2.arg().getObservables(&nset, obs2) ;
803 obs1->add(obs2,
true) ;
810 for(
auto const& arg : *obs1) {
818 obs1->add(
_x.arg(),
true) ;
827 for(
auto const& arg : *obs1) {
837 obs1->add(
_x.arg(),
true) ;
867 if (
_xprime.absArg() &&
string(histObservable.
GetName())==
_x.absArg()->GetName()) {
870 return histObservable ;
883 const RooArgSet* auxProto,
bool verbose)
const
896 cxcoutI(Generation) <<
"RooFFTConvPdf::genContext() input p.d.f " <<
_pdf1.arg().GetName()
897 <<
" has internal generator that is safe to use in current context" << std::endl ;
900 cxcoutI(Generation) <<
"RooFFTConvPdf::genContext() input p.d.f. " <<
_pdf2.arg().GetName()
901 <<
" has internal generator that is safe to use in current context" << std::endl ;
904 cxcoutI(Generation) <<
"RooFFTConvPdf::genContext() generation requested for observables other than the convolution observable " <<
_x.arg().GetName() << std::endl ;
908 if (numAddDep>0 || !pdfCanDir || !resCanDir) {
911 cxcoutI(Generation) <<
"RooFFTConvPdf::genContext() selecting accept/reject generator context because one or both of the input "
912 <<
"p.d.f.s cannot use internal generator and/or "
913 <<
"observables other than the convolution variable are requested for generation" << std::endl ;
914 return new RooGenContext(*
this,vars,prototype,auxProto,verbose) ;
918 cxcoutI(Generation) <<
"RooFFTConvPdf::genContext() selecting specialized convolution generator context as both input "
919 <<
"p.d.fs are safe for internal generator and only "
920 <<
"the convolution observables is requested for generation" << std::endl ;
933 coutE(InputArguments) <<
"RooFFTConvPdf::setBufferFraction(" <<
GetName() <<
") fraction should be greater than or equal to zero" << std::endl ;
967 os <<
_pdf1.arg().GetName() <<
"(" <<
_x.arg().GetName() <<
") (*) " <<
_pdf2.arg().GetName() <<
"(" <<
_x.arg().GetName() <<
") " ;
980 _pdf1.arg().getParameters(&argSet, params1);
981 _pdf2.arg().getParameters(&argSet, params2);
int Int_t
Signed integer 4 bytes (int).
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Common abstract base class for objects that represent a value and a "shape" in RooFit.
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 > 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
PdfCacheElem(const RooAbsCachedPdf &self, const RooArgSet *nset)
Constructor of cache object which owns RooDataHist cache histogram, RooHistPdf pdf that represents is...
virtual const char * binningName() const
RooObjCacheManager _cacheMgr
! The cache manager
bool contains(const char *name) const
Check if collection contains an argument with a specific name.
virtual bool remove(const RooAbsArg &var, bool silent=false, bool matchByNameOnly=false)
Remove the specified argument from our list.
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
RooAbsPdf()
Default constructor.
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.
double getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
RooArgList is a container object that can hold multiple RooAbsArg objects.
RooArgSet is a container object that can hold multiple RooAbsArg objects.
RooArgSet * snapshot(bool deepCopy=true) const
Use RooAbsCollection::snapshot(), but return as RooArgSet.
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.
Int_t getIndex(const RooAbsCollection &coord, bool fast=false) const
Calculate bin number of the given coordinates.
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.
Container class to hold unbinned data.
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
friend class RooConvGenContext
RooSetProxy _params
Effective parameters of this p.d.f.
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.
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.
std::vector< double > scanPdf(RooRealVar &obs, RooAbsPdf &pdf, double normVal, 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 ...
~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.
friend class FFTCacheElem
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...
Variable that can be changed from the outside.
bool hasBinning(const char *name) const override
Returns true if variable has a binning named 'name'.
void setRange(const char *name, double min, double max, bool shared=true)
Set a fit or plotting range.
void setBinning(const RooAbsBinning &binning, const char *name=nullptr, bool shared=true)
Add given binning under name 'name' with this variable.
const RooAbsBinning & getBinning(const char *name=nullptr, bool verbose=true, bool createOnTheFly=false, bool shared=true) const override
Return binning definition with name.
const T & arg() const
Return reference to object held in proxy.
const char * GetName() const override
Returns name of object.
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)
double normalizeWithNaNPacking(RooAbsPdf const &pdf, double rawVal, double normVal)
T * OwningPtr
An alias for raw pointers for indicating that the return type of a RooFit function is an owning point...
OwningPtr< T > makeOwningPtr(std::unique_ptr< T > &&ptr)
Internal helper to turn a std::unique_ptr<T> into an OwningPtr.