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