Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TF1NormSum.cxx
Go to the documentation of this file.
1 // @(#)root/hist:$Id$
2 // Authors: Lorenzo Moneta, AurĂ©lie Flandi 27/08/14
3 //
4 /**********************************************************************
5  * *
6  * Copyright (c) 2015 ROOT Team, CERN/PH-SFT *
7  * *
8  * *
9  **********************************************************************/
10 
11 #include "TROOT.h"
12 #include "TClass.h"
13 #include "TMath.h"
14 #include "TF1NormSum.h"
15 #include "Math/WrappedFunction.h"
16 #include "Math/WrappedTF1.h"
17 
18 ClassImp(TF1NormSum);
19 
20 /** \class TF1NormSum
21  \ingroup Hist
22 Class adding two functions: c1*f1+c2*f2
23 */
24 
25 ////////////////////////////////////////////////////////////////////////////////
26 /// Function to find and rename duplicate parameters with the same name
27 
28 template<class Iterator>
29 void FixDuplicateNames(Iterator begin, Iterator end) {
30 
31  // make a map of values
32 
33  std::multimap<TString, int > parMap;
34  for (Iterator it = begin; it != end; ++it) {
35  parMap.insert( std::make_pair( *it, std::distance(begin,it) ) );
36  }
37  for ( auto & elem : parMap) {
38  TString name = elem.first;
39  int n = parMap.count( name);
40  if (n > 1 ) {
41  std::pair <std::multimap<TString,int>::iterator, std::multimap<TString,int>::iterator> ret;
42  ret = parMap.equal_range(name);
43  int i = 0;
44  for (std::multimap<TString,int>::iterator it=ret.first; it!=ret.second; ++it) {
45  *(begin+it->second) = TString::Format("%s%d",name.Data(),++i);
46  }
47  }
48  }
49 
50 }
51 
52 ////////////////////////////////////////////////////////////////////////////////
53 
54 void TF1NormSum::InitializeDataMembers(const std::vector<TF1 *> &functions, const std::vector<Double_t> &coeffs,
55  Double_t scale)
56 {
57 
58  fScale = scale;
59  fCoeffs = coeffs;
60  fNOfFunctions = functions.size();
61  fCstIndexes = std::vector < Int_t > (fNOfFunctions);
62  fParNames = std::vector<TString> (fNOfFunctions);
63  fParNames.reserve(3*fNOfFunctions); // enlarge capacity for function parameters
64 
65  // fill fFunctions with unique_ptr's
66  fFunctions = std::vector<std::unique_ptr<TF1>>(functions.size());
67  for (unsigned int n = 0; n < fNOfFunctions; n++) {
68  // use TF1::Copy and not clone to copy the TF1 pointers
69  // and use IsA()::New() in case we have base class pointers
70  TF1 * f = (TF1*) functions[n]->IsA()->New();
71  functions[n]->Copy(*f);
72  fFunctions[n] = std::unique_ptr<TF1>(f);
73 
74 
75  if (!fFunctions[n])
76  Fatal("InitializeDataMembers", "Invalid input function -- abort");
77 
78  fFunctions[n]->SetBit(TF1::kNotGlobal, kTRUE);
79  }
80 
81  for (unsigned int n=0; n < fNOfFunctions; n++)
82  {
83  int npar = fFunctions[n] -> GetNpar();
84  fCstIndexes[n] = fFunctions[n] -> GetParNumber("Constant");//return -1 if there is no constant parameter
85  fParNames[n] = TString::Format("Coeff%d",n);
86  if (fCstIndexes[n]!= -1) //if there exists a constant parameter
87  {
88  fFunctions[n] -> FixParameter(fCstIndexes[n], 1.); // fixes the parameters called "Constant" to 1
89  int k = 0; // index for the temp array, k wil go form 0 until fNofNonCstParameter
90  for (int i=0; i<npar; i++) // go through all the parameter to
91  {
92  if (i==fCstIndexes[n]) continue; // go to next step if this is the constant parameter
93  fParNames.push_back( fFunctions[n] -> GetParName(i) );
94  k++;
95  }
96  }
97  else {
98  for (int i=0; i < npar; i++) //go through all the parameter to
99  {
100  fParNames.push_back( fFunctions[n] -> GetParName(i) );
101  }
102  }
103  //normalize the functions if it is not already done (do at the end so constant parameter is not zero)
104  if (!fFunctions[n] -> IsEvalNormalized()) fFunctions[n] -> SetNormalized(true);
105  }
106 
107  // Set range
108  if (fNOfFunctions == 0) {
109  fXmin = 0.;
110  fXmax = 1.;
111  // Info("InitializeDataMembers", "Initializing empty TF1NormSum with default [0,1] range");
112  } else {
113  fFunctions[0]->GetRange(fXmin, fXmax);
114  if (fXmin >= fXmax) {
115  fXmin = 0.;
116  fXmax = 1.;
117  // Info("InitializeDataMembers", "Initializing empty TF1NormSum with default [0,1] range");
118  }
119  for (unsigned int n = 1; n < fNOfFunctions; n++) {
120  fFunctions[n]->SetRange(fXmin, fXmax);
121  fFunctions[n]->Update();
122  }
123  }
124 
125  FixDuplicateNames(fParNames.begin() + fNOfFunctions, fParNames.end());
126 }
127 
128 ////////////////////////////////////////////////////////////////////////////////
129 
130 TF1NormSum::TF1NormSum()
131 {
132  fNOfFunctions = 0;
133  fScale = 1.;
134  fFunctions = std::vector<std::unique_ptr<TF1>>(0); // Vector of size fNOfFunctions containing TF1 functions
135  fCoeffs = std::vector < Double_t >(0) ; // Vector of size fNOfFunctions containing coefficients in front of each function
136  fCstIndexes = std::vector < Int_t > (0);
137  fXmin = 0; // Dummy values of xmin and xmax
138  fXmax = 1;
139 }
140 
141 ////////////////////////////////////////////////////////////////////////////////
142 
143 TF1NormSum::TF1NormSum(const std::vector <TF1*> &functions, const std::vector <Double_t> &coeffs, Double_t scale)
144 {
145  InitializeDataMembers(functions, coeffs, scale);
146 }
147 
148 ////////////////////////////////////////////////////////////////////////////////
149 /// TF1NormSum constructor taking 2 functions, and 2 coefficients (if not equal to 1)
150 
151 TF1NormSum::TF1NormSum(TF1* function1, TF1* function2, Double_t coeff1, Double_t coeff2, Double_t scale)
152 {
153  std::vector<TF1 *> functions(2);
154  std::vector < Double_t > coeffs(2);
155 
156  functions = {function1, function2};
157  coeffs = {coeff1, coeff2};
158 
159  InitializeDataMembers(functions, coeffs,scale);
160 }
161 
162 ////////////////////////////////////////////////////////////////////////////////
163 /// TF1NormSum constructor taking 3 functions, and 3 coefficients (if not equal to 1)
164 
165 TF1NormSum::TF1NormSum(TF1* function1, TF1* function2, TF1* function3, Double_t coeff1, Double_t coeff2, Double_t coeff3, Double_t scale)
166 {
167  std::vector<TF1 *> functions(3);
168  std::vector < Double_t > coeffs(3);
169 
170  functions = {function1, function2, function3};
171  coeffs = {coeff1, coeff2, coeff3};
172 
173  InitializeDataMembers(functions, coeffs,scale);
174 }
175 
176 ////////////////////////////////////////////////////////////////////////////////
177 /// TF1NormSum constructor taking any addition of formulas with coefficient or not
178 ///
179 /// - example 1 : 2.*expo + gauss + 0.5* gauss
180 /// - example 2 : expo + 0.3*f1 if f1 is defined in the list of functions
181 
182 TF1NormSum::TF1NormSum(const TString &formula, Double_t xmin, Double_t xmax)
183 {
184  TF1::InitStandardFunctions();
185 
186  TObjArray *arrayall = formula.Tokenize("*+");
187  TObjArray *arraytimes = formula.Tokenize("*") ;
188  Int_t noffunctions = (formula.Tokenize("+")) -> GetEntries();
189  Int_t nofobj = arrayall -> GetEntries();
190  Int_t nofcoeffs = nofobj - noffunctions;
191 
192  std::vector<TF1 *> functions(noffunctions);
193  std::vector < Double_t > coeffs(noffunctions);
194  std::vector < TString > funcstringall(nofobj);
195  std::vector < Int_t > indexsizetimes(nofcoeffs+1);
196  std::vector < Bool_t > isacoeff(nofobj);//1 is it is a coeff, 0 if it is a functions
197 
198  for (int i=0; i<nofobj; i++)
199  {
200  funcstringall[i] = ((TObjString*)((*arrayall)[i])) -> GetString();
201  funcstringall[i].ReplaceAll(" ","");
202  }
203  //algorithm to determine which object is a coefficient and which is a function
204  //uses the fact that the last item of funcstringtimes[i].Tokenize("+") is always a coeff.
205  Int_t j = 0;
206  Int_t k = 1;
207  for (int i=0; i<nofcoeffs+1; i++)
208  {
209  indexsizetimes[i] = ( ( ( (TObjString*)(*arraytimes)[i] ) -> GetString() ).Tokenize("+") ) -> GetEntries();
210  while (k < indexsizetimes[i])
211  {
212  isacoeff[k+j-1] = 0;
213  k++;
214  }
215  j = j+indexsizetimes[i];
216  if (j==nofobj) isacoeff[j-1] = 0; //the last one is never a coeff
217  else isacoeff[j-1] = 1;
218  k = 1;
219  }
220 
221  Double_t old_xmin = 0.0, old_xmax = 0.0;
222  k = 0; // index of term in funcstringall
223  for (int i=0; i<noffunctions; i++)
224  {
225  // first, handle coefficient
226  if (isacoeff[k]) {
227  coeffs[i] = funcstringall[k].Atof();
228  k++;
229  } else {
230  coeffs[i] = 1.;
231  }
232 
233  // then, handle function
234  functions[i] = (TF1 *)(gROOT->GetListOfFunctions()->FindObject(funcstringall[k]));
235  if (!functions[i])
236  Error("TF1NormSum", "Function %s does not exist", funcstringall[k].Data());
237  // (set range for first function, which determines range of whole TF1NormSum)
238  if (i == 0) {
239  functions[i]->GetRange(old_xmin, old_xmax);
240  functions[i]->SetRange(xmin, xmax);
241  }
242 
243  k++;
244  }
245  InitializeDataMembers(functions, coeffs,1.);
246 
247  // Set range of first function back to original state
248  if (noffunctions > 0 && functions[0])
249  functions[0]->SetRange(old_xmin, old_xmax);
250 }
251 
252 ////////////////////////////////////////////////////////////////////////////////
253 /// Copy constructor (necessary to hold unique_ptr as member variable)
254 
255 TF1NormSum::TF1NormSum(const TF1NormSum &nsum)
256 {
257  nsum.Copy((TObject &)*this);
258 }
259 
260 ////////////////////////////////////////////////////////////////////////////////
261 /// Operator =
262 
263 TF1NormSum &TF1NormSum::operator=(const TF1NormSum &rhs)
264 {
265  if (this != &rhs)
266  rhs.Copy(*this);
267  return *this;
268 }
269 
270 ////////////////////////////////////////////////////////////////////////////////
271 /// Overload the parenthesis to add the functions
272 
273 double TF1NormSum::operator()(const Double_t *x, const Double_t *p)
274 {
275  // first refresh the parameters
276  if (p != 0)
277  SetParameters(p);
278 
279  Double_t sum = 0.;
280  for (unsigned int n=0; n<fNOfFunctions; n++)
281  sum += fCoeffs[n]*(fFunctions[n] -> EvalPar(x,0));
282 
283  // normalize by a scale parameter (typically the bin width)
284  return fScale * sum;
285 }
286 
287 ////////////////////////////////////////////////////////////////////////////////
288 /// Return array of parameters
289 
290 std::vector<double> TF1NormSum::GetParameters() const {
291  std::vector<double> params(GetNpar() );
292  int offset = 0;
293  int nOfNonCstParams = 0;
294  for (unsigned int n=0; n<fNOfFunctions; n++)
295  {
296  params[n] = fCoeffs[n]; // copy the coefficients
297  offset += nOfNonCstParams; // offset to go along the list of parameters
298  int k = 0;
299  for (int j = 0; j < fFunctions[n]->GetNpar(); ++j) {
300  if (j != fCstIndexes[n]) {
301  params[k+fNOfFunctions+offset] = fFunctions[n]->GetParameter(j);
302  k++;
303  }
304  }
305  nOfNonCstParams = k;
306  }
307  return params;
308 }
309 ////////////////////////////////////////////////////////////////////////////////
310 /// Initialize array of all parameters.
311 ///
312 /// double *params must contains first an array of the coefficients, then an array of the parameters.
313 
314 void TF1NormSum::SetParameters(const Double_t *params) // params should have the size [fNOfFunctions][fNOfNonCstParams]
315 {
316  for (unsigned int n=0; n<fNOfFunctions; n++) //initialization of the coefficients
317  {
318  fCoeffs[n] = params[n];
319  }
320  Int_t offset = 0;
321  int k = 0; // k indicates the number of non-constant parameter per function
322  for (unsigned int n=0; n<fNOfFunctions; n++)
323  {
324  bool equalParams = true;
325  Double_t * funcParams = fFunctions[n]->GetParameters();
326  int npar = fFunctions[n]->GetNpar();
327  offset += k; // offset to go along the list of parameters
328  k = 0; // reset k value for next function
329  for (int i = 0; i < npar; ++i) {
330  // constant parameters can be only one
331  if (i != fCstIndexes[n])
332  {
333  // check if they are equal
334  equalParams &= (funcParams[i] == params[k+fNOfFunctions+offset] );
335  funcParams[i] = params[k+fNOfFunctions+offset];
336  k++;
337  }
338  }
339  // update function integral if not equal
340  if (!equalParams) fFunctions[n]->Update();
341 
342  }
343 }
344 
345 ////////////////////////////////////////////////////////////////////////////////
346 /// Initialize array of all parameters.
347 ///
348 /// Overload the TF1::SetParameters() method.
349 /// A maximum of 10 parameters must be used, with first the coefficients, then the parameters
350 
351 void TF1NormSum::SetParameters(Double_t p0, Double_t p1, Double_t p2, Double_t p3, Double_t p4,
352  Double_t p5, Double_t p6, Double_t p7, Double_t p8, Double_t p9, Double_t p10)
353 {
354  const double params[] = {p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10};
355  TF1NormSum::SetParameters(params);
356 
357 }
358 
359 ////////////////////////////////////////////////////////////////////////////////
360 /// Return the number of (non constant) parameters including the coefficients: for 2 functions: c1,c2,p0,p1,p2,p3...
361 
362 Int_t TF1NormSum::GetNpar() const
363 {
364  Int_t nofparams = 0;
365  for (unsigned int n=0; n<fNOfFunctions; ++n)
366  {
367  nofparams += fFunctions[n]->GetNpar();
368  if (fCstIndexes[n] >= 0) nofparams -= 1;
369  }
370  return nofparams + fNOfFunctions; //fNOfFunctions for the coefficients
371 }
372 
373 ////////////////////////////////////////////////////////////////////////////////
374 
375 void TF1NormSum::SetRange(Double_t a, Double_t b)
376 {
377  if (a >= b) {
378  Warning("SetRange", "Invalid range: %f >= %f", a, b);
379  return;
380  }
381 
382  fXmin = a;
383  fXmax = b;
384 
385  for (unsigned int n = 0; n < fNOfFunctions; n++) {
386  fFunctions[n]->SetRange(a, b);
387  fFunctions[n]->Update();
388  }
389 }
390 
391 ////////////////////////////////////////////////////////////////////////////////
392 
393 void TF1NormSum::GetRange(Double_t &a, Double_t &b) const
394 {
395  a = fXmin;
396  b = fXmax;
397 }
398 
399 ////////////////////////////////////////////////////////////////////////////////
400 /// Update the component functions of the normalized sum
401 
402 void TF1NormSum::Update()
403 {
404  for (unsigned int n = 0; n < fNOfFunctions; n++)
405  fFunctions[n]->Update();
406 }
407 
408 ////////////////////////////////////////////////////////////////////////////////
409 
410 void TF1NormSum::Copy(TObject &obj) const
411 {
412  ((TF1NormSum &)obj).fNOfFunctions = fNOfFunctions;
413  ((TF1NormSum &)obj).fScale = fScale;
414  ((TF1NormSum &)obj).fXmin = fXmin;
415  ((TF1NormSum &)obj).fXmax = fXmax;
416  ((TF1NormSum &)obj).fCoeffs = fCoeffs;
417  ((TF1NormSum &)obj).fCstIndexes = fCstIndexes;
418  ((TF1NormSum &)obj).fParNames = fParNames;
419 
420  // Clone objects in unique_ptr's
421  ((TF1NormSum &)obj).fFunctions = std::vector<std::unique_ptr<TF1>>(fNOfFunctions);
422  for (unsigned int n = 0; n < fNOfFunctions; n++) {
423  TF1 * f = (TF1*) fFunctions[n]->IsA()->New();
424  fFunctions[n]->Copy(*f);
425  ((TF1NormSum &)obj).fFunctions[n] = std::unique_ptr<TF1>(f);
426  }
427 }