ROOT  6.06/09
Reference Guide
TF1Convolution.cxx
Go to the documentation of this file.
1 // @(#)root/hist:$Id$
2 // Authors: L. Moneta, A. Flandi 08/2014
3 //
4 /**********************************************************************
5  * *
6  * Copyright (c) 2015 ROOT Team, CERN/PH-SFT *
7  * *
8  * *
9  **********************************************************************/
10 //
11 // TF1Convolution.cxx
12 //
13 //
14 // Created by AurĂ©lie Flandi on 27.08.14.
15 //
16 //
17 //
18 
19 #include "TF1Convolution.h"
20 #include "Riostream.h"
21 #include "TROOT.h"
22 #include "TObject.h"
23 #include "TObjString.h"
24 #include "TMath.h"
25 #include "Math/Integrator.h"
27 #include "Math/IntegratorOptions.h"
28 #include "Math/GaussIntegrator.h"
31 #include "Math/Functor.h"
32 #include "TVirtualFFT.h"
33 #include "TClass.h"
34 
35 ////////////////////////////////////////////////////////////////////////////////
36 /** \class TF1Convolution
37  \ingroup Hist
38  \brief Class wrapping convolution of two functions
39 
40 Class wrapping convolution of two functions: evaluation of \f$\int f(x)g(x-t)dx\f$
41 
42 The convolution is performed by default using FFTW if it is available .
43 One can pass optionally the range of the convolution (by default the first function range is used).
44 Note that when using Discrete Fouriere Transform (as FFTW), it is a circular transform, so the functions should be
45 approximatly zero at the end of the range. If they are significantly different than zero on one side (e.g. the left side)
46 a spill over will occur on the other side (e.g right side).
47 If no function range is given by default the function1 range + 10% is used
48 One shoud use also a not too small number of points for the DFT (a minimum of 1000). By default 10000 points are used.
49 */////////////////////////////////////////////////////////////////////////////////
50 
51 class TF1Convolution_EvalWrapper
52 {
53  std::shared_ptr < TF1 > fFunction1;
54  std::shared_ptr < TF1 > fFunction2;
55  Double_t fT0;
56 
57 public:
58 
59  TF1Convolution_EvalWrapper(std::shared_ptr<TF1> & f1 , std::shared_ptr<TF1> & f2, Double_t t)
60  : fFunction1(f1), fFunction2(f2), fT0(t)
61  {
62  }
64  {
65  return fFunction1->Eval(x) * fFunction2->Eval(fT0-x);
66  }
67 };
68 
69 ////////////////////////////////////////////////////////////////////////////////
70 /// use copy instead of Clone
71 
72 void TF1Convolution::InitializeDataMembers(TF1* function1, TF1* function2, Bool_t useFFT)
73 {
74  if (function1) {
75  TF1 * fnew1 = (TF1*) function1->IsA()->New();
76  function1->Copy(*fnew1);
77  fFunction1 = std::shared_ptr<TF1>(fnew1);
78  }
79  if (function2) {
80  TF1 * fnew2 = (TF1*) function2->IsA()->New();
81  function2->Copy(*fnew2);
82  fFunction2 = std::shared_ptr<TF1>(fnew2);
83  }
84  if (fFunction1.get() == nullptr|| fFunction2.get() == nullptr)
85  Fatal("InitializeDataMembers","Invalid functions - Abort");
86 
87  // add by default an extra 10% on each side
88  fFunction1->GetRange(fXmin, fXmax);
89  Double_t range = fXmax - fXmin;
90  fXmin -= 0.1*range;
91  fXmax += 0.1*range;
92  fNofParams1 = fFunction1->GetNpar();
93  fNofParams2 = fFunction2->GetNpar();
94  fParams1 = std::vector<Double_t>(fNofParams1);
95  fParams2 = std::vector<Double_t>(fNofParams2);
96  fCstIndex = fFunction2-> GetParNumber("Constant");
97  fFlagFFT = useFFT;
98  fFlagGraph = false;
99  fNofPoints = 10000;
100 
101  //std::cout<<"before: NofParams2 = "<<fNofParams2<<std::endl;
102 
103  fParNames.reserve( fNofParams1 + fNofParams2);
104  for (int i=0; i<fNofParams1; i++)
105  {
106  fParams1[i] = fFunction1 -> GetParameter(i);
107  fParNames.push_back(fFunction1 -> GetParName(i) );
108  }
109  for (int i=0; i<fNofParams2; i++)
110  {
111  fParams2[i] = fFunction2 -> GetParameter(i);
112  if (i != fCstIndex) fParNames.push_back(fFunction2 -> GetParName(i) );
113  }
114  if (fCstIndex!=-1)
115  {
116  fFunction2 -> FixParameter(fCstIndex,1.);
117  fNofParams2 = fNofParams2-1;
118  fParams2.erase(fParams2.begin()+fCstIndex);
119  }
120 }
121 ////////////////////////////////////////////////////////////////////////////////
122 /// constructor from the two function pointer and a flag is using FFT
123 
124 TF1Convolution::TF1Convolution(TF1* function1, TF1* function2, Bool_t useFFT)
125 {
126  InitializeDataMembers(function1,function2, useFFT);
127 }
128 
129 ////////////////////////////////////////////////////////////////////////////////
130 /// constructor from the two function pointer and the convolution range
131 
133 {
134  InitializeDataMembers(function1, function2,useFFT);
135  if (xmin < xmax) {
136  fXmin = xmin;
137  fXmax = xmax;
138  }
139 }
140 
141 ////////////////////////////////////////////////////////////////////////////////
142 /// constructor from a formula expression as f1 * f2 where f1 and f2 are two functions known to ROOT
143 
145 {
147 
148  TObjArray *objarray = formula.Tokenize("*");
149  std::vector < TString > stringarray(2);
150  std::vector < TF1* > funcarray(2);
151  for (int i=0; i<2; i++)
152  {
153  stringarray[i] = ((TObjString*)((*objarray)[i])) -> GetString();
154  stringarray[i].ReplaceAll(" ","");
155  funcarray[i] = (TF1*)(gROOT -> GetListOfFunctions() -> FindObject(stringarray[i]));
156  // case function is not found try to use as a TFormula
157  if (funcarray[i] == nullptr) {
158  TF1 * f = new TF1(TString::Format("f_conv_%d",i+1),stringarray[i]);
159  if (!f->GetFormula()->IsValid() )
160  Error("TF1Convolution","Invalid formula : %s",stringarray[i].Data() );
161  if (i == 0)
162  fFunction1 = std::shared_ptr<TF1>(f);
163  else
164  fFunction2 = std::shared_ptr<TF1>(f);
165  }
166  }
167  InitializeDataMembers(funcarray[0], funcarray[1],useFFT);
168  if (xmin < xmax) {
169  fXmin = xmin;
170  fXmax = xmax;
171  }
172 }
173 
174 ////////////////////////////////////////////////////////////////////////////////
175 /// constructor from 2 function names where f1 and f2 are two functions known to ROOT
176 /// if the function names are not knwon to ROOT then a corresponding
177 
179 {
181  (TString)formula1.ReplaceAll(" ","");
182  (TString)formula2.ReplaceAll(" ","");
183  TF1* f1 = (TF1*)(gROOT -> GetListOfFunctions() -> FindObject(formula1));
184  TF1* f2 = (TF1*)(gROOT -> GetListOfFunctions() -> FindObject(formula2));
185  // if function do not exists try using TFormula
186  if (!f1) {
187  fFunction1 = std::shared_ptr<TF1>(new TF1("f_conv_1",formula1) );
188  if (!fFunction1->GetFormula()->IsValid() )
189  Error("TF1Convolution","Invalid formula for : %s",formula1.Data() );
190  }
191  if (!f2) {
192  fFunction2 = std::shared_ptr<TF1>(new TF1("f_conv_1",formula2) );
193  if (!fFunction2->GetFormula()->IsValid() )
194  Error("TF1Convolution","Invalid formula for : %s",formula2.Data() );
195  }
196  // if f1 or f2 are null ptr are not used in InitializeDataMembers
197  InitializeDataMembers(f1, f2,useFFT);
198  if (xmin < xmax) {
199  fXmin = xmin;
200  fXmax = xmax;
201  }
202 }
203 
204 ////////////////////////////////////////////////////////////////////////////////
205 ///perform the FFT of the two functions
206 
208 {
209  if (gDebug)
210  Info("MakeFFTConv","Making FFT convolution using %d points in range [%g,%g]",fNofPoints,fXmin,fXmax);
211 
212  std::vector < Double_t > x (fNofPoints);
213  std::vector < Double_t > in1(fNofPoints);
214  std::vector < Double_t > in2(fNofPoints);
215 
216  TVirtualFFT *fft1 = TVirtualFFT::FFT(1, &fNofPoints, "R2C K");
217  TVirtualFFT *fft2 = TVirtualFFT::FFT(1, &fNofPoints, "R2C K");
218  if (fft1 == nullptr || fft2 == nullptr) {
219  Warning("MakeFFTConv","Cannot use FFT, probably FFTW package is not available. Switch to numerical convolution");
220  fFlagFFT = false;
221  return;
222  }
223 
224  // apply a shift in order to have the second function centered around middle of the range of the convolution
225  Double_t shift2 = 0.5*(fXmin+fXmax);
226  Double_t x2;
227  for (int i=0; i<fNofPoints; i++)
228  {
229  x[i] = fXmin + (fXmax-fXmin)/(fNofPoints-1)*i;
230  x2 = x[i] - shift2;
231  in1[i] = fFunction1 -> EvalPar( &x[i], nullptr);
232  in2[i] = fFunction2 -> EvalPar( &x2, nullptr);
233  fft1 -> SetPoint(i, in1[i]);
234  fft2 -> SetPoint(i, in2[i]);
235  }
236  fft1 -> Transform();
237  fft2 -> Transform();
238 
239  //inverse transformation of the product
240 
241  TVirtualFFT *fftinverse = TVirtualFFT::FFT(1, &fNofPoints, "C2R K");
242  Double_t re1, re2, im1, im2, out_re, out_im;
243 
244  for (int i=0;i<=fNofPoints/2.;i++)
245  {
246  fft1 -> GetPointComplex(i,re1,im1);
247  fft2 -> GetPointComplex(i,re2,im2);
248  out_re = re1*re2 - im1*im2;
249  out_im = re1*im2 + re2*im1;
250  fftinverse -> SetPoint(i, out_re, out_im);
251  }
252  fftinverse -> Transform();
253 
254  // fill a graph with the result of the convolution
255  if (!fGraphConv) fGraphConv = std::shared_ptr< TGraph >(new TGraph(fNofPoints));
256 
257  for (int i=0;i<fNofPoints;i++)
258  {
259  // we need this since we have applied a shift in the middle of f2
260  int j = i + fNofPoints/2;
261  if (j >= fNofPoints) j -= fNofPoints;
262  // need to normalize by dividing by the number of points and multiply by the bin width = Range/Number of points
263  fGraphConv->SetPoint(i, x[i], fftinverse->GetPointReal(j)*(fXmax-fXmin)/(fNofPoints*fNofPoints) );
264  }
265  fFlagGraph = true; // we can use the graph
266 }
267 
268 ////////////////////////////////////////////////////////////////////////////////
269 
271 {
272  if (!fFlagGraph) MakeFFTConv();
273  // if cannot make FFT use numconv
274  if (fGraphConv)
275  return fGraphConv -> Eval(t);
276  else
277  return EvalNumConv(t);
278 }
279 
280 ////////////////////////////////////////////////////////////////////////////////
281 /// perform numerical convolution
282 /// could in principle cache the integral in a Graph as it is done for the FFTW
283 
285 {
286  TF1Convolution_EvalWrapper fconv( fFunction1, fFunction2, t);
287  Double_t result = 0;
288 
290  if (fXmin != - TMath::Infinity() && fXmax != TMath::Infinity() )
291  result = integrator.Integral(fXmin, fXmax);
292  else if (fXmin == - TMath::Infinity() && fXmax != TMath::Infinity() )
293  result = integrator.IntegralLow(fXmax);
294  else if (fXmin != - TMath::Infinity() && fXmax == TMath::Infinity() )
295  result = integrator.IntegralUp(fXmin);
296  else if (fXmin == - TMath::Infinity() && fXmax == TMath::Infinity() )
297  result = integrator.Integral();
298 
299  return result;
300 }
301 
302 ////////////////////////////////////////////////////////////////////////////////
303 
304 Double_t TF1Convolution::operator()(Double_t* t, Double_t* p)//used in TF1 when doing the fit, will be valuated at each point
305 {
306  if (p!=0) TF1Convolution::SetParameters(p); // first refresh the parameters
307 
308  Double_t result = 0.;
309  if (fFlagFFT) result = EvalFFTConv(t[0]);
310  else result = EvalNumConv(t[0]);
311  return result;
312 }
313 ////////////////////////////////////////////////////////////////////////////////
314 
316 {
317  if (n<0) return;
318  fNofPoints = n;
319  if (fGraphConv) fGraphConv -> Set(fNofPoints); //set nof points of the Tgraph
320  fFlagGraph = false; // to indicate we need to re-do the graph
321 }
322 
323 ////////////////////////////////////////////////////////////////////////////////
324 
326 {
327  bool equalParams = true;
328  for (int i=0; i<fNofParams1; i++) {
329  fFunction1 -> SetParameter(i,p[i]);
330  equalParams &= ( fParams1[i] == p[i] );
331  fParams1[i] = p[i];
332  }
333  Int_t k = 0;
334  Int_t offset = 0;
335  Int_t offset2 = 0;
336  if (fCstIndex!=-1) offset = 1;
337  Int_t totalnofparams = fNofParams1+fNofParams2+offset;
338  for (int i=fNofParams1; i<totalnofparams; i++) {
339  if (k==fCstIndex)
340  {
341  k++;
342  offset2=1;
343  continue;
344  }
345  fFunction2 -> SetParameter(k,p[i-offset2]);
346  equalParams &= ( fParams2[k-offset2] == p[i-offset2] );
347  fParams2[k-offset2] = p[i-offset2];
348  k++;
349  }
350  // std::cout << "parameters for function1 : ";
351  // for (int i = 0; i < fFunction1->GetNpar(); ++i)
352  // std::cout << fFunction1->GetParameter(i) << " ";
353  // std::cout << "\nparameters for function2 : ";
354  // for (int i = 0; i < fFunction2->GetNpar(); ++i)
355  // std::cout << fFunction2->GetParameter(i) << " ";
356  // std::cout << std::endl;
357 
358 //do the graph for FFT convolution
359  if (!equalParams) fFlagGraph = false; // to indicate we need to re-do the convolution
360  // if (fFlagFFT)
361  // {
362  // MakeFFTConv();
363  // }
364 }
365 
366 ////////////////////////////////////////////////////////////////////////////////
367 
369  Double_t p4, Double_t p5, Double_t p6, Double_t p7)
370 {
371  Double_t params[]={p0,p1,p2,p3,p4,p5,p6,p7};
373 }
374 
375 ////////////////////////////////////////////////////////////////////////////////
376 
378 {
379  if (percentage<0) return;
380  double range = fXmax = fXmin;
381  fXmin -= percentage * range;
382  fXmax += percentage * range;
383  fFlagGraph = false; // to indicate we need to re-do the convolution
384 }
385 
386 ////////////////////////////////////////////////////////////////////////////////
387 
389 {
390  if (a>=b) return;
391  fXmin = a;
392  fXmax = b;
393  if (fFlagFFT && ( a==-TMath::Infinity() || b==TMath::Infinity() ) )
394  {
395  Warning("TF1Convolution::SetRange()","In FFT mode, range can not be infinite. Infinity has been replaced by range of first function plus a bufferzone to avoid spillover.");
396  if (a ==-TMath::Infinity()) fXmin = fFunction1 -> GetXmin();
397  if ( b== TMath::Infinity()) fXmax = fFunction1 -> GetXmax();
398  // add a spill over of 10% in this case
399  SetExtraRange(0.1);
400  }
401  fFlagGraph = false; // to indicate we need to re-do the convolution
402 }
An array of TObjects.
Definition: TObjArray.h:39
float xmin
Definition: THbookFile.cxx:93
virtual TFormula * GetFormula()
Definition: TF1.h:339
static double p3(double t, double a, double b, double c, double d)
void Fatal(const char *location, const char *msgfmt,...)
Collectable string class.
Definition: TObjString.h:32
TString & ReplaceAll(const TString &s1, const TString &s2)
Definition: TString.h:635
std::shared_ptr< TGraph > fGraphConv
#define gROOT
Definition: TROOT.h:340
Double_t GetXmin() const
Basic string class.
Definition: TString.h:137
std::vector< Double_t > fParams2
int Int_t
Definition: RtypesCore.h:41
bool Bool_t
Definition: RtypesCore.h:59
TArc * a
Definition: textangle.C:12
const char * Data() const
Definition: TString.h:349
static const double x2[5]
Double_t x[n]
Definition: legend1.C:17
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString...
Definition: TString.cxx:2334
Double_t GetXmax() const
static double p2(double t, double a, double b, double c)
virtual void Copy(TObject &f1) const
Copy this F1 to a new F1.
Definition: TF1.cxx:759
void MakeFFTConv()
perform the FFT of the two functions
void Info(const char *location, const char *msgfmt,...)
std::vector< std::vector< double > > Data
void InitializeDataMembers(TF1 *function1, TF1 *function2, Bool_t useFFT)
use copy instead of Clone
static IntegrationOneDim::Type DefaultIntegratorType()
Double_t Infinity()
Definition: TMath.h:648
void Error(const char *location, const char *msgfmt,...)
double IntegralLow(const IGenFunction &f, double b)
evaluate the Integral of a function f over the over the semi-infinite interval (-inf,b)
Definition: Integrator.h:300
double Integral(Function &f, double a, double b)
evaluate the Integral of a function f over the defined interval (a,b)
Definition: Integrator.h:506
void SetRange(Double_t a, Double_t b)
double IntegralUp(const IGenFunction &f, double a)
evaluate the Integral of a function f over the semi-infinite interval (a,+inf)
Definition: Integrator.h:282
virtual Double_t GetPointReal(Int_t ipoint, Bool_t fromInput=kFALSE) const =0
static TVirtualFFT * FFT(Int_t ndim, Int_t *n, Option_t *option)
Returns a pointer to the FFT of requested size and type.
Double_t EvalNumConv(Double_t t)
perform numerical convolution could in principle cache the integral in a Graph as it is done for the ...
void SetNofPointsFFT(Int_t n)
User Class for performing numerical integration of a function in one dimension.
Definition: Integrator.h:102
static void InitStandardFunctions()
Create the basic function objects.
Definition: TF1.cxx:2236
static double p1(double t, double a, double b)
float xmax
Definition: THbookFile.cxx:93
TF1Convolution(TF1 *function1, TF1 *function2, Bool_t useFFT=true)
constructor from the two function pointer and a flag is using FFT
void Warning(const char *location, const char *msgfmt,...)
TObjArray * Tokenize(const TString &delim) const
This function is used to isolate sequential tokens in a TString.
Definition: TString.cxx:2240
TVirtualFFT is an interface class for Fast Fourier Transforms.
Definition: TVirtualFFT.h:92
TRObject operator()(const T1 &t1) const
const char * GetParName(Int_t ipar) const
double f(double x)
double Double_t
Definition: RtypesCore.h:55
Double_t operator()(Double_t *t, Double_t *p)
void SetParameters(Double_t *p)
Double_t EvalFFTConv(Double_t t)
Bool_t IsValid() const
Definition: TFormula.h:186
double f2(const double *x)
1-Dim function class
Definition: TF1.h:149
A Graph is a graphics object made of two arrays X and Y with npoints each.
Definition: TGraph.h:53
TF1 * f1
Definition: legend1.C:11
R__EXTERN Int_t gDebug
Definition: Rtypes.h:128
std::vector< TString > fParNames
double result[121]
std::vector< Double_t > fParams1
const Int_t n
Definition: legend1.C:16
void SetExtraRange(Double_t percentage)