Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TFormula.cxx
Go to the documentation of this file.
1// @(#)root/hist:$Id$
2// Author: Maciej Zimnoch 30/09/2013
3
4/*************************************************************************
5 * Copyright (C) 1995-2013, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12#include "TROOT.h"
13#include "TBuffer.h"
14#include "TMethod.h"
15#include "TF1.h"
16#include "TMethodCall.h"
17#include "TError.h"
18#include "TInterpreter.h"
19#include "TInterpreterValue.h"
20#include "TFormula.h"
21#include "TRegexp.h"
22
23#include "ROOT/StringUtils.hxx"
24
25#include <functional>
26#include <iomanip>
27#include <iostream>
28#include <limits>
29#include <memory>
30#include <set>
31#include <stack>
32#include <sstream>
33#include <unordered_map>
34
35using std::map, std::pair, std::make_pair, std::list, std::max, std::string;
36
37#ifdef WIN32
38#pragma optimize("",off)
39#endif
40#include "v5/TFormula.h"
41
42
43namespace {
44
45std::string doubleToString(double val)
46{
47 std::stringstream ss;
48 ss << std::setprecision(std::numeric_limits<double>::max_digits10) << val;
49 return ss.str();
50}
51
52bool areMatching(char opening, char closing)
53{
54 return (opening == '(' && closing == ')') || (opening == '{' && closing == '}') ||
55 (opening == '[' && closing == ']') || (opening == '\"' && closing == '\"');
56}
57
58bool isBalanced(const TString &s)
59{
60 std::stack<char> i;
61 auto sLength = s.Length();
62 for (Ssiz_t c = 0; c < sLength; ++c) {
63 if (s[c] == '[' || s[c] == '{' || s[c] == '(') {
64 i.push(s[c]);
65 } else if (s[c] == ']' || s[c] == '}' || s[c] == ')') {
66 if (i.empty() || !areMatching(i.top(), s[c])) {
67 Error("TFormula", "Found unbalanced char '%c' (expected closing of '%c') at index %d of '%s'", s[c], i.top(), c,
68 s.Data());
69 return false;
70 } else
71 i.pop();
72 } else if (s[c] == '\"') {
73 if (i.empty())
74 i.push(s[c]);
75 else if (areMatching(i.top(), s[c]))
76 i.pop();
77 else {
78 i.push(s[c]);
79 }
80 }
81 }
82 if (!i.empty()) {
83 Error("TFormula", "String '%s' with %zu unbalanced chars.", s.Data(), i.size());
84 return false;
85 }
86 return true;
87}
88
89// In the interpreter, we must match the SIMD width used by compiled ROOT.
90// ROOT::Double_v aliases the best available native SIMD type, which may differ
91// between compiled and interpreted contexts (e.g. with -march=native).
92// Therefore, we explicitly select the fixed-size SIMD type corresponding to
93// the native SIMD width used in compiled ROOT.
94std::string vectorizedArgType()
95{
96#ifdef R__HAS_STD_EXPERIMENTAL_SIMD
97 auto n = ROOT::Double_v::size();
98 return "std::experimental::resize_simd_t<" + std::to_string(n) + ", ROOT::Double_v>";
99#else
100 // For other possible VecCore backends, we assume using the same type is fine.
101 return "ROOT::Double_v";
102#endif
103}
104
105} // namespace
106
107/** \class TFormula TFormula.h "inc/TFormula.h"
108 \ingroup Hist
109 The Formula class
110
111 This is a new version of the TFormula class based on Cling.
112 This class is not 100% backward compatible with the old TFormula class, which is still available in ROOT as
113 `ROOT::v5::TFormula`. Some of the TFormula member functions available in version 5, such as
114 `Analyze` and `AnalyzeFunction` are not available in the new TFormula.
115 On the other hand formula expressions which were valid in version 5 are still valid in TFormula version 6
116
117 This class has been implemented during Google Summer of Code 2013 by Maciej Zimnoch.
118
119 ### Example of valid expressions:
120
121 - `sin(x)/x`
122 - `[0]*sin(x) + [1]*exp(-[2]*x)`
123 - `x + y**2`
124 - `x^2 + y^2`
125 - `[0]*pow([1],4)`
126 - `2*pi*sqrt(x/y)`
127 - `gaus(0)*expo(3) + ypol3(5)*x`
128 - `gausn(0)*expo(3) + ypol3(5)*x`
129 - `gaus(x, [0..2]) + expo(y, [3..4])`
130
131 In the last examples above:
132
133 - `gaus(0)` is a substitute for `[0]*exp(-0.5*((x-[1])/[2])**2)`
134 and (0) means start numbering parameters at 0
135 - `gausn(0)` is a substitute for `[0]*exp(-0.5*((x-[1])/[2])**2)/(sqrt(2*pi)*[2]))`
136 and (0) means start numbering parameters at 0
137 - `expo(3)` is a substitute for `exp([3]+[4]*x)`
138 - `pol3(5)` is a substitute for `par[5]+par[6]*x+par[7]*x**2+par[8]*x**3`
139 (`PolN` stands for Polynomial of degree N)
140 - `gaus(x, [0..2])` is a more explicit way of writing `gaus(0)`
141 - `expo(y, [3..4])` is a substitute for `exp([3]+[4]*y)`
142
143 See below the [full list of predefined functions](\ref FormulaFuncs) which can be used as shortcuts in
144 TFormula.
145
146 `TMath` functions can be part of the expression, eg:
147
148 - `TMath::Landau(x)*sin(x)`
149 - `TMath::Erf(x)`
150
151 Formula may contain constants, eg:
152
153 - `sqrt2`
154 - `e`
155 - `pi`
156 - `ln10`
157 - `infinity`
158
159 and more.
160
161 Formulas may also contain other user-defined ROOT functions defined with a
162 TFormula, eg, where `f1` is defined on one x-dimension and 2 parameters:
163
164 - `f1(x, [omega], [phi])`
165 - `f1([0..1])`
166 - `f1([1], [0])`
167 - `f1(y)`
168
169 To replace only parameter names, the dimension variable can be dropped.
170 Alternatively, to change only the dimension variable, the parameters can be
171 dropped. Note that if a parameter is dropped or keeps its old name, its old
172 value will be copied to the new function. The syntax used in the examples
173 above also applies to the predefined parametrized functions like `gaus` and
174 `expo`.
175
176 Comparisons operators are also supported `(&&, ||, ==, <=, >=, !)`
177
178 Examples:
179
180 `sin(x*(x<0.5 || x>1))`
181
182 If the result of a comparison is TRUE, the result is 1, otherwise 0.
183
184 Already predefined names can be given. For example, if the formula
185
186 `TFormula old("old",sin(x*(x<0.5 || x>1)))`
187
188 one can assign a name to the formula. By default the name of the object = title = formula itself.
189
190 `TFormula new("new","x*old")`
191
192 is equivalent to:
193
194 `TFormula new("new","x*sin(x*(x<0.5 || x>1))")`
195
196 The class supports unlimited number of variables and parameters.
197 By default the names which can be used for the variables are `x,y,z,t` or
198 `x[0],x[1],x[2],x[3],....x[N]` for N-dimensional formulas.
199
200 This class is not anymore the base class for the function classes `TF1`, but it has now
201 a data member of TF1 which can be accessed via `TF1::GetFormula`.
202
203 TFormula supports gradient and hessian calculations through clad.
204 To calculate the gradient one needs to first declare a `CladStorage` of the
205 same size as the number of parameters and then pass the variables and the
206 created `CladStorage`:
207
208 ```
209 TFormula f("f", "x*[0] - y*[1]");
210 Double_t p[] = {40, 30};
211 Double_t x[] = {1, 2};
212 f.SetParameters(p);
213 TFormula::CladStorage grad(2);
214 f.GradientPar(x, grad);
215 ```
216
217 The process is similar for hessians, except that the size of the created
218 CladStorage should be the square of the number of parameters because
219 `HessianPar` returns a flattened matrix:
220
221 ```
222 TFormula::CladStorage hess(4);
223 f.HessianPar(x, hess);
224 ```
225
226 \anchor FormulaFuncs
227 ### List of predefined functions
228
229 The list of available predefined functions which can be used as shortcuts is the following:
230 1. One Dimensional functions:
231 - `gaus` is a substitute for `[Constant]*exp(-0.5*((x-[Mean])/[Sigma])*((x-[Mean])/[Sigma]))`
232 - `landau` is a substitute for `[Constant]*TMath::Landau (x,[MPV],[Sigma],false)`
233 - `expo` is a substitute for `exp([Constant]+[Slope]*x)`
234 - `crystalball` is substitute for `[Constant]*ROOT::Math::crystalball_function (x,[Alpha],[N],[Sigma],[Mean])`
235 - `breitwigner` is a substitute for `[p0]*ROOT::Math::breitwigner_pdf (x,[p2],[p1])`
236 - `pol0,1,2,...N` is a substitute for a polynomial of degree `N` :
237 `([p0]+[p1]*x+[p2]*pow(x,2)+....[pN]*pow(x,N)`
238 - `cheb0,1,2,...N` is a substitute for a Chebyshev polynomial of degree `N`:
239 `ROOT::Math::Chebyshev10(x,[p0],[p1],[p2],...[pN])`. Note the maximum N allowed here is 10.
240 2. Two Dimensional functions:
241 - `xygaus` is a substitute for `[Constant]*exp(-0.5*pow(((x-[MeanX])/[SigmaX]),2 )- 0.5*pow(((y-[MeanY])/[SigmaY]),2))`, a 2d Gaussian without correlation.
242 - `bigaus` is a substitute for `[Constant]*ROOT::Math::bigaussian_pdf (x,y,[SigmaX],[SigmaY],[Rho],[MeanX],[MeanY])`, a 2d gaussian including a correlation parameter.
243 Note the different order of parameters for bigaus: [Constant]=[0], [SigmaX]=[2], [SigmaY]=[4], [Rho]=[5], [MeanX]=[1], [MeanY]=[3]
244 3. Three Dimensional functions:
245 - `xyzgaus` is for a 3d Gaussians without correlations:
246 `[Constant]*exp(-0.5*pow(((x-[MeanX])/[SigmaX]),2 )- 0.5*pow(((y-[MeanY])/[SigmaY]),2 )- 0.5*pow(((z-[MeanZ])/[SigmaZ]),2))`
247
248
249 ### An expanded note on variables and parameters
250
251 In a TFormula, a variable is a defined by a name `x`, `y`, `z` or `t` or an
252 index like `x[0]`, `x[1]`, `x[2]`; that is `x[N]` where N is an integer.
253
254 ```
255 TFormula("", "x[0] * x[1] + 10")
256 ```
257
258 Parameters are similar and can take any name. It is specified using brackets
259 e.g. `[expected_mass]` or `[0]`.
260
261 ```
262 TFormula("", "exp([expected_mass])-1")
263 ```
264
265 Variables and parameters can be combined in the same TFormula. Here we consider
266 a very simple case where we have an exponential decay after some time t and a
267 number of events with timestamps for which we want to evaluate this function.
268
269 ```
270 TFormula tf ("", "[0]*exp(-[1]*t)");
271 tf.SetParameter(0, 1);
272 tf.SetParameter(1, 0.5);
273
274 for (auto & event : events) {
275 tf.Eval(event.t);
276 }
277 ```
278
279 The distinction between variables and parameters arose from the TFormula's
280 application in fitting. There parameters are fitted to the data provided
281 through variables. In other applications this distinction can go away.
282
283 Parameter values can be provided dynamically using `TFormula::EvalPar`
284 instead of `TFormula::Eval`. In this way parameters can be used identically
285 to variables. See below for an example that uses only parameters to model a
286 function.
287
288 ```
289 Int_t params[2] = {1, 2}; // {vel_x, vel_y}
290 TFormula tf ("", "[vel_x]/sqrt(([vel_x + vel_y])**2)");
291
292 tf.EvalPar(nullptr, params);
293 ```
294
295 ### A note on operators
296
297 All operators of C/C++ are allowed in a TFormula with a few caveats.
298
299 The operators `|`, `&`, `%` can be used but will raise an error if used in
300 conjunction with a variable or a parameter. Variables and parameters are treated
301 as doubles internally for which these operators are not defined.
302 This means the following command will run successfully
303 ```root -q -e TFormula("", "x+(10%3)").Eval(0)```
304 but not
305 ```root -q -e TFormula("", "x%10").Eval(0)```.
306
307 The operator `^` is defined to mean exponentiation instead of the C/C++
308 interpretation xor. `**` is added, also meaning exponentiation.
309
310 The operators `++` and `@` are added, and are shorthand for the a linear
311 function. That means the expression `x@2` will be expanded to
312 ```[n]*x + [n+1]*2``` where n is the first previously unused parameter number.
313
314 \class TFormulaFunction
315 Helper class for TFormula
316
317 \class TFormulaVariable
318 Another helper class for TFormula
319
320 \class TFormulaParamOrder
321 Functor defining the parameter order
322*/
323
324// prefix used for function name passed to Cling
325static const TString gNamePrefix = "TFormula__";
326
327// static map of function pointers and expressions
328//static std::unordered_map<std::string, TInterpreter::CallFuncIFacePtr_t::Generic_t> gClingFunctions = std::unordered_map<TString, TInterpreter::CallFuncIFacePtr_t::Generic_t>();
329static std::unordered_map<std::string, void *> gClingFunctions = std::unordered_map<std::string, void * >();
330
332{
333 auto **fromv5 = (ROOT::v5::TFormula **)from;
334 auto **target = (TFormula **)to;
335
336 for (int i = 0; i < nobjects; ++i) {
337 if (fromv5[i] && target[i]) {
338 TFormula fnew(fromv5[i]->GetName(), fromv5[i]->GetExpFormula());
339 *(target[i]) = fnew;
340 target[i]->SetParameters(fromv5[i]->GetParameters());
341 }
342 }
343}
344
345using TFormulaUpdater_t = void (*)(Int_t nobjects, TObject **from, TObject **to);
347
349
350////////////////////////////////////////////////////////////////////////////////
352{
353 // operator ":" must be handled separately
354 const static std::set<char> ops {'+','^','-','/','*','<','>','|','&','!','=','?','%'};
355 return ops.end() != ops.find(c);
356}
357
358////////////////////////////////////////////////////////////////////////////////
360{
361 // Note that square brackets do not count as brackets here!!!
362 char brackets[] = { ')','(','{','}'};
363 Int_t bracketsLen = sizeof(brackets)/sizeof(char);
364 for(Int_t i = 0; i < bracketsLen; ++i)
365 if(brackets[i] == c)
366 return true;
367 return false;
368}
369
370////////////////////////////////////////////////////////////////////////////////
372{
373 return !IsBracket(c) && !IsOperator(c) && c != ',' && c != ' ';
374}
375
376////////////////////////////////////////////////////////////////////////////////
378{
379 return name == "x" || name == "z" || name == "y" || name == "t";
380}
381
382////////////////////////////////////////////////////////////////////////////////
384{
385 // check if the character at position i is part of a scientific notation
386 if ( (formula[i] == 'e' || formula[i] == 'E') && (i > 0 && i < formula.Length()-1) ) {
387 // handle cases: 2e+3 2e-3 2e3 and 2.e+3
388 if ( (isdigit(formula[i-1]) || formula[i-1] == '.') && ( isdigit(formula[i+1]) || formula[i+1] == '+' || formula[i+1] == '-' ) )
389 return true;
390 }
391 return false;
392}
393
394////////////////////////////////////////////////////////////////////////////////
396{
397 // check if the character at position i is part of a scientific notation
398 if ( (formula[i] == 'x' || formula[i] == 'X') && (i > 0 && i < formula.Length()-1) && formula[i-1] == '0') {
399 if (isdigit(formula[i+1]) )
400 return true;
401 static char hex_values[12] = { 'a','A', 'b','B','c','C','d','D','e','E','f','F'};
402 for (int jjj = 0; jjj < 12; ++jjj) {
403 if (formula[i+1] == hex_values[jjj])
404 return true;
405 }
406 }
407 // else
408 // return false;
409 // // handle cases: 2e+3 2e-3 2e3 and 2.e+3
410 // if ( (isdigit(formula[i-1]) || formula[i-1] == '.') && ( isdigit(formula[i+1]) || formula[i+1] == '+' || formula[i+1] == '-' ) )
411 // return true;
412 // }
413 return false;
414}
415
416////////////////////////////////////////////////////////////////////////////
417/// check if given position is in a parameter name i.e. within "[ ]"
418Bool_t TFormula::IsAParameterName(const TString & formula, int pos) {
419
421 if (pos == 0 || pos == formula.Length()-1) return false;
422 for (int i = pos-1; i >=0; i--) {
423 if (formula[i] == ']' ) return false;
424 if (formula[i] == '[' ) {
426 break;
427 }
428 }
429 if (!foundOpenParenthesis ) return false;
430
431 // search after the position
432 for (int i = pos+1; i < formula.Length(); i++) {
433 if (formula[i] == ']' ) return true;
434 }
435 return false;
436}
437
438
439////////////////////////////////////////////////////////////////////////////////
441 // implement comparison used to set parameter orders in TFormula
442 // want p2 to be before p10
443
444 // Returns true if (a < b), meaning a comes before b, and false if (a >= b)
445
446 TRegexp numericPattern("p?[0-9]+");
447 Ssiz_t len; // buffer to store length of regex match
448
449 int patternStart = numericPattern.Index(a, &len);
450 bool aNumeric = (patternStart == 0 && len == a.Length());
451
452 patternStart = numericPattern.Index(b, &len);
453 bool bNumeric = (patternStart == 0 && len == b.Length());
454
455 if (aNumeric && !bNumeric)
456 return true; // assume a (numeric) is always before b (not numeric)
457 else if (!aNumeric && bNumeric)
458 return false; // b comes before a
459 else if (!aNumeric && !bNumeric)
460 return a < b;
461 else {
462 int aInt = (a[0] == 'p') ? TString(a(1, a.Length())).Atoi() : a.Atoi();
463 int bInt = (b[0] == 'p') ? TString(b(1, b.Length())).Atoi() : b.Atoi();
464 return aInt < bInt;
465 }
466
467}
468
469////////////////////////////////////////////////////////////////////////////////
471{
472 /// Apply the name substitutions to the formula, doing all replacements in one pass
473
474 for (int i = 0; i < formula.Length(); i++) {
475 // start of name
476 // (a little subtle, since we want to match names like "{V0}" and "[0]")
477 if (isalpha(formula[i]) || formula[i] == '{' || formula[i] == '[') {
478 int j; // index to end of name
479 for (j = i + 1;
480 j < formula.Length() && (IsFunctionNameChar(formula[j]) // square brackets are function name chars
481 || (formula[i] == '{' && formula[j] == '}'));
482 j++)
483 ;
484 TString name = (TString)formula(i, j - i);
485
486 // std::cout << "Looking for name: " << name << std::endl;
487
488 // if we find the name, do the substitution
489 if (substitutions.find(name) != substitutions.end()) {
490 formula.Replace(i, name.Length(), "(" + substitutions[name] + ")");
491 i += substitutions[name].Length() + 2 - 1; // +2 for parentheses
492 // std::cout << "made substitution: " << name << " to " << substitutions[name] << std::endl;
493 } else if (isalpha(formula[i])) {
494 // if formula[i] is alpha, can skip to end of candidate name, otherwise, we'll just
495 // move one character ahead and try again
496 i += name.Length() - 1;
497 }
498 }
499 }
500}
501
502////////////////////////////////////////////////////////////////////////////////
504{
505 fName = "";
506 fTitle = "";
507 fClingInput = "";
508 fReadyToExecute = false;
509 fClingInitialized = false;
510 fAllParametersSetted = false;
511 fNdim = 0;
512 fNpar = 0;
513 fNumber = 0;
514 fClingName = "";
515 fFormula = "";
516 fLambdaPtr = nullptr;
517}
518
519////////////////////////////////////////////////////////////////////////////////
520static bool IsReservedName(const char* name)
521{
522 if (strlen(name)!=1) return false;
523 for (auto const & specialName : {"x","y","z","t"}){
524 if (strcmp(name,specialName)==0) return true;
525 }
526 return false;
527}
528
529////////////////////////////////////////////////////////////////////////////////
531{
532
533 // N.B. a memory leak may happen if user set bit after constructing the object,
534 // Setting of bit should be done only internally
537 gROOT->GetListOfFunctions()->Remove(this);
538 }
539
540 int nLinParts = fLinearParts.size();
541 if (nLinParts > 0) {
542 for (int i = 0; i < nLinParts; ++i) delete fLinearParts[i];
543 }
544}
545
546////////////////////////////////////////////////////////////////////////////////
547TFormula::TFormula(const char *name, const char *formula, bool addToGlobList, bool vectorize) :
548 TNamed(name,formula),
549 fClingInput(formula),fFormula(formula)
550{
551 fReadyToExecute = false;
552 fClingInitialized = false;
553 fNdim = 0;
554 fNpar = 0;
555 fNumber = 0;
556 fLambdaPtr = nullptr;
558#ifndef R__HAS_STD_EXPERIMENTAL_SIMD
559 fVectorized = false;
560#endif
561
562 FillDefaults();
563
564
565 //fName = gNamePrefix + name; // is this needed
566
567 // do not process null formulas.
568 if (!fFormula.IsNull() ) {
570
571 bool ok = PrepareFormula(fFormula);
572 // if the formula has been correctly initialized add to the list of global functions
573 if (ok) {
574 if (addToGlobList && gROOT) {
575 TFormula *old = nullptr;
577 old = dynamic_cast<TFormula *>(gROOT->GetListOfFunctions()->FindObject(name));
578 if (old)
579 gROOT->GetListOfFunctions()->Remove(old);
580 if (IsReservedName(name))
581 Error("TFormula", "The name %s is reserved as a TFormula variable name.\n", name);
582 else
583 gROOT->GetListOfFunctions()->Add(this);
584 }
586 }
587 }
588}
589
590////////////////////////////////////////////////////////////////////////////////
591/// Constructor from a full compile-able C++ expression
592
593TFormula::TFormula(const char *name, const char *formula, int ndim, int npar, bool addToGlobList) :
594 TNamed(name,formula),
595 fClingInput(formula),fFormula(formula)
596{
597 fReadyToExecute = false;
598 fClingInitialized = false;
599 fNpar = 0;
600 fNumber = 0;
601 fLambdaPtr = nullptr;
602 fFuncPtr = nullptr;
603 fGradFuncPtr = nullptr;
604 fHessFuncPtr = nullptr;
605
606
607 fNdim = ndim;
608 for (int i = 0; i < npar; ++i) {
609 DoAddParameter(TString::Format("p%d",i), 0, false);
610 }
612 assert (fNpar == npar);
613
614 bool ret = InitLambdaExpression(formula);
615
616 if (ret) {
617
619
620 fReadyToExecute = true;
621
622 if (addToGlobList && gROOT) {
623 TFormula *old = nullptr;
625 old = dynamic_cast<TFormula*> ( gROOT->GetListOfFunctions()->FindObject(name) );
626 if (old)
627 gROOT->GetListOfFunctions()->Remove(old);
628 if (IsReservedName(name))
629 Error("TFormula","The name %s is reserved as a TFormula variable name.\n",name);
630 else
631 gROOT->GetListOfFunctions()->Add(this);
632 }
634 }
635 else
636 Error("TFormula","Syntax error in building the lambda expression %s", formula );
637}
638
639////////////////////////////////////////////////////////////////////////////////
641 TNamed(formula.GetName(),formula.GetTitle())
642{
643 formula.TFormula::Copy(*this);
644
647 TFormula *old = (TFormula*)gROOT->GetListOfFunctions()->FindObject(formula.GetName());
648 if (old)
649 gROOT->GetListOfFunctions()->Remove(old);
650
651 if (IsReservedName(formula.GetName())) {
652 Error("TFormula","The name %s is reserved as a TFormula variable name.\n",formula.GetName());
653 } else
654 gROOT->GetListOfFunctions()->Add(this);
655 }
656
657}
658
659////////////////////////////////////////////////////////////////////////////////
660/// = operator.
661
663{
664 if (this != &rhs)
665 rhs.TFormula::Copy(*this);
666 return *this;
667}
668
669////////////////////////////////////////////////////////////////////////////////
671
672 std::string lambdaExpression = formula;
673
674 // check if formula exist already in the map
675 {
677
679 if (funcit != gClingFunctions.end() ) {
680 fLambdaPtr = funcit->second;
681 fClingInitialized = true;
682 return true;
683 }
684 }
685
686 // to be sure the interpreter is initialized
689
690 // set the cling name using hash of the static formulae map
691 auto hasher = gClingFunctions.hash_function();
693
694 //lambdaExpression = TString::Format("[&](double * x, double *){ return %s ;}",formula);
695 //TString lambdaName = TString::Format("mylambda_%s",GetName() );
696 TString lineExpr = TString::Format("std::function<double(double*,double*)> %s = %s ;",lambdaName.Data(), lambdaExpression.c_str() );
697 gInterpreter->ProcessLine(lineExpr);
698 fLambdaPtr = (void*) gInterpreter->ProcessLine(TString(lambdaName)+TString(";")); // add ; to avoid printing
699 if (fLambdaPtr != nullptr) {
701 gClingFunctions.insert ( std::make_pair ( lambdaExpression, fLambdaPtr) );
702 fClingInitialized = true;
703 return true;
704 }
705 fClingInitialized = false;
706 return false;
707}
708
709////////////////////////////////////////////////////////////////////////////////
710/// Compile the given expression with Cling
711/// backward compatibility method to be used in combination with the empty constructor
712/// if no expression is given , the current stored formula (retrieved with GetExpFormula()) or the title is used.
713/// return 0 if the formula compilation is successful
714
715Int_t TFormula::Compile(const char *expression)
716{
717 TString formula = expression;
718 if (formula.IsNull() ) {
719 formula = fFormula;
720 if (formula.IsNull() ) formula = GetTitle();
721 }
722
723 if (formula.IsNull() ) return -1;
724
725 // do not re-process if it was done before
726 if (IsValid() && formula == fFormula ) return 0;
727
728 // clear if a formula was already existing
729 if (!fFormula.IsNull() ) Clear();
730
731 fFormula = formula;
732
735 return (ret) ? 0 : 1;
736 }
737
738 if (fVars.empty() ) FillDefaults();
739 // prepare the formula for Cling
740 //printf("compile: processing formula %s\n",fFormula.Data() );
742 // pass formula in CLing
744
745 return (ret) ? 0 : 1;
746}
747
748////////////////////////////////////////////////////////////////////////////////
749void TFormula::Copy(TObject &obj) const
750{
751 TNamed::Copy(obj);
752 // need to copy also cling parameters
753 TFormula & fnew = dynamic_cast<TFormula&>(obj);
754
755 fnew.fClingParameters = fClingParameters;
756 fnew.fClingVariables = fClingVariables;
757
758 fnew.fFuncs = fFuncs;
759 fnew.fVars = fVars;
760 fnew.fParams = fParams;
761 fnew.fConsts = fConsts;
762 fnew.fFunctionsShortcuts = fFunctionsShortcuts;
763 fnew.fFormula = fFormula;
764 fnew.fNdim = fNdim;
765 fnew.fNpar = fNpar;
766 fnew.fNumber = fNumber;
767 fnew.fVectorized = fVectorized;
768 fnew.SetParameters(GetParameters());
769 // copy Linear parts (it is a vector of TFormula pointers) needs to be copied one by one
770 // looping at all the elements
771 // delete first previous elements
772 int nLinParts = fnew.fLinearParts.size();
773 if (nLinParts > 0) {
774 for (int i = 0; i < nLinParts; ++i) delete fnew.fLinearParts[i];
775 fnew.fLinearParts.clear();
776 }
777 // old size that needs to be copied
778 nLinParts = fLinearParts.size();
779 if (nLinParts > 0) {
780 fnew.fLinearParts.reserve(nLinParts);
781 for (int i = 0; i < nLinParts; ++i) {
782 TFormula * linearNew = new TFormula();
784 if (linearOld) {
785 linearOld->Copy(*linearNew);
786 fnew.fLinearParts.push_back(linearNew);
787 }
788 else
789 Warning("Copy","Function %s - expr %s has a dummy linear part %d",GetName(),GetExpFormula().Data(),i);
790 }
791 }
792
793 fnew.fClingInput = fClingInput;
794 fnew.fReadyToExecute = fReadyToExecute;
795 fnew.fClingInitialized = fClingInitialized.load();
796 fnew.fAllParametersSetted = fAllParametersSetted;
797 fnew.fClingName = fClingName;
798 fnew.fSavedInputFormula = fSavedInputFormula;
799 fnew.fLazyInitialization = fLazyInitialization;
800
801 // case of function based on a C++ expression (lambda's) which is ready to be compiled
803
804 bool ret = fnew.InitLambdaExpression(fnew.fFormula);
805 if (ret) {
806 fnew.SetBit(TFormula::kLambda);
807 fnew.fReadyToExecute = true;
808 }
809 else {
810 Error("TFormula","Syntax error in building the lambda expression %s", fFormula.Data() );
811 fnew.fReadyToExecute = false;
812 }
813 }
814
815 // use copy-constructor of TMethodCall
816 // if c++-14 could use std::make_unique
817 TMethodCall *m = (fMethod) ? new TMethodCall(*fMethod) : nullptr;
818 fnew.fMethod.reset(m);
819
820 fnew.fFuncPtr = fFuncPtr;
821 fnew.fGradGenerationInput = fGradGenerationInput;
822 fnew.fHessGenerationInput = fHessGenerationInput;
823 fnew.fGradFuncPtr = fGradFuncPtr;
824 fnew.fHessFuncPtr = fHessFuncPtr;
825
826}
827
828////////////////////////////////////////////////////////////////////////////////
829/// Clear the formula setting expression to empty and reset the variables and
830/// parameters containers.
831
833{
834 fNdim = 0;
835 fNpar = 0;
836 fNumber = 0;
837 fFormula = "";
838 fClingName = "";
839
840 fMethod.reset();
841
842 fClingVariables.clear();
843 fClingParameters.clear();
844 fReadyToExecute = false;
845 fClingInitialized = false;
846 fAllParametersSetted = false;
847 fFuncs.clear();
848 fVars.clear();
849 fParams.clear();
850 fConsts.clear();
851 fFunctionsShortcuts.clear();
852
853 // delete linear parts
854 int nLinParts = fLinearParts.size();
855 if (nLinParts > 0) {
856 for (int i = 0; i < nLinParts; ++i) delete fLinearParts[i];
857 }
858 fLinearParts.clear();
859
860}
861
862// Returns nullptr on failure.
863static std::unique_ptr<TMethodCall>
864prepareMethod(bool HasParameters, bool HasVariables, const char* FuncName,
865 bool IsVectorized, bool AddCladArrayRef = false) {
866 std::unique_ptr<TMethodCall>
867 Method = std::make_unique<TMethodCall>();
868
870 if (HasVariables || HasParameters) {
871 if (IsVectorized)
872 prototypeArguments.Append(vectorizedArgType() + " const*");
873 else
874 prototypeArguments.Append("Double_t const*");
875 }
877 prototypeArguments.Append(",");
878 prototypeArguments.Append("Double_t*");
879 };
880 if (HasParameters)
882
883 // We need an extra Double_t* for the gradient return result.
884 if (AddCladArrayRef) {
885 prototypeArguments.Append(",");
886 prototypeArguments.Append("Double_t*");
887 }
888
889 // Initialize the method call using real function name (cling name) defined
890 // by ProcessFormula
891 Method->InitWithPrototype(FuncName, prototypeArguments);
892 if (!Method->IsValid()) {
893 Error("prepareMethod",
894 "Can't compile function %s prototype with arguments %s", FuncName,
895 prototypeArguments.Data());
896 return nullptr;
897 }
898
899 return Method;
900}
901
903 if (!method) return nullptr;
904 CallFunc_t *callfunc = method->GetCallFunc();
905
907 Error("prepareFuncPtr", "Callfunc retuned from Cling is not valid");
908 return nullptr;
909 }
910
912 = gCling->CallFunc_IFacePtr(callfunc).fGeneric;
913 if (!Result) {
914 Error("prepareFuncPtr", "Compiled function pointer is null");
915 return nullptr;
916 }
917 return Result;
918}
919
920////////////////////////////////////////////////////////////////////////////////
921/// Sets TMethodCall to function inside Cling environment.
922/// TFormula uses it to execute function.
923/// After call, TFormula should be ready to evaluate formula.
924/// Returns false on failure.
925
927{
928 if (!fMethod) {
929 Bool_t hasParameters = (fNpar > 0);
930 Bool_t hasVariables = (fNdim > 0);
932 if (!fMethod) return false;
934 }
935 return fFuncPtr;
936}
937
938////////////////////////////////////////////////////////////////////////////////
939/// Inputs formula, transferred to C++ code into Cling
940
942{
943
945 // make sure the interpreter is initialized
948
949 // Trigger autoloading / autoparsing (ROOT-9840):
950 TString triggerAutoparsing = "namespace ROOT_TFormula_triggerAutoParse {\n"; triggerAutoparsing += fClingInput + "\n}";
952
953 // add pragma for optimization of the formula
954 fClingInput = TString("#pragma cling optimize(2)\n") + fClingInput;
955
956 // Now that all libraries and headers are loaded, Declare() a performant version
957 // of the same code:
960 if (!fClingInitialized) Error("InputFormulaIntoCling","Error compiling formula expression in Cling");
961 }
962}
963
964////////////////////////////////////////////////////////////////////////////////
965/// Fill structures with default variables, constants and function shortcuts
966
968{
969 const TString defvars[] = { "x","y","z","t"};
970 const pair<TString, Double_t> defconsts[] = {{"pi", TMath::Pi()},
971 {"sqrt2", TMath::Sqrt2()},
972 {"infinity", TMath::Infinity()},
973 {"e", TMath::E()},
974 {"ln10", TMath::Ln10()},
975 {"loge", TMath::LogE()},
976 {"c", TMath::C()},
977 {"g", TMath::G()},
978 {"h", TMath::H()},
979 {"k", TMath::K()},
980 {"sigma", TMath::Sigma()},
981 {"r", TMath::R()},
982 {"eg", TMath::EulerGamma()},
983 {"true", 1},
984 {"false", 0}};
985 // const pair<TString,Double_t> defconsts[] = { {"pi",TMath::Pi()}, {"sqrt2",TMath::Sqrt2()},
986 // {"infinity",TMath::Infinity()}, {"ln10",TMath::Ln10()},
987 // {"loge",TMath::LogE()}, {"true",1},{"false",0} };
988 const std::pair<TString, TString> funShortcuts[] = {
989 {"sign", "TMath::Sign"}, {"binomial", "TMath::Binomial"}, {"sq", "TMath::Sq"}};
990
991 std::vector<TString> defvars2(10);
992 for (int i = 0; i < 9; ++i)
993 defvars2[i] = TString::Format("x[%d]",i);
994
995 for (const auto &var : defvars) {
996 int pos = fVars.size();
997 fVars[var] = TFormulaVariable(var, 0, pos);
998 fClingVariables.push_back(0);
999 }
1000 // add also the variables defined like x[0],x[1],x[2],...
1001 // support up to x[9] - if needed extend that to higher value
1002 // const int maxdim = 10;
1003 // for (int i = 0; i < maxdim; ++i) {
1004 // TString xvar = TString::Format("x[%d]",i);
1005 // fVars[xvar] = TFormulaVariable(xvar,0,i);
1006 // fClingVariables.push_back(0);
1007 // }
1008
1009 for (auto con : defconsts) {
1010 fConsts[con.first] = con.second;
1011 }
1012 for (auto fun : funShortcuts) {
1013 fFunctionsShortcuts[fun.first] = fun.second;
1014 }
1015}
1016
1017////////////////////////////////////////////////////////////////////////////////
1018/// \brief Handling Polynomial Notation (polN)
1019///
1020/// This section describes how polynomials are handled in the code.
1021///
1022/// - If any name appears before 'pol', it is treated as a variable used in the polynomial.
1023/// - Example: `varpol2(5)` will be replaced with `[5] + [6]*var + [7]*var^2`.
1024/// - Empty name is treated like variable `x`.
1025///
1026/// - The extended format allows direct variable specification:
1027/// - Example: `pol2(x, 0)` or `pol(x, [A])`.
1028///
1029/// - Traditional syntax: `polN` represents a polynomial of degree `N`:
1030/// - `ypol1` → `[p0] + [p1] * y`
1031/// - `pol1(x, 0)` → `[p0] + [p1] * x`
1032/// - `pol2(y, 1)` → `[p1] + [p2] * y + [p3] * TMath::Sq(y)`
1033////////////////////////////////////////////////////////////////////////////////
1034
1036{
1037
1038 Int_t polPos = formula.Index("pol");
1039 while (polPos != kNPOS && !IsAParameterName(formula, polPos)) {
1040 Bool_t defaultVariable = false;
1041 TString variable;
1042 Int_t openingBracketPos = formula.Index('(', polPos);
1044 Bool_t defaultDegree = true;
1045 Int_t degree = 1, counter = 0;
1047 std::vector<TString> paramNames;
1048
1049 // check for extended format
1050 Bool_t isNewFormat = false;
1051 if (!defaultCounter) {
1052 TString args = formula(openingBracketPos + 1, formula.Index(')', polPos) - openingBracketPos - 1);
1053 if (args.Contains(",")) {
1054 isNewFormat = true;
1055 sdegree = formula(polPos + 3, openingBracketPos - polPos - 3);
1056 if (!sdegree.IsDigit())
1057 sdegree = "1";
1058 degree = sdegree.Atoi();
1059
1060 std::vector<TString> tokens;
1061 std::stringstream ss(args.Data());
1062 std::string token;
1063 while (std::getline(ss, token, ',')) { // spliting string at ,
1064 tokens.push_back(TString(token));
1065 }
1066
1067 if (!tokens.empty()) {
1068 variable = tokens[0];
1069 variable.Strip(TString::kBoth);
1070
1071 // extract counter if provided as a numeric value, without this it was not working with [A], [B]
1072 if (tokens.size() > 1) {
1075 if (counterStr.IsDigit()) {
1076 counter = counterStr.Atoi();
1077 }
1078 }
1079
1080 // store parameter names for later use
1081 for (size_t i = 1; i < tokens.size(); i++) {
1082 TString paramStr = tokens[i];
1083 paramStr.Strip(TString::kBoth);
1084 if (paramStr.BeginsWith("[") && paramStr.EndsWith("]")) {
1085 paramStr = paramStr(1, paramStr.Length() - 2);
1086 paramNames.push_back(paramStr);
1087
1088 if (fParams.find(paramStr) == fParams.end()) {
1090 fClingParameters.push_back(0.0);
1091 fNpar++;
1092 }
1093 }
1094 }
1095 }
1096 }
1097 }
1098
1099 // handle original format if not new format
1100 if (!isNewFormat) {
1101 if (!defaultCounter) {
1102 sdegree = formula(polPos + 3, openingBracketPos - polPos - 3);
1103 if (!sdegree.IsDigit())
1104 defaultCounter = true;
1105 }
1106 if (!defaultCounter) {
1107 degree = sdegree.Atoi();
1108 counter = TString(formula(openingBracketPos + 1, formula.Index(')', polPos) - openingBracketPos)).Atoi();
1109 } else {
1110 Int_t temp = polPos + 3;
1111 while (temp < formula.Length() && isdigit(formula[temp])) {
1112 defaultDegree = false;
1113 temp++;
1114 }
1115 degree = TString(formula(polPos + 3, temp - polPos - 3)).Atoi();
1116 }
1117
1118 if (polPos - 1 < 0 || !IsFunctionNameChar(formula[polPos - 1]) || formula[polPos - 1] == ':') {
1119 variable = "x";
1120 defaultVariable = true;
1121 } else {
1122 Int_t tmp = polPos - 1;
1123 while (tmp >= 0 && IsFunctionNameChar(formula[tmp]) && formula[tmp] != ':') {
1124 tmp--;
1125 }
1126 variable = formula(tmp + 1, polPos - (tmp + 1));
1127 }
1128 }
1129
1130 // build replacement string (modified)
1132 if (isNewFormat && !paramNames.empty()) {
1133 for (Int_t i = 0; i <= degree && i < static_cast<Int_t>(paramNames.size()); i++) {
1134 if (i == 0) {
1135 replacement = TString::Format("[%s]", paramNames[i].Data());
1136 } else if (i == 1) {
1137 replacement.Append(TString::Format("+[%s]*%s", paramNames[i].Data(), variable.Data()));
1138 } else {
1139 replacement.Append(TString::Format("+[%s]*%s^%d", paramNames[i].Data(), variable.Data(), i));
1140 }
1141 }
1142 } else {
1143 replacement = TString::Format("[%d]", counter);
1144 for (Int_t i = 1; i <= degree; i++) {
1145 if (i == 1) {
1146 replacement.Append(TString::Format("+[%d]*%s", counter + i, variable.Data()));
1147 } else {
1148 replacement.Append(TString::Format("+[%d]*%s^%d", counter + i, variable.Data(), i));
1149 }
1150 }
1151 }
1152
1153 if (degree > 0) {
1154 replacement.Insert(0, '(');
1155 replacement.Append(')');
1156 }
1157
1158 // create patern based on format either new or old
1159 TString pattern;
1160 if (isNewFormat) {
1161 pattern = formula(polPos, formula.Index(')', polPos) - polPos + 1);
1162 } else if (defaultCounter && !defaultDegree) {
1163 pattern = TString::Format("%spol%d", (defaultVariable ? "" : variable.Data()), degree);
1164 } else if (defaultCounter && defaultDegree) {
1165 pattern = TString::Format("%spol", (defaultVariable ? "" : variable.Data()));
1166 } else {
1167 pattern = TString::Format("%spol%d(%d)", (defaultVariable ? "" : variable.Data()), degree, counter);
1168 }
1169
1170 if (!formula.Contains(pattern)) {
1171 Error("HandlePolN", "Error handling polynomial function - expression is %s - trying to replace %s with %s ",
1172 formula.Data(), pattern.Data(), replacement.Data());
1173 break;
1174 }
1175
1176 if (formula == pattern) {
1177 SetBit(kLinear, true);
1178 fNumber = 300 + degree;
1179 }
1180
1181 formula.ReplaceAll(pattern, replacement);
1182 polPos = formula.Index("pol");
1183 }
1184}
1185
1186////////////////////////////////////////////////////////////////////////////////
1187/// Handling parametrized functions
1188/// Function can be normalized, and have different variable then x.
1189/// Variables should be placed in brackets after function name.
1190/// No brackets are treated like [x].
1191/// Normalized function has char 'n' after name, eg.
1192/// gausn[var](0) will be replaced with [0]*exp(-0.5*((var-[1])/[2])^2)/(sqrt(2*pi)*[2])
1193///
1194/// Adding function is easy, just follow these rules, and add to
1195/// `TFormula::FillParametrizedFunctions` defined further below:
1196///
1197/// - Key for function map is pair of name and dimension of function
1198/// - value of key is a pair function body and normalized function body
1199/// - {Vn} is a place where variable appear, n represents n-th variable from variable list.
1200/// Count starts from 0.
1201/// - [num] stands for parameter number.
1202/// If user pass to function argument 5, num will stand for (5 + num) parameter.
1203///
1204
1206{
1207 // define all parametrized functions
1210
1212 functionsNumbers["gaus"] = 100;
1213 functionsNumbers["bigaus"] = 102;
1214 functionsNumbers["landau"] = 400;
1215 functionsNumbers["expo"] = 200;
1216 functionsNumbers["crystalball"] = 500;
1217
1218 // replace old names xygaus -> gaus[x,y]
1219 formula.ReplaceAll("xyzgaus","gaus[x,y,z]");
1220 formula.ReplaceAll("xygausn","gausn[x,y]");
1221 formula.ReplaceAll("xygaus","gaus[x,y]");
1222 formula.ReplaceAll("xgaus","gaus[x]");
1223 formula.ReplaceAll("ygaus","gaus[y]");
1224 formula.ReplaceAll("zgaus","gaus[z]");
1225 formula.ReplaceAll("xyexpo","expo[x,y]");
1226 formula.ReplaceAll("xexpo","expo[x]");
1227 formula.ReplaceAll("yexpo","expo[y]");
1228 formula.ReplaceAll("zexpo","expo[z]");
1229 formula.ReplaceAll("xylandaun","landaun[x,y]");
1230 formula.ReplaceAll("xylandau","landau[x,y]");
1231 // at the moment pre-defined functions have no more than 3 dimensions
1232 const char * defaultVariableNames[] = { "x","y","z"};
1233
1234 for (map<pair<TString, Int_t>, pair<TString, TString>>::iterator it = functions.begin(); it != functions.end();
1235 ++it) {
1236
1237 TString funName = it->first.first;
1238 Int_t funDim = it->first.second;
1239 Int_t funPos = formula.Index(funName);
1240
1241 // std::cout << formula << " ---- " << funName << " " << funPos << std::endl;
1242 while (funPos != kNPOS && !IsAParameterName(formula, funPos)) {
1243
1244 // should also check that function is not something else (e.g. exponential - parse the expo)
1245 Int_t lastFunPos = funPos + funName.Length();
1246
1247 // check that first and last character is not a special character
1248 Int_t iposBefore = funPos - 1;
1249 // std::cout << "looping on funpos is " << funPos << " formula is " << formula << " function " << funName <<
1250 // std::endl;
1251 if (iposBefore >= 0) {
1252 assert(iposBefore < formula.Length());
1253 //if (isalpha(formula[iposBefore])) {
1254 if (IsFunctionNameChar(formula[iposBefore])) {
1255 // std::cout << "previous character for function " << funName << " is " << formula[iposBefore] << "- skip
1256 // " << std::endl;
1257 funPos = formula.Index(funName, lastFunPos);
1258 continue;
1259 }
1260 }
1261
1262 Bool_t isNormalized = false;
1263 if (lastFunPos < formula.Length()) {
1264 // check if function is normalized by looking at "n" character after function name (e.g. gausn)
1265 isNormalized = (formula[lastFunPos] == 'n');
1266 if (isNormalized)
1267 lastFunPos += 1;
1268 if (lastFunPos < formula.Length()) {
1269 char c = formula[lastFunPos];
1270 // check if also last character is not alphanumeric or is not an operator and not a parenthesis ( or [.
1271 // Parenthesis [] are used to express the variables
1272 if (isalnum(c) || (!IsOperator(c) && c != '(' && c != ')' && c != '[' && c != ']')) {
1273 // std::cout << "last character for function " << funName << " is " << c << " skip .." << std::endl;
1274 funPos = formula.Index(funName, lastFunPos);
1275 continue;
1276 }
1277 }
1278 }
1279
1280 if (isNormalized) {
1281 SetBit(kNormalized, true);
1282 }
1283 std::vector<TString> variables;
1284 Int_t dim = 0;
1285 TString varList = "";
1286 Bool_t defaultVariables = false;
1287
1288 // check if function has specified the [...] e.g. gaus[x,y]
1289 Int_t openingBracketPos = funPos + funName.Length() + (isNormalized ? 1 : 0);
1291 if (openingBracketPos >= formula.Length() || formula[openingBracketPos] != '[') {
1292 dim = funDim;
1293 variables.resize(dim);
1294 for (Int_t idim = 0; idim < dim; ++idim)
1295 variables[idim] = defaultVariableNames[idim];
1296 defaultVariables = true;
1297 } else {
1298 // in case of [..] found, assume they specify all the variables. Use it to get function dimension
1301 dim = varList.CountChar(',') + 1;
1302 variables.resize(dim);
1303 Int_t Nvar = 0;
1304 TString varName = "";
1305 for (Int_t i = 0; i < varList.Length(); ++i) {
1306 if (IsFunctionNameChar(varList[i])) {
1307 varName.Append(varList[i]);
1308 }
1309 if (varList[i] == ',') {
1310 variables[Nvar] = varName;
1311 varName = "";
1312 Nvar++;
1313 }
1314 }
1315 if (varName != "") // we will miss last variable
1316 {
1317 variables[Nvar] = varName;
1318 }
1319 }
1320 // check if dimension obtained from [...] is compatible with what is defined in existing pre-defined functions
1321 // std::cout << " Found dim = " << dim << " and function dimension is " << funDim << std::endl;
1322 if (dim != funDim) {
1323 pair<TString, Int_t> key = make_pair(funName, dim);
1324 if (functions.find(key) == functions.end()) {
1325 Error("PreProcessFormula", "Dimension of function %s is detected to be of dimension %d and is not "
1326 "compatible with existing pre-defined function which has dim %d",
1327 funName.Data(), dim, funDim);
1328 return;
1329 }
1330 // skip the particular function found - we might find later on the corresponding pre-defined function
1331 funPos = formula.Index(funName, lastFunPos);
1332 continue;
1333 }
1334 // look now for the (..) brackets to get the parameter counter (e.g. gaus(0) + gaus(3) )
1335 // need to start for a position
1337 bool defaultCounter = (openingParenthesisPos > formula.Length() || formula[openingParenthesisPos] != '(');
1338
1339 // Int_t openingParenthesisPos = formula.Index('(',funPos);
1340 // Bool_t defaultCounter = (openingParenthesisPos == kNPOS);
1341 Int_t counter;
1342 if (defaultCounter) {
1343 counter = 0;
1344 } else {
1345 // Check whether this is just a number in parentheses. If not, leave
1346 // it to `HandleFunctionArguments` to be parsed
1347
1348 TRegexp counterPattern("([0-9]+)");
1349 Ssiz_t len;
1350 if (counterPattern.Index(formula, &len, openingParenthesisPos) == -1) {
1351 funPos = formula.Index(funName, funPos + 1);
1352 continue;
1353 } else {
1354 counter =
1355 TString(formula(openingParenthesisPos + 1, formula.Index(')', funPos) - openingParenthesisPos - 1))
1356 .Atoi();
1357 }
1358 }
1359 // std::cout << "openingParenthesisPos " << openingParenthesisPos << " counter is " << counter << std::endl;
1360
1361 TString body = (isNormalized ? it->second.second : it->second.first);
1362 if (isNormalized && body == "") {
1363 Error("PreprocessFormula", "%d dimension function %s has no normalized form.", it->first.second,
1364 funName.Data());
1365 break;
1366 }
1367 for (int i = 0; i < body.Length(); ++i) {
1368 if (body[i] == '{') {
1369 // replace {Vn} with variable names
1370 i += 2; // skip '{' and 'V'
1371 Int_t num = TString(body(i, body.Index('}', i) - i)).Atoi();
1372 TString variable = variables[num];
1373 TString pattern = TString::Format("{V%d}", num);
1374 i -= 2; // restore original position
1375 body.Replace(i, pattern.Length(), variable, variable.Length());
1376 i += variable.Length() - 1; // update i to reflect change in body string
1377 } else if (body[i] == '[') {
1378 // update parameter counters in case of many functions (e.g. gaus(0)+gaus(3) )
1379 Int_t tmp = i;
1380 while (tmp < body.Length() && body[tmp] != ']') {
1381 tmp++;
1382 }
1383 Int_t num = TString(body(i + 1, tmp - 1 - i)).Atoi();
1384 num += counter;
1385 TString replacement = TString::Format("%d", num);
1386
1387 body.Replace(i + 1, tmp - 1 - i, replacement, replacement.Length());
1388 i += replacement.Length() + 1;
1389 }
1390 }
1391 TString pattern;
1393 pattern = TString::Format("%s%s", funName.Data(), (isNormalized ? "n" : ""));
1394 }
1396 pattern = TString::Format("%s%s(%d)", funName.Data(), (isNormalized ? "n" : ""), counter);
1397 }
1399 pattern = TString::Format("%s%s[%s]", funName.Data(), (isNormalized ? "n" : ""), varList.Data());
1400 }
1402 pattern =
1403 TString::Format("%s%s[%s](%d)", funName.Data(), (isNormalized ? "n" : ""), varList.Data(), counter);
1404 }
1406
1407 // set the number (only in case a function exists without anything else
1408 if (fNumber == 0 && formula.Length() <= (pattern.Length() - funPos) + 1) { // leave 1 extra
1409 fNumber = functionsNumbers[funName] + 10 * (dim - 1);
1410 }
1411
1412 // std::cout << " replace " << pattern << " with " << replacement << std::endl;
1413
1414 formula.Replace(funPos, pattern.Length(), replacement, replacement.Length());
1415
1416 funPos = formula.Index(funName);
1417 }
1418 // std::cout << " End loop of " << funName << " formula is now " << formula << std::endl;
1419 }
1420}
1421
1422////////////////////////////////////////////////////////////////////////////////
1423/// Handling parameter ranges, in the form of [1..5]
1425{
1426 TRegexp rangePattern("\\[[0-9]+\\.\\.[0-9]+\\]");
1427 Ssiz_t len;
1428 int matchIdx = 0;
1429 while ((matchIdx = rangePattern.Index(formula, &len, matchIdx)) != -1) {
1430 int startIdx = matchIdx + 1;
1431 int endIdx = formula.Index("..", startIdx) + 2; // +2 for ".."
1432 int startCnt = TString(formula(startIdx, formula.Length())).Atoi();
1433 int endCnt = TString(formula(endIdx, formula.Length())).Atoi();
1434
1435 if (endCnt <= startCnt)
1436 Error("HandleParamRanges", "End parameter (%d) <= start parameter (%d) in parameter range", endCnt, startCnt);
1437
1438 TString newString = "[";
1439 for (int cnt = startCnt; cnt < endCnt; cnt++)
1440 newString += TString::Format("%d],[", cnt);
1441 newString += TString::Format("%d]", endCnt);
1442
1443 // std::cout << "newString generated by HandleParamRanges is " << newString << std::endl;
1444 formula.Replace(matchIdx, formula.Index("]", matchIdx) + 1 - matchIdx, newString);
1445
1446 matchIdx += newString.Length();
1447 }
1448
1449 // std::cout << "final formula is now " << formula << std::endl;
1450}
1451
1452////////////////////////////////////////////////////////////////////////////////
1453/// Handling user functions (and parametrized functions)
1454/// to take variables and optionally parameters as arguments
1456{
1457 // std::cout << "calling `HandleFunctionArguments` on " << formula << std::endl;
1458
1459 // Define parametrized functions, in case we need to use them
1460 std::map<std::pair<TString, Int_t>, std::pair<TString, TString>> parFunctions;
1462
1463 // loop through characters
1464 for (Int_t i = 0; i < formula.Length(); ++i) {
1465 // List of things to ignore (copied from `TFormula::ExtractFunctors`)
1466
1467 // ignore things that start with square brackets
1468 if (formula[i] == '[') {
1469 while (i < formula.Length() && formula[i] != ']')
1470 i++;
1471 continue;
1472 }
1473 // ignore strings
1474 if (i < formula.Length() && formula[i] == '\"') {
1475 do
1476 i++;
1477 while (i < formula.Length() && formula[i] != '\"');
1478 continue;
1479 }
1480 // ignore numbers (scientific notation)
1481 if (IsScientificNotation(formula, i))
1482 continue;
1483 // ignore x in hexadecimal number
1484 if (IsHexadecimal(formula, i)) {
1485 while (i < formula.Length() && !IsOperator(formula[i]))
1486 i++;
1487 continue;
1488 }
1489
1490 // investigate possible start of function name
1491 if (i < formula.Length() && isalpha(formula[i]) && !IsOperator(formula[i])) {
1492 // std::cout << "character : " << i << " " << formula[i] << " is not an operator and is alpha" << std::endl;
1493
1494 int j; // index to end of name
1495 for (j = i; j < formula.Length() && IsFunctionNameChar(formula[j]); j++)
1496 ;
1497 TString name = (TString)formula(i, j - i);
1498 // std::cout << "parsed name " << name << std::endl;
1499
1500 // Count arguments (careful about parentheses depth)
1501 // Make list of indices where each argument is separated
1502 int nArguments = 1;
1503 int depth = 1;
1504 std::vector<int> argSeparators;
1505 argSeparators.push_back(j); // opening parenthesis
1506 int k; // index for end of closing parenthesis
1507 for (k = j + 1; depth >= 1 && k < formula.Length(); k++) {
1508 if (formula[k] == ',' && depth == 1) {
1509 nArguments++;
1510 argSeparators.push_back(k);
1511 } else if (formula[k] == '(')
1512 depth++;
1513 else if (formula[k] == ')')
1514 depth--;
1515 }
1516 argSeparators.push_back(k - 1); // closing parenthesis
1517
1518 // retrieve `f` (code copied from ExtractFunctors)
1519 TObject *obj = nullptr;
1520 {
1522 obj = gROOT->GetListOfFunctions()->FindObject(name);
1523 }
1524 TFormula *f = dynamic_cast<TFormula *>(obj);
1525 if (!f) {
1526 // maybe object is a TF1
1527 TF1 *f1 = dynamic_cast<TF1 *>(obj);
1528 if (f1)
1529 f = f1->GetFormula();
1530 }
1531 // `f` should be found by now, if it was a user-defined function.
1532 // The other possibility we need to consider is that this is a
1533 // parametrized function (else case below)
1534
1535 bool nameRecognized = (f != nullptr);
1536
1537 // Get ndim, npar, and replacementFormula of function
1538 int ndim = 0;
1539 int npar = 0;
1541 if (f) {
1542 ndim = f->GetNdim();
1543 npar = f->GetNpar();
1544 replacementFormula = f->GetExpFormula();
1545 } else {
1546 // otherwise, try to match default parametrized functions
1547
1548 for (const auto &keyval : parFunctions) {
1549 // (name, ndim)
1550 const pair<TString, Int_t> &name_ndim = keyval.first;
1551 // (formula without normalization, formula with normalization)
1553
1554 // match names like gaus, gausn, breitwigner
1555 if (name == name_ndim.first)
1557 else if (name == name_ndim.first + "n" && formulaPair.second != "")
1559 else
1560 continue;
1561
1562 // set ndim
1563 ndim = name_ndim.second;
1564
1565 // go through replacementFormula to find the number of parameters
1566 npar = 0;
1567 int idx = 0;
1568 while ((idx = replacementFormula.Index('[', idx)) != kNPOS) {
1569 npar = max(npar, 1 + TString(replacementFormula(idx + 1, replacementFormula.Length())).Atoi());
1570 idx = replacementFormula.Index(']', idx);
1571 if (idx == kNPOS)
1572 Error("HandleFunctionArguments", "Square brackets not matching in formula %s",
1573 (const char *)replacementFormula);
1574 }
1575 // npar should be set correctly now
1576
1577 // break if number of arguments is good (note: `gaus`, has two
1578 // definitions with different numbers of arguments, but it works
1579 // out so that it should be unambiguous)
1580 if (nArguments == ndim + npar || nArguments == npar || nArguments == ndim) {
1581 nameRecognized = true;
1582 break;
1583 }
1584 }
1585 }
1586 if (nameRecognized && ndim > 4)
1587 Error("HandleFunctionArguments", "Number of dimensions %d greater than 4. Cannot parse formula.", ndim);
1588
1589 // if we have "recognizedName(...", then apply substitutions
1590 if (nameRecognized && j < formula.Length() && formula[j] == '(') {
1591 // std::cout << "naive replacement formula: " << replacementFormula << std::endl;
1592 // std::cout << "formula: " << formula << std::endl;
1593
1594 // map to rename each argument in `replacementFormula`
1596
1597 const char *defaultVariableNames[] = {"x", "y", "z", "t"};
1598
1599 // check nArguments and add to argSubstitutions map as appropriate
1600 bool canReplace = false;
1601 if (nArguments == ndim + npar) {
1602 // loop through all variables and parameters, filling in argSubstitutions
1603 for (int argNr = 0; argNr < nArguments; argNr++) {
1604
1605 // Get new name (for either variable or parameter)
1607 TString(formula(argSeparators[argNr] + 1, argSeparators[argNr + 1] - argSeparators[argNr] - 1));
1608 PreProcessFormula(newName); // so that nesting works
1609
1610 // Get old name(s)
1611 // and add to argSubstitutions map as appropriate
1612 if (argNr < ndim) { // variable
1613 TString oldName = (f) ? TString::Format("x[%d]", argNr) : TString::Format("{V%d}", argNr);
1615
1616 if (f)
1618
1619 } else { // parameter
1620 int parNr = argNr - ndim;
1622 (f) ? TString::Format("[%s]", f->GetParName(parNr)) : TString::Format("[%d]", parNr);
1624
1625 // If the name stays the same, keep the old value of the parameter
1626 if (f && oldName == newName)
1627 DoAddParameter(f->GetParName(parNr), f->GetParameter(parNr), false);
1628 }
1629 }
1630
1631 canReplace = true;
1632 } else if (nArguments == npar) {
1633 // Try to assume variables are implicit (need all arguments to be
1634 // parameters)
1635
1636 // loop to check if all arguments are parameters
1637 bool varsImplicit = true;
1638 for (int argNr = 0; argNr < nArguments && varsImplicit; argNr++) {
1639 int openIdx = argSeparators[argNr] + 1;
1640 int closeIdx = argSeparators[argNr + 1] - 1;
1641
1642 // check brackets on either end
1643 if (formula[openIdx] != '[' || formula[closeIdx] != ']' || closeIdx <= openIdx + 1)
1644 varsImplicit = false;
1645
1646 // check that the middle is a single function-name
1647 for (int idx = openIdx + 1; idx < closeIdx && varsImplicit; idx++)
1648 if (!IsFunctionNameChar(formula[idx]))
1649 varsImplicit = false;
1650
1651 if (!varsImplicit)
1652 Warning("HandleFunctionArguments",
1653 "Argument %d is not a parameter. Cannot assume variables are implicit.", argNr);
1654 }
1655
1656 // loop to replace parameter names
1657 if (varsImplicit) {
1658 // if parametrized function, still need to replace parameter names
1659 if (!f) {
1660 for (int dim = 0; dim < ndim; dim++) {
1662 }
1663 }
1664
1665 for (int argNr = 0; argNr < nArguments; argNr++) {
1667 (f) ? TString::Format("[%s]", f->GetParName(argNr)) : TString::Format("[%d]", argNr);
1669 TString(formula(argSeparators[argNr] + 1, argSeparators[argNr + 1] - argSeparators[argNr] - 1));
1670
1671 // preprocess the formula so that nesting works
1674
1675 // If the name stays the same, keep the old value of the parameter
1676 if (f && oldName == newName)
1677 DoAddParameter(f->GetParName(argNr), f->GetParameter(argNr), false);
1678 }
1679
1680 canReplace = true;
1681 }
1682 }
1683 if (!canReplace && nArguments == ndim) {
1684 // Treat parameters as implicit
1685
1686 // loop to replace variable names
1687 for (int argNr = 0; argNr < nArguments; argNr++) {
1688 TString oldName = (f) ? TString::Format("x[%d]", argNr) : TString::Format("{V%d}", argNr);
1690 TString(formula(argSeparators[argNr] + 1, argSeparators[argNr + 1] - argSeparators[argNr] - 1));
1691
1692 // preprocess so nesting works
1695
1696 if (f) // x, y, z are not used in parametrized function definitions
1698 }
1699
1700 if (f) {
1701 // keep old values of the parameters
1702 for (int parNr = 0; parNr < npar; parNr++)
1703 DoAddParameter(f->GetParName(parNr), f->GetParameter(parNr), false);
1704 }
1705
1706 canReplace = true;
1707 }
1708
1709 if (canReplace)
1711 // std::cout << "after replacement, replacementFormula is " << replacementFormula << std::endl;
1712
1713 if (canReplace) {
1714 // std::cout << "about to replace position " << i << " length " << k-i << " in formula : " << formula <<
1715 // std::endl;
1716 formula.Replace(i, k - i, replacementFormula);
1717 i += replacementFormula.Length() - 1; // skip to end of replacement
1718 // std::cout << "new formula is : " << formula << std::endl;
1719 } else {
1720 Warning("HandleFunctionArguments", "Unable to make replacement. Number of parameters doesn't work : "
1721 "%d arguments, %d dimensions, %d parameters",
1722 nArguments, ndim, npar);
1723 i = j;
1724 }
1725
1726 } else {
1727 i = j; // skip to end of candidate "name"
1728 }
1729 }
1730 }
1731
1732}
1733
1734////////////////////////////////////////////////////////////////////////////////
1735/// Handling exponentiation
1736/// Can handle multiple carets, eg.
1737/// 2^3^4 will be treated like 2^(3^4)
1738
1740{
1741 Int_t caretPos = formula.Last('^');
1742 while (caretPos != kNPOS && !IsAParameterName(formula, caretPos)) {
1743
1744 TString right, left;
1745 Int_t temp = caretPos;
1746 temp--;
1747 // get the expression in ( ) which has the operator^ applied
1748 if (formula[temp] == ')') {
1749 Int_t depth = 1;
1750 temp--;
1751 while (depth != 0 && temp > 0) {
1752 if (formula[temp] == ')')
1753 depth++;
1754 if (formula[temp] == '(')
1755 depth--;
1756 temp--;
1757 }
1758 if (depth == 0)
1759 temp++;
1760 }
1761 // this in case of someting like sin(x+2)^2
1762 do {
1763 temp--; // go down one
1764 // handle scientific notation cases (1.e-2 ^ 3 )
1765 if (temp >= 2 && IsScientificNotation(formula, temp - 1))
1766 temp -= 3;
1767 } while (temp >= 0 && !IsOperator(formula[temp]) && !IsBracket(formula[temp]));
1768
1769 assert(temp + 1 >= 0);
1770 Int_t leftPos = temp + 1;
1771 left = formula(leftPos, caretPos - leftPos);
1772 // std::cout << "left to replace is " << left << std::endl;
1773
1774 // look now at the expression after the ^ operator
1775 temp = caretPos;
1776 temp++;
1777 if (temp >= formula.Length()) {
1778 Error("HandleExponentiation", "Invalid position of operator ^");
1779 return;
1780 }
1781 if (formula[temp] == '(') {
1782 Int_t depth = 1;
1783 temp++;
1784 while (depth != 0 && temp < formula.Length()) {
1785 if (formula[temp] == ')')
1786 depth--;
1787 if (formula[temp] == '(')
1788 depth++;
1789 temp++;
1790 }
1791 temp--;
1792 } else {
1793 // handle case first character is operator - or + continue
1794 if (formula[temp] == '-' || formula[temp] == '+')
1795 temp++;
1796 // handle cases x^-2 or x^+2
1797 // need to handle also cases x^sin(x+y)
1798 Int_t depth = 0;
1799 // stop right expression if is an operator or if is a ")" from a zero depth
1800 while (temp < formula.Length() && ((depth > 0) || !IsOperator(formula[temp]))) {
1801 temp++;
1802 // handle scientific notation cases (1.e-2 ^ 3 )
1803 if (temp >= 2 && IsScientificNotation(formula, temp))
1804 temp += 2;
1805 // for internal parenthesis
1806 if (temp < formula.Length() && formula[temp] == '(')
1807 depth++;
1808 if (temp < formula.Length() && formula[temp] == ')') {
1809 if (depth > 0)
1810 depth--;
1811 else
1812 break; // case of end of a previously started expression e.g. sin(x^2)
1813 }
1814 }
1815 }
1816 right = formula(caretPos + 1, (temp - 1) - caretPos);
1817 // std::cout << "right to replace is " << right << std::endl;
1818
1819 TString pattern = TString::Format("%s^%s", left.Data(), right.Data());
1820 TString replacement = TString::Format("pow(%s,%s)", left.Data(), right.Data());
1821
1822 // special case for square function
1823 if (right == "2"){
1824 replacement = TString::Format("TMath::Sq(%s)",left.Data());
1825 }
1826
1827 // std::cout << "pattern : " << pattern << std::endl;
1828 // std::cout << "replacement : " << replacement << std::endl;
1829 formula.Replace(leftPos, pattern.Length(), replacement, replacement.Length());
1830
1831 caretPos = formula.Last('^');
1832 }
1833}
1834
1835
1836////////////////////////////////////////////////////////////////////////////////
1837/// Handle linear functions defined with the operator ++ (@ is the shorthand).
1838
1840{
1841 if(formula.Length() == 0) return;
1842 auto terms = ROOT::Split(formula.Data(), "@");
1843 if(terms.size() <= 1) return; // function is not linear
1844 // Handle Linear functions identified with "@" operator
1845 fLinearParts.reserve(terms.size());
1847 int delimeterPos = 0;
1848 for(std::size_t iTerm = 0; iTerm < terms.size(); ++iTerm) {
1849 if (iTerm < terms.size() - 1) { // N terms, N - 1 @
1850 // determine the position of the "@" operator in the formula
1851 delimeterPos += terms[iTerm].size() + iTerm;
1852 if(IsAParameterName(formula, delimeterPos)) {
1853 // append the current term and the remaining formula unchanged to the expanded formula
1854 expandedFormula += terms[iTerm];
1855 expandedFormula += formula(delimeterPos, formula.Length() - delimeterPos);
1856 break;
1857 }
1858 }
1859 SetBit(kLinear, true);
1860 auto termName = std::string("__linear") + std::to_string(iTerm+1);
1861 fLinearParts.push_back(new TFormula(termName.c_str(), terms[iTerm].c_str(), false));
1862 std::stringstream ss;
1863 ss << "([" << iTerm << "]*(" << terms[iTerm] << "))";
1864 expandedFormula += ss.str();
1865 if(iTerm < terms.size() - 1) expandedFormula += "+";
1866 }
1867 formula = expandedFormula;
1868}
1869
1870////////////////////////////////////////////////////////////////////////////////
1871/// Preprocessing of formula
1872/// Replace all ** by ^, all ++ by @, and removes spaces.
1873/// Handle also parametrized functions like polN,gaus,expo,landau
1874/// and exponentiation.
1875/// Similar functionality should be added here.
1876
1878{
1879 formula.ReplaceAll("**","^");
1880 formula.ReplaceAll("++","@"); // for linear functions
1881 formula.ReplaceAll(" ","");
1882 HandlePolN(formula);
1884 HandleParamRanges(formula);
1885 HandleFunctionArguments(formula);
1886 HandleExponentiation(formula);
1887 // "++" (now @ since we just called ReplaceAll a few lines above) will be dealt with HandleLinear
1888 HandleLinear(formula);
1889 // special case for "--" and "++"
1890 // ("++" needs to be written with whitespace that is removed before but then we re-add it again
1891 formula.ReplaceAll("--","- -");
1892 formula.ReplaceAll("++","+ +");
1893}
1894
1895////////////////////////////////////////////////////////////////////////////////
1896/// prepare the formula to be executed
1897/// normally is called with fFormula
1898
1900{
1901 fFuncs.clear();
1902 fReadyToExecute = false;
1903
1904 // Check for balanced parentheses
1905 if (!isBalanced(formula)) {
1906 return false;
1907 }
1908
1909 ExtractFunctors(formula);
1910
1911 // update the expression with the new formula
1912 fFormula = formula;
1913 // save formula to parse variable and parameters for Cling
1914 fClingInput = formula;
1915 // replace all { and }
1916 fFormula.ReplaceAll("{","");
1917 fFormula.ReplaceAll("}","");
1918
1919 // std::cout << "functors are extracted formula is " << std::endl;
1920 // std::cout << fFormula << std::endl << std::endl;
1921
1922 fFuncs.sort();
1923 fFuncs.unique();
1924
1925 // use inputFormula for Cling
1927
1928 // for pre-defined functions (need after processing)
1929 if (fNumber != 0) SetPredefinedParamNames();
1930
1932}
1933
1934////////////////////////////////////////////////////////////////////////////////
1935/// Extracts functors from formula, and put them in fFuncs.
1936/// Simple grammar:
1937/// - `<function>` := name(arg1,arg2...)
1938/// - `<variable>` := name
1939/// - `<parameter>` := [number]
1940/// - `<name>` := String containing lower and upper letters, numbers, underscores
1941/// - `<number>` := Integer number
1942/// Operators are omitted.
1943
1945{
1946 // std::cout << "Commencing ExtractFunctors on " << formula << std::endl;
1947
1948 TString name = "";
1949 TString body = "";
1950 // printf("formula is : %s \n",formula.Data() );
1951 for (Int_t i = 0; i < formula.Length(); ++i) {
1952
1953 // std::cout << "loop on character : " << i << " " << formula[i] << std::endl;
1954 // case of parameters
1955 if (formula[i] == '[') {
1956 Int_t tmp = i;
1957 i++;
1958 TString param = "";
1959 while (i < formula.Length() && formula[i] != ']') {
1960 param.Append(formula[i++]);
1961 }
1962 i++;
1963 // rename parameter name XX to pXX
1964 // std::cout << "examine parameters " << param << std::endl;
1965 int paramIndex = -1;
1966 if (param.IsDigit()) {
1967 paramIndex = param.Atoi();
1968 param.Insert(0, 'p'); // needed for the replacement
1969 if (paramIndex >= fNpar || fParams.find(param) == fParams.end()) {
1970 // add all parameters up to given index found
1971 for (int idx = 0; idx <= paramIndex; ++idx) {
1972 TString pname = TString::Format("p%d", idx);
1973 if (fParams.find(pname) == fParams.end())
1974 DoAddParameter(pname, 0, false);
1975 }
1976 }
1977 } else {
1978 // handle whitespace characters in parname
1979 param.ReplaceAll("\\s", " ");
1980
1981 // only add if parameter does not already exist, because maybe
1982 // `HandleFunctionArguments` already assigned a default value to the
1983 // parameter
1984 if (fParams.find(param) == fParams.end() || GetParNumber(param) < 0 ||
1985 (unsigned)GetParNumber(param) >= fClingParameters.size()) {
1986 // std::cout << "Setting parameter " << param << " to 0" << std::endl;
1987 DoAddParameter(param, 0, false);
1988 }
1989 }
1990 TString replacement = TString::Format("{[%s]}", param.Data());
1991 formula.Replace(tmp, i - tmp, replacement, replacement.Length());
1992 fFuncs.push_back(TFormulaFunction(param));
1993 // we need to change index i after replacing since string length changes
1994 // and we need to re-calculate i position
1995 int deltai = replacement.Length() - (i-tmp);
1996 i += deltai;
1997 // printf("found parameter %s \n",param.Data() );
1998 continue;
1999 }
2000 // case of strings
2001 if (formula[i] == '\"') {
2002 // look for next instance of "\"
2003 do {
2004 i++;
2005 } while (i < formula.Length() && formula[i] != '\"');
2006 }
2007 // case of e or E for numbers in exponential notation (e.g. 2.2e-3)
2008 if (IsScientificNotation(formula, i))
2009 continue;
2010 // case of x for hexadecimal numbers
2011 if (IsHexadecimal(formula, i)) {
2012 // find position of operator
2013 // do not check cases if character is not only a to f, but accept anything
2014 while (i < formula.Length() && !IsOperator(formula[i])) {
2015 i++;
2016 }
2017 continue;
2018 }
2019
2020 // std::cout << "investigating character : " << i << " " << formula[i] << " of formula " << formula <<
2021 // std::endl;
2022 // look for variable and function names. They start in C++ with alphanumeric characters
2023 if (isalpha(formula[i]) &&
2024 !IsOperator(formula[i])) // not really needed to check if operator (if isalpha is not an operator)
2025 {
2026 // std::cout << "character : " << i << " " << formula[i] << " is not an operator and is alpha " <<
2027 // std::endl;
2028
2029 while (i < formula.Length() && IsFunctionNameChar(formula[i])) {
2030 // need special case for separating operator ":" from scope operator "::"
2031 if (formula[i] == ':' && ((i + 1) < formula.Length())) {
2032 if (formula[i + 1] == ':') {
2033 // case of :: (scopeOperator)
2034 name.Append("::");
2035 i += 2;
2036 continue;
2037 } else
2038 break;
2039 }
2040
2041 name.Append(formula[i++]);
2042 }
2043 // printf(" build a name %s \n",name.Data() );
2044 if (formula[i] == '(') {
2045 i++;
2046 if (i < formula.Length() && formula[i] == ')') {
2047 fFuncs.push_back(TFormulaFunction(name, body, 0));
2048 name = body = "";
2049 continue;
2050 }
2051 Int_t depth = 1;
2052 Int_t args = 1; // we will miss first argument
2053 while (depth != 0 && i < formula.Length()) {
2054 switch (formula[i]) {
2055 case '(': depth++; break;
2056 case ')': depth--; break;
2057 case ',':
2058 if (depth == 1)
2059 args++;
2060 break;
2061 }
2062 if (depth != 0) // we don't want last ')' inside body
2063 {
2064 body.Append(formula[i++]);
2065 }
2066 }
2067 Int_t originalBodyLen = body.Length();
2069 formula.Replace(i - originalBodyLen, originalBodyLen, body, body.Length());
2070 i += body.Length() - originalBodyLen;
2071 fFuncs.push_back(TFormulaFunction(name, body, args));
2072 } else {
2073
2074 // std::cout << "check if character : " << i << " " << formula[i] << " from name " << name << " is a
2075 // function " << std::endl;
2076
2077 // check if function is provided by gROOT
2078 TObject *obj = nullptr;
2079 // exclude case function name is x,y,z,t
2080 if (!IsReservedName(name))
2081 {
2083 obj = gROOT->GetListOfFunctions()->FindObject(name);
2084 }
2085 TFormula *f = dynamic_cast<TFormula *>(obj);
2086 if (!f) {
2087 // maybe object is a TF1
2088 TF1 *f1 = dynamic_cast<TF1 *>(obj);
2089 if (f1)
2090 f = f1->GetFormula();
2091 }
2092 if (f) {
2093 // Replacing user formula the old way (as opposed to 'HandleFunctionArguments')
2094 // Note this is only for replacing functions that do
2095 // not specify variables and/or parameters in brackets
2096 // (the other case is done by `HandleFunctionArguments`)
2097
2098 TString replacementFormula = f->GetExpFormula();
2099
2100 // analyze expression string
2101 // std::cout << "formula to replace for " << f->GetName() << " is " << replacementFormula <<
2102 // std::endl;
2104 // we need to define different parameters if we use the unnamed default parameters ([0])
2105 // I need to replace all the terms in the functor for backward compatibility of the case
2106 // f1("[0]*x") f2("[0]*x") f1+f2 - it is weird but it is better to support
2107 // std::cout << "current number of parameter is " << fNpar << std::endl;
2108 int nparOffset = 0;
2109 // if (fParams.find("0") != fParams.end() ) {
2110 // do in any case if parameters are existing
2111 std::vector<TString> newNames;
2112 if (fNpar > 0) {
2113 nparOffset = fNpar;
2114 newNames.resize(f->GetNpar());
2115 // start from higher number to avoid overlap
2116 for (int jpar = f->GetNpar() - 1; jpar >= 0; --jpar) {
2117 // parameters name have a "p" added in front
2118 TString pj = TString(f->GetParName(jpar));
2119 if (pj[0] == 'p' && TString(pj(1, pj.Length())).IsDigit()) {
2120 TString oldName = TString::Format("[%s]", f->GetParName(jpar));
2122 // std::cout << "replace - parameter " << f->GetParName(jpar) << " with " << newName <<
2123 // std::endl;
2124 replacementFormula.ReplaceAll(oldName, newName);
2126 } else
2127 newNames[jpar] = f->GetParName(jpar);
2128 }
2129 // std::cout << "after replacing params " << replacementFormula << std::endl;
2130 }
2132 // std::cout << "after re-extracting functors " << replacementFormula << std::endl;
2133
2134 // set parameter value from replacement formula
2135 for (int jpar = 0; jpar < f->GetNpar(); ++jpar) {
2136 if (nparOffset > 0) {
2137 // parameter have an offset- so take this into account
2138 assert((int)newNames.size() == f->GetNpar());
2139 SetParameter(newNames[jpar], f->GetParameter(jpar));
2140 } else
2141 // names are the same between current formula and replaced one
2142 SetParameter(f->GetParName(jpar), f->GetParameter(jpar));
2143 }
2144 // need to add parenthesis at begin and end of replacementFormula
2145 replacementFormula.Insert(0, '(');
2146 replacementFormula.Insert(replacementFormula.Length(), ')');
2147 formula.Replace(i - name.Length(), name.Length(), replacementFormula, replacementFormula.Length());
2148 // move forward the index i of the main loop
2149 i += replacementFormula.Length() - name.Length();
2150
2151 // we have extracted all the functor for "fname"
2152 // std::cout << "We have extracted all the functors for fname" << std::endl;
2153 // std::cout << " i = " << i << " f[i] = " << formula[i] << " - " << formula << std::endl;
2154 name = "";
2155
2156 continue;
2157 }
2158
2159 // add now functor in
2160 TString replacement = TString::Format("{%s}", name.Data());
2161 formula.Replace(i - name.Length(), name.Length(), replacement, replacement.Length());
2162 i += 2;
2163 fFuncs.push_back(TFormulaFunction(name));
2164 }
2165 }
2166 name = body = "";
2167 }
2168}
2169
2170////////////////////////////////////////////////////////////////////////////////
2171/// Iterates through functors in fFuncs and performs the appropriate action.
2172/// If functor has 0 arguments (has only name) can be:
2173/// - variable
2174/// * will be replaced with x[num], where x is an array containing value of this variable under num.
2175/// - pre-defined formula
2176/// * will be replaced with formulas body
2177/// - constant
2178/// * will be replaced with constant value
2179/// - parameter
2180/// * will be replaced with p[num], where p is an array containing value of this parameter under num.
2181/// If has arguments it can be :
2182/// - function shortcut, eg. sin
2183/// * will be replaced with fullname of function, eg. sin -> TMath::Sin
2184/// - function from cling environment, eg. TMath::BreitWigner(x,y,z)
2185/// * first check if function exists, and has same number of arguments, then accept it and set as found.
2186/// If all functors after iteration are matched with corresponding action,
2187/// it inputs C++ code of formula into cling, and sets flag that formula is ready to evaluate.
2188
2190{
2191 // std::cout << "Begin: formula is " << formula << " list of functors " << fFuncs.size() << std::endl;
2192
2193 for (list<TFormulaFunction>::iterator funcsIt = fFuncs.begin(); funcsIt != fFuncs.end(); ++funcsIt) {
2195
2196 // std::cout << "fun is " << fun.GetName() << std::endl;
2197
2198 if (fun.fFound)
2199 continue;
2200 if (fun.IsFuncCall()) {
2201 // replace with pre-defined functions
2202 map<TString, TString>::iterator it = fFunctionsShortcuts.find(fun.GetName());
2203 if (it != fFunctionsShortcuts.end()) {
2204 TString shortcut = it->first;
2205 TString full = it->second;
2206 // std::cout << " functor " << fun.GetName() << " found - replace " << shortcut << " with " << full << " in
2207 // " << formula << std::endl;
2208 // replace all functors
2209 Ssiz_t index = formula.Index(shortcut, 0);
2210 while (index != kNPOS) {
2211 // check that function is not in a namespace and is not in other characters
2212 // std::cout << "analyzing " << shortcut << " in " << formula << std::endl;
2213 Ssiz_t i2 = index + shortcut.Length();
2214 if ((index > 0) && (isalpha(formula[index - 1]) || formula[index - 1] == ':')) {
2215 index = formula.Index(shortcut, i2);
2216 continue;
2217 }
2218 if (i2 < formula.Length() && formula[i2] != '(') {
2219 index = formula.Index(shortcut, i2);
2220 continue;
2221 }
2222 // now replace the string
2223 formula.Replace(index, shortcut.Length(), full);
2224 Ssiz_t inext = index + full.Length();
2225 index = formula.Index(shortcut, inext);
2226 fun.fFound = true;
2227 }
2228 }
2229 // for functions we can live it to cling to decide if it is a valid function or NOT
2230 // We don't need to retrieve this information from the ROOT interpreter
2231 // we assume that the function is then found and all the following code does not need to be there
2232#ifdef TFORMULA_CHECK_FUNCTIONS
2233
2234 if (fun.fName.Contains("::")) // add support for nested namespaces
2235 {
2236 // look for last occurrence of "::"
2237 std::string name(fun.fName.Data());
2238 size_t index = name.rfind("::");
2239 assert(index != std::string::npos);
2240 TString className = fun.fName(0, fun.fName(0, index).Length());
2241 TString functionName = fun.fName(index + 2, fun.fName.Length());
2242
2243 Bool_t silent = true;
2244 TClass *tclass = TClass::GetClass(className, silent);
2245 // std::cout << "looking for class " << className << std::endl;
2246 const TList *methodList = tclass->GetListOfAllPublicMethods();
2247 TIter next(methodList);
2248 TMethod *p;
2249 while ((p = (TMethod *)next())) {
2250 if (strcmp(p->GetName(), functionName.Data()) == 0 &&
2251 (fun.GetNargs() <= p->GetNargs() && fun.GetNargs() >= p->GetNargs() - p->GetNargsOpt())) {
2252 fun.fFound = true;
2253 break;
2254 }
2255 }
2256 }
2257 if (!fun.fFound) {
2258 // try to look into all the global functions in gROOT
2259 TFunction *f;
2260 {
2262 f = (TFunction *)gROOT->GetListOfGlobalFunctions(true)->FindObject(fun.fName);
2263 }
2264 // if found a function with matching arguments
2265 if (f && fun.GetNargs() <= f->GetNargs() && fun.GetNargs() >= f->GetNargs() - f->GetNargsOpt()) {
2266 fun.fFound = true;
2267 }
2268 }
2269
2270 if (!fun.fFound) {
2271 // ignore not found functions
2272 if (gDebug)
2273 Info("TFormula", "Could not find %s function with %d argument(s)", fun.GetName(), fun.GetNargs());
2274 fun.fFound = false;
2275 }
2276#endif
2277 } else {
2278 TFormula *old = nullptr;
2279 {
2281 old = (TFormula *)gROOT->GetListOfFunctions()->FindObject(gNamePrefix + fun.fName);
2282 }
2283 if (old) {
2284 // we should not go here (this analysis is done before in ExtractFunctors)
2285 assert(false);
2286 fun.fFound = true;
2287 TString pattern = TString::Format("{%s}", fun.GetName());
2291 formula.ReplaceAll(pattern, replacement);
2292 continue;
2293 }
2294 // looking for default variables defined in fVars
2295
2296 map<TString, TFormulaVariable>::iterator varsIt = fVars.find(fun.GetName());
2297 if (varsIt != fVars.end()) {
2298
2299 TString name = (*varsIt).second.GetName();
2300 Double_t value = (*varsIt).second.fValue;
2301
2302 AddVariable(name, value); // this set the cling variable
2303 if (!fVars[name].fFound) {
2304
2305 fVars[name].fFound = true;
2306 int varDim = (*varsIt).second.fArrayPos; // variable dimensions (0 for x, 1 for y, 2, for z)
2307 if (varDim >= fNdim) {
2308 fNdim = varDim + 1;
2309
2310 // we need to be sure that all other variables are added with position less
2311 for (auto &v : fVars) {
2312 if (v.second.fArrayPos < varDim && !v.second.fFound) {
2313 AddVariable(v.first, v.second.fValue);
2314 v.second.fFound = true;
2315 }
2316 }
2317 }
2318 }
2319 // remove the "{.. }" added around the variable
2320 TString pattern = TString::Format("{%s}", name.Data());
2321 TString replacement = TString::Format("x[%d]", (*varsIt).second.fArrayPos);
2322 formula.ReplaceAll(pattern, replacement);
2323
2324 // std::cout << "Found an observable for " << fun.GetName() << std::endl;
2325
2326 fun.fFound = true;
2327 continue;
2328 }
2329 // check for observables defined as x[0],x[1],....
2330 // maybe could use a regular expression here
2331 // only in case match with defined variables is not successful
2332 TString funname = fun.GetName();
2333 if (funname.Contains("x[") && funname.Contains("]")) {
2334 TString sdigit = funname(2, funname.Index("]"));
2335 int digit = sdigit.Atoi();
2336 if (digit >= fNdim) {
2337 fNdim = digit + 1;
2338 // we need to add the variables in fVars all of them before x[n]
2339 for (int j = 0; j < fNdim; ++j) {
2340 TString vname = TString::Format("x[%d]", j);
2341 if (fVars.find(vname) == fVars.end()) {
2343 fVars[vname].fFound = true;
2344 AddVariable(vname, 0.);
2345 }
2346 }
2347 }
2348 // std::cout << "Found matching observable for " << funname << std::endl;
2349 fun.fFound = true;
2350 // remove the "{.. }" added around the variable
2351 TString pattern = TString::Format("{%s}", funname.Data());
2352 formula.ReplaceAll(pattern, funname);
2353 continue;
2354 }
2355 //}
2356
2357 auto paramsIt = fParams.find(fun.GetName());
2358 if (paramsIt != fParams.end()) {
2359 // TString name = (*paramsIt).second.GetName();
2360 TString pattern = TString::Format("{[%s]}", fun.GetName());
2361 // std::cout << "pattern is " << pattern << std::endl;
2362 if (formula.Index(pattern) != kNPOS) {
2363 // TString replacement = TString::Format("p[%d]",(*paramsIt).second.fArrayPos);
2364 TString replacement = TString::Format("p[%d]", (*paramsIt).second);
2365 // std::cout << "replace pattern " << pattern << " with " << replacement << std::endl;
2366 formula.ReplaceAll(pattern, replacement);
2367 }
2368 fun.fFound = true;
2369 continue;
2370 } else {
2371 // std::cout << "functor " << fun.GetName() << " is not a parameter " << std::endl;
2372 }
2373
2374 // looking for constants (needs to be done after looking at the parameters)
2375 map<TString, Double_t>::iterator constIt = fConsts.find(fun.GetName());
2376 if (constIt != fConsts.end()) {
2377 TString pattern = TString::Format("{%s}", fun.GetName());
2378 formula.ReplaceAll(pattern, doubleToString(constIt->second));
2379 fun.fFound = true;
2380 // std::cout << "constant with name " << fun.GetName() << " is found " << std::endl;
2381 continue;
2382 }
2383
2384 fun.fFound = false;
2385 }
2386 }
2387 // std::cout << "End: formula is " << formula << std::endl;
2388
2389 // ignore case of functors have been matched - try to pass it to Cling
2390 if (!fReadyToExecute) {
2391 fReadyToExecute = true;
2392 Bool_t hasVariables = (fNdim > 0);
2393 Bool_t hasParameters = (fNpar > 0);
2394 if (!hasParameters) {
2395 fAllParametersSetted = true;
2396 }
2397 // assume a function without variables is always 1-dimensional ???
2398 // if (hasParameters && !hasVariables) {
2399 // fNdim = 1;
2400 // AddVariable("x", 0);
2401 // hasVariables = true;
2402 // }
2403 // does not make sense to vectorize function which is of FNDim=0
2404 if (!hasVariables) fVectorized=false;
2405 // when there are no variables but only parameter we still need to ad
2406 //Bool_t hasBoth = hasVariables && hasParameters;
2407 Bool_t inputIntoCling = (formula.Length() > 0);
2408 if (inputIntoCling) {
2409 // save copy of inputFormula in a std::strig for the unordered map
2410 // and also formula is same as FClingInput typically and it will be modified
2411 std::string inputFormula(formula.Data());
2412
2413 // The name we really use for the unordered map will have a flag that
2414 // says whether the formula is vectorized
2415 std::string inputFormulaVecFlag = inputFormula;
2416 if (fVectorized)
2417 inputFormulaVecFlag += " (vectorized)";
2418
2419 TString argType = fVectorized ? vectorizedArgType() : "Double_t";
2420
2421 // valid input formula - try to put into Cling (in case of no variables but only parameter we need to add the standard signature)
2422 TString argumentsPrototype = TString::Format("%s%s%s", ( (hasVariables || hasParameters) ? (argType + " const *x").Data() : ""),
2423 (hasParameters ? "," : ""), (hasParameters ? "Double_t *p" : ""));
2424
2425 // set the name for Cling using the hash_function
2427
2428 // check if formula exist already in the map
2430
2431 // std::cout << "gClingFunctions list" << std::endl;
2432 // for (auto thing : gClingFunctions)
2433 // std::cout << "gClingFunctions : " << thing.first << std::endl;
2434
2436
2437 if (funcit != gClingFunctions.end()) {
2439 fClingInitialized = true;
2440 inputIntoCling = false;
2441 }
2442
2443
2444
2445 // set the cling name using hash of the static formulae map
2446 auto hasher = gClingFunctions.hash_function();
2448
2449 fClingInput = TString::Format("%s %s(%s){ return %s ; }", argType.Data(), fClingName.Data(),
2450 argumentsPrototype.Data(), inputFormula.c_str());
2451
2452
2453 // std::cout << "Input Formula " << inputFormula << " \t vec formula : " << inputFormulaVecFlag << std::endl;
2454 // std::cout << "Cling functions existing " << std::endl;
2455 // for (auto & ff : gClingFunctions)
2456 // std::cout << ff.first << std::endl;
2457 // std::cout << "\n";
2458 // std::cout << fClingName << std::endl;
2459
2460 // this is not needed (maybe can be re-added in case of recompilation of identical expressions
2461 // // check in case of a change if need to re-initialize
2462 // if (fClingInitialized) {
2463 // if (oldClingInput == fClingInput)
2464 // inputIntoCling = false;
2465 // else
2466 // fClingInitialized = false;
2467 // }
2468
2469 if (inputIntoCling) {
2470 if (!fLazyInitialization) {
2472 if (fClingInitialized) {
2473 // if Cling has been successfully initialized
2474 // put function ptr in the static map
2476 gClingFunctions.insert(std::make_pair(inputFormulaVecFlag, (void *)fFuncPtr));
2477 }
2478 }
2479 if (!fClingInitialized) {
2480 // needed in case of lazy initialization of failure compiling the expression
2482 }
2483
2484 } else {
2485 fAllParametersSetted = true;
2486 fClingInitialized = true;
2487 }
2488 }
2489 }
2490
2491 // In case of a Cling Error check components which are not found in Cling
2492 // check that all formula components are matched otherwise emit an error
2494 //Bool_t allFunctorsMatched = false;
2495 for (list<TFormulaFunction>::iterator it = fFuncs.begin(); it != fFuncs.end(); ++it) {
2496 // functions are now by default always not checked
2497 if (!it->fFound && !it->IsFuncCall()) {
2498 //allFunctorsMatched = false;
2499 if (it->GetNargs() == 0)
2500 Error("ProcessFormula", "\"%s\" has not been matched in the formula expression", it->GetName());
2501 else
2502 Error("ProcessFormula", "Could not find %s function with %d argument(s)", it->GetName(), it->GetNargs());
2503 }
2504 }
2505 Error("ProcessFormula","Formula \"%s\" is invalid !", GetExpFormula().Data() );
2506 fReadyToExecute = false;
2507 }
2508
2509 // clean up un-used default variables in case formula is valid
2510 //if (fClingInitialized && fReadyToExecute) {
2511 //don't check fClingInitialized in case of lazy execution
2512 if (fReadyToExecute) {
2513 auto itvar = fVars.begin();
2514 // need this loop because after erase change iterators
2515 do {
2516 if (!itvar->second.fFound) {
2517 // std::cout << "Erase variable " << itvar->first << std::endl;
2518 itvar = fVars.erase(itvar);
2519 } else
2520 itvar++;
2521 } while (itvar != fVars.end());
2522 }
2523}
2524
2525////////////////////////////////////////////////////////////////////////////////
2526/// Fill map with parametrized functions
2527
2529{
2530 // map< pair<TString,Int_t> ,pair<TString,TString> > functions;
2531 functions.insert(
2532 make_pair(make_pair("gaus", 1), make_pair("[0]*exp(-0.5*(({V0}-[1])/[2])*(({V0}-[1])/[2]))",
2533 "[0]*exp(-0.5*(({V0}-[1])/[2])*(({V0}-[1])/[2]))/(sqrt(2*pi)*[2])")));
2534 functions.insert(make_pair(make_pair("landau", 1), make_pair("[0]*TMath::Landau({V0},[1],[2],false)",
2535 "[0]*TMath::Landau({V0},[1],[2],true)")));
2536 functions.insert(make_pair(make_pair("expo", 1), make_pair("exp([0]+[1]*{V0})", "")));
2537 functions.insert(
2538 make_pair(make_pair("crystalball", 1), make_pair("[0]*ROOT::Math::crystalball_function({V0},[3],[4],[2],[1])",
2539 "[0]*ROOT::Math::crystalball_pdf({V0},[3],[4],[2],[1])")));
2540 functions.insert(
2541 make_pair(make_pair("breitwigner", 1), make_pair("[0]*ROOT::Math::breitwigner_pdf({V0},[2],[1])",
2542 "[0]*ROOT::Math::breitwigner_pdf({V0},[2],[4],[1])")));
2543 // chebyshev polynomial
2544 functions.insert(make_pair(make_pair("cheb0", 1), make_pair("ROOT::Math::Chebyshev0({V0},[0])", "")));
2545 functions.insert(make_pair(make_pair("cheb1", 1), make_pair("ROOT::Math::Chebyshev1({V0},[0],[1])", "")));
2546 functions.insert(make_pair(make_pair("cheb2", 1), make_pair("ROOT::Math::Chebyshev2({V0},[0],[1],[2])", "")));
2547 functions.insert(make_pair(make_pair("cheb3", 1), make_pair("ROOT::Math::Chebyshev3({V0},[0],[1],[2],[3])", "")));
2548 functions.insert(
2549 make_pair(make_pair("cheb4", 1), make_pair("ROOT::Math::Chebyshev4({V0},[0],[1],[2],[3],[4])", "")));
2550 functions.insert(
2551 make_pair(make_pair("cheb5", 1), make_pair("ROOT::Math::Chebyshev5({V0},[0],[1],[2],[3],[4],[5])", "")));
2552 functions.insert(
2553 make_pair(make_pair("cheb6", 1), make_pair("ROOT::Math::Chebyshev6({V0},[0],[1],[2],[3],[4],[5],[6])", "")));
2554 functions.insert(
2555 make_pair(make_pair("cheb7", 1), make_pair("ROOT::Math::Chebyshev7({V0},[0],[1],[2],[3],[4],[5],[6],[7])", "")));
2556 functions.insert(make_pair(make_pair("cheb8", 1),
2557 make_pair("ROOT::Math::Chebyshev8({V0},[0],[1],[2],[3],[4],[5],[6],[7],[8])", "")));
2558 functions.insert(make_pair(make_pair("cheb9", 1),
2559 make_pair("ROOT::Math::Chebyshev9({V0},[0],[1],[2],[3],[4],[5],[6],[7],[8],[9])", "")));
2560 functions.insert(
2561 make_pair(make_pair("cheb10", 1),
2562 make_pair("ROOT::Math::Chebyshev10({V0},[0],[1],[2],[3],[4],[5],[6],[7],[8],[9],[10])", "")));
2563 // 2-dimensional functions
2564 functions.insert(
2565 make_pair(make_pair("gaus", 2), make_pair("[0]*exp(-0.5*(({V0}-[1])/[2])^2 - 0.5*(({V1}-[3])/[4])^2)", "")));
2566 functions.insert(
2567 make_pair(make_pair("landau", 2),
2568 make_pair("[0]*TMath::Landau({V0},[1],[2],false)*TMath::Landau({V1},[3],[4],false)", "TMath::Landau({V0},[0],[1],true)*TMath::Landau({V1},[2],[3],true)")));
2569 functions.insert(
2570 make_pair(make_pair("expo", 2),
2571 make_pair("exp([0]+[1]*{V0}+[2]*{V1})", "")));
2572 // 3-dimensional function
2573 functions.insert(
2574 make_pair(make_pair("gaus", 3), make_pair("[0]*exp(-0.5*(({V0}-[1])/[2])^2 - 0.5*(({V1}-[3])/[4])^2 - 0.5*(({V2}-[5])/[6])^2)", "")));
2575 // 2-d gaussian with correlations
2576 functions.insert(
2577 make_pair(make_pair("bigaus", 2), make_pair("[0]*ROOT::Math::bigaussian_pdf({V0},{V1},[2],[4],[5],[1],[3])",
2578 "[0]*ROOT::Math::bigaussian_pdf({V0},{V1},[2],[4],[5],[1],[3])")));
2579}
2580
2581////////////////////////////////////////////////////////////////////////////////
2582/// Set parameter names only in case of pre-defined functions.
2583
2585
2586 if (fNumber == 0) return;
2587
2588 if (fNumber == 100) { // Gaussian
2589 SetParName(0,"Constant");
2590 SetParName(1,"Mean");
2591 SetParName(2,"Sigma");
2592 return;
2593 }
2594 if (fNumber == 110) {
2595 SetParName(0,"Constant");
2596 SetParName(1,"MeanX");
2597 SetParName(2,"SigmaX");
2598 SetParName(3,"MeanY");
2599 SetParName(4,"SigmaY");
2600 return;
2601 }
2602 if (fNumber == 120) {
2603 SetParName(0,"Constant");
2604 SetParName(1,"MeanX");
2605 SetParName(2,"SigmaX");
2606 SetParName(3,"MeanY");
2607 SetParName(4,"SigmaY");
2608 SetParName(5,"MeanZ");
2609 SetParName(6,"SigmaZ");
2610 return;
2611 }
2612 if (fNumber == 112) { // bigaus
2613 SetParName(0,"Constant");
2614 SetParName(1,"MeanX");
2615 SetParName(2,"SigmaX");
2616 SetParName(3,"MeanY");
2617 SetParName(4,"SigmaY");
2618 SetParName(5,"Rho");
2619 return;
2620 }
2621 if (fNumber == 200) { // exponential
2622 SetParName(0,"Constant");
2623 SetParName(1,"Slope");
2624 return;
2625 }
2626 if (fNumber == 400) { // landau
2627 SetParName(0,"Constant");
2628 SetParName(1,"MPV");
2629 SetParName(2,"Sigma");
2630 return;
2631 }
2632 if (fNumber == 500) { // crystal-ball
2633 SetParName(0,"Constant");
2634 SetParName(1,"Mean");
2635 SetParName(2,"Sigma");
2636 SetParName(3,"Alpha");
2637 SetParName(4,"N");
2638 return;
2639 }
2640 if (fNumber == 600) { // breit-wigner
2641 SetParName(0,"Constant");
2642 SetParName(1,"Mean");
2643 SetParName(2,"Gamma");
2644 return;
2645 }
2646 // if formula is a polynomial (or chebyshev), set parameter names
2647 // not needed anymore (p0 is assigned by default)
2648 // if (fNumber == (300+fNpar-1) ) {
2649 // for (int i = 0; i < fNpar; i++) SetParName(i,TString::Format("p%d",i));
2650 // return;
2651 // }
2652
2653 // // general case if parameters are digits (XX) change to pXX
2654 // auto paramMap = fParams; // need to copy the map because SetParName is going to modify it
2655 // for ( auto & p : paramMap) {
2656 // if (p.first.IsDigit() )
2657 // SetParName(p.second,TString::Format("p%s",p.first.Data()));
2658 // }
2659
2660 return;
2661}
2662
2663////////////////////////////////////////////////////////////////////////////////
2664/// Return linear part.
2665
2667{
2668 if (!fLinearParts.empty()) {
2669 int n = fLinearParts.size();
2670 if (i < 0 || i >= n ) {
2671 Error("GetLinearPart","Formula %s has only %d linear parts - requested %d",GetName(),n,i);
2672 return nullptr;
2673 }
2674 return fLinearParts[i];
2675 }
2676 return nullptr;
2677}
2678
2679////////////////////////////////////////////////////////////////////////////////
2680/// Adds variable to known variables, and reprocess formula.
2681
2683{
2684 if (fVars.find(name) != fVars.end()) {
2685 TFormulaVariable &var = fVars[name];
2686 var.fValue = value;
2687
2688 // If the position is not defined in the Cling vectors, make space for it
2689 // but normally is variable is defined in fVars a slot should be also present in fClingVariables
2690 if (var.fArrayPos < 0) {
2691 var.fArrayPos = fVars.size();
2692 }
2693 if (var.fArrayPos >= (int)fClingVariables.size()) {
2694 fClingVariables.resize(var.fArrayPos + 1);
2695 }
2697 } else {
2698 TFormulaVariable var(name, value, fVars.size());
2699 fVars[name] = var;
2700 fClingVariables.push_back(value);
2701 if (!fFormula.IsNull()) {
2702 // printf("process formula again - %s \n",fClingInput.Data() );
2704 }
2705 }
2706}
2707
2708////////////////////////////////////////////////////////////////////////////////
2709/// Adds multiple variables.
2710/// First argument is an array of pairs<TString,Double>, where
2711/// first argument is name of variable,
2712/// second argument represents value.
2713/// size - number of variables passed in first argument
2714
2716{
2717 Bool_t anyNewVar = false;
2718 for (Int_t i = 0; i < size; ++i) {
2719
2720 const TString &vname = vars[i];
2721
2723 if (var.fArrayPos < 0) {
2724
2725 var.fName = vname;
2726 var.fArrayPos = fVars.size();
2727 anyNewVar = true;
2728 var.fValue = 0;
2729 if (var.fArrayPos >= (int)fClingVariables.capacity()) {
2730 Int_t multiplier = 2;
2731 if (fFuncs.size() > 100) {
2733 }
2734 fClingVariables.reserve(multiplier * fClingVariables.capacity());
2735 }
2736 fClingVariables.push_back(0.0);
2737 }
2738 // else
2739 // {
2740 // var.fValue = v.second;
2741 // fClingVariables[var.fArrayPos] = v.second;
2742 // }
2743 }
2744 if (anyNewVar && !fFormula.IsNull()) {
2746 }
2747}
2748
2749////////////////////////////////////////////////////////////////////////////////
2750/// Set the name of the formula. We need to allow the list of function to
2751/// properly handle the hashes.
2752
2753void TFormula::SetName(const char* name)
2754{
2755 if (IsReservedName(name)) {
2756 Error("SetName", "The name \'%s\' is reserved as a TFormula variable name.\n"
2757 "\tThis function will not be renamed.",
2758 name);
2759 } else {
2760 // Here we need to remove and re-add to keep the hashes consistent with
2761 // the underlying names.
2762 auto listOfFunctions = gROOT->GetListOfFunctions();
2763 TObject* thisAsFunctionInList = nullptr;
2765 if (listOfFunctions){
2766 thisAsFunctionInList = listOfFunctions->FindObject(this);
2768 }
2771 }
2772}
2773
2774////////////////////////////////////////////////////////////////////////////////
2775///
2776/// Sets multiple variables.
2777/// First argument is an array of pairs<TString,Double>, where
2778/// first argument is name of variable,
2779/// second argument represents value.
2780/// size - number of variables passed in first argument
2781
2783{
2784 for(Int_t i = 0; i < size; ++i)
2785 {
2786 auto &v = vars[i];
2787 if (fVars.find(v.first) != fVars.end()) {
2788 fVars[v.first].fValue = v.second;
2789 fClingVariables[fVars[v.first].fArrayPos] = v.second;
2790 } else {
2791 Error("SetVariables", "Variable %s is not defined.", v.first.Data());
2792 }
2793 }
2794}
2795
2796////////////////////////////////////////////////////////////////////////////////
2797/// Returns variable value.
2798
2800{
2801 const auto nameIt = fVars.find(name);
2802 if (fVars.end() == nameIt) {
2803 Error("GetVariable", "Variable %s is not defined.", name);
2804 return -1;
2805 }
2806 return nameIt->second.fValue;
2807}
2808
2809////////////////////////////////////////////////////////////////////////////////
2810/// Returns variable number (positon in array) given its name.
2811
2813{
2814 const auto nameIt = fVars.find(name);
2815 if (fVars.end() == nameIt) {
2816 Error("GetVarNumber", "Variable %s is not defined.", name);
2817 return -1;
2818 }
2819 return nameIt->second.fArrayPos;
2820}
2821
2822////////////////////////////////////////////////////////////////////////////////
2823/// Returns variable name given its position in the array.
2824
2826{
2827 if (ivar < 0 || ivar >= fNdim) return "";
2828
2829 // need to loop on the map to find corresponding variable
2830 for ( auto & v : fVars) {
2831 if (v.second.fArrayPos == ivar) return v.first;
2832 }
2833 Error("GetVarName","Variable with index %d not found !!",ivar);
2834 //return TString::Format("x%d",ivar);
2835 return "";
2836}
2837
2838////////////////////////////////////////////////////////////////////////////////
2839/// Sets variable value.
2840
2842{
2843 if (fVars.find(name) == fVars.end()) {
2844 Error("SetVariable", "Variable %s is not defined.", name.Data());
2845 return;
2846 }
2847 fVars[name].fValue = value;
2848 fClingVariables[fVars[name].fArrayPos] = value;
2849}
2850
2851////////////////////////////////////////////////////////////////////////////////
2852/// Adds parameter to known parameters.
2853/// User should use SetParameter, because parameters are added during initialization part,
2854/// and after that adding new will be pointless.
2855
2857{
2858 //std::cout << "adding parameter " << name << std::endl;
2859
2860 // if parameter is already defined in fParams - just set the new value
2861 if(fParams.find(name) != fParams.end() )
2862 {
2863 int ipos = fParams[name];
2864 // TFormulaVariable & par = fParams[name];
2865 // par.fValue = value;
2866 if (ipos < 0) {
2867 ipos = fParams.size();
2868 fParams[name] = ipos;
2869 }
2870 //
2871 if (ipos >= (int)fClingParameters.size()) {
2872 if (ipos >= (int)fClingParameters.capacity())
2873 fClingParameters.reserve(TMath::Max(int(fParams.size()), ipos + 1));
2874 fClingParameters.insert(fClingParameters.end(), ipos + 1 - fClingParameters.size(), 0.0);
2875 }
2877 } else {
2878 // new parameter defined
2879 fNpar++;
2880 // TFormulaVariable(name,value,fParams.size());
2881 int pos = fParams.size();
2882 // fParams.insert(std::make_pair<TString,TFormulaVariable>(name,TFormulaVariable(name,value,pos)));
2883 auto ret = fParams.insert(std::make_pair(name, pos));
2884 // map returns a std::pair<iterator, bool>
2885 // use map the order for default position of parameters in the vector
2886 // (i.e use the alphabetic order)
2887 if (ret.second) {
2888 // a new element is inserted
2889 if (ret.first == fParams.begin())
2890 pos = 0;
2891 else {
2892 auto previous = (ret.first);
2893 --previous;
2894 pos = previous->second + 1;
2895 }
2896
2897 if (pos < (int)fClingParameters.size())
2898 fClingParameters.insert(fClingParameters.begin() + pos, value);
2899 else {
2900 // this should not happen
2901 if (pos > (int)fClingParameters.size())
2902 Warning("inserting parameter %s at pos %d when vector size is %d \n", name.Data(), pos,
2903 (int)fClingParameters.size());
2904
2905 if (pos >= (int)fClingParameters.capacity())
2906 fClingParameters.reserve(TMath::Max(int(fParams.size()), pos + 1));
2907 fClingParameters.insert(fClingParameters.end(), pos + 1 - fClingParameters.size(), 0.0);
2908 fClingParameters[pos] = value;
2909 }
2910
2911 // need to adjust all other positions
2912 for (auto it = ret.first; it != fParams.end(); ++it) {
2913 it->second = pos;
2914 pos++;
2915 }
2916
2917 // for (auto & p : fParams)
2918 // std::cout << "Parameter " << p.first << " position " << p.second << " value " <<
2919 // fClingParameters[p.second] << std::endl;
2920 // printf("inserted parameters size params %d size cling %d \n",fParams.size(), fClingParameters.size() );
2921 }
2922 if (processFormula) {
2923 // replace first in input parameter name with [name]
2926 }
2927 }
2928}
2929
2930////////////////////////////////////////////////////////////////////////////////
2931/// Return parameter index given a name (return -1 for not existing parameters)
2932/// non need to print an error
2933
2934Int_t TFormula::GetParNumber(const char * name) const {
2935 auto it = fParams.find(name);
2936 if (it == fParams.end()) {
2937 return -1;
2938 }
2939 return it->second;
2940
2941}
2942
2943////////////////////////////////////////////////////////////////////////////////
2944/// Returns parameter value given by string.
2945
2947{
2948 const int i = GetParNumber(name);
2949 if (i == -1) {
2950 Error("GetParameter","Parameter %s is not defined.",name);
2951 return TMath::QuietNaN();
2952 }
2953
2954 return GetParameter( i );
2955}
2956
2957////////////////////////////////////////////////////////////////////////////////
2958/// Return parameter value given by integer.
2959
2961{
2962 //TString name = TString::Format("%d",param);
2963 if(param >=0 && param < (int) fClingParameters.size())
2964 return fClingParameters[param];
2965 Error("GetParameter","wrong index used - use GetParameter(name)");
2966 return TMath::QuietNaN();
2967}
2968
2969////////////////////////////////////////////////////////////////////////////////
2970/// Return parameter name given by integer.
2971
2972const char * TFormula::GetParName(Int_t ipar) const
2973{
2974 if (ipar < 0 || ipar >= fNpar) return "";
2975
2976 // need to loop on the map to find corresponding parameter
2977 for ( auto & p : fParams) {
2978 if (p.second == ipar) return p.first.Data();
2979 }
2980 Error("GetParName","Parameter with index %d not found !!",ipar);
2981 //return TString::Format("p%d",ipar);
2982 return "";
2983}
2984
2985////////////////////////////////////////////////////////////////////////////////
2987{
2988 if(!fClingParameters.empty())
2989 return const_cast<Double_t*>(&fClingParameters[0]);
2990 return nullptr;
2991}
2992
2994{
2995 for (Int_t i = 0; i < fNpar; ++i) {
2996 if (Int_t(fClingParameters.size()) > i)
2997 params[i] = fClingParameters[i];
2998 else
2999 params[i] = -1;
3000 }
3001}
3002
3003////////////////////////////////////////////////////////////////////////////////
3004/// Sets parameter value.
3005
3007{
3009
3010 // do we need this ???
3011#ifdef OLDPARAMS
3012 if (fParams.find(name) == fParams.end()) {
3013 Error("SetParameter", "Parameter %s is not defined.", name.Data());
3014 return;
3015 }
3016 fParams[name].fValue = value;
3017 fParams[name].fFound = true;
3018 fClingParameters[fParams[name].fArrayPos] = value;
3019 fAllParametersSetted = true;
3020 for (map<TString, TFormulaVariable>::iterator it = fParams.begin(); it != fParams.end(); ++it) {
3021 if (!it->second.fFound) {
3022 fAllParametersSetted = false;
3023 break;
3024 }
3025 }
3026#endif
3027}
3028
3029#ifdef OLDPARAMS
3030
3031////////////////////////////////////////////////////////////////////////////////
3032/// Set multiple parameters.
3033/// First argument is an array of pairs<TString,Double>, where
3034/// first argument is name of parameter,
3035/// second argument represents value.
3036/// size - number of params passed in first argument
3037
3039{
3040 for(Int_t i = 0 ; i < size ; ++i)
3041 {
3042 pair<TString, Double_t> p = params[i];
3043 if (fParams.find(p.first) == fParams.end()) {
3044 Error("SetParameters", "Parameter %s is not defined", p.first.Data());
3045 continue;
3046 }
3047 fParams[p.first].fValue = p.second;
3048 fParams[p.first].fFound = true;
3049 fClingParameters[fParams[p.first].fArrayPos] = p.second;
3050 }
3051 fAllParametersSetted = true;
3052 for (map<TString, TFormulaVariable>::iterator it = fParams.begin(); it != fParams.end(); ++it) {
3053 if (!it->second.fFound) {
3054 fAllParametersSetted = false;
3055 break;
3056 }
3057 }
3058}
3059#endif
3060
3061////////////////////////////////////////////////////////////////////////////////
3063{
3064 if(!params || size < 0 || size > fNpar) return;
3065 // reset vector of cling parameters
3066 if (size != (int) fClingParameters.size() ) {
3067 Warning("SetParameters","size is not same of cling parameter size %d - %d",size,int(fClingParameters.size()) );
3068 for (Int_t i = 0; i < size; ++i) {
3069 TString name = TString::Format("%d", i);
3070 SetParameter(name, params[i]);
3071 }
3072 return;
3073 }
3074 fAllParametersSetted = true;
3075 std::copy(params, params+size, fClingParameters.begin() );
3076}
3077
3078////////////////////////////////////////////////////////////////////////////////
3079/// Set a vector of parameters value.
3080/// Order in the vector is by default the alphabetic order given to the parameters
3081/// apart if the users has defined explicitly the parameter names
3082
3084{
3085 DoSetParameters(params,fNpar);
3086}
3087
3088////////////////////////////////////////////////////////////////////////////////
3089/// Set a parameter given a parameter index.
3090/// The parameter index is by default the alphabetic order given to the parameters,
3091/// apart if the users has defined explicitly the parameter names.
3092
3094{
3095 if (param < 0 || param >= fNpar) return;
3096 assert(int(fClingParameters.size()) == fNpar);
3097 fClingParameters[param] = value;
3098 // TString name = TString::Format("%d",param);
3099 // SetParameter(name,value);
3100}
3101
3102////////////////////////////////////////////////////////////////////////////////
3103void TFormula::SetParName(Int_t ipar, const char * name)
3104{
3105
3107 Error("SetParName","Wrong Parameter index %d ",ipar);
3108 return;
3109 }
3111 // find parameter with given index
3112 for ( auto &it : fParams) {
3113 if (it.second == ipar) {
3114 oldName = it.first;
3115 fParams.erase(oldName);
3116 fParams.insert(std::make_pair(name, ipar) );
3117 break;
3118 }
3119 }
3120 if (oldName.IsNull() ) {
3121 Error("SetParName","Parameter %d is not existing.",ipar);
3122 return;
3123 }
3124
3125 //replace also parameter name in formula expression in case is not a lambda
3127
3128}
3129
3130////////////////////////////////////////////////////////////////////////////////
3131/// Replace in Formula expression the parameter name.
3132
3134 if (!formula.IsNull() ) {
3135 bool found = false;
3136 for(list<TFormulaFunction>::iterator it = fFuncs.begin(); it != fFuncs.end(); ++it)
3137 {
3138 if (oldName == it->GetName()) {
3139 found = true;
3140 it->fName = name;
3141 break;
3142 }
3143 }
3144 if (!found) {
3145 Error("SetParName", "Parameter %s is not defined.", oldName.Data());
3146 return;
3147 }
3148 // change whitespace to \s to avoid problems in parsing
3150 newName.ReplaceAll(" ", "\\s");
3151 TString pattern = TString::Format("[%s]", oldName.Data());
3152 TString replacement = TString::Format("[%s]", newName.Data());
3153 formula.ReplaceAll(pattern, replacement);
3154 }
3155
3156}
3157
3158////////////////////////////////////////////////////////////////////////////////
3160{
3161#ifdef R__HAS_STD_EXPERIMENTAL_SIMD
3162 if (fNdim == 0) {
3163 Info("SetVectorized","Cannot vectorized a function of zero dimension");
3164 return;
3165 }
3166 if (vectorized != fVectorized) {
3167 if (!fFormula)
3168 Error("SetVectorized", "Cannot set vectorized to %d -- Formula is missing", vectorized);
3169
3171 // no need to JIT a new signature in case of zero dimension
3172 //if (fNdim== 0) return;
3173 fClingInitialized = false;
3174 fReadyToExecute = false;
3175 fClingName = "";
3177
3178 fMethod.reset();
3179
3182 }
3183#else
3184 if (vectorized)
3185 Warning("SetVectorized", "Cannot set vectorized -- try building with C++20 on Linux");
3186#endif
3187}
3188
3189////////////////////////////////////////////////////////////////////////////////
3190Double_t TFormula::EvalPar(const Double_t *x,const Double_t *params) const
3191{
3192 if (!fVectorized)
3193 return DoEval(x, params);
3194
3195#ifdef R__HAS_STD_EXPERIMENTAL_SIMD
3196
3197 if (fNdim == 0 || !x) {
3198 ROOT::Double_v ret = DoEvalVec(nullptr, params);
3199 return ret[0];
3200 }
3201
3202 // otherwise, regular Double_t inputs on a vectorized function
3203
3204 // convert our input into vectors then convert back
3205 if (gDebug)
3206 Info("EvalPar", "Function is vectorized - converting Double_t into ROOT::Double_v and back");
3207
3208 if (fNdim < 5) {
3209 const int maxDim = 4;
3210 std::array<ROOT::Double_v, maxDim> xvec;
3211 for (int i = 0; i < fNdim; i++)
3212 xvec[i] = x[i];
3213
3214 ROOT::Double_v ans = DoEvalVec(xvec.data(), params);
3215 return ans[0];
3216 }
3217 // allocating a vector is much slower (we do only for dim > 4)
3218 std::vector<ROOT::Double_v> xvec(fNdim);
3219 for (int i = 0; i < fNdim; i++)
3220 xvec[i] = x[i];
3221
3222 ROOT::Double_v ans = DoEvalVec(xvec.data(), params);
3223 return ans[0];
3224
3225#else
3226 // this should never happen, because fVectorized can only be set true with
3227 // R__HAS_STD_EXPERIMENTAL_SIMD, but just in case:
3228 Error("EvalPar", "Formula is vectorized (even though vectorizaton is not supported!)");
3229 return TMath::QuietNaN();
3230#endif
3231}
3232
3234
3235static bool functionExists(const string &Name) {
3236 return gInterpreter->GetFunction(/*cl*/nullptr, Name.c_str());
3237}
3238
3240#ifdef ROOT_SUPPORT_CLAD
3241 if (!IsCladRuntimeIncluded) {
3242 IsCladRuntimeIncluded = true;
3243 gInterpreter->Declare("#include <Math/CladDerivator.h>\n#pragma clad OFF");
3244 }
3245#else
3246 IsCladRuntimeIncluded = false;
3247#endif
3248}
3249
3250static bool
3252 std::string &GenerationInput) {
3253 std::string ReqFuncName = FuncName + "_req";
3254 // We want to call clad::differentiate(TFormula_id);
3255 GenerationInput = std::string("#pragma cling optimize(2)\n") +
3256 "#pragma clad ON\n" +
3257 "void " + ReqFuncName + "() {\n" +
3258 CladStatement + "\n " +
3259 "}\n" +
3260 "#pragma clad OFF";
3261
3262 return gInterpreter->Declare(GenerationInput.c_str());
3263}
3264
3267 Bool_t hasParameters = (Npar > 0);
3268 Bool_t hasVariables = (Ndim > 0);
3269 std::unique_ptr<TMethodCall>
3271 Vectorized, /*AddCladArrayRef*/ true);
3272 return prepareFuncPtr(method.get());
3273}
3274
3276 const Double_t *pars, Double_t *result, const Int_t /*result_size*/)
3277{
3278 void *args[3];
3279 args[0] = &vars;
3280 if (!pars) {
3281 // __attribute__((used)) extern "C" void __cf_0(void* obj, int nargs, void** args, void* ret)
3282 // {
3283 // if (ret) {
3284 // new (ret) (double) (((double (&)(double*))TFormula____id)(*(double**)args[0]));
3285 // return;
3286 // } else {
3287 // ((double (&)(double*))TFormula____id)(*(double**)args[0]);
3288 // return;
3289 // }
3290 // }
3291 args[1] = &result;
3292 (*FuncPtr)(nullptr, 2, args, /*ret*/ nullptr); // We do not use ret in a return-void func.
3293 } else {
3294 // __attribute__((used)) extern "C" void __cf_0(void* obj, int nargs, void** args, void* ret)
3295 // {
3296 // ((void (&)(double*, double*, double*))TFormula____id_grad_1)(*(double**)args[0],
3297 // *(double**)args[1],
3298 // *(double**)args[2]);
3299 // return;
3300 // }
3301 args[1] = &pars;
3302 args[2] = &result;
3303 (*FuncPtr)(nullptr, 3, args, /*ret*/nullptr); // We do not use ret in a return-void func.
3304 }
3305}
3306
3307/// returns true on success.
3309 // We already have generated the gradient.
3310 if (fGradFuncPtr)
3311 return true;
3312
3314 return false;
3315
3318 return false;
3319
3320 // Check if the gradient request was made as part of another TFormula.
3321 // This can happen when we create multiple TFormula objects with the same
3322 // formula. In that case, the hasher will give identical id and we can
3323 // reuse the already generated gradient function.
3325 std::string GradientCall
3326 ("clad::gradient(" + std::string(fClingName.Data()) + ", \"p\");");
3330 return false;
3331 }
3332
3334 return true;
3335}
3336
3337// Compute the gradient with respect to the parameter passing
3338/// a CladStorageObject, i.e. a std::vector, which has the size as the nnumber of parameters.
3339/// Note that the result buffer needs to be initialized to zero before passing it to this function.
3341{
3342 if (DoEval(x) == TMath::QuietNaN())
3343 return;
3344
3345 if (!fClingInitialized) {
3346 Error("GradientPar", "Could not initialize the formula!");
3347 return;
3348 }
3349
3350 if (!GenerateGradientPar()) {
3351 Error("GradientPar", "Could not generate a gradient for the formula %s!",
3352 fClingName.Data());
3353 return;
3354 }
3355
3356 if ((int)result.size() < fNpar) {
3357 Warning("GradientPar",
3358 "The size of gradient result is %zu but %d is required. Resizing.",
3359 result.size(), fNpar);
3360 result.resize(fNpar);
3361 }
3362 GradientPar(x, result.data());
3363}
3364/// Compute the gradient with respect to the parameter passing
3365/// a buffer with a size at least equal to the number of parameters.
3366/// Note that the result buffer needs to be initialized to zero before passed to this function.
3368 const Double_t *vars = (x) ? x : fClingVariables.data();
3369 const Double_t *pars = (fNpar <= 0) ? nullptr : fClingParameters.data();
3371}
3372
3373/// returns true on success.
3375{
3376 // We already have generated the hessian.
3377 if (fHessFuncPtr)
3378 return true;
3379
3381 return false;
3382
3385 return false;
3386
3387 // Check if the hessian request was made as part of another TFormula.
3388 // This can happen when we create multiple TFormula objects with the same
3389 // formula. In that case, the hasher will give identical id and we can
3390 // reuse the already generated hessian function.
3392 std::string indexes = (fNpar - 1 == 0) ? "0" : std::string("0:")
3393 + std::to_string(fNpar - 1);
3394 std::string HessianCall
3395 ("clad::hessian(" + std::string(fClingName.Data()) + ", \"p["
3396 + indexes + "]\" );");
3399 return false;
3400 }
3401
3403 return true;
3404}
3405
3407{
3408 if (DoEval(x) == TMath::QuietNaN())
3409 return;
3410
3411 if (!fClingInitialized) {
3412 Error("HessianPar", "Could not initialize the formula!");
3413 return;
3414 }
3415
3416 if (!GenerateHessianPar()) {
3417 Error("HessianPar", "Could not generate a hessian for the formula %s!",
3418 fClingName.Data());
3419 return;
3420 }
3421
3422 if ((int)result.size() < fNpar) {
3423 Warning("HessianPar",
3424 "The size of hessian result is %zu but %d is required. Resizing.",
3425 result.size(), fNpar * fNpar);
3426 result.resize(fNpar * fNpar);
3427 }
3428 HessianPar(x, result.data());
3429}
3430
3432 const Double_t *vars = (x) ? x : fClingVariables.data();
3433 const Double_t *pars = (fNpar <= 0) ? nullptr : fClingParameters.data();
3435}
3436
3437////////////////////////////////////////////////////////////////////////////////
3438#ifdef R__HAS_STD_EXPERIMENTAL_SIMD
3439// ROOT::Double_v TFormula::Eval(ROOT::Double_v x, ROOT::Double_v y, ROOT::Double_v z, ROOT::Double_v t) const
3440// {
3441// ROOT::Double_v xxx[] = {x, y, z, t};
3442// return EvalPar(xxx, nullptr);
3443// }
3444
3445ROOT::Double_v TFormula::EvalParVec(const ROOT::Double_v *x, const Double_t *params) const
3446{
3447 if (fVectorized)
3448 return DoEvalVec(x, params);
3449
3450 if (fNdim == 0 || !x)
3451 return DoEval(nullptr, params); // automatic conversion to vectorized
3452
3453 // otherwise, trying to input vectors into a scalar function
3454
3455 if (gDebug)
3456 Info("EvalPar", "Function is not vectorized - converting ROOT::Double_v into Double_t and back");
3457
3458 const int vecSize = ROOT::Double_v::size();
3459 std::vector<Double_t> xscalars(vecSize*fNdim);
3460
3461 for (int i = 0; i < vecSize; i++)
3462 for (int j = 0; j < fNdim; j++)
3463 xscalars[i * fNdim + j] = x[j][i];
3464
3465 ROOT::Double_v answers(0.);
3466 for (int i = 0; i < vecSize; i++)
3467 answers[i] = DoEval(&xscalars[i * fNdim], params);
3468
3469 return answers;
3470}
3471#endif
3472
3473////////////////////////////////////////////////////////////////////////////////
3474/// Evaluate formula.
3475/// If formula is not ready to execute(missing parameters/variables),
3476/// print these which are not known.
3477/// If parameter has default value, and has not been set, appropriate warning is shown.
3478
3479Double_t TFormula::DoEval(const double * x, const double * params) const
3480{
3481 if(!fReadyToExecute)
3482 {
3483 Error("Eval", "Formula is invalid and not ready to execute ");
3484 for (auto it = fFuncs.begin(); it != fFuncs.end(); ++it) {
3485 TFormulaFunction fun = *it;
3486 if (!fun.fFound) {
3487 printf("%s is unknown.\n", fun.GetName());
3488 }
3489 }
3490 return TMath::QuietNaN();
3491 }
3492
3493 // Lazy initialization is set and needed when reading from a file
3495 // try recompiling the formula. We need to lock because this is not anymore thread safe
3497 // check again in case another thread has initialized the formula (see ROOT-10994)
3498 if (!fClingInitialized) {
3499 auto thisFormula = const_cast<TFormula*>(this);
3500 thisFormula->ReInitializeEvalMethod();
3501 }
3502 if (!fClingInitialized) {
3503 Error("DoEval", "Formula has error and it is not properly initialized ");
3504 return TMath::QuietNaN();
3505 }
3506 }
3507
3508 if (fLambdaPtr && TestBit(TFormula::kLambda)) {// case of lambda functions
3509 std::function<double(double *, double *)> & fptr = * ( (std::function<double(double *, double *)> *) fLambdaPtr);
3510 assert(x);
3511 //double * v = (x) ? const_cast<double*>(x) : const_cast<double*>(fClingVariables.data());
3512 double * v = const_cast<double*>(x);
3513 double * p = (params) ? const_cast<double*>(params) : const_cast<double*>(fClingParameters.data());
3514 return fptr(v, p);
3515 }
3516
3517
3518 Double_t result = 0;
3519 void* args[2];
3520 double * vars = (x) ? const_cast<double*>(x) : const_cast<double*>(fClingVariables.data());
3521 args[0] = &vars;
3522 if (fNpar <= 0) {
3523 (*fFuncPtr)(nullptr, 1, args, &result);
3524 } else {
3525 double *pars = (params) ? const_cast<double *>(params) : const_cast<double *>(fClingParameters.data());
3526 args[1] = &pars;
3527 (*fFuncPtr)(nullptr, 2, args, &result);
3528 }
3529 return result;
3530}
3531
3532////////////////////////////////////////////////////////////////////////////////
3533// Copied from DoEval, but this is the vectorized version
3534#ifdef R__HAS_STD_EXPERIMENTAL_SIMD
3535ROOT::Double_v TFormula::DoEvalVec(const ROOT::Double_v *x, const double *params) const
3536{
3537 if (!fReadyToExecute) {
3538 Error("Eval", "Formula is invalid and not ready to execute ");
3539 for (auto it = fFuncs.begin(); it != fFuncs.end(); ++it) {
3540 TFormulaFunction fun = *it;
3541 if (!fun.fFound) {
3542 printf("%s is unknown.\n", fun.GetName());
3543 }
3544 }
3545 return TMath::QuietNaN();
3546 }
3547 // todo maybe save lambda ptr stuff for later
3548
3550 // try recompiling the formula. We need to lock because this is not anymore thread safe
3552 // check again in case another thread has initialized the formula (see ROOT-10994)
3553 if (!fClingInitialized) {
3554 auto thisFormula = const_cast<TFormula*>(this);
3555 thisFormula->ReInitializeEvalMethod();
3556 }
3557 if (!fClingInitialized) {
3558 Error("DoEval", "Formula has error and it is not properly initialized ");
3559 ROOT::Double_v res = TMath::QuietNaN();
3560 return res;
3561 }
3562 }
3563
3564 ROOT::Double_v result = 0;
3565 void *args[2];
3566
3567 ROOT::Double_v *vars = const_cast<ROOT::Double_v *>(x);
3568 args[0] = &vars;
3569 if (fNpar <= 0) {
3570 (*fFuncPtr)(0, 1, args, &result);
3571 }else {
3572 double *pars = (params) ? const_cast<double *>(params) : const_cast<double *>(fClingParameters.data());
3573 args[1] = &pars;
3574 (*fFuncPtr)(0, 2, args, &result);
3575 }
3576 return result;
3577}
3578#endif // R__HAS_STD_EXPERIMENTAL_SIMD
3579
3580//////////////////////////////////////////////////////////////////////////////
3581/// Re-initialize eval method
3582///
3583/// This function is called by DoEval and DoEvalVector in case of a previous failure
3584/// or in case of reading from a file
3585////////////////////////////////////////////////////////////////////////////////
3587
3588
3589 if (TestBit(TFormula::kLambda) ) {
3590 Info("ReInitializeEvalMethod","compile now lambda expression function using Cling");
3592 fLazyInitialization = false;
3593 return;
3594 }
3595 fMethod.reset();
3596
3597 if (!fLazyInitialization) Warning("ReInitializeEvalMethod", "Formula is NOT properly initialized - try calling again TFormula::PrepareEvalMethod");
3598 //else Info("ReInitializeEvalMethod", "Compile now the formula expression using Cling");
3599
3600 // check first if formula exists in the global map
3601 {
3602
3604
3605 // std::cout << "gClingFunctions list" << std::endl;
3606 // for (auto thing : gClingFunctions)
3607 // std::cout << "gClingFunctions : " << thing.first << std::endl;
3608
3610
3611 if (funcit != gClingFunctions.end()) {
3613 fClingInitialized = true;
3614 fLazyInitialization = false;
3615 return;
3616 }
3617 }
3618 // compile now formula using cling
3620 if (fClingInitialized && !fLazyInitialization) Info("ReInitializeEvalMethod", "Formula is now properly initialized !!");
3621 fLazyInitialization = false;
3622
3623 // add function pointer in global map
3624 if (fClingInitialized) {
3625 R__ASSERT(!fSavedInputFormula.empty());
3626 // if Cling has been successfully initialized
3627 // put function ptr in the static map
3629 gClingFunctions.insert(std::make_pair(fSavedInputFormula, (void *)fFuncPtr));
3630 }
3631
3632
3633 return;
3634}
3635
3636////////////////////////////////////////////////////////////////////////////////
3637/// Return the expression formula.
3638///
3639/// - If option = "P" replace the parameter names with their values
3640/// - If option = "CLING" return the actual expression used to build the function passed to Cling
3641/// - If option = "CLINGP" replace in the Cling expression the parameter with their values
3642
3644{
3645 TString opt(option);
3646 if (opt.IsNull() || TestBit(TFormula::kLambda) ) return fFormula;
3647 opt.ToUpper();
3648
3649 // if (opt.Contains("N") ) {
3650 // TString formula = fFormula;
3651 // ReplaceParName(formula, ....)
3652 // }
3653
3654 if (opt.Contains("CLING") ) {
3655 std::string clingFunc = fClingInput.Data();
3656 std::size_t found = clingFunc.find("return");
3657 std::size_t found2 = clingFunc.rfind(';');
3658 if (found == std::string::npos || found2 == std::string::npos) {
3659 Error("GetExpFormula","Invalid Cling expression - return default formula expression");
3660 return fFormula;
3661 }
3662 TString clingFormula = fClingInput(found+7,found2-found-7);
3663 // to be implemented
3664 if (!opt.Contains("P")) return clingFormula;
3665 // replace all "p[" with "[parname"
3666 int i = 0;
3667 while (i < clingFormula.Length()-2 ) {
3668 // look for p[number
3669 if (clingFormula[i] == 'p' && clingFormula[i+1] == '[' && isdigit(clingFormula[i+2]) ) {
3670 int j = i+3;
3671 while ( j < clingFormula.Length() && isdigit(clingFormula[j]) ) { j++;}
3672 if ( j >= clingFormula.Length() || clingFormula[j] != ']') {
3673 Error("GetExpFormula","Parameters not found - invalid expression - return default cling formula");
3674 return clingFormula;
3675 }
3676 TString parNumbName = clingFormula(i+2,j-i-2);
3677 int parNumber = parNumbName.Atoi();
3679 std::stringstream ss;
3681 clingFormula.Replace(i,j-i+1, replacement);
3682 i += replacement.size();
3683 }
3684 i++;
3685 }
3686 return clingFormula;
3687 }
3688 if (opt.Contains("P") ) {
3689 // replace parameter names with their values
3691 int i = 0;
3692 while (i < expFormula.Length()-2 ) {
3693 // look for [parName]
3694 if (expFormula[i] == '[') {
3695 int j = i+1;
3696 while ( j < expFormula.Length() && expFormula[j] != ']' ) { j++;}
3697 if ( j >= expFormula.Length() || expFormula[j] != ']') {
3698 Error("GetExpFormula","Parameter names not found - invalid expression - return default formula");
3699 return expFormula;
3700 }
3701 TString parName = expFormula(i+1,j-i-1);
3702 std::stringstream ss;
3704 expFormula.Replace(i,j-i+1, replacement);
3705 i += replacement.size();
3706 }
3707 i++;
3708 }
3709 return expFormula;
3710 }
3711 Warning("GetExpFormula","Invalid option - return default formula expression");
3712 return fFormula;
3713}
3714
3716 std::unique_ptr<TInterpreterValue> v = gInterpreter->MakeInterpreterValue();
3717 std::string s("(void (&)(Double_t const *, Double_t *, Double_t *)) ");
3718 s += GetGradientFuncName();
3719 gInterpreter->Evaluate(s.c_str(), *v);
3720 return v->ToString();
3721}
3722
3724 std::unique_ptr<TInterpreterValue> v = gInterpreter->MakeInterpreterValue();
3725 gInterpreter->Evaluate(GetHessianFuncName().c_str(), *v);
3726 return v->ToString();
3727}
3728
3729////////////////////////////////////////////////////////////////////////////////
3730/// Print the formula and its attributes.
3731
3733{
3734 printf(" %20s : %s Ndim= %d, Npar= %d, Number= %d \n",GetName(),GetTitle(), fNdim,fNpar,fNumber);
3735 printf(" Formula expression: \n");
3736 printf("\t%s \n",fFormula.Data() );
3737 TString opt(option);
3738 opt.ToUpper();
3739 // do an evaluation as a cross-check
3740 //if (fReadyToExecute) Eval();
3741
3742 if (opt.Contains("V") ) {
3743 if (fNdim > 0 && !TestBit(TFormula::kLambda)) {
3744 printf("List of Variables: \n");
3745 assert(int(fClingVariables.size()) >= fNdim);
3746 for ( int ivar = 0; ivar < fNdim ; ++ivar) {
3747 printf("Var%4d %20s = %10f \n",ivar,GetVarName(ivar).Data(), fClingVariables[ivar]);
3748 }
3749 }
3750 if (fNpar > 0) {
3751 printf("List of Parameters: \n");
3752 if ( int(fClingParameters.size()) < fNpar)
3753 Error("Print","Number of stored parameters in vector %zu in map %zu is different than fNpar %d",fClingParameters.size(), fParams.size(), fNpar);
3754 assert(int(fClingParameters.size()) >= fNpar);
3755 // print with order passed to Cling function
3756 for ( int ipar = 0; ipar < fNpar ; ++ipar) {
3757 printf("Par%4d %20s = %10f \n",ipar,GetParName(ipar), fClingParameters[ipar] );
3758 }
3759 }
3760 printf("Expression passed to Cling:\n");
3761 printf("\t%s\n",fClingInput.Data() );
3762 if (fGradFuncPtr) {
3763 printf("Generated Gradient:\n");
3764 printf("%s\n", fGradGenerationInput.c_str());
3765 printf("%s\n", GetGradientFormula().Data());
3766 }
3767 if(fHessFuncPtr) {
3768 printf("Generated Hessian:\n");
3769 printf("%s\n", fHessGenerationInput.c_str());
3770 printf("%s\n", GetHessianFormula().Data());
3771 }
3772 }
3773 if(!fReadyToExecute)
3774 {
3775 Warning("Print", "Formula is not ready to execute. Missing parameters/variables");
3776 for (list<TFormulaFunction>::const_iterator it = fFuncs.begin(); it != fFuncs.end(); ++it) {
3777 TFormulaFunction fun = *it;
3778 if (!fun.fFound) {
3779 printf("%s is unknown.\n", fun.GetName());
3780 }
3781 }
3782 }
3783 if (!fAllParametersSetted) {
3784 // we can skip this
3785 // Info("Print","Not all parameters are set.");
3786 // for(map<TString,TFormulaVariable>::const_iterator it = fParams.begin(); it != fParams.end(); ++it)
3787 // {
3788 // pair<TString,TFormulaVariable> param = *it;
3789 // if(!param.second.fFound)
3790 // {
3791 // printf("%s has default value %lf\n",param.first.Data(),param.second.GetInitialValue());
3792 // }
3793 // }
3794 }
3795}
3796
3797////////////////////////////////////////////////////////////////////////////////
3798/// Stream a class object.
3799
3801{
3802 if (b.IsReading() ) {
3803 UInt_t R__s, R__c;
3804 Version_t v = b.ReadVersion(&R__s, &R__c);
3805 //std::cout << "version " << v << std::endl;
3806 if (v <= 8 && v > 3 && v != 6) {
3807 // old TFormula class
3809 // read old TFormula class
3810 fold->Streamer(b, v, R__s, R__c, TFormula::Class());
3811 //std::cout << "read old tformula class " << std::endl;
3812 TFormula fnew(fold->GetName(), fold->GetExpFormula() );
3813
3814 *this = fnew;
3815
3816 // printf("copying content in a new TFormula \n");
3817 SetParameters(fold->GetParameters() );
3818 if (!fReadyToExecute ) {
3819 Error("Streamer","Old formula read from file is NOT valid");
3820 Print("v");
3821 }
3822 delete fold;
3823 return;
3824 }
3825 else if (v > 8) {
3826 // new TFormula class
3827 b.ReadClassBuffer(TFormula::Class(), this, v, R__s, R__c);
3828
3829 //std::cout << "reading npar = " << GetNpar() << std::endl;
3830
3831 // initialize the formula
3832 // need to set size of fClingVariables which is transient
3833 //fClingVariables.resize(fNdim);
3834
3835 // case of formula contains only parameters
3836 if (fFormula.IsNull() ) return;
3837
3838
3839 // store parameter values, names and order
3840 std::vector<double> parValues = fClingParameters;
3841 auto paramMap = fParams;
3842 fNpar = fParams.size();
3843
3844 fLazyInitialization = true; // when reading we initialize the formula later to avoid problem of recursive Jitting
3845
3846 if (!TestBit(TFormula::kLambda) ) {
3847
3848 // save dimension read from the file (stored for V >=12)
3849 // and we check after initializing if it is the same
3850 int ndim = fNdim;
3851 fNdim = 0;
3852
3853 //std::cout << "Streamer::Reading preprocess the formula " << fFormula << " ndim = " << fNdim << " npar = " << fNpar << std::endl;
3854 // for ( auto &p : fParams)
3855 // std::cout << "parameter " << p.first << " index " << p.second << std::endl;
3856
3857 fClingParameters.clear(); // need to be reset before re-initializing it
3858
3859 FillDefaults();
3860
3861
3863
3864 //std::cout << "Streamer::after pre-process the formula " << fFormula << " ndim = " << fNdim << " npar = " << fNpar << std::endl;
3865
3867
3868 //std::cout << "Streamer::after prepared " << fClingInput << " ndim = " << fNdim << " npar = " << fNpar << std::endl;
3869
3870
3871 // restore parameter values
3872 if (fNpar != (int) parValues.size() ) {
3873 Error("Streamer","number of parameters computed (%d) is not same as the stored parameters (%d)",fNpar,int(parValues.size()) );
3874 Print("v");
3875 }
3876 if (v > 11 && fNdim != ndim) {
3877 Error("Streamer","number of dimension computed (%d) is not same as the stored value (%d)",fNdim, ndim );
3878 Print("v");
3879 }
3880 }
3881 else {
3882 // we also delay the initialization of lamda expressions
3883 if (!fLazyInitialization) {
3885 if (ret) {
3886 fClingInitialized = true;
3887 }
3888 }else {
3889 fReadyToExecute = true;
3890 }
3891 }
3892 assert(fNpar == (int) parValues.size() );
3893 std::copy( parValues.begin(), parValues.end(), fClingParameters.begin() );
3894 // restore parameter names and order
3895 if (fParams.size() != paramMap.size() ) {
3896 Warning("Streamer","number of parameters list found (%zu) is not same as the stored one (%zu) - use re-created list",fParams.size(),paramMap.size()) ;
3897 //Print("v");
3898 }
3899 else
3900 //assert(fParams.size() == paramMap.size() );
3901 fParams = paramMap;
3902
3903 // input formula into Cling
3904 // need to replace in cling the name of the pointer of this object
3905 // TString oldClingName = fClingName;
3906 // fClingName.Replace(fClingName.Index("_0x")+1,fClingName.Length(), TString::Format("%p",this) );
3907 // fClingInput.ReplaceAll(oldClingName, fClingName);
3908 // InputFormulaIntoCling();
3909
3910 if (!TestBit(kNotGlobal)) {
3912 gROOT->GetListOfFunctions()->Add(this);
3913 }
3914 if (!fReadyToExecute ) {
3915 Error("Streamer","Formula read from file is NOT ready to execute");
3916 Print("v");
3917 }
3918 //std::cout << "reading 2 npar = " << GetNpar() << std::endl;
3919
3920 return;
3921 }
3922 else {
3923 Error("Streamer","Reading version %d is not supported",v);
3924 return;
3925 }
3926 }
3927 else {
3928 // case of writing
3929 b.WriteClassBuffer(TFormula::Class(), this);
3930 // std::cout << "writing npar = " << GetNpar() << std::endl;
3931 }
3932}
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define a(i)
Definition RSha256.hxx:99
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
bool Bool_t
Boolean (0=false, 1=true) (bool)
Definition RtypesCore.h:77
int Int_t
Signed integer 4 bytes (int)
Definition RtypesCore.h:59
short Version_t
Class version identifier (short)
Definition RtypesCore.h:79
double Double_t
Double 8 bytes.
Definition RtypesCore.h:73
constexpr Ssiz_t kNPOS
The equivalent of std::string::npos for the ROOT class TString.
Definition RtypesCore.h:131
const char Option_t
Option string (const char)
Definition RtypesCore.h:80
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:125
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition TError.cxx:208
void GetParameters(TFitEditor::FuncParams_t &pars, TF1 *func)
Stores the parameters of the given function into pars.
static std::unordered_map< std::string, void * > gClingFunctions
Definition TFormula.cxx:329
static void IncludeCladRuntime(Bool_t &IsCladRuntimeIncluded)
void(*)(Int_t nobjects, TObject **from, TObject **to) TFormulaUpdater_t
Definition TFormula.cxx:345
static const TString gNamePrefix
Definition TFormula.cxx:325
static void CallCladFunction(TInterpreter::CallFuncIFacePtr_t::Generic_t FuncPtr, const Double_t *vars, const Double_t *pars, Double_t *result, const Int_t)
static std::unique_ptr< TMethodCall > prepareMethod(bool HasParameters, bool HasVariables, const char *FuncName, bool IsVectorized, bool AddCladArrayRef=false)
Definition TFormula.cxx:864
static bool DeclareGenerationInput(std::string FuncName, std::string CladStatement, std::string &GenerationInput)
static bool IsReservedName(const char *name)
Definition TFormula.cxx:520
bool R__SetClonesArrayTFormulaUpdater(TFormulaUpdater_t func)
static bool functionExists(const string &Name)
int R__RegisterTFormulaUpdaterTrigger
Definition TFormula.cxx:348
static TInterpreter::CallFuncIFacePtr_t::Generic_t prepareFuncPtr(TMethodCall *method)
Definition TFormula.cxx:902
static void R__v5TFormulaUpdater(Int_t nobjects, TObject **from, TObject **to)
Definition TFormula.cxx:331
static TInterpreter::CallFuncIFacePtr_t::Generic_t GetFuncPtr(std::string FuncName, Int_t Npar, Int_t Ndim, Bool_t Vectorized)
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t option
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t target
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t UChar_t len
char name[80]
Definition TGX11.cxx:148
R__EXTERN TInterpreter * gCling
#define gInterpreter
Int_t gDebug
Global variable setting the debug level. Set to 0 to disable, increase it in steps of 1 to increase t...
Definition TROOT.cxx:783
R__EXTERN TVirtualMutex * gROOTMutex
Definition TROOT.h:63
#define gROOT
Definition TROOT.h:426
#define R__LOCKGUARD(mutex)
const_iterator begin() const
const_iterator end() const
The FORMULA class (ROOT version 5)
Definition TFormula.h:65
Buffer base class used for serializing objects.
Definition TBuffer.h:43
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
const TList * GetListOfAllPublicMethods(Bool_t load=kTRUE)
Returns a list of all public methods of this class and its base classes.
Definition TClass.cxx:3903
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2994
1-Dim function class
Definition TF1.h:182
virtual TFormula * GetFormula()
Definition TF1.h:418
Helper class for TFormula.
Definition TFormula.h:32
Another helper class for TFormula.
Definition TFormula.h:65
TString fName
Definition TFormula.h:67
Double_t fValue
Definition TFormula.h:68
The Formula class.
Definition TFormula.h:89
CallFuncSignature fHessFuncPtr
! Function pointer, owned by the JIT.
Definition TFormula.h:109
Double_t GetParameter(const char *name) const
Returns parameter value given by string.
Bool_t PrepareFormula(TString &formula)
prepare the formula to be executed normally is called with fFormula
std::atomic< Bool_t > fClingInitialized
! Transient to force re-initialization
Definition TFormula.h:97
void GradientPar(const Double_t *x, TFormula::CladStorage &result)
Compute the gradient employing automatic differentiation.
void FillDefaults()
Fill structures with default variables, constants and function shortcuts.
Definition TFormula.cxx:967
TString GetHessianFormula() const
void Clear(Option_t *option="") override
Clear the formula setting expression to empty and reset the variables and parameters containers.
Definition TFormula.cxx:832
const TObject * GetLinearPart(Int_t i) const
Return linear part.
void InputFormulaIntoCling()
Inputs formula, transferred to C++ code into Cling.
Definition TFormula.cxx:941
void DoAddParameter(const TString &name, Double_t value, bool processFormula)
Adds parameter to known parameters.
void HandleLinear(TString &formula)
Handle linear functions defined with the operator ++ (@ is the shorthand).
TString fClingName
! Unique name passed to Cling to define the function ( double clingName(double*x, double*p) )
Definition TFormula.h:101
TString GetVarName(Int_t ivar) const
Returns variable name given its position in the array.
void SetVariable(const TString &name, Double_t value)
Sets variable value.
void HessianPar(const Double_t *x, TFormula::CladStorage &result)
Compute the gradient employing automatic differentiation.
Int_t GetParNumber(const char *name) const
Return parameter index given a name (return -1 for not existing parameters) non need to print an erro...
void DoSetParameters(const Double_t *p, Int_t size)
bool GenerateHessianPar()
Generate hessian computation routine with respect to the parameters.
TString GetGradientFormula() const
void HandleParametrizedFunctions(TString &formula)
Handling parametrized functions Function can be normalized, and have different variable then x.
~TFormula() override
Definition TFormula.cxx:530
TInterpreter::CallFuncIFacePtr_t::Generic_t CallFuncSignature
Definition TFormula.h:104
Double_t * GetParameters() const
void SetParName(Int_t ipar, const char *name)
void SetName(const char *name) override
Set the name of the formula.
std::string GetHessianFuncName() const
Definition TFormula.h:130
std::string fGradGenerationInput
! Input query to clad to generate a gradient
Definition TFormula.h:105
static Bool_t IsAParameterName(const TString &formula, int ipos)
check if given position is in a parameter name i.e. within "[ ]"
Definition TFormula.cxx:418
bool HasGradientGenerationFailed() const
Definition TFormula.h:133
void ReplaceParamName(TString &formula, const TString &oldname, const TString &name)
Replace in Formula expression the parameter name.
void SetPredefinedParamNames()
Set parameter names only in case of pre-defined functions.
void Copy(TObject &f1) const override
Copy this to obj.
Definition TFormula.cxx:749
static TClass * Class()
std::map< TString, Double_t > fConsts
!
Definition TFormula.h:145
std::map< TString, TString > fFunctionsShortcuts
!
Definition TFormula.h:146
void HandleParamRanges(TString &formula)
Handling parameter ranges, in the form of [1..5].
std::vector< TObject * > fLinearParts
Vector of linear functions.
Definition TFormula.h:151
static Bool_t IsBracket(const char c)
Definition TFormula.cxx:359
Bool_t fVectorized
Whether we should use vectorized or regular variables.
Definition TFormula.h:152
TString fClingInput
! Input function passed to Cling
Definition TFormula.h:93
Bool_t PrepareEvalMethod()
Sets TMethodCall to function inside Cling environment.
Definition TFormula.cxx:926
Int_t fNumber
Number used to identify pre-defined functions (gaus, expo,..)
Definition TFormula.h:150
std::string GetGradientFuncName() const
Definition TFormula.h:127
static bool fIsCladRuntimeIncluded
Definition TFormula.h:111
std::vector< Double_t > CladStorage
Definition TFormula.h:182
Double_t GetVariable(const char *name) const
Returns variable value.
std::list< TFormulaFunction > fFuncs
!
Definition TFormula.h:142
Bool_t fAllParametersSetted
Flag to control if all parameters are setted.
Definition TFormula.h:98
void ProcessFormula(TString &formula)
Iterates through functors in fFuncs and performs the appropriate action.
static Bool_t IsOperator(const char c)
Definition TFormula.cxx:351
bool HasHessianGenerationFailed() const
Definition TFormula.h:136
void SetVectorized(Bool_t vectorized)
const char * GetParName(Int_t ipar) const
Return parameter name given by integer.
std::map< TString, TFormulaVariable > fVars
! List of variable names
Definition TFormula.h:143
CallFuncSignature fFuncPtr
! Function pointer, owned by the JIT.
Definition TFormula.h:107
void HandlePolN(TString &formula)
Handling Polynomial Notation (polN)
static Bool_t IsHexadecimal(const TString &formula, int ipos)
Definition TFormula.cxx:395
void ExtractFunctors(TString &formula)
Extracts functors from formula, and put them in fFuncs.
Bool_t InitLambdaExpression(const char *formula)
Definition TFormula.cxx:670
void SetParameters(const Double_t *params)
Set a vector of parameters value.
std::string fSavedInputFormula
! Unique name used to defined the function and used in the global map (need to be saved in case of la...
Definition TFormula.h:102
Bool_t IsValid() const
Definition TFormula.h:273
void ReplaceAllNames(TString &formula, std::map< TString, TString > &substitutions)
Definition TFormula.cxx:470
static Bool_t IsDefaultVariableName(const TString &name)
Definition TFormula.cxx:377
void FillParametrizedFunctions(std::map< std::pair< TString, Int_t >, std::pair< TString, TString > > &functions)
Fill map with parametrized functions.
Double_t EvalPar(const Double_t *x, const Double_t *params=nullptr) const
void AddVariables(const TString *vars, const Int_t size)
Adds multiple variables.
Int_t GetVarNumber(const char *name) const
Returns variable number (positon in array) given its name.
std::vector< Double_t > fClingVariables
! Cached variables
Definition TFormula.h:94
std::unique_ptr< TMethodCall > fMethod
! Pointer to methodcall
Definition TFormula.h:100
TString fFormula
String representing the formula expression.
Definition TFormula.h:147
static Bool_t IsScientificNotation(const TString &formula, int ipos)
Definition TFormula.cxx:383
CallFuncSignature fGradFuncPtr
! Function pointer, owned by the JIT.
Definition TFormula.h:108
std::vector< Double_t > fClingParameters
Parameter values.
Definition TFormula.h:95
void SetParameter(const char *name, Double_t value)
Sets parameter value.
void Print(Option_t *option="") const override
Print the formula and its attributes.
void PreProcessFormula(TString &formula)
Preprocessing of formula Replace all ** by ^, all ++ by @, and removes spaces.
TString GetExpFormula(Option_t *option="") const
Return the expression formula.
Int_t fNdim
Dimension - needed for lambda expressions.
Definition TFormula.h:148
void SetVariables(const std::pair< TString, Double_t > *vars, const Int_t size)
Sets multiple variables.
void ReInitializeEvalMethod()
Re-initialize eval method.
@ kNotGlobal
Don't store in gROOT->GetListOfFunction (it should be protected)
Definition TFormula.h:177
@ kLambda
Set to true if TFormula has been build with a lambda.
Definition TFormula.h:180
@ kLinear
Set to true if the TFormula is for linear fitting.
Definition TFormula.h:179
@ kNormalized
Set to true if the TFormula (ex gausn) is normalized.
Definition TFormula.h:178
void * fLambdaPtr
! Pointer to the lambda function
Definition TFormula.h:110
TFormula & operator=(const TFormula &rhs)
= operator.
Definition TFormula.cxx:662
Int_t Compile(const char *expression="")
Compile the given expression with Cling backward compatibility method to be used in combination with ...
Definition TFormula.cxx:715
Int_t fNpar
! Number of parameter (transient since we save the vector)
Definition TFormula.h:149
void HandleFunctionArguments(TString &formula)
Handling user functions (and parametrized functions) to take variables and optionally parameters as a...
std::map< TString, Int_t, TFormulaParamOrder > fParams
|| List of parameter names
Definition TFormula.h:144
void AddVariable(const TString &name, Double_t value=0)
Adds variable to known variables, and reprocess formula.
Bool_t fLazyInitialization
! Transient flag to control lazy initialization (needed for reading from files)
Definition TFormula.h:99
void HandleExponentiation(TString &formula)
Handling exponentiation Can handle multiple carets, eg.
static Bool_t IsFunctionNameChar(const char c)
Definition TFormula.cxx:371
std::string fHessGenerationInput
! Input query to clad to generate a hessian
Definition TFormula.h:106
Double_t DoEval(const Double_t *x, const Double_t *p=nullptr) const
Evaluate formula.
Bool_t fReadyToExecute
! Transient to force initialization
Definition TFormula.h:96
bool GenerateGradientPar()
Generate gradient computation routine with respect to the parameters.
void Streamer(TBuffer &) override
Stream a class object.
Global functions class (global functions are obtained from CINT).
Definition TFunction.h:30
virtual Longptr_t ProcessLine(const char *line, EErrorCode *error=nullptr)=0
virtual Bool_t Declare(const char *code)=0
virtual Bool_t CallFunc_IsValid(CallFunc_t *) const
virtual CallFuncIFacePtr_t CallFunc_IFacePtr(CallFunc_t *) const
A doubly linked list.
Definition TList.h:38
Method or function calling interface.
Definition TMethodCall.h:37
Each ROOT class (see TClass) has a linked list of methods.
Definition TMethod.h:38
The TNamed class is the base class for all named ROOT classes.
Definition TNamed.h:29
void Copy(TObject &named) const override
Copy this to obj.
Definition TNamed.cxx:93
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
const char * GetTitle() const override
Returns title of object.
Definition TNamed.h:50
TString fTitle
Definition TNamed.h:33
TString fName
Definition TNamed.h:32
virtual void SetName(const char *name)
Set the name of the TNamed.
Definition TNamed.cxx:149
Mother of all ROOT objects.
Definition TObject.h:42
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition TObject.h:204
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:1081
virtual TObject * FindObject(const char *name) const
Must be redefined in derived classes.
Definition TObject.cxx:422
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:885
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1095
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:1069
Regular expression class.
Definition TRegexp.h:31
Basic string class.
Definition TString.h:138
Ssiz_t Length() const
Definition TString.h:425
TString & Insert(Ssiz_t pos, const char *s)
Definition TString.h:670
Int_t Atoi() const
Return integer value of string.
Definition TString.cxx:1994
TSubString Strip(EStripType s=kTrailing, char c=' ') const
Return a substring of self stripped at beginning and/or end.
Definition TString.cxx:1170
TString & Replace(Ssiz_t pos, Ssiz_t n, const char *s)
Definition TString.h:703
const char * Data() const
Definition TString.h:384
Bool_t IsDigit() const
Returns true if all characters in string are digits (0-9) or white spaces, i.e.
Definition TString.cxx:1836
TString & ReplaceAll(const TString &s1, const TString &s2)
Definition TString.h:713
@ kBoth
Definition TString.h:284
Ssiz_t Last(char c) const
Find last occurrence of a character c.
Definition TString.cxx:938
void ToUpper()
Change string to upper case.
Definition TString.cxx:1202
Bool_t IsNull() const
Definition TString.h:422
TString & Append(const char *cs)
Definition TString.h:581
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:2384
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:641
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition TString.h:660
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
TF1 * f1
Definition legend1.C:11
std::vector< std::string > Split(std::string_view str, std::string_view delims, bool skipEmpty=false)
Splits a string at each character in delims.
TROOT * GetROOT()
Definition TROOT.cxx:552
constexpr Double_t G()
Gravitational constant in: .
Definition TMath.h:138
constexpr Double_t C()
Velocity of light in .
Definition TMath.h:117
Short_t Max(Short_t a, Short_t b)
Returns the largest of a and b.
Definition TMathBase.h:249
Double_t QuietNaN()
Returns a quiet NaN as defined by IEEE 754.
Definition TMath.h:913
Double_t Floor(Double_t x)
Rounds x downward, returning the largest integral value that is not greater than x.
Definition TMath.h:691
constexpr Double_t K()
Boltzmann's constant in : .
Definition TMath.h:250
constexpr Double_t Sqrt2()
Definition TMath.h:89
constexpr Double_t E()
Base of natural log: .
Definition TMath.h:96
constexpr Double_t Sigma()
Stefan-Boltzmann constant in : .
Definition TMath.h:273
constexpr Double_t H()
Planck's constant in : .
Definition TMath.h:191
constexpr Double_t LogE()
Base-10 log of e (to convert ln to log)
Definition TMath.h:110
constexpr Double_t Ln10()
Natural log of 10 (to convert log to ln)
Definition TMath.h:103
constexpr Double_t EulerGamma()
Euler-Mascheroni Constant.
Definition TMath.h:335
constexpr Double_t Pi()
Definition TMath.h:40
constexpr Double_t R()
Universal gas constant ( ) in
Definition TMath.h:305
Double_t Log10(Double_t x)
Returns the common (base-10) logarithm of x.
Definition TMath.h:773
Double_t Infinity()
Returns an infinity as defined by the IEEE standard.
Definition TMath.h:928
bool operator()(const TString &a, const TString &b) const
Definition TFormula.cxx:440
void(* Generic_t)(void *, int, void **, void *)
TMarker m
Definition textangle.C:8