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