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 VECCORE_ENABLE_VC
60 auto n = ROOT::Double_v::size();
61 return "Vc::fixed_size_simd<double, " + std::to_string(n) + ">";
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_VECCORE
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 if (fVectorized) {
976 } else {
977 for (auto fun : funShortcuts) {
978 fFunctionsShortcuts[fun.first] = fun.second;
979 }
980 }
981}
982
983////////////////////////////////////////////////////////////////////////////////
984/// Fill the shortcuts for vectorized functions
985/// We will replace for example sin with vecCore::Mat::Sin
986///
987
989#ifdef R__HAS_VECCORE
991 { {"sin","vecCore::math::Sin" },
992 {"cos","vecCore::math::Cos" }, {"exp","vecCore::math::Exp"}, {"log","vecCore::math::Log"}, {"log10","vecCore::math::Log10"},
993 {"tan","vecCore::math::Tan"},
994 //{"sinh","vecCore::math::Sinh"}, {"cosh","vecCore::math::Cosh"},{"tanh","vecCore::math::Tanh"},
995 {"asin","vecCore::math::ASin"},
996 {"acos","TMath::Pi()/2-vecCore::math::ASin"},
997 {"atan","vecCore::math::ATan"},
998 {"atan2","vecCore::math::ATan2"}, {"sqrt","vecCore::math::Sqrt"},
999 {"ceil","vecCore::math::Ceil"}, {"floor","vecCore::math::Floor"}, {"pow","vecCore::math::Pow"},
1000 {"cbrt","vecCore::math::Cbrt"},{"abs","vecCore::math::Abs"},
1001 {"min","vecCore::math::Min"},{"max","vecCore::math::Max"},{"sign","vecCore::math::Sign" }
1002 //{"sq","TMath::Sq"}, {"binomial","TMath::Binomial"} // this last two functions will not work in vectorized mode
1003 };
1004 // replace in the data member maps fFunctionsShortcuts
1005 for (auto fun : vecFunShortcuts) {
1006 fFunctionsShortcuts[fun.first] = fun.second;
1007 }
1008#endif
1009 // do nothing in case Veccore is not enabled
1010}
1011
1012////////////////////////////////////////////////////////////////////////////////
1013/// \brief Handling Polynomial Notation (polN)
1014///
1015/// This section describes how polynomials are handled in the code.
1016///
1017/// - If any name appears before 'pol', it is treated as a variable used in the polynomial.
1018/// - Example: `varpol2(5)` will be replaced with `[5] + [6]*var + [7]*var^2`.
1019/// - Empty name is treated like variable `x`.
1020///
1021/// - The extended format allows direct variable specification:
1022/// - Example: `pol2(x, 0)` or `pol(x, [A])`.
1023///
1024/// - Traditional syntax: `polN` represents a polynomial of degree `N`:
1025/// - `ypol1` → `[p0] + [p1] * y`
1026/// - `pol1(x, 0)` → `[p0] + [p1] * x`
1027/// - `pol2(y, 1)` → `[p1] + [p2] * y + [p3] * TMath::Sq(y)`
1028////////////////////////////////////////////////////////////////////////////////
1029
1031{
1032
1033 Int_t polPos = formula.Index("pol");
1034 while (polPos != kNPOS && !IsAParameterName(formula, polPos)) {
1035 Bool_t defaultVariable = false;
1036 TString variable;
1037 Int_t openingBracketPos = formula.Index('(', polPos);
1039 Bool_t defaultDegree = true;
1040 Int_t degree = 1, counter = 0;
1042 std::vector<TString> paramNames;
1043
1044 // check for extended format
1045 Bool_t isNewFormat = false;
1046 if (!defaultCounter) {
1047 TString args = formula(openingBracketPos + 1, formula.Index(')', polPos) - openingBracketPos - 1);
1048 if (args.Contains(",")) {
1049 isNewFormat = true;
1050 sdegree = formula(polPos + 3, openingBracketPos - polPos - 3);
1051 if (!sdegree.IsDigit())
1052 sdegree = "1";
1053 degree = sdegree.Atoi();
1054
1055 std::vector<TString> tokens;
1056 std::stringstream ss(args.Data());
1057 std::string token;
1058 while (std::getline(ss, token, ',')) { // spliting string at ,
1059 tokens.push_back(TString(token));
1060 }
1061
1062 if (!tokens.empty()) {
1063 variable = tokens[0];
1064 variable.Strip(TString::kBoth);
1065
1066 // extract counter if provided as a numeric value, without this it was not working with [A], [B]
1067 if (tokens.size() > 1) {
1070 if (counterStr.IsDigit()) {
1071 counter = counterStr.Atoi();
1072 }
1073 }
1074
1075 // store parameter names for later use
1076 for (size_t i = 1; i < tokens.size(); i++) {
1077 TString paramStr = tokens[i];
1078 paramStr.Strip(TString::kBoth);
1079 if (paramStr.BeginsWith("[") && paramStr.EndsWith("]")) {
1080 paramStr = paramStr(1, paramStr.Length() - 2);
1081 paramNames.push_back(paramStr);
1082
1083 if (fParams.find(paramStr) == fParams.end()) {
1085 fClingParameters.push_back(0.0);
1086 fNpar++;
1087 }
1088 }
1089 }
1090 }
1091 }
1092 }
1093
1094 // handle original format if not new format
1095 if (!isNewFormat) {
1096 if (!defaultCounter) {
1097 sdegree = formula(polPos + 3, openingBracketPos - polPos - 3);
1098 if (!sdegree.IsDigit())
1099 defaultCounter = true;
1100 }
1101 if (!defaultCounter) {
1102 degree = sdegree.Atoi();
1103 counter = TString(formula(openingBracketPos + 1, formula.Index(')', polPos) - openingBracketPos)).Atoi();
1104 } else {
1105 Int_t temp = polPos + 3;
1106 while (temp < formula.Length() && isdigit(formula[temp])) {
1107 defaultDegree = false;
1108 temp++;
1109 }
1110 degree = TString(formula(polPos + 3, temp - polPos - 3)).Atoi();
1111 }
1112
1113 if (polPos - 1 < 0 || !IsFunctionNameChar(formula[polPos - 1]) || formula[polPos - 1] == ':') {
1114 variable = "x";
1115 defaultVariable = true;
1116 } else {
1117 Int_t tmp = polPos - 1;
1118 while (tmp >= 0 && IsFunctionNameChar(formula[tmp]) && formula[tmp] != ':') {
1119 tmp--;
1120 }
1121 variable = formula(tmp + 1, polPos - (tmp + 1));
1122 }
1123 }
1124
1125 // build replacement string (modified)
1127 if (isNewFormat && !paramNames.empty()) {
1128 for (Int_t i = 0; i <= degree && i < static_cast<Int_t>(paramNames.size()); i++) {
1129 if (i == 0) {
1130 replacement = TString::Format("[%s]", paramNames[i].Data());
1131 } else if (i == 1) {
1132 replacement.Append(TString::Format("+[%s]*%s", paramNames[i].Data(), variable.Data()));
1133 } else {
1134 replacement.Append(TString::Format("+[%s]*%s^%d", paramNames[i].Data(), variable.Data(), i));
1135 }
1136 }
1137 } else {
1138 replacement = TString::Format("[%d]", counter);
1139 for (Int_t i = 1; i <= degree; i++) {
1140 if (i == 1) {
1141 replacement.Append(TString::Format("+[%d]*%s", counter + i, variable.Data()));
1142 } else {
1143 replacement.Append(TString::Format("+[%d]*%s^%d", counter + i, variable.Data(), i));
1144 }
1145 }
1146 }
1147
1148 if (degree > 0) {
1149 replacement.Insert(0, '(');
1150 replacement.Append(')');
1151 }
1152
1153 // create patern based on format either new or old
1154 TString pattern;
1155 if (isNewFormat) {
1156 pattern = formula(polPos, formula.Index(')', polPos) - polPos + 1);
1157 } else if (defaultCounter && !defaultDegree) {
1158 pattern = TString::Format("%spol%d", (defaultVariable ? "" : variable.Data()), degree);
1159 } else if (defaultCounter && defaultDegree) {
1160 pattern = TString::Format("%spol", (defaultVariable ? "" : variable.Data()));
1161 } else {
1162 pattern = TString::Format("%spol%d(%d)", (defaultVariable ? "" : variable.Data()), degree, counter);
1163 }
1164
1165 if (!formula.Contains(pattern)) {
1166 Error("HandlePolN", "Error handling polynomial function - expression is %s - trying to replace %s with %s ",
1167 formula.Data(), pattern.Data(), replacement.Data());
1168 break;
1169 }
1170
1171 if (formula == pattern) {
1172 SetBit(kLinear, true);
1173 fNumber = 300 + degree;
1174 }
1175
1176 formula.ReplaceAll(pattern, replacement);
1177 polPos = formula.Index("pol");
1178 }
1179}
1180
1181////////////////////////////////////////////////////////////////////////////////
1182/// Handling parametrized functions
1183/// Function can be normalized, and have different variable then x.
1184/// Variables should be placed in brackets after function name.
1185/// No brackets are treated like [x].
1186/// Normalized function has char 'n' after name, eg.
1187/// gausn[var](0) will be replaced with [0]*exp(-0.5*((var-[1])/[2])^2)/(sqrt(2*pi)*[2])
1188///
1189/// Adding function is easy, just follow these rules, and add to
1190/// `TFormula::FillParametrizedFunctions` defined further below:
1191///
1192/// - Key for function map is pair of name and dimension of function
1193/// - value of key is a pair function body and normalized function body
1194/// - {Vn} is a place where variable appear, n represents n-th variable from variable list.
1195/// Count starts from 0.
1196/// - [num] stands for parameter number.
1197/// If user pass to function argument 5, num will stand for (5 + num) parameter.
1198///
1199
1201{
1202 // define all parametrized functions
1205
1207 functionsNumbers["gaus"] = 100;
1208 functionsNumbers["bigaus"] = 102;
1209 functionsNumbers["landau"] = 400;
1210 functionsNumbers["expo"] = 200;
1211 functionsNumbers["crystalball"] = 500;
1212
1213 // replace old names xygaus -> gaus[x,y]
1214 formula.ReplaceAll("xyzgaus","gaus[x,y,z]");
1215 formula.ReplaceAll("xygausn","gausn[x,y]");
1216 formula.ReplaceAll("xygaus","gaus[x,y]");
1217 formula.ReplaceAll("xgaus","gaus[x]");
1218 formula.ReplaceAll("ygaus","gaus[y]");
1219 formula.ReplaceAll("zgaus","gaus[z]");
1220 formula.ReplaceAll("xyexpo","expo[x,y]");
1221 formula.ReplaceAll("xexpo","expo[x]");
1222 formula.ReplaceAll("yexpo","expo[y]");
1223 formula.ReplaceAll("zexpo","expo[z]");
1224 formula.ReplaceAll("xylandaun","landaun[x,y]");
1225 formula.ReplaceAll("xylandau","landau[x,y]");
1226 // at the moment pre-defined functions have no more than 3 dimensions
1227 const char * defaultVariableNames[] = { "x","y","z"};
1228
1229 for (map<pair<TString, Int_t>, pair<TString, TString>>::iterator it = functions.begin(); it != functions.end();
1230 ++it) {
1231
1232 TString funName = it->first.first;
1233 Int_t funDim = it->first.second;
1234 Int_t funPos = formula.Index(funName);
1235
1236 // std::cout << formula << " ---- " << funName << " " << funPos << std::endl;
1237 while (funPos != kNPOS && !IsAParameterName(formula, funPos)) {
1238
1239 // should also check that function is not something else (e.g. exponential - parse the expo)
1240 Int_t lastFunPos = funPos + funName.Length();
1241
1242 // check that first and last character is not a special character
1243 Int_t iposBefore = funPos - 1;
1244 // std::cout << "looping on funpos is " << funPos << " formula is " << formula << " function " << funName <<
1245 // std::endl;
1246 if (iposBefore >= 0) {
1247 assert(iposBefore < formula.Length());
1248 //if (isalpha(formula[iposBefore])) {
1249 if (IsFunctionNameChar(formula[iposBefore])) {
1250 // std::cout << "previous character for function " << funName << " is " << formula[iposBefore] << "- skip
1251 // " << std::endl;
1252 funPos = formula.Index(funName, lastFunPos);
1253 continue;
1254 }
1255 }
1256
1257 Bool_t isNormalized = false;
1258 if (lastFunPos < formula.Length()) {
1259 // check if function is normalized by looking at "n" character after function name (e.g. gausn)
1260 isNormalized = (formula[lastFunPos] == 'n');
1261 if (isNormalized)
1262 lastFunPos += 1;
1263 if (lastFunPos < formula.Length()) {
1264 char c = formula[lastFunPos];
1265 // check if also last character is not alphanumeric or is not an operator and not a parenthesis ( or [.
1266 // Parenthesis [] are used to express the variables
1267 if (isalnum(c) || (!IsOperator(c) && c != '(' && c != ')' && c != '[' && c != ']')) {
1268 // std::cout << "last character for function " << funName << " is " << c << " skip .." << std::endl;
1269 funPos = formula.Index(funName, lastFunPos);
1270 continue;
1271 }
1272 }
1273 }
1274
1275 if (isNormalized) {
1276 SetBit(kNormalized, true);
1277 }
1278 std::vector<TString> variables;
1279 Int_t dim = 0;
1280 TString varList = "";
1281 Bool_t defaultVariables = false;
1282
1283 // check if function has specified the [...] e.g. gaus[x,y]
1284 Int_t openingBracketPos = funPos + funName.Length() + (isNormalized ? 1 : 0);
1286 if (openingBracketPos > formula.Length() || formula[openingBracketPos] != '[') {
1287 dim = funDim;
1288 variables.resize(dim);
1289 for (Int_t idim = 0; idim < dim; ++idim)
1290 variables[idim] = defaultVariableNames[idim];
1291 defaultVariables = true;
1292 } else {
1293 // in case of [..] found, assume they specify all the variables. Use it to get function dimension
1296 dim = varList.CountChar(',') + 1;
1297 variables.resize(dim);
1298 Int_t Nvar = 0;
1299 TString varName = "";
1300 for (Int_t i = 0; i < varList.Length(); ++i) {
1301 if (IsFunctionNameChar(varList[i])) {
1302 varName.Append(varList[i]);
1303 }
1304 if (varList[i] == ',') {
1305 variables[Nvar] = varName;
1306 varName = "";
1307 Nvar++;
1308 }
1309 }
1310 if (varName != "") // we will miss last variable
1311 {
1312 variables[Nvar] = varName;
1313 }
1314 }
1315 // check if dimension obtained from [...] is compatible with what is defined in existing pre-defined functions
1316 // std::cout << " Found dim = " << dim << " and function dimension is " << funDim << std::endl;
1317 if (dim != funDim) {
1318 pair<TString, Int_t> key = make_pair(funName, dim);
1319 if (functions.find(key) == functions.end()) {
1320 Error("PreProcessFormula", "Dimension of function %s is detected to be of dimension %d and is not "
1321 "compatible with existing pre-defined function which has dim %d",
1322 funName.Data(), dim, funDim);
1323 return;
1324 }
1325 // skip the particular function found - we might find later on the corresponding pre-defined function
1326 funPos = formula.Index(funName, lastFunPos);
1327 continue;
1328 }
1329 // look now for the (..) brackets to get the parameter counter (e.g. gaus(0) + gaus(3) )
1330 // need to start for a position
1332 bool defaultCounter = (openingParenthesisPos > formula.Length() || formula[openingParenthesisPos] != '(');
1333
1334 // Int_t openingParenthesisPos = formula.Index('(',funPos);
1335 // Bool_t defaultCounter = (openingParenthesisPos == kNPOS);
1336 Int_t counter;
1337 if (defaultCounter) {
1338 counter = 0;
1339 } else {
1340 // Check whether this is just a number in parentheses. If not, leave
1341 // it to `HandleFunctionArguments` to be parsed
1342
1343 TRegexp counterPattern("([0-9]+)");
1344 Ssiz_t len;
1345 if (counterPattern.Index(formula, &len, openingParenthesisPos) == -1) {
1346 funPos = formula.Index(funName, funPos + 1);
1347 continue;
1348 } else {
1349 counter =
1350 TString(formula(openingParenthesisPos + 1, formula.Index(')', funPos) - openingParenthesisPos - 1))
1351 .Atoi();
1352 }
1353 }
1354 // std::cout << "openingParenthesisPos " << openingParenthesisPos << " counter is " << counter << std::endl;
1355
1356 TString body = (isNormalized ? it->second.second : it->second.first);
1357 if (isNormalized && body == "") {
1358 Error("PreprocessFormula", "%d dimension function %s has no normalized form.", it->first.second,
1359 funName.Data());
1360 break;
1361 }
1362 for (int i = 0; i < body.Length(); ++i) {
1363 if (body[i] == '{') {
1364 // replace {Vn} with variable names
1365 i += 2; // skip '{' and 'V'
1366 Int_t num = TString(body(i, body.Index('}', i) - i)).Atoi();
1367 TString variable = variables[num];
1368 TString pattern = TString::Format("{V%d}", num);
1369 i -= 2; // restore original position
1370 body.Replace(i, pattern.Length(), variable, variable.Length());
1371 i += variable.Length() - 1; // update i to reflect change in body string
1372 } else if (body[i] == '[') {
1373 // update parameter counters in case of many functions (e.g. gaus(0)+gaus(3) )
1374 Int_t tmp = i;
1375 while (tmp < body.Length() && body[tmp] != ']') {
1376 tmp++;
1377 }
1378 Int_t num = TString(body(i + 1, tmp - 1 - i)).Atoi();
1379 num += counter;
1380 TString replacement = TString::Format("%d", num);
1381
1382 body.Replace(i + 1, tmp - 1 - i, replacement, replacement.Length());
1383 i += replacement.Length() + 1;
1384 }
1385 }
1386 TString pattern;
1388 pattern = TString::Format("%s%s", funName.Data(), (isNormalized ? "n" : ""));
1389 }
1391 pattern = TString::Format("%s%s(%d)", funName.Data(), (isNormalized ? "n" : ""), counter);
1392 }
1394 pattern = TString::Format("%s%s[%s]", funName.Data(), (isNormalized ? "n" : ""), varList.Data());
1395 }
1397 pattern =
1398 TString::Format("%s%s[%s](%d)", funName.Data(), (isNormalized ? "n" : ""), varList.Data(), counter);
1399 }
1401
1402 // set the number (only in case a function exists without anything else
1403 if (fNumber == 0 && formula.Length() <= (pattern.Length() - funPos) + 1) { // leave 1 extra
1404 fNumber = functionsNumbers[funName] + 10 * (dim - 1);
1405 }
1406
1407 // std::cout << " replace " << pattern << " with " << replacement << std::endl;
1408
1409 formula.Replace(funPos, pattern.Length(), replacement, replacement.Length());
1410
1411 funPos = formula.Index(funName);
1412 }
1413 // std::cout << " End loop of " << funName << " formula is now " << formula << std::endl;
1414 }
1415}
1416
1417////////////////////////////////////////////////////////////////////////////////
1418/// Handling parameter ranges, in the form of [1..5]
1420{
1421 TRegexp rangePattern("\\[[0-9]+\\.\\.[0-9]+\\]");
1422 Ssiz_t len;
1423 int matchIdx = 0;
1424 while ((matchIdx = rangePattern.Index(formula, &len, matchIdx)) != -1) {
1425 int startIdx = matchIdx + 1;
1426 int endIdx = formula.Index("..", startIdx) + 2; // +2 for ".."
1427 int startCnt = TString(formula(startIdx, formula.Length())).Atoi();
1428 int endCnt = TString(formula(endIdx, formula.Length())).Atoi();
1429
1430 if (endCnt <= startCnt)
1431 Error("HandleParamRanges", "End parameter (%d) <= start parameter (%d) in parameter range", endCnt, startCnt);
1432
1433 TString newString = "[";
1434 for (int cnt = startCnt; cnt < endCnt; cnt++)
1435 newString += TString::Format("%d],[", cnt);
1436 newString += TString::Format("%d]", endCnt);
1437
1438 // std::cout << "newString generated by HandleParamRanges is " << newString << std::endl;
1439 formula.Replace(matchIdx, formula.Index("]", matchIdx) + 1 - matchIdx, newString);
1440
1441 matchIdx += newString.Length();
1442 }
1443
1444 // std::cout << "final formula is now " << formula << std::endl;
1445}
1446
1447////////////////////////////////////////////////////////////////////////////////
1448/// Handling user functions (and parametrized functions)
1449/// to take variables and optionally parameters as arguments
1451{
1452 // std::cout << "calling `HandleFunctionArguments` on " << formula << std::endl;
1453
1454 // Define parametrized functions, in case we need to use them
1455 std::map<std::pair<TString, Int_t>, std::pair<TString, TString>> parFunctions;
1457
1458 // loop through characters
1459 for (Int_t i = 0; i < formula.Length(); ++i) {
1460 // List of things to ignore (copied from `TFormula::ExtractFunctors`)
1461
1462 // ignore things that start with square brackets
1463 if (formula[i] == '[') {
1464 while (formula[i] != ']')
1465 i++;
1466 continue;
1467 }
1468 // ignore strings
1469 if (formula[i] == '\"') {
1470 do
1471 i++;
1472 while (formula[i] != '\"');
1473 continue;
1474 }
1475 // ignore numbers (scientific notation)
1476 if (IsScientificNotation(formula, i))
1477 continue;
1478 // ignore x in hexadecimal number
1479 if (IsHexadecimal(formula, i)) {
1480 while (!IsOperator(formula[i]) && i < formula.Length())
1481 i++;
1482 continue;
1483 }
1484
1485 // investigate possible start of function name
1486 if (isalpha(formula[i]) && !IsOperator(formula[i])) {
1487 // std::cout << "character : " << i << " " << formula[i] << " is not an operator and is alpha" << std::endl;
1488
1489 int j; // index to end of name
1490 for (j = i; j < formula.Length() && IsFunctionNameChar(formula[j]); j++)
1491 ;
1492 TString name = (TString)formula(i, j - i);
1493 // std::cout << "parsed name " << name << std::endl;
1494
1495 // Count arguments (careful about parentheses depth)
1496 // Make list of indices where each argument is separated
1497 int nArguments = 1;
1498 int depth = 1;
1499 std::vector<int> argSeparators;
1500 argSeparators.push_back(j); // opening parenthesis
1501 int k; // index for end of closing parenthesis
1502 for (k = j + 1; depth >= 1 && k < formula.Length(); k++) {
1503 if (formula[k] == ',' && depth == 1) {
1504 nArguments++;
1505 argSeparators.push_back(k);
1506 } else if (formula[k] == '(')
1507 depth++;
1508 else if (formula[k] == ')')
1509 depth--;
1510 }
1511 argSeparators.push_back(k - 1); // closing parenthesis
1512
1513 // retrieve `f` (code copied from ExtractFunctors)
1514 TObject *obj = nullptr;
1515 {
1517 obj = gROOT->GetListOfFunctions()->FindObject(name);
1518 }
1519 TFormula *f = dynamic_cast<TFormula *>(obj);
1520 if (!f) {
1521 // maybe object is a TF1
1522 TF1 *f1 = dynamic_cast<TF1 *>(obj);
1523 if (f1)
1524 f = f1->GetFormula();
1525 }
1526 // `f` should be found by now, if it was a user-defined function.
1527 // The other possibility we need to consider is that this is a
1528 // parametrized function (else case below)
1529
1530 bool nameRecognized = (f != nullptr);
1531
1532 // Get ndim, npar, and replacementFormula of function
1533 int ndim = 0;
1534 int npar = 0;
1536 if (f) {
1537 ndim = f->GetNdim();
1538 npar = f->GetNpar();
1539 replacementFormula = f->GetExpFormula();
1540 } else {
1541 // otherwise, try to match default parametrized functions
1542
1543 for (const auto &keyval : parFunctions) {
1544 // (name, ndim)
1545 const pair<TString, Int_t> &name_ndim = keyval.first;
1546 // (formula without normalization, formula with normalization)
1548
1549 // match names like gaus, gausn, breitwigner
1550 if (name == name_ndim.first)
1552 else if (name == name_ndim.first + "n" && formulaPair.second != "")
1554 else
1555 continue;
1556
1557 // set ndim
1558 ndim = name_ndim.second;
1559
1560 // go through replacementFormula to find the number of parameters
1561 npar = 0;
1562 int idx = 0;
1563 while ((idx = replacementFormula.Index('[', idx)) != kNPOS) {
1564 npar = max(npar, 1 + TString(replacementFormula(idx + 1, replacementFormula.Length())).Atoi());
1565 idx = replacementFormula.Index(']', idx);
1566 if (idx == kNPOS)
1567 Error("HandleFunctionArguments", "Square brackets not matching in formula %s",
1568 (const char *)replacementFormula);
1569 }
1570 // npar should be set correctly now
1571
1572 // break if number of arguments is good (note: `gaus`, has two
1573 // definitions with different numbers of arguments, but it works
1574 // out so that it should be unambiguous)
1575 if (nArguments == ndim + npar || nArguments == npar || nArguments == ndim) {
1576 nameRecognized = true;
1577 break;
1578 }
1579 }
1580 }
1581 if (nameRecognized && ndim > 4)
1582 Error("HandleFunctionArguments", "Number of dimensions %d greater than 4. Cannot parse formula.", ndim);
1583
1584 // if we have "recognizedName(...", then apply substitutions
1585 if (nameRecognized && j < formula.Length() && formula[j] == '(') {
1586 // std::cout << "naive replacement formula: " << replacementFormula << std::endl;
1587 // std::cout << "formula: " << formula << std::endl;
1588
1589 // map to rename each argument in `replacementFormula`
1591
1592 const char *defaultVariableNames[] = {"x", "y", "z", "t"};
1593
1594 // check nArguments and add to argSubstitutions map as appropriate
1595 bool canReplace = false;
1596 if (nArguments == ndim + npar) {
1597 // loop through all variables and parameters, filling in argSubstitutions
1598 for (int argNr = 0; argNr < nArguments; argNr++) {
1599
1600 // Get new name (for either variable or parameter)
1602 TString(formula(argSeparators[argNr] + 1, argSeparators[argNr + 1] - argSeparators[argNr] - 1));
1603 PreProcessFormula(newName); // so that nesting works
1604
1605 // Get old name(s)
1606 // and add to argSubstitutions map as appropriate
1607 if (argNr < ndim) { // variable
1608 TString oldName = (f) ? TString::Format("x[%d]", argNr) : TString::Format("{V%d}", argNr);
1610
1611 if (f)
1613
1614 } else { // parameter
1615 int parNr = argNr - ndim;
1617 (f) ? TString::Format("[%s]", f->GetParName(parNr)) : TString::Format("[%d]", parNr);
1619
1620 // If the name stays the same, keep the old value of the parameter
1621 if (f && oldName == newName)
1622 DoAddParameter(f->GetParName(parNr), f->GetParameter(parNr), false);
1623 }
1624 }
1625
1626 canReplace = true;
1627 } else if (nArguments == npar) {
1628 // Try to assume variables are implicit (need all arguments to be
1629 // parameters)
1630
1631 // loop to check if all arguments are parameters
1632 bool varsImplicit = true;
1633 for (int argNr = 0; argNr < nArguments && varsImplicit; argNr++) {
1634 int openIdx = argSeparators[argNr] + 1;
1635 int closeIdx = argSeparators[argNr + 1] - 1;
1636
1637 // check brackets on either end
1638 if (formula[openIdx] != '[' || formula[closeIdx] != ']' || closeIdx <= openIdx + 1)
1639 varsImplicit = false;
1640
1641 // check that the middle is a single function-name
1642 for (int idx = openIdx + 1; idx < closeIdx && varsImplicit; idx++)
1643 if (!IsFunctionNameChar(formula[idx]))
1644 varsImplicit = false;
1645
1646 if (!varsImplicit)
1647 Warning("HandleFunctionArguments",
1648 "Argument %d is not a parameter. Cannot assume variables are implicit.", argNr);
1649 }
1650
1651 // loop to replace parameter names
1652 if (varsImplicit) {
1653 // if parametrized function, still need to replace parameter names
1654 if (!f) {
1655 for (int dim = 0; dim < ndim; dim++) {
1657 }
1658 }
1659
1660 for (int argNr = 0; argNr < nArguments; argNr++) {
1662 (f) ? TString::Format("[%s]", f->GetParName(argNr)) : TString::Format("[%d]", argNr);
1664 TString(formula(argSeparators[argNr] + 1, argSeparators[argNr + 1] - argSeparators[argNr] - 1));
1665
1666 // preprocess the formula so that nesting works
1669
1670 // If the name stays the same, keep the old value of the parameter
1671 if (f && oldName == newName)
1672 DoAddParameter(f->GetParName(argNr), f->GetParameter(argNr), false);
1673 }
1674
1675 canReplace = true;
1676 }
1677 }
1678 if (!canReplace && nArguments == ndim) {
1679 // Treat parameters as implicit
1680
1681 // loop to replace variable names
1682 for (int argNr = 0; argNr < nArguments; argNr++) {
1683 TString oldName = (f) ? TString::Format("x[%d]", argNr) : TString::Format("{V%d}", argNr);
1685 TString(formula(argSeparators[argNr] + 1, argSeparators[argNr + 1] - argSeparators[argNr] - 1));
1686
1687 // preprocess so nesting works
1690
1691 if (f) // x, y, z are not used in parametrized function definitions
1693 }
1694
1695 if (f) {
1696 // keep old values of the parameters
1697 for (int parNr = 0; parNr < npar; parNr++)
1698 DoAddParameter(f->GetParName(parNr), f->GetParameter(parNr), false);
1699 }
1700
1701 canReplace = true;
1702 }
1703
1704 if (canReplace)
1706 // std::cout << "after replacement, replacementFormula is " << replacementFormula << std::endl;
1707
1708 if (canReplace) {
1709 // std::cout << "about to replace position " << i << " length " << k-i << " in formula : " << formula <<
1710 // std::endl;
1711 formula.Replace(i, k - i, replacementFormula);
1712 i += replacementFormula.Length() - 1; // skip to end of replacement
1713 // std::cout << "new formula is : " << formula << std::endl;
1714 } else {
1715 Warning("HandleFunctionArguments", "Unable to make replacement. Number of parameters doesn't work : "
1716 "%d arguments, %d dimensions, %d parameters",
1717 nArguments, ndim, npar);
1718 i = j;
1719 }
1720
1721 } else {
1722 i = j; // skip to end of candidate "name"
1723 }
1724 }
1725 }
1726
1727}
1728
1729////////////////////////////////////////////////////////////////////////////////
1730/// Handling exponentiation
1731/// Can handle multiple carets, eg.
1732/// 2^3^4 will be treated like 2^(3^4)
1733
1735{
1736 Int_t caretPos = formula.Last('^');
1737 while (caretPos != kNPOS && !IsAParameterName(formula, caretPos)) {
1738
1739 TString right, left;
1740 Int_t temp = caretPos;
1741 temp--;
1742 // get the expression in ( ) which has the operator^ applied
1743 if (formula[temp] == ')') {
1744 Int_t depth = 1;
1745 temp--;
1746 while (depth != 0 && temp > 0) {
1747 if (formula[temp] == ')')
1748 depth++;
1749 if (formula[temp] == '(')
1750 depth--;
1751 temp--;
1752 }
1753 if (depth == 0)
1754 temp++;
1755 }
1756 // this in case of someting like sin(x+2)^2
1757 do {
1758 temp--; // go down one
1759 // handle scientific notation cases (1.e-2 ^ 3 )
1760 if (temp >= 2 && IsScientificNotation(formula, temp - 1))
1761 temp -= 3;
1762 } while (temp >= 0 && !IsOperator(formula[temp]) && !IsBracket(formula[temp]));
1763
1764 assert(temp + 1 >= 0);
1765 Int_t leftPos = temp + 1;
1766 left = formula(leftPos, caretPos - leftPos);
1767 // std::cout << "left to replace is " << left << std::endl;
1768
1769 // look now at the expression after the ^ operator
1770 temp = caretPos;
1771 temp++;
1772 if (temp >= formula.Length()) {
1773 Error("HandleExponentiation", "Invalid position of operator ^");
1774 return;
1775 }
1776 if (formula[temp] == '(') {
1777 Int_t depth = 1;
1778 temp++;
1779 while (depth != 0 && temp < formula.Length()) {
1780 if (formula[temp] == ')')
1781 depth--;
1782 if (formula[temp] == '(')
1783 depth++;
1784 temp++;
1785 }
1786 temp--;
1787 } else {
1788 // handle case first character is operator - or + continue
1789 if (formula[temp] == '-' || formula[temp] == '+')
1790 temp++;
1791 // handle cases x^-2 or x^+2
1792 // need to handle also cases x^sin(x+y)
1793 Int_t depth = 0;
1794 // stop right expression if is an operator or if is a ")" from a zero depth
1795 while (temp < formula.Length() && ((depth > 0) || !IsOperator(formula[temp]))) {
1796 temp++;
1797 // handle scientific notation cases (1.e-2 ^ 3 )
1798 if (temp >= 2 && IsScientificNotation(formula, temp))
1799 temp += 2;
1800 // for internal parenthesis
1801 if (temp < formula.Length() && formula[temp] == '(')
1802 depth++;
1803 if (temp < formula.Length() && formula[temp] == ')') {
1804 if (depth > 0)
1805 depth--;
1806 else
1807 break; // case of end of a previously started expression e.g. sin(x^2)
1808 }
1809 }
1810 }
1811 right = formula(caretPos + 1, (temp - 1) - caretPos);
1812 // std::cout << "right to replace is " << right << std::endl;
1813
1814 TString pattern = TString::Format("%s^%s", left.Data(), right.Data());
1815 TString replacement = TString::Format("pow(%s,%s)", left.Data(), right.Data());
1816
1817 // special case for square function
1818 if (right == "2"){
1819 replacement = TString::Format("TMath::Sq(%s)",left.Data());
1820 }
1821
1822 // std::cout << "pattern : " << pattern << std::endl;
1823 // std::cout << "replacement : " << replacement << std::endl;
1824 formula.Replace(leftPos, pattern.Length(), replacement, replacement.Length());
1825
1826 caretPos = formula.Last('^');
1827 }
1828}
1829
1830
1831////////////////////////////////////////////////////////////////////////////////
1832/// Handle linear functions defined with the operator ++.
1833
1835{
1836 if(formula.Length() == 0) return;
1837 auto terms = ROOT::Split(formula.Data(), "@");
1838 if(terms.size() <= 1) return; // function is not linear
1839 // Handle Linear functions identified with "@" operator
1840 fLinearParts.reserve(terms.size());
1842 int delimeterPos = 0;
1843 for(std::size_t iTerm = 0; iTerm < terms.size(); ++iTerm) {
1844 // determine the position of the "@" operator in the formula
1845 delimeterPos += terms[iTerm].size() + (iTerm == 0);
1846 if(IsAParameterName(formula, delimeterPos)) {
1847 // append the current term and the remaining formula unchanged to the expanded formula
1848 expandedFormula += terms[iTerm];
1849 expandedFormula += formula(delimeterPos, formula.Length() - (delimeterPos + 1));
1850 break;
1851 }
1852 SetBit(kLinear, true);
1853 auto termName = std::string("__linear") + std::to_string(iTerm+1);
1854 fLinearParts.push_back(new TFormula(termName.c_str(), terms[iTerm].c_str(), false));
1855 std::stringstream ss;
1856 ss << "([" << iTerm << "]*(" << terms[iTerm] << "))";
1857 expandedFormula += ss.str();
1858 if(iTerm < terms.size() - 1) expandedFormula += "+";
1859 }
1860 formula = expandedFormula;
1861}
1862
1863////////////////////////////////////////////////////////////////////////////////
1864/// Preprocessing of formula
1865/// Replace all ** by ^, and removes spaces.
1866/// Handle also parametrized functions like polN,gaus,expo,landau
1867/// and exponentiation.
1868/// Similar functionality should be added here.
1869
1871{
1872 formula.ReplaceAll("**","^");
1873 formula.ReplaceAll("++","@"); // for linear functions
1874 formula.ReplaceAll(" ","");
1875 HandlePolN(formula);
1877 HandleParamRanges(formula);
1878 HandleFunctionArguments(formula);
1879 HandleExponentiation(formula);
1880 // "++" wil be dealt with Handle Linear
1881 HandleLinear(formula);
1882 // special case for "--" and "++"
1883 // ("++" needs to be written with whitespace that is removed before but then we re-add it again
1884 formula.ReplaceAll("--","- -");
1885 formula.ReplaceAll("++","+ +");
1886}
1887
1888////////////////////////////////////////////////////////////////////////////////
1889/// prepare the formula to be executed
1890/// normally is called with fFormula
1891
1893{
1894 fFuncs.clear();
1895 fReadyToExecute = false;
1896 ExtractFunctors(formula);
1897
1898 // update the expression with the new formula
1899 fFormula = formula;
1900 // save formula to parse variable and parameters for Cling
1901 fClingInput = formula;
1902 // replace all { and }
1903 fFormula.ReplaceAll("{","");
1904 fFormula.ReplaceAll("}","");
1905
1906 // std::cout << "functors are extracted formula is " << std::endl;
1907 // std::cout << fFormula << std::endl << std::endl;
1908
1909 fFuncs.sort();
1910 fFuncs.unique();
1911
1912 // use inputFormula for Cling
1914
1915 // for pre-defined functions (need after processing)
1916 if (fNumber != 0) SetPredefinedParamNames();
1917
1919}
1920
1921////////////////////////////////////////////////////////////////////////////////
1922/// Extracts functors from formula, and put them in fFuncs.
1923/// Simple grammar:
1924/// - `<function>` := name(arg1,arg2...)
1925/// - `<variable>` := name
1926/// - `<parameter>` := [number]
1927/// - `<name>` := String containing lower and upper letters, numbers, underscores
1928/// - `<number>` := Integer number
1929/// Operators are omitted.
1930
1932{
1933 // std::cout << "Commencing ExtractFunctors on " << formula << std::endl;
1934
1935 TString name = "";
1936 TString body = "";
1937 // printf("formula is : %s \n",formula.Data() );
1938 for (Int_t i = 0; i < formula.Length(); ++i) {
1939
1940 // std::cout << "loop on character : " << i << " " << formula[i] << std::endl;
1941 // case of parameters
1942 if (formula[i] == '[') {
1943 Int_t tmp = i;
1944 i++;
1945 TString param = "";
1946 while (i < formula.Length() && formula[i] != ']') {
1947 param.Append(formula[i++]);
1948 }
1949 i++;
1950 // rename parameter name XX to pXX
1951 // std::cout << "examine parameters " << param << std::endl;
1952 int paramIndex = -1;
1953 if (param.IsDigit()) {
1954 paramIndex = param.Atoi();
1955 param.Insert(0, 'p'); // needed for the replacement
1956 if (paramIndex >= fNpar || fParams.find(param) == fParams.end()) {
1957 // add all parameters up to given index found
1958 for (int idx = 0; idx <= paramIndex; ++idx) {
1959 TString pname = TString::Format("p%d", idx);
1960 if (fParams.find(pname) == fParams.end())
1961 DoAddParameter(pname, 0, false);
1962 }
1963 }
1964 } else {
1965 // handle whitespace characters in parname
1966 param.ReplaceAll("\\s", " ");
1967
1968 // only add if parameter does not already exist, because maybe
1969 // `HandleFunctionArguments` already assigned a default value to the
1970 // parameter
1971 if (fParams.find(param) == fParams.end() || GetParNumber(param) < 0 ||
1972 (unsigned)GetParNumber(param) >= fClingParameters.size()) {
1973 // std::cout << "Setting parameter " << param << " to 0" << std::endl;
1974 DoAddParameter(param, 0, false);
1975 }
1976 }
1977 TString replacement = TString::Format("{[%s]}", param.Data());
1978 formula.Replace(tmp, i - tmp, replacement, replacement.Length());
1979 fFuncs.push_back(TFormulaFunction(param));
1980 // we need to change index i after replacing since string length changes
1981 // and we need to re-calculate i position
1982 int deltai = replacement.Length() - (i-tmp);
1983 i += deltai;
1984 // printf("found parameter %s \n",param.Data() );
1985 continue;
1986 }
1987 // case of strings
1988 if (formula[i] == '\"') {
1989 // look for next instance of "\"
1990 do {
1991 i++;
1992 } while (formula[i] != '\"');
1993 }
1994 // case of e or E for numbers in exponential notation (e.g. 2.2e-3)
1995 if (IsScientificNotation(formula, i))
1996 continue;
1997 // case of x for hexadecimal numbers
1998 if (IsHexadecimal(formula, i)) {
1999 // find position of operator
2000 // do not check cases if character is not only a to f, but accept anything
2001 while (!IsOperator(formula[i]) && i < formula.Length()) {
2002 i++;
2003 }
2004 continue;
2005 }
2006
2007 // std::cout << "investigating character : " << i << " " << formula[i] << " of formula " << formula <<
2008 // std::endl;
2009 // look for variable and function names. They start in C++ with alphanumeric characters
2010 if (isalpha(formula[i]) &&
2011 !IsOperator(formula[i])) // not really needed to check if operator (if isalpha is not an operator)
2012 {
2013 // std::cout << "character : " << i << " " << formula[i] << " is not an operator and is alpha " <<
2014 // std::endl;
2015
2016 while (i < formula.Length() && IsFunctionNameChar(formula[i])) {
2017 // need special case for separating operator ":" from scope operator "::"
2018 if (formula[i] == ':' && ((i + 1) < formula.Length())) {
2019 if (formula[i + 1] == ':') {
2020 // case of :: (scopeOperator)
2021 name.Append("::");
2022 i += 2;
2023 continue;
2024 } else
2025 break;
2026 }
2027
2028 name.Append(formula[i++]);
2029 }
2030 // printf(" build a name %s \n",name.Data() );
2031 if (formula[i] == '(') {
2032 i++;
2033 if (formula[i] == ')') {
2034 fFuncs.push_back(TFormulaFunction(name, body, 0));
2035 name = body = "";
2036 continue;
2037 }
2038 Int_t depth = 1;
2039 Int_t args = 1; // we will miss first argument
2040 while (depth != 0 && i < formula.Length()) {
2041 switch (formula[i]) {
2042 case '(': depth++; break;
2043 case ')': depth--; break;
2044 case ',':
2045 if (depth == 1)
2046 args++;
2047 break;
2048 }
2049 if (depth != 0) // we don't want last ')' inside body
2050 {
2051 body.Append(formula[i++]);
2052 }
2053 }
2054 Int_t originalBodyLen = body.Length();
2056 formula.Replace(i - originalBodyLen, originalBodyLen, body, body.Length());
2057 i += body.Length() - originalBodyLen;
2058 fFuncs.push_back(TFormulaFunction(name, body, args));
2059 } else {
2060
2061 // std::cout << "check if character : " << i << " " << formula[i] << " from name " << name << " is a
2062 // function " << std::endl;
2063
2064 // check if function is provided by gROOT
2065 TObject *obj = nullptr;
2066 // exclude case function name is x,y,z,t
2067 if (!IsReservedName(name))
2068 {
2070 obj = gROOT->GetListOfFunctions()->FindObject(name);
2071 }
2072 TFormula *f = dynamic_cast<TFormula *>(obj);
2073 if (!f) {
2074 // maybe object is a TF1
2075 TF1 *f1 = dynamic_cast<TF1 *>(obj);
2076 if (f1)
2077 f = f1->GetFormula();
2078 }
2079 if (f) {
2080 // Replacing user formula the old way (as opposed to 'HandleFunctionArguments')
2081 // Note this is only for replacing functions that do
2082 // not specify variables and/or parameters in brackets
2083 // (the other case is done by `HandleFunctionArguments`)
2084
2085 TString replacementFormula = f->GetExpFormula();
2086
2087 // analyze expression string
2088 // std::cout << "formula to replace for " << f->GetName() << " is " << replacementFormula <<
2089 // std::endl;
2091 // we need to define different parameters if we use the unnamed default parameters ([0])
2092 // I need to replace all the terms in the functor for backward compatibility of the case
2093 // f1("[0]*x") f2("[0]*x") f1+f2 - it is weird but it is better to support
2094 // std::cout << "current number of parameter is " << fNpar << std::endl;
2095 int nparOffset = 0;
2096 // if (fParams.find("0") != fParams.end() ) {
2097 // do in any case if parameters are existing
2098 std::vector<TString> newNames;
2099 if (fNpar > 0) {
2100 nparOffset = fNpar;
2101 newNames.resize(f->GetNpar());
2102 // start from higher number to avoid overlap
2103 for (int jpar = f->GetNpar() - 1; jpar >= 0; --jpar) {
2104 // parameters name have a "p" added in front
2105 TString pj = TString(f->GetParName(jpar));
2106 if (pj[0] == 'p' && TString(pj(1, pj.Length())).IsDigit()) {
2107 TString oldName = TString::Format("[%s]", f->GetParName(jpar));
2109 // std::cout << "replace - parameter " << f->GetParName(jpar) << " with " << newName <<
2110 // std::endl;
2111 replacementFormula.ReplaceAll(oldName, newName);
2113 } else
2114 newNames[jpar] = f->GetParName(jpar);
2115 }
2116 // std::cout << "after replacing params " << replacementFormula << std::endl;
2117 }
2119 // std::cout << "after re-extracting functors " << replacementFormula << std::endl;
2120
2121 // set parameter value from replacement formula
2122 for (int jpar = 0; jpar < f->GetNpar(); ++jpar) {
2123 if (nparOffset > 0) {
2124 // parameter have an offset- so take this into account
2125 assert((int)newNames.size() == f->GetNpar());
2126 SetParameter(newNames[jpar], f->GetParameter(jpar));
2127 } else
2128 // names are the same between current formula and replaced one
2129 SetParameter(f->GetParName(jpar), f->GetParameter(jpar));
2130 }
2131 // need to add parenthesis at begin and end of replacementFormula
2132 replacementFormula.Insert(0, '(');
2133 replacementFormula.Insert(replacementFormula.Length(), ')');
2134 formula.Replace(i - name.Length(), name.Length(), replacementFormula, replacementFormula.Length());
2135 // move forward the index i of the main loop
2136 i += replacementFormula.Length() - name.Length();
2137
2138 // we have extracted all the functor for "fname"
2139 // std::cout << "We have extracted all the functors for fname" << std::endl;
2140 // std::cout << " i = " << i << " f[i] = " << formula[i] << " - " << formula << std::endl;
2141 name = "";
2142
2143 continue;
2144 }
2145
2146 // add now functor in
2147 TString replacement = TString::Format("{%s}", name.Data());
2148 formula.Replace(i - name.Length(), name.Length(), replacement, replacement.Length());
2149 i += 2;
2150 fFuncs.push_back(TFormulaFunction(name));
2151 }
2152 }
2153 name = body = "";
2154 }
2155}
2156
2157////////////////////////////////////////////////////////////////////////////////
2158/// Iterates through functors in fFuncs and performs the appropriate action.
2159/// If functor has 0 arguments (has only name) can be:
2160/// - variable
2161/// * will be replaced with x[num], where x is an array containing value of this variable under num.
2162/// - pre-defined formula
2163/// * will be replaced with formulas body
2164/// - constant
2165/// * will be replaced with constant value
2166/// - parameter
2167/// * will be replaced with p[num], where p is an array containing value of this parameter under num.
2168/// If has arguments it can be :
2169/// - function shortcut, eg. sin
2170/// * will be replaced with fullname of function, eg. sin -> TMath::Sin
2171/// - function from cling environment, eg. TMath::BreitWigner(x,y,z)
2172/// * first check if function exists, and has same number of arguments, then accept it and set as found.
2173/// If all functors after iteration are matched with corresponding action,
2174/// it inputs C++ code of formula into cling, and sets flag that formula is ready to evaluate.
2175
2177{
2178 // std::cout << "Begin: formula is " << formula << " list of functors " << fFuncs.size() << std::endl;
2179
2180 for (list<TFormulaFunction>::iterator funcsIt = fFuncs.begin(); funcsIt != fFuncs.end(); ++funcsIt) {
2182
2183 // std::cout << "fun is " << fun.GetName() << std::endl;
2184
2185 if (fun.fFound)
2186 continue;
2187 if (fun.IsFuncCall()) {
2188 // replace with pre-defined functions
2189 map<TString, TString>::iterator it = fFunctionsShortcuts.find(fun.GetName());
2190 if (it != fFunctionsShortcuts.end()) {
2191 TString shortcut = it->first;
2192 TString full = it->second;
2193 // std::cout << " functor " << fun.GetName() << " found - replace " << shortcut << " with " << full << " in
2194 // " << formula << std::endl;
2195 // replace all functors
2196 Ssiz_t index = formula.Index(shortcut, 0);
2197 while (index != kNPOS) {
2198 // check that function is not in a namespace and is not in other characters
2199 // std::cout << "analyzing " << shortcut << " in " << formula << std::endl;
2200 Ssiz_t i2 = index + shortcut.Length();
2201 if ((index > 0) && (isalpha(formula[index - 1]) || formula[index - 1] == ':')) {
2202 index = formula.Index(shortcut, i2);
2203 continue;
2204 }
2205 if (i2 < formula.Length() && formula[i2] != '(') {
2206 index = formula.Index(shortcut, i2);
2207 continue;
2208 }
2209 // now replace the string
2210 formula.Replace(index, shortcut.Length(), full);
2211 Ssiz_t inext = index + full.Length();
2212 index = formula.Index(shortcut, inext);
2213 fun.fFound = true;
2214 }
2215 }
2216 // for functions we can live it to cling to decide if it is a valid function or NOT
2217 // We don't need to retrieve this information from the ROOT interpreter
2218 // we assume that the function is then found and all the following code does not need to be there
2219#ifdef TFORMULA_CHECK_FUNCTIONS
2220
2221 if (fun.fName.Contains("::")) // add support for nested namespaces
2222 {
2223 // look for last occurrence of "::"
2224 std::string name(fun.fName.Data());
2225 size_t index = name.rfind("::");
2226 assert(index != std::string::npos);
2227 TString className = fun.fName(0, fun.fName(0, index).Length());
2228 TString functionName = fun.fName(index + 2, fun.fName.Length());
2229
2230 Bool_t silent = true;
2231 TClass *tclass = TClass::GetClass(className, silent);
2232 // std::cout << "looking for class " << className << std::endl;
2233 const TList *methodList = tclass->GetListOfAllPublicMethods();
2234 TIter next(methodList);
2235 TMethod *p;
2236 while ((p = (TMethod *)next())) {
2237 if (strcmp(p->GetName(), functionName.Data()) == 0 &&
2238 (fun.GetNargs() <= p->GetNargs() && fun.GetNargs() >= p->GetNargs() - p->GetNargsOpt())) {
2239 fun.fFound = true;
2240 break;
2241 }
2242 }
2243 }
2244 if (!fun.fFound) {
2245 // try to look into all the global functions in gROOT
2246 TFunction *f;
2247 {
2249 f = (TFunction *)gROOT->GetListOfGlobalFunctions(true)->FindObject(fun.fName);
2250 }
2251 // if found a function with matching arguments
2252 if (f && fun.GetNargs() <= f->GetNargs() && fun.GetNargs() >= f->GetNargs() - f->GetNargsOpt()) {
2253 fun.fFound = true;
2254 }
2255 }
2256
2257 if (!fun.fFound) {
2258 // ignore not found functions
2259 if (gDebug)
2260 Info("TFormula", "Could not find %s function with %d argument(s)", fun.GetName(), fun.GetNargs());
2261 fun.fFound = false;
2262 }
2263#endif
2264 } else {
2265 TFormula *old = nullptr;
2266 {
2268 old = (TFormula *)gROOT->GetListOfFunctions()->FindObject(gNamePrefix + fun.fName);
2269 }
2270 if (old) {
2271 // we should not go here (this analysis is done before in ExtractFunctors)
2272 assert(false);
2273 fun.fFound = true;
2274 TString pattern = TString::Format("{%s}", fun.GetName());
2278 formula.ReplaceAll(pattern, replacement);
2279 continue;
2280 }
2281 // looking for default variables defined in fVars
2282
2283 map<TString, TFormulaVariable>::iterator varsIt = fVars.find(fun.GetName());
2284 if (varsIt != fVars.end()) {
2285
2286 TString name = (*varsIt).second.GetName();
2287 Double_t value = (*varsIt).second.fValue;
2288
2289 AddVariable(name, value); // this set the cling variable
2290 if (!fVars[name].fFound) {
2291
2292 fVars[name].fFound = true;
2293 int varDim = (*varsIt).second.fArrayPos; // variable dimensions (0 for x, 1 for y, 2, for z)
2294 if (varDim >= fNdim) {
2295 fNdim = varDim + 1;
2296
2297 // we need to be sure that all other variables are added with position less
2298 for (auto &v : fVars) {
2299 if (v.second.fArrayPos < varDim && !v.second.fFound) {
2300 AddVariable(v.first, v.second.fValue);
2301 v.second.fFound = true;
2302 }
2303 }
2304 }
2305 }
2306 // remove the "{.. }" added around the variable
2307 TString pattern = TString::Format("{%s}", name.Data());
2308 TString replacement = TString::Format("x[%d]", (*varsIt).second.fArrayPos);
2309 formula.ReplaceAll(pattern, replacement);
2310
2311 // std::cout << "Found an observable for " << fun.GetName() << std::endl;
2312
2313 fun.fFound = true;
2314 continue;
2315 }
2316 // check for observables defined as x[0],x[1],....
2317 // maybe could use a regular expression here
2318 // only in case match with defined variables is not successful
2319 TString funname = fun.GetName();
2320 if (funname.Contains("x[") && funname.Contains("]")) {
2321 TString sdigit = funname(2, funname.Index("]"));
2322 int digit = sdigit.Atoi();
2323 if (digit >= fNdim) {
2324 fNdim = digit + 1;
2325 // we need to add the variables in fVars all of them before x[n]
2326 for (int j = 0; j < fNdim; ++j) {
2327 TString vname = TString::Format("x[%d]", j);
2328 if (fVars.find(vname) == fVars.end()) {
2330 fVars[vname].fFound = true;
2331 AddVariable(vname, 0.);
2332 }
2333 }
2334 }
2335 // std::cout << "Found matching observable for " << funname << std::endl;
2336 fun.fFound = true;
2337 // remove the "{.. }" added around the variable
2338 TString pattern = TString::Format("{%s}", funname.Data());
2339 formula.ReplaceAll(pattern, funname);
2340 continue;
2341 }
2342 //}
2343
2344 auto paramsIt = fParams.find(fun.GetName());
2345 if (paramsIt != fParams.end()) {
2346 // TString name = (*paramsIt).second.GetName();
2347 TString pattern = TString::Format("{[%s]}", fun.GetName());
2348 // std::cout << "pattern is " << pattern << std::endl;
2349 if (formula.Index(pattern) != kNPOS) {
2350 // TString replacement = TString::Format("p[%d]",(*paramsIt).second.fArrayPos);
2351 TString replacement = TString::Format("p[%d]", (*paramsIt).second);
2352 // std::cout << "replace pattern " << pattern << " with " << replacement << std::endl;
2353 formula.ReplaceAll(pattern, replacement);
2354 }
2355 fun.fFound = true;
2356 continue;
2357 } else {
2358 // std::cout << "functor " << fun.GetName() << " is not a parameter " << std::endl;
2359 }
2360
2361 // looking for constants (needs to be done after looking at the parameters)
2362 map<TString, Double_t>::iterator constIt = fConsts.find(fun.GetName());
2363 if (constIt != fConsts.end()) {
2364 TString pattern = TString::Format("{%s}", fun.GetName());
2365 formula.ReplaceAll(pattern, doubleToString(constIt->second));
2366 fun.fFound = true;
2367 // std::cout << "constant with name " << fun.GetName() << " is found " << std::endl;
2368 continue;
2369 }
2370
2371 fun.fFound = false;
2372 }
2373 }
2374 // std::cout << "End: formula is " << formula << std::endl;
2375
2376 // ignore case of functors have been matched - try to pass it to Cling
2377 if (!fReadyToExecute) {
2378 fReadyToExecute = true;
2379 Bool_t hasVariables = (fNdim > 0);
2380 Bool_t hasParameters = (fNpar > 0);
2381 if (!hasParameters) {
2382 fAllParametersSetted = true;
2383 }
2384 // assume a function without variables is always 1-dimensional ???
2385 // if (hasParameters && !hasVariables) {
2386 // fNdim = 1;
2387 // AddVariable("x", 0);
2388 // hasVariables = true;
2389 // }
2390 // does not make sense to vectorize function which is of FNDim=0
2391 if (!hasVariables) fVectorized=false;
2392 // when there are no variables but only parameter we still need to ad
2393 //Bool_t hasBoth = hasVariables && hasParameters;
2394 Bool_t inputIntoCling = (formula.Length() > 0);
2395 if (inputIntoCling) {
2396 // save copy of inputFormula in a std::strig for the unordered map
2397 // and also formula is same as FClingInput typically and it will be modified
2398 std::string inputFormula(formula.Data());
2399
2400 // The name we really use for the unordered map will have a flag that
2401 // says whether the formula is vectorized
2402 std::string inputFormulaVecFlag = inputFormula;
2403 if (fVectorized)
2404 inputFormulaVecFlag += " (vectorized)";
2405
2406 TString argType = fVectorized ? vectorizedArgType() : "Double_t";
2407
2408 // valid input formula - try to put into Cling (in case of no variables but only parameter we need to add the standard signature)
2409 TString argumentsPrototype = TString::Format("%s%s%s", ( (hasVariables || hasParameters) ? (argType + " const *x").Data() : ""),
2410 (hasParameters ? "," : ""), (hasParameters ? "Double_t *p" : ""));
2411
2412 // set the name for Cling using the hash_function
2414
2415 // check if formula exist already in the map
2417
2418 // std::cout << "gClingFunctions list" << std::endl;
2419 // for (auto thing : gClingFunctions)
2420 // std::cout << "gClingFunctions : " << thing.first << std::endl;
2421
2423
2424 if (funcit != gClingFunctions.end()) {
2426 fClingInitialized = true;
2427 inputIntoCling = false;
2428 }
2429
2430
2431
2432 // set the cling name using hash of the static formulae map
2433 auto hasher = gClingFunctions.hash_function();
2435
2436 fClingInput = TString::Format("%s %s(%s){ return %s ; }", argType.Data(), fClingName.Data(),
2437 argumentsPrototype.Data(), inputFormula.c_str());
2438
2439
2440 // std::cout << "Input Formula " << inputFormula << " \t vec formula : " << inputFormulaVecFlag << std::endl;
2441 // std::cout << "Cling functions existing " << std::endl;
2442 // for (auto & ff : gClingFunctions)
2443 // std::cout << ff.first << std::endl;
2444 // std::cout << "\n";
2445 // std::cout << fClingName << std::endl;
2446
2447 // this is not needed (maybe can be re-added in case of recompilation of identical expressions
2448 // // check in case of a change if need to re-initialize
2449 // if (fClingInitialized) {
2450 // if (oldClingInput == fClingInput)
2451 // inputIntoCling = false;
2452 // else
2453 // fClingInitialized = false;
2454 // }
2455
2456 if (inputIntoCling) {
2457 if (!fLazyInitialization) {
2459 if (fClingInitialized) {
2460 // if Cling has been successfully initialized
2461 // put function ptr in the static map
2463 gClingFunctions.insert(std::make_pair(inputFormulaVecFlag, (void *)fFuncPtr));
2464 }
2465 }
2466 if (!fClingInitialized) {
2467 // needed in case of lazy initialization of failure compiling the expression
2469 }
2470
2471 } else {
2472 fAllParametersSetted = true;
2473 fClingInitialized = true;
2474 }
2475 }
2476 }
2477
2478 // In case of a Cling Error check components which are not found in Cling
2479 // check that all formula components are matched otherwise emit an error
2481 //Bool_t allFunctorsMatched = false;
2482 for (list<TFormulaFunction>::iterator it = fFuncs.begin(); it != fFuncs.end(); ++it) {
2483 // functions are now by default always not checked
2484 if (!it->fFound && !it->IsFuncCall()) {
2485 //allFunctorsMatched = false;
2486 if (it->GetNargs() == 0)
2487 Error("ProcessFormula", "\"%s\" has not been matched in the formula expression", it->GetName());
2488 else
2489 Error("ProcessFormula", "Could not find %s function with %d argument(s)", it->GetName(), it->GetNargs());
2490 }
2491 }
2492 Error("ProcessFormula","Formula \"%s\" is invalid !", GetExpFormula().Data() );
2493 fReadyToExecute = false;
2494 }
2495
2496 // clean up un-used default variables in case formula is valid
2497 //if (fClingInitialized && fReadyToExecute) {
2498 //don't check fClingInitialized in case of lazy execution
2499 if (fReadyToExecute) {
2500 auto itvar = fVars.begin();
2501 // need this loop because after erase change iterators
2502 do {
2503 if (!itvar->second.fFound) {
2504 // std::cout << "Erase variable " << itvar->first << std::endl;
2505 itvar = fVars.erase(itvar);
2506 } else
2507 itvar++;
2508 } while (itvar != fVars.end());
2509 }
2510}
2511
2512////////////////////////////////////////////////////////////////////////////////
2513/// Fill map with parametrized functions
2514
2516{
2517 // map< pair<TString,Int_t> ,pair<TString,TString> > functions;
2518 functions.insert(
2519 make_pair(make_pair("gaus", 1), make_pair("[0]*exp(-0.5*(({V0}-[1])/[2])*(({V0}-[1])/[2]))",
2520 "[0]*exp(-0.5*(({V0}-[1])/[2])*(({V0}-[1])/[2]))/(sqrt(2*pi)*[2])")));
2521 functions.insert(make_pair(make_pair("landau", 1), make_pair("[0]*TMath::Landau({V0},[1],[2],false)",
2522 "[0]*TMath::Landau({V0},[1],[2],true)")));
2523 functions.insert(make_pair(make_pair("expo", 1), make_pair("exp([0]+[1]*{V0})", "")));
2524 functions.insert(
2525 make_pair(make_pair("crystalball", 1), make_pair("[0]*ROOT::Math::crystalball_function({V0},[3],[4],[2],[1])",
2526 "[0]*ROOT::Math::crystalball_pdf({V0},[3],[4],[2],[1])")));
2527 functions.insert(
2528 make_pair(make_pair("breitwigner", 1), make_pair("[0]*ROOT::Math::breitwigner_pdf({V0},[2],[1])",
2529 "[0]*ROOT::Math::breitwigner_pdf({V0},[2],[4],[1])")));
2530 // chebyshev polynomial
2531 functions.insert(make_pair(make_pair("cheb0", 1), make_pair("ROOT::Math::Chebyshev0({V0},[0])", "")));
2532 functions.insert(make_pair(make_pair("cheb1", 1), make_pair("ROOT::Math::Chebyshev1({V0},[0],[1])", "")));
2533 functions.insert(make_pair(make_pair("cheb2", 1), make_pair("ROOT::Math::Chebyshev2({V0},[0],[1],[2])", "")));
2534 functions.insert(make_pair(make_pair("cheb3", 1), make_pair("ROOT::Math::Chebyshev3({V0},[0],[1],[2],[3])", "")));
2535 functions.insert(
2536 make_pair(make_pair("cheb4", 1), make_pair("ROOT::Math::Chebyshev4({V0},[0],[1],[2],[3],[4])", "")));
2537 functions.insert(
2538 make_pair(make_pair("cheb5", 1), make_pair("ROOT::Math::Chebyshev5({V0},[0],[1],[2],[3],[4],[5])", "")));
2539 functions.insert(
2540 make_pair(make_pair("cheb6", 1), make_pair("ROOT::Math::Chebyshev6({V0},[0],[1],[2],[3],[4],[5],[6])", "")));
2541 functions.insert(
2542 make_pair(make_pair("cheb7", 1), make_pair("ROOT::Math::Chebyshev7({V0},[0],[1],[2],[3],[4],[5],[6],[7])", "")));
2543 functions.insert(make_pair(make_pair("cheb8", 1),
2544 make_pair("ROOT::Math::Chebyshev8({V0},[0],[1],[2],[3],[4],[5],[6],[7],[8])", "")));
2545 functions.insert(make_pair(make_pair("cheb9", 1),
2546 make_pair("ROOT::Math::Chebyshev9({V0},[0],[1],[2],[3],[4],[5],[6],[7],[8],[9])", "")));
2547 functions.insert(
2548 make_pair(make_pair("cheb10", 1),
2549 make_pair("ROOT::Math::Chebyshev10({V0},[0],[1],[2],[3],[4],[5],[6],[7],[8],[9],[10])", "")));
2550 // 2-dimensional functions
2551 functions.insert(
2552 make_pair(make_pair("gaus", 2), make_pair("[0]*exp(-0.5*(({V0}-[1])/[2])^2 - 0.5*(({V1}-[3])/[4])^2)", "")));
2553 functions.insert(
2554 make_pair(make_pair("landau", 2),
2555 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)")));
2556 functions.insert(
2557 make_pair(make_pair("expo", 2),
2558 make_pair("exp([0]+[1]*{V0}+[2]*{V1})", "")));
2559 // 3-dimensional function
2560 functions.insert(
2561 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)", "")));
2562 // 2-d gaussian with correlations
2563 functions.insert(
2564 make_pair(make_pair("bigaus", 2), make_pair("[0]*ROOT::Math::bigaussian_pdf({V0},{V1},[2],[4],[5],[1],[3])",
2565 "[0]*ROOT::Math::bigaussian_pdf({V0},{V1},[2],[4],[5],[1],[3])")));
2566}
2567
2568////////////////////////////////////////////////////////////////////////////////
2569/// Set parameter names only in case of pre-defined functions.
2570
2572
2573 if (fNumber == 0) return;
2574
2575 if (fNumber == 100) { // Gaussian
2576 SetParName(0,"Constant");
2577 SetParName(1,"Mean");
2578 SetParName(2,"Sigma");
2579 return;
2580 }
2581 if (fNumber == 110) {
2582 SetParName(0,"Constant");
2583 SetParName(1,"MeanX");
2584 SetParName(2,"SigmaX");
2585 SetParName(3,"MeanY");
2586 SetParName(4,"SigmaY");
2587 return;
2588 }
2589 if (fNumber == 120) {
2590 SetParName(0,"Constant");
2591 SetParName(1,"MeanX");
2592 SetParName(2,"SigmaX");
2593 SetParName(3,"MeanY");
2594 SetParName(4,"SigmaY");
2595 SetParName(5,"MeanZ");
2596 SetParName(6,"SigmaZ");
2597 return;
2598 }
2599 if (fNumber == 112) { // bigaus
2600 SetParName(0,"Constant");
2601 SetParName(1,"MeanX");
2602 SetParName(2,"SigmaX");
2603 SetParName(3,"MeanY");
2604 SetParName(4,"SigmaY");
2605 SetParName(5,"Rho");
2606 return;
2607 }
2608 if (fNumber == 200) { // exponential
2609 SetParName(0,"Constant");
2610 SetParName(1,"Slope");
2611 return;
2612 }
2613 if (fNumber == 400) { // landau
2614 SetParName(0,"Constant");
2615 SetParName(1,"MPV");
2616 SetParName(2,"Sigma");
2617 return;
2618 }
2619 if (fNumber == 500) { // crystal-ball
2620 SetParName(0,"Constant");
2621 SetParName(1,"Mean");
2622 SetParName(2,"Sigma");
2623 SetParName(3,"Alpha");
2624 SetParName(4,"N");
2625 return;
2626 }
2627 if (fNumber == 600) { // breit-wigner
2628 SetParName(0,"Constant");
2629 SetParName(1,"Mean");
2630 SetParName(2,"Gamma");
2631 return;
2632 }
2633 // if formula is a polynomial (or chebyshev), set parameter names
2634 // not needed anymore (p0 is assigned by default)
2635 // if (fNumber == (300+fNpar-1) ) {
2636 // for (int i = 0; i < fNpar; i++) SetParName(i,TString::Format("p%d",i));
2637 // return;
2638 // }
2639
2640 // // general case if parameters are digits (XX) change to pXX
2641 // auto paramMap = fParams; // need to copy the map because SetParName is going to modify it
2642 // for ( auto & p : paramMap) {
2643 // if (p.first.IsDigit() )
2644 // SetParName(p.second,TString::Format("p%s",p.first.Data()));
2645 // }
2646
2647 return;
2648}
2649
2650////////////////////////////////////////////////////////////////////////////////
2651/// Return linear part.
2652
2654{
2655 if (!fLinearParts.empty()) {
2656 int n = fLinearParts.size();
2657 if (i < 0 || i >= n ) {
2658 Error("GetLinearPart","Formula %s has only %d linear parts - requested %d",GetName(),n,i);
2659 return nullptr;
2660 }
2661 return fLinearParts[i];
2662 }
2663 return nullptr;
2664}
2665
2666////////////////////////////////////////////////////////////////////////////////
2667/// Adds variable to known variables, and reprocess formula.
2668
2670{
2671 if (fVars.find(name) != fVars.end()) {
2672 TFormulaVariable &var = fVars[name];
2673 var.fValue = value;
2674
2675 // If the position is not defined in the Cling vectors, make space for it
2676 // but normally is variable is defined in fVars a slot should be also present in fClingVariables
2677 if (var.fArrayPos < 0) {
2678 var.fArrayPos = fVars.size();
2679 }
2680 if (var.fArrayPos >= (int)fClingVariables.size()) {
2681 fClingVariables.resize(var.fArrayPos + 1);
2682 }
2684 } else {
2685 TFormulaVariable var(name, value, fVars.size());
2686 fVars[name] = var;
2687 fClingVariables.push_back(value);
2688 if (!fFormula.IsNull()) {
2689 // printf("process formula again - %s \n",fClingInput.Data() );
2691 }
2692 }
2693}
2694
2695////////////////////////////////////////////////////////////////////////////////
2696/// Adds multiple variables.
2697/// First argument is an array of pairs<TString,Double>, where
2698/// first argument is name of variable,
2699/// second argument represents value.
2700/// size - number of variables passed in first argument
2701
2703{
2704 Bool_t anyNewVar = false;
2705 for (Int_t i = 0; i < size; ++i) {
2706
2707 const TString &vname = vars[i];
2708
2710 if (var.fArrayPos < 0) {
2711
2712 var.fName = vname;
2713 var.fArrayPos = fVars.size();
2714 anyNewVar = true;
2715 var.fValue = 0;
2716 if (var.fArrayPos >= (int)fClingVariables.capacity()) {
2717 Int_t multiplier = 2;
2718 if (fFuncs.size() > 100) {
2720 }
2721 fClingVariables.reserve(multiplier * fClingVariables.capacity());
2722 }
2723 fClingVariables.push_back(0.0);
2724 }
2725 // else
2726 // {
2727 // var.fValue = v.second;
2728 // fClingVariables[var.fArrayPos] = v.second;
2729 // }
2730 }
2731 if (anyNewVar && !fFormula.IsNull()) {
2733 }
2734}
2735
2736////////////////////////////////////////////////////////////////////////////////
2737/// Set the name of the formula. We need to allow the list of function to
2738/// properly handle the hashes.
2739
2740void TFormula::SetName(const char* name)
2741{
2742 if (IsReservedName(name)) {
2743 Error("SetName", "The name \'%s\' is reserved as a TFormula variable name.\n"
2744 "\tThis function will not be renamed.",
2745 name);
2746 } else {
2747 // Here we need to remove and re-add to keep the hashes consistent with
2748 // the underlying names.
2749 auto listOfFunctions = gROOT->GetListOfFunctions();
2750 TObject* thisAsFunctionInList = nullptr;
2752 if (listOfFunctions){
2753 thisAsFunctionInList = listOfFunctions->FindObject(this);
2755 }
2758 }
2759}
2760
2761////////////////////////////////////////////////////////////////////////////////
2762///
2763/// Sets multiple variables.
2764/// First argument is an array of pairs<TString,Double>, where
2765/// first argument is name of variable,
2766/// second argument represents value.
2767/// size - number of variables passed in first argument
2768
2770{
2771 for(Int_t i = 0; i < size; ++i)
2772 {
2773 auto &v = vars[i];
2774 if (fVars.find(v.first) != fVars.end()) {
2775 fVars[v.first].fValue = v.second;
2776 fClingVariables[fVars[v.first].fArrayPos] = v.second;
2777 } else {
2778 Error("SetVariables", "Variable %s is not defined.", v.first.Data());
2779 }
2780 }
2781}
2782
2783////////////////////////////////////////////////////////////////////////////////
2784/// Returns variable value.
2785
2787{
2788 const auto nameIt = fVars.find(name);
2789 if (fVars.end() == nameIt) {
2790 Error("GetVariable", "Variable %s is not defined.", name);
2791 return -1;
2792 }
2793 return nameIt->second.fValue;
2794}
2795
2796////////////////////////////////////////////////////////////////////////////////
2797/// Returns variable number (positon in array) given its name.
2798
2800{
2801 const auto nameIt = fVars.find(name);
2802 if (fVars.end() == nameIt) {
2803 Error("GetVarNumber", "Variable %s is not defined.", name);
2804 return -1;
2805 }
2806 return nameIt->second.fArrayPos;
2807}
2808
2809////////////////////////////////////////////////////////////////////////////////
2810/// Returns variable name given its position in the array.
2811
2813{
2814 if (ivar < 0 || ivar >= fNdim) return "";
2815
2816 // need to loop on the map to find corresponding variable
2817 for ( auto & v : fVars) {
2818 if (v.second.fArrayPos == ivar) return v.first;
2819 }
2820 Error("GetVarName","Variable with index %d not found !!",ivar);
2821 //return TString::Format("x%d",ivar);
2822 return "";
2823}
2824
2825////////////////////////////////////////////////////////////////////////////////
2826/// Sets variable value.
2827
2829{
2830 if (fVars.find(name) == fVars.end()) {
2831 Error("SetVariable", "Variable %s is not defined.", name.Data());
2832 return;
2833 }
2834 fVars[name].fValue = value;
2835 fClingVariables[fVars[name].fArrayPos] = value;
2836}
2837
2838////////////////////////////////////////////////////////////////////////////////
2839/// Adds parameter to known parameters.
2840/// User should use SetParameter, because parameters are added during initialization part,
2841/// and after that adding new will be pointless.
2842
2844{
2845 //std::cout << "adding parameter " << name << std::endl;
2846
2847 // if parameter is already defined in fParams - just set the new value
2848 if(fParams.find(name) != fParams.end() )
2849 {
2850 int ipos = fParams[name];
2851 // TFormulaVariable & par = fParams[name];
2852 // par.fValue = value;
2853 if (ipos < 0) {
2854 ipos = fParams.size();
2855 fParams[name] = ipos;
2856 }
2857 //
2858 if (ipos >= (int)fClingParameters.size()) {
2859 if (ipos >= (int)fClingParameters.capacity())
2860 fClingParameters.reserve(TMath::Max(int(fParams.size()), ipos + 1));
2861 fClingParameters.insert(fClingParameters.end(), ipos + 1 - fClingParameters.size(), 0.0);
2862 }
2864 } else {
2865 // new parameter defined
2866 fNpar++;
2867 // TFormulaVariable(name,value,fParams.size());
2868 int pos = fParams.size();
2869 // fParams.insert(std::make_pair<TString,TFormulaVariable>(name,TFormulaVariable(name,value,pos)));
2870 auto ret = fParams.insert(std::make_pair(name, pos));
2871 // map returns a std::pair<iterator, bool>
2872 // use map the order for default position of parameters in the vector
2873 // (i.e use the alphabetic order)
2874 if (ret.second) {
2875 // a new element is inserted
2876 if (ret.first == fParams.begin())
2877 pos = 0;
2878 else {
2879 auto previous = (ret.first);
2880 --previous;
2881 pos = previous->second + 1;
2882 }
2883
2884 if (pos < (int)fClingParameters.size())
2885 fClingParameters.insert(fClingParameters.begin() + pos, value);
2886 else {
2887 // this should not happen
2888 if (pos > (int)fClingParameters.size())
2889 Warning("inserting parameter %s at pos %d when vector size is %d \n", name.Data(), pos,
2890 (int)fClingParameters.size());
2891
2892 if (pos >= (int)fClingParameters.capacity())
2893 fClingParameters.reserve(TMath::Max(int(fParams.size()), pos + 1));
2894 fClingParameters.insert(fClingParameters.end(), pos + 1 - fClingParameters.size(), 0.0);
2895 fClingParameters[pos] = value;
2896 }
2897
2898 // need to adjust all other positions
2899 for (auto it = ret.first; it != fParams.end(); ++it) {
2900 it->second = pos;
2901 pos++;
2902 }
2903
2904 // for (auto & p : fParams)
2905 // std::cout << "Parameter " << p.first << " position " << p.second << " value " <<
2906 // fClingParameters[p.second] << std::endl;
2907 // printf("inserted parameters size params %d size cling %d \n",fParams.size(), fClingParameters.size() );
2908 }
2909 if (processFormula) {
2910 // replace first in input parameter name with [name]
2913 }
2914 }
2915}
2916
2917////////////////////////////////////////////////////////////////////////////////
2918/// Return parameter index given a name (return -1 for not existing parameters)
2919/// non need to print an error
2920
2921Int_t TFormula::GetParNumber(const char * name) const {
2922 auto it = fParams.find(name);
2923 if (it == fParams.end()) {
2924 return -1;
2925 }
2926 return it->second;
2927
2928}
2929
2930////////////////////////////////////////////////////////////////////////////////
2931/// Returns parameter value given by string.
2932
2934{
2935 const int i = GetParNumber(name);
2936 if (i == -1) {
2937 Error("GetParameter","Parameter %s is not defined.",name);
2938 return TMath::QuietNaN();
2939 }
2940
2941 return GetParameter( i );
2942}
2943
2944////////////////////////////////////////////////////////////////////////////////
2945/// Return parameter value given by integer.
2946
2948{
2949 //TString name = TString::Format("%d",param);
2950 if(param >=0 && param < (int) fClingParameters.size())
2951 return fClingParameters[param];
2952 Error("GetParameter","wrong index used - use GetParameter(name)");
2953 return TMath::QuietNaN();
2954}
2955
2956////////////////////////////////////////////////////////////////////////////////
2957/// Return parameter name given by integer.
2958
2959const char * TFormula::GetParName(Int_t ipar) const
2960{
2961 if (ipar < 0 || ipar >= fNpar) return "";
2962
2963 // need to loop on the map to find corresponding parameter
2964 for ( auto & p : fParams) {
2965 if (p.second == ipar) return p.first.Data();
2966 }
2967 Error("GetParName","Parameter with index %d not found !!",ipar);
2968 //return TString::Format("p%d",ipar);
2969 return "";
2970}
2971
2972////////////////////////////////////////////////////////////////////////////////
2974{
2975 if(!fClingParameters.empty())
2976 return const_cast<Double_t*>(&fClingParameters[0]);
2977 return nullptr;
2978}
2979
2981{
2982 for (Int_t i = 0; i < fNpar; ++i) {
2983 if (Int_t(fClingParameters.size()) > i)
2984 params[i] = fClingParameters[i];
2985 else
2986 params[i] = -1;
2987 }
2988}
2989
2990////////////////////////////////////////////////////////////////////////////////
2991/// Sets parameter value.
2992
2994{
2996
2997 // do we need this ???
2998#ifdef OLDPARAMS
2999 if (fParams.find(name) == fParams.end()) {
3000 Error("SetParameter", "Parameter %s is not defined.", name.Data());
3001 return;
3002 }
3003 fParams[name].fValue = value;
3004 fParams[name].fFound = true;
3005 fClingParameters[fParams[name].fArrayPos] = value;
3006 fAllParametersSetted = true;
3007 for (map<TString, TFormulaVariable>::iterator it = fParams.begin(); it != fParams.end(); ++it) {
3008 if (!it->second.fFound) {
3009 fAllParametersSetted = false;
3010 break;
3011 }
3012 }
3013#endif
3014}
3015
3016#ifdef OLDPARAMS
3017
3018////////////////////////////////////////////////////////////////////////////////
3019/// Set multiple parameters.
3020/// First argument is an array of pairs<TString,Double>, where
3021/// first argument is name of parameter,
3022/// second argument represents value.
3023/// size - number of params passed in first argument
3024
3026{
3027 for(Int_t i = 0 ; i < size ; ++i)
3028 {
3029 pair<TString, Double_t> p = params[i];
3030 if (fParams.find(p.first) == fParams.end()) {
3031 Error("SetParameters", "Parameter %s is not defined", p.first.Data());
3032 continue;
3033 }
3034 fParams[p.first].fValue = p.second;
3035 fParams[p.first].fFound = true;
3036 fClingParameters[fParams[p.first].fArrayPos] = p.second;
3037 }
3038 fAllParametersSetted = true;
3039 for (map<TString, TFormulaVariable>::iterator it = fParams.begin(); it != fParams.end(); ++it) {
3040 if (!it->second.fFound) {
3041 fAllParametersSetted = false;
3042 break;
3043 }
3044 }
3045}
3046#endif
3047
3048////////////////////////////////////////////////////////////////////////////////
3050{
3051 if(!params || size < 0 || size > fNpar) return;
3052 // reset vector of cling parameters
3053 if (size != (int) fClingParameters.size() ) {
3054 Warning("SetParameters","size is not same of cling parameter size %d - %d",size,int(fClingParameters.size()) );
3055 for (Int_t i = 0; i < size; ++i) {
3056 TString name = TString::Format("%d", i);
3057 SetParameter(name, params[i]);
3058 }
3059 return;
3060 }
3061 fAllParametersSetted = true;
3062 std::copy(params, params+size, fClingParameters.begin() );
3063}
3064
3065////////////////////////////////////////////////////////////////////////////////
3066/// Set a vector of parameters value.
3067/// Order in the vector is by default the alphabetic order given to the parameters
3068/// apart if the users has defined explicitly the parameter names
3069
3071{
3072 DoSetParameters(params,fNpar);
3073}
3074
3075////////////////////////////////////////////////////////////////////////////////
3076/// Set a parameter given a parameter index.
3077/// The parameter index is by default the alphabetic order given to the parameters,
3078/// apart if the users has defined explicitly the parameter names.
3079
3081{
3082 if (param < 0 || param >= fNpar) return;
3083 assert(int(fClingParameters.size()) == fNpar);
3084 fClingParameters[param] = value;
3085 // TString name = TString::Format("%d",param);
3086 // SetParameter(name,value);
3087}
3088
3089////////////////////////////////////////////////////////////////////////////////
3090void TFormula::SetParName(Int_t ipar, const char * name)
3091{
3092
3094 Error("SetParName","Wrong Parameter index %d ",ipar);
3095 return;
3096 }
3098 // find parameter with given index
3099 for ( auto &it : fParams) {
3100 if (it.second == ipar) {
3101 oldName = it.first;
3102 fParams.erase(oldName);
3103 fParams.insert(std::make_pair(name, ipar) );
3104 break;
3105 }
3106 }
3107 if (oldName.IsNull() ) {
3108 Error("SetParName","Parameter %d is not existing.",ipar);
3109 return;
3110 }
3111
3112 //replace also parameter name in formula expression in case is not a lambda
3114
3115}
3116
3117////////////////////////////////////////////////////////////////////////////////
3118/// Replace in Formula expression the parameter name.
3119
3121 if (!formula.IsNull() ) {
3122 bool found = false;
3123 for(list<TFormulaFunction>::iterator it = fFuncs.begin(); it != fFuncs.end(); ++it)
3124 {
3125 if (oldName == it->GetName()) {
3126 found = true;
3127 it->fName = name;
3128 break;
3129 }
3130 }
3131 if (!found) {
3132 Error("SetParName", "Parameter %s is not defined.", oldName.Data());
3133 return;
3134 }
3135 // change whitespace to \s to avoid problems in parsing
3137 newName.ReplaceAll(" ", "\\s");
3138 TString pattern = TString::Format("[%s]", oldName.Data());
3139 TString replacement = TString::Format("[%s]", newName.Data());
3140 formula.ReplaceAll(pattern, replacement);
3141 }
3142
3143}
3144
3145////////////////////////////////////////////////////////////////////////////////
3147{
3148#ifdef R__HAS_VECCORE
3149 if (fNdim == 0) {
3150 Info("SetVectorized","Cannot vectorized a function of zero dimension");
3151 return;
3152 }
3153 if (vectorized != fVectorized) {
3154 if (!fFormula)
3155 Error("SetVectorized", "Cannot set vectorized to %d -- Formula is missing", vectorized);
3156
3158 // no need to JIT a new signature in case of zero dimension
3159 //if (fNdim== 0) return;
3160 fClingInitialized = false;
3161 fReadyToExecute = false;
3162 fClingName = "";
3164
3165 fMethod.reset();
3166
3167 FillVecFunctionsShurtCuts(); // to replace with the right vectorized signature (e.g. sin -> vecCore::math::Sin)
3170 }
3171#else
3172 if (vectorized)
3173 Warning("SetVectorized", "Cannot set vectorized -- try building with option -Dbuiltin_veccore=On");
3174#endif
3175}
3176
3177////////////////////////////////////////////////////////////////////////////////
3178Double_t TFormula::EvalPar(const Double_t *x,const Double_t *params) const
3179{
3180 if (!fVectorized)
3181 return DoEval(x, params);
3182
3183#ifdef R__HAS_VECCORE
3184
3185 if (fNdim == 0 || !x) {
3186 ROOT::Double_v ret = DoEvalVec(nullptr, params);
3187 return vecCore::Get( ret, 0 );
3188 }
3189
3190 // otherwise, regular Double_t inputs on a vectorized function
3191
3192 // convert our input into vectors then convert back
3193 if (gDebug)
3194 Info("EvalPar", "Function is vectorized - converting Double_t into ROOT::Double_v and back");
3195
3196 if (fNdim < 5) {
3197 const int maxDim = 4;
3198 std::array<ROOT::Double_v, maxDim> xvec;
3199 for (int i = 0; i < fNdim; i++)
3200 xvec[i] = x[i];
3201
3202 ROOT::Double_v ans = DoEvalVec(xvec.data(), params);
3203 return vecCore::Get(ans, 0);
3204 }
3205 // allocating a vector is much slower (we do only for dim > 4)
3206 std::vector<ROOT::Double_v> xvec(fNdim);
3207 for (int i = 0; i < fNdim; i++)
3208 xvec[i] = x[i];
3209
3210 ROOT::Double_v ans = DoEvalVec(xvec.data(), params);
3211 return vecCore::Get(ans, 0);
3212
3213#else
3214 // this should never happen, because fVectorized can only be set true with
3215 // R__HAS_VECCORE, but just in case:
3216 Error("EvalPar", "Formula is vectorized (even though VECCORE is disabled!)");
3217 return TMath::QuietNaN();
3218#endif
3219}
3220
3222
3223static bool functionExists(const string &Name) {
3224 return gInterpreter->GetFunction(/*cl*/nullptr, Name.c_str());
3225}
3226
3228#ifdef ROOT_SUPPORT_CLAD
3229 if (!IsCladRuntimeIncluded) {
3230 IsCladRuntimeIncluded = true;
3231 gInterpreter->Declare("#include <Math/CladDerivator.h>\n#pragma clad OFF");
3232 }
3233#else
3234 IsCladRuntimeIncluded = false;
3235#endif
3236}
3237
3238static bool
3240 std::string &GenerationInput) {
3241 std::string ReqFuncName = FuncName + "_req";
3242 // We want to call clad::differentiate(TFormula_id);
3243 GenerationInput = std::string("#pragma cling optimize(2)\n") +
3244 "#pragma clad ON\n" +
3245 "void " + ReqFuncName + "() {\n" +
3246 CladStatement + "\n " +
3247 "}\n" +
3248 "#pragma clad OFF";
3249
3250 return gInterpreter->Declare(GenerationInput.c_str());
3251}
3252
3255 Bool_t hasParameters = (Npar > 0);
3256 Bool_t hasVariables = (Ndim > 0);
3257 std::unique_ptr<TMethodCall>
3259 Vectorized, /*AddCladArrayRef*/ true);
3260 return prepareFuncPtr(method.get());
3261}
3262
3264 const Double_t *pars, Double_t *result, const Int_t /*result_size*/)
3265{
3266 void *args[3];
3267 args[0] = &vars;
3268 if (!pars) {
3269 // __attribute__((used)) extern "C" void __cf_0(void* obj, int nargs, void** args, void* ret)
3270 // {
3271 // if (ret) {
3272 // new (ret) (double) (((double (&)(double*))TFormula____id)(*(double**)args[0]));
3273 // return;
3274 // } else {
3275 // ((double (&)(double*))TFormula____id)(*(double**)args[0]);
3276 // return;
3277 // }
3278 // }
3279 args[1] = &result;
3280 (*FuncPtr)(nullptr, 2, args, /*ret*/ nullptr); // We do not use ret in a return-void func.
3281 } else {
3282 // __attribute__((used)) extern "C" void __cf_0(void* obj, int nargs, void** args, void* ret)
3283 // {
3284 // ((void (&)(double*, double*, double*))TFormula____id_grad_1)(*(double**)args[0],
3285 // *(double**)args[1],
3286 // *(double**)args[2]);
3287 // return;
3288 // }
3289 args[1] = &pars;
3290 args[2] = &result;
3291 (*FuncPtr)(nullptr, 3, args, /*ret*/nullptr); // We do not use ret in a return-void func.
3292 }
3293}
3294
3295/// returns true on success.
3297 // We already have generated the gradient.
3298 if (fGradFuncPtr)
3299 return true;
3300
3302 return false;
3303
3306 return false;
3307
3308 // Check if the gradient request was made as part of another TFormula.
3309 // This can happen when we create multiple TFormula objects with the same
3310 // formula. In that case, the hasher will give identical id and we can
3311 // reuse the already generated gradient function.
3313 std::string GradientCall
3314 ("clad::gradient(" + std::string(fClingName.Data()) + ", \"p\");");
3318 return false;
3319 }
3320
3322 return true;
3323}
3324
3325// Compute the gradient with respect to the parameter passing
3326/// a CladStorageObject, i.e. a std::vector, which has the size as the nnumber of parameters.
3327/// Note that the result buffer needs to be initialized to zero before passing it to this function.
3329{
3330 if (DoEval(x) == TMath::QuietNaN())
3331 return;
3332
3333 if (!fClingInitialized) {
3334 Error("GradientPar", "Could not initialize the formula!");
3335 return;
3336 }
3337
3338 if (!GenerateGradientPar()) {
3339 Error("GradientPar", "Could not generate a gradient for the formula %s!",
3340 fClingName.Data());
3341 return;
3342 }
3343
3344 if ((int)result.size() < fNpar) {
3345 Warning("GradientPar",
3346 "The size of gradient result is %zu but %d is required. Resizing.",
3347 result.size(), fNpar);
3348 result.resize(fNpar);
3349 }
3350 GradientPar(x, result.data());
3351}
3352/// Compute the gradient with respect to the parameter passing
3353/// a buffer with a size at least equal to the number of parameters.
3354/// Note that the result buffer needs to be initialized to zero before passed to this function.
3356 const Double_t *vars = (x) ? x : fClingVariables.data();
3357 const Double_t *pars = (fNpar <= 0) ? nullptr : fClingParameters.data();
3359}
3360
3361/// returns true on success.
3363{
3364 // We already have generated the hessian.
3365 if (fHessFuncPtr)
3366 return true;
3367
3369 return false;
3370
3373 return false;
3374
3375 // Check if the hessian request was made as part of another TFormula.
3376 // This can happen when we create multiple TFormula objects with the same
3377 // formula. In that case, the hasher will give identical id and we can
3378 // reuse the already generated hessian function.
3380 std::string indexes = (fNpar - 1 == 0) ? "0" : std::string("0:")
3381 + std::to_string(fNpar - 1);
3382 std::string HessianCall
3383 ("clad::hessian(" + std::string(fClingName.Data()) + ", \"p["
3384 + indexes + "]\" );");
3387 return false;
3388 }
3389
3391 return true;
3392}
3393
3395{
3396 if (DoEval(x) == TMath::QuietNaN())
3397 return;
3398
3399 if (!fClingInitialized) {
3400 Error("HessianPar", "Could not initialize the formula!");
3401 return;
3402 }
3403
3404 if (!GenerateHessianPar()) {
3405 Error("HessianPar", "Could not generate a hessian for the formula %s!",
3406 fClingName.Data());
3407 return;
3408 }
3409
3410 if ((int)result.size() < fNpar) {
3411 Warning("HessianPar",
3412 "The size of hessian result is %zu but %d is required. Resizing.",
3413 result.size(), fNpar * fNpar);
3414 result.resize(fNpar * fNpar);
3415 }
3416 HessianPar(x, result.data());
3417}
3418
3420 const Double_t *vars = (x) ? x : fClingVariables.data();
3421 const Double_t *pars = (fNpar <= 0) ? nullptr : fClingParameters.data();
3423}
3424
3425////////////////////////////////////////////////////////////////////////////////
3426#ifdef R__HAS_VECCORE
3427// ROOT::Double_v TFormula::Eval(ROOT::Double_v x, ROOT::Double_v y, ROOT::Double_v z, ROOT::Double_v t) const
3428// {
3429// ROOT::Double_v xxx[] = {x, y, z, t};
3430// return EvalPar(xxx, nullptr);
3431// }
3432
3433ROOT::Double_v TFormula::EvalParVec(const ROOT::Double_v *x, const Double_t *params) const
3434{
3435 if (fVectorized)
3436 return DoEvalVec(x, params);
3437
3438 if (fNdim == 0 || !x)
3439 return DoEval(nullptr, params); // automatic conversion to vectorized
3440
3441 // otherwise, trying to input vectors into a scalar function
3442
3443 if (gDebug)
3444 Info("EvalPar", "Function is not vectorized - converting ROOT::Double_v into Double_t and back");
3445
3446 const int vecSize = vecCore::VectorSize<ROOT::Double_v>();
3447 std::vector<Double_t> xscalars(vecSize*fNdim);
3448
3449 for (int i = 0; i < vecSize; i++)
3450 for (int j = 0; j < fNdim; j++)
3451 xscalars[i*fNdim+j] = vecCore::Get(x[j],i);
3452
3454 for (int i = 0; i < vecSize; i++)
3455 vecCore::Set(answers, i, DoEval(&xscalars[i*fNdim], params));
3456
3457 return answers;
3458}
3459#endif
3460
3461////////////////////////////////////////////////////////////////////////////////
3462/// Evaluate formula.
3463/// If formula is not ready to execute(missing parameters/variables),
3464/// print these which are not known.
3465/// If parameter has default value, and has not been set, appropriate warning is shown.
3466
3467Double_t TFormula::DoEval(const double * x, const double * params) const
3468{
3469 if(!fReadyToExecute)
3470 {
3471 Error("Eval", "Formula is invalid and not ready to execute ");
3472 for (auto it = fFuncs.begin(); it != fFuncs.end(); ++it) {
3473 TFormulaFunction fun = *it;
3474 if (!fun.fFound) {
3475 printf("%s is unknown.\n", fun.GetName());
3476 }
3477 }
3478 return TMath::QuietNaN();
3479 }
3480
3481 // Lazy initialization is set and needed when reading from a file
3483 // try recompiling the formula. We need to lock because this is not anymore thread safe
3485 // check again in case another thread has initialized the formula (see ROOT-10994)
3486 if (!fClingInitialized) {
3487 auto thisFormula = const_cast<TFormula*>(this);
3488 thisFormula->ReInitializeEvalMethod();
3489 }
3490 if (!fClingInitialized) {
3491 Error("DoEval", "Formula has error and it is not properly initialized ");
3492 return TMath::QuietNaN();
3493 }
3494 }
3495
3496 if (fLambdaPtr && TestBit(TFormula::kLambda)) {// case of lambda functions
3497 std::function<double(double *, double *)> & fptr = * ( (std::function<double(double *, double *)> *) fLambdaPtr);
3498 assert(x);
3499 //double * v = (x) ? const_cast<double*>(x) : const_cast<double*>(fClingVariables.data());
3500 double * v = const_cast<double*>(x);
3501 double * p = (params) ? const_cast<double*>(params) : const_cast<double*>(fClingParameters.data());
3502 return fptr(v, p);
3503 }
3504
3505
3506 Double_t result = 0;
3507 void* args[2];
3508 double * vars = (x) ? const_cast<double*>(x) : const_cast<double*>(fClingVariables.data());
3509 args[0] = &vars;
3510 if (fNpar <= 0) {
3511 (*fFuncPtr)(nullptr, 1, args, &result);
3512 } else {
3513 double *pars = (params) ? const_cast<double *>(params) : const_cast<double *>(fClingParameters.data());
3514 args[1] = &pars;
3515 (*fFuncPtr)(nullptr, 2, args, &result);
3516 }
3517 return result;
3518}
3519
3520////////////////////////////////////////////////////////////////////////////////
3521// Copied from DoEval, but this is the vectorized version
3522#ifdef R__HAS_VECCORE
3523ROOT::Double_v TFormula::DoEvalVec(const ROOT::Double_v *x, const double *params) const
3524{
3525 if (!fReadyToExecute) {
3526 Error("Eval", "Formula is invalid and not ready to execute ");
3527 for (auto it = fFuncs.begin(); it != fFuncs.end(); ++it) {
3528 TFormulaFunction fun = *it;
3529 if (!fun.fFound) {
3530 printf("%s is unknown.\n", fun.GetName());
3531 }
3532 }
3533 return TMath::QuietNaN();
3534 }
3535 // todo maybe save lambda ptr stuff for later
3536
3538 // try recompiling the formula. We need to lock because this is not anymore thread safe
3540 // check again in case another thread has initialized the formula (see ROOT-10994)
3541 if (!fClingInitialized) {
3542 auto thisFormula = const_cast<TFormula*>(this);
3543 thisFormula->ReInitializeEvalMethod();
3544 }
3545 if (!fClingInitialized) {
3546 Error("DoEval", "Formula has error and it is not properly initialized ");
3548 return res;
3549 }
3550 }
3551
3553 void *args[2];
3554
3555 ROOT::Double_v *vars = const_cast<ROOT::Double_v *>(x);
3556 args[0] = &vars;
3557 if (fNpar <= 0) {
3558 (*fFuncPtr)(0, 1, args, &result);
3559 }else {
3560 double *pars = (params) ? const_cast<double *>(params) : const_cast<double *>(fClingParameters.data());
3561 args[1] = &pars;
3562 (*fFuncPtr)(0, 2, args, &result);
3563 }
3564 return result;
3565}
3566#endif // R__HAS_VECCORE
3567
3568
3569//////////////////////////////////////////////////////////////////////////////
3570/// Re-initialize eval method
3571///
3572/// This function is called by DoEval and DoEvalVector in case of a previous failure
3573/// or in case of reading from a file
3574////////////////////////////////////////////////////////////////////////////////
3576
3577
3578 if (TestBit(TFormula::kLambda) ) {
3579 Info("ReInitializeEvalMethod","compile now lambda expression function using Cling");
3581 fLazyInitialization = false;
3582 return;
3583 }
3584 fMethod.reset();
3585
3586 if (!fLazyInitialization) Warning("ReInitializeEvalMethod", "Formula is NOT properly initialized - try calling again TFormula::PrepareEvalMethod");
3587 //else Info("ReInitializeEvalMethod", "Compile now the formula expression using Cling");
3588
3589 // check first if formula exists in the global map
3590 {
3591
3593
3594 // std::cout << "gClingFunctions list" << std::endl;
3595 // for (auto thing : gClingFunctions)
3596 // std::cout << "gClingFunctions : " << thing.first << std::endl;
3597
3599
3600 if (funcit != gClingFunctions.end()) {
3602 fClingInitialized = true;
3603 fLazyInitialization = false;
3604 return;
3605 }
3606 }
3607 // compile now formula using cling
3609 if (fClingInitialized && !fLazyInitialization) Info("ReInitializeEvalMethod", "Formula is now properly initialized !!");
3610 fLazyInitialization = false;
3611
3612 // add function pointer in global map
3613 if (fClingInitialized) {
3614 R__ASSERT(!fSavedInputFormula.empty());
3615 // if Cling has been successfully initialized
3616 // put function ptr in the static map
3618 gClingFunctions.insert(std::make_pair(fSavedInputFormula, (void *)fFuncPtr));
3619 }
3620
3621
3622 return;
3623}
3624
3625////////////////////////////////////////////////////////////////////////////////
3626/// Return the expression formula.
3627///
3628/// - If option = "P" replace the parameter names with their values
3629/// - If option = "CLING" return the actual expression used to build the function passed to Cling
3630/// - If option = "CLINGP" replace in the Cling expression the parameter with their values
3631
3633{
3634 TString opt(option);
3635 if (opt.IsNull() || TestBit(TFormula::kLambda) ) return fFormula;
3636 opt.ToUpper();
3637
3638 // if (opt.Contains("N") ) {
3639 // TString formula = fFormula;
3640 // ReplaceParName(formula, ....)
3641 // }
3642
3643 if (opt.Contains("CLING") ) {
3644 std::string clingFunc = fClingInput.Data();
3645 std::size_t found = clingFunc.find("return");
3646 std::size_t found2 = clingFunc.rfind(';');
3647 if (found == std::string::npos || found2 == std::string::npos) {
3648 Error("GetExpFormula","Invalid Cling expression - return default formula expression");
3649 return fFormula;
3650 }
3651 TString clingFormula = fClingInput(found+7,found2-found-7);
3652 // to be implemented
3653 if (!opt.Contains("P")) return clingFormula;
3654 // replace all "p[" with "[parname"
3655 int i = 0;
3656 while (i < clingFormula.Length()-2 ) {
3657 // look for p[number
3658 if (clingFormula[i] == 'p' && clingFormula[i+1] == '[' && isdigit(clingFormula[i+2]) ) {
3659 int j = i+3;
3660 while ( isdigit(clingFormula[j]) ) { j++;}
3661 if (clingFormula[j] != ']') {
3662 Error("GetExpFormula","Parameters not found - invalid expression - return default cling formula");
3663 return clingFormula;
3664 }
3665 TString parNumbName = clingFormula(i+2,j-i-2);
3666 int parNumber = parNumbName.Atoi();
3668 std::stringstream ss;
3670 clingFormula.Replace(i,j-i+1, replacement);
3671 i += replacement.size();
3672 }
3673 i++;
3674 }
3675 return clingFormula;
3676 }
3677 if (opt.Contains("P") ) {
3678 // replace parameter names with their values
3680 int i = 0;
3681 while (i < expFormula.Length()-2 ) {
3682 // look for [parName]
3683 if (expFormula[i] == '[') {
3684 int j = i+1;
3685 while ( expFormula[j] != ']' ) { j++;}
3686 if (expFormula[j] != ']') {
3687 Error("GetExpFormula","Parameter names not found - invalid expression - return default formula");
3688 return expFormula;
3689 }
3690 TString parName = expFormula(i+1,j-i-1);
3691 std::stringstream ss;
3693 expFormula.Replace(i,j-i+1, replacement);
3694 i += replacement.size();
3695 }
3696 i++;
3697 }
3698 return expFormula;
3699 }
3700 Warning("GetExpFormula","Invalid option - return default formula expression");
3701 return fFormula;
3702}
3703
3705 std::unique_ptr<TInterpreterValue> v = gInterpreter->MakeInterpreterValue();
3706 std::string s("(void (&)(Double_t const *, Double_t *, Double_t *)) ");
3707 s += GetGradientFuncName();
3708 gInterpreter->Evaluate(s.c_str(), *v);
3709 return v->ToString();
3710}
3711
3713 std::unique_ptr<TInterpreterValue> v = gInterpreter->MakeInterpreterValue();
3714 gInterpreter->Evaluate(GetHessianFuncName().c_str(), *v);
3715 return v->ToString();
3716}
3717
3718////////////////////////////////////////////////////////////////////////////////
3719/// Print the formula and its attributes.
3720
3722{
3723 printf(" %20s : %s Ndim= %d, Npar= %d, Number= %d \n",GetName(),GetTitle(), fNdim,fNpar,fNumber);
3724 printf(" Formula expression: \n");
3725 printf("\t%s \n",fFormula.Data() );
3726 TString opt(option);
3727 opt.ToUpper();
3728 // do an evaluation as a cross-check
3729 //if (fReadyToExecute) Eval();
3730
3731 if (opt.Contains("V") ) {
3732 if (fNdim > 0 && !TestBit(TFormula::kLambda)) {
3733 printf("List of Variables: \n");
3734 assert(int(fClingVariables.size()) >= fNdim);
3735 for ( int ivar = 0; ivar < fNdim ; ++ivar) {
3736 printf("Var%4d %20s = %10f \n",ivar,GetVarName(ivar).Data(), fClingVariables[ivar]);
3737 }
3738 }
3739 if (fNpar > 0) {
3740 printf("List of Parameters: \n");
3741 if ( int(fClingParameters.size()) < fNpar)
3742 Error("Print","Number of stored parameters in vector %zu in map %zu is different than fNpar %d",fClingParameters.size(), fParams.size(), fNpar);
3743 assert(int(fClingParameters.size()) >= fNpar);
3744 // print with order passed to Cling function
3745 for ( int ipar = 0; ipar < fNpar ; ++ipar) {
3746 printf("Par%4d %20s = %10f \n",ipar,GetParName(ipar), fClingParameters[ipar] );
3747 }
3748 }
3749 printf("Expression passed to Cling:\n");
3750 printf("\t%s\n",fClingInput.Data() );
3751 if (fGradFuncPtr) {
3752 printf("Generated Gradient:\n");
3753 printf("%s\n", fGradGenerationInput.c_str());
3754 printf("%s\n", GetGradientFormula().Data());
3755 }
3756 if(fHessFuncPtr) {
3757 printf("Generated Hessian:\n");
3758 printf("%s\n", fHessGenerationInput.c_str());
3759 printf("%s\n", GetHessianFormula().Data());
3760 }
3761 }
3762 if(!fReadyToExecute)
3763 {
3764 Warning("Print", "Formula is not ready to execute. Missing parameters/variables");
3765 for (list<TFormulaFunction>::const_iterator it = fFuncs.begin(); it != fFuncs.end(); ++it) {
3766 TFormulaFunction fun = *it;
3767 if (!fun.fFound) {
3768 printf("%s is unknown.\n", fun.GetName());
3769 }
3770 }
3771 }
3772 if (!fAllParametersSetted) {
3773 // we can skip this
3774 // Info("Print","Not all parameters are set.");
3775 // for(map<TString,TFormulaVariable>::const_iterator it = fParams.begin(); it != fParams.end(); ++it)
3776 // {
3777 // pair<TString,TFormulaVariable> param = *it;
3778 // if(!param.second.fFound)
3779 // {
3780 // printf("%s has default value %lf\n",param.first.Data(),param.second.GetInitialValue());
3781 // }
3782 // }
3783 }
3784}
3785
3786////////////////////////////////////////////////////////////////////////////////
3787/// Stream a class object.
3788
3790{
3791 if (b.IsReading() ) {
3792 UInt_t R__s, R__c;
3793 Version_t v = b.ReadVersion(&R__s, &R__c);
3794 //std::cout << "version " << v << std::endl;
3795 if (v <= 8 && v > 3 && v != 6) {
3796 // old TFormula class
3798 // read old TFormula class
3799 fold->Streamer(b, v, R__s, R__c, TFormula::Class());
3800 //std::cout << "read old tformula class " << std::endl;
3801 TFormula fnew(fold->GetName(), fold->GetExpFormula() );
3802
3803 *this = fnew;
3804
3805 // printf("copying content in a new TFormula \n");
3806 SetParameters(fold->GetParameters() );
3807 if (!fReadyToExecute ) {
3808 Error("Streamer","Old formula read from file is NOT valid");
3809 Print("v");
3810 }
3811 delete fold;
3812 return;
3813 }
3814 else if (v > 8) {
3815 // new TFormula class
3816 b.ReadClassBuffer(TFormula::Class(), this, v, R__s, R__c);
3817
3818 //std::cout << "reading npar = " << GetNpar() << std::endl;
3819
3820 // initialize the formula
3821 // need to set size of fClingVariables which is transient
3822 //fClingVariables.resize(fNdim);
3823
3824 // case of formula contains only parameters
3825 if (fFormula.IsNull() ) return;
3826
3827
3828 // store parameter values, names and order
3829 std::vector<double> parValues = fClingParameters;
3830 auto paramMap = fParams;
3831 fNpar = fParams.size();
3832
3833 fLazyInitialization = true; // when reading we initialize the formula later to avoid problem of recursive Jitting
3834
3835 if (!TestBit(TFormula::kLambda) ) {
3836
3837 // save dimension read from the file (stored for V >=12)
3838 // and we check after initializing if it is the same
3839 int ndim = fNdim;
3840 fNdim = 0;
3841
3842 //std::cout << "Streamer::Reading preprocess the formula " << fFormula << " ndim = " << fNdim << " npar = " << fNpar << std::endl;
3843 // for ( auto &p : fParams)
3844 // std::cout << "parameter " << p.first << " index " << p.second << std::endl;
3845
3846 fClingParameters.clear(); // need to be reset before re-initializing it
3847
3848 FillDefaults();
3849
3850
3852
3853 //std::cout << "Streamer::after pre-process the formula " << fFormula << " ndim = " << fNdim << " npar = " << fNpar << std::endl;
3854
3856
3857 //std::cout << "Streamer::after prepared " << fClingInput << " ndim = " << fNdim << " npar = " << fNpar << std::endl;
3858
3859
3860 // restore parameter values
3861 if (fNpar != (int) parValues.size() ) {
3862 Error("Streamer","number of parameters computed (%d) is not same as the stored parameters (%d)",fNpar,int(parValues.size()) );
3863 Print("v");
3864 }
3865 if (v > 11 && fNdim != ndim) {
3866 Error("Streamer","number of dimension computed (%d) is not same as the stored value (%d)",fNdim, ndim );
3867 Print("v");
3868 }
3869 }
3870 else {
3871 // we also delay the initialization of lamda expressions
3872 if (!fLazyInitialization) {
3874 if (ret) {
3875 fClingInitialized = true;
3876 }
3877 }else {
3878 fReadyToExecute = true;
3879 }
3880 }
3881 assert(fNpar == (int) parValues.size() );
3882 std::copy( parValues.begin(), parValues.end(), fClingParameters.begin() );
3883 // restore parameter names and order
3884 if (fParams.size() != paramMap.size() ) {
3885 Warning("Streamer","number of parameters list found (%zu) is not same as the stored one (%zu) - use re-created list",fParams.size(),paramMap.size()) ;
3886 //Print("v");
3887 }
3888 else
3889 //assert(fParams.size() == paramMap.size() );
3890 fParams = paramMap;
3891
3892 // input formula into Cling
3893 // need to replace in cling the name of the pointer of this object
3894 // TString oldClingName = fClingName;
3895 // fClingName.Replace(fClingName.Index("_0x")+1,fClingName.Length(), TString::Format("%p",this) );
3896 // fClingInput.ReplaceAll(oldClingName, fClingName);
3897 // InputFormulaIntoCling();
3898
3899 if (!TestBit(kNotGlobal)) {
3901 gROOT->GetListOfFunctions()->Add(this);
3902 }
3903 if (!fReadyToExecute ) {
3904 Error("Streamer","Formula read from file is NOT ready to execute");
3905 Print("v");
3906 }
3907 //std::cout << "reading 2 npar = " << GetNpar() << std::endl;
3908
3909 return;
3910 }
3911 else {
3912 Error("Streamer","Reading version %d is not supported",v);
3913 return;
3914 }
3915 }
3916 else {
3917 // case of writing
3918 b.WriteClassBuffer(TFormula::Class(), this);
3919 // std::cout << "writing npar = " << GetNpar() << std::endl;
3920 }
3921}
#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:411
#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:433
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:131
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:134
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:146
std::map< TString, TString > fFunctionsShortcuts
!
Definition TFormula.h:147
void HandleParamRanges(TString &formula)
Handling parameter ranges, in the form of [1..5].
std::vector< TObject * > fLinearParts
Vector of linear functions.
Definition TFormula.h:152
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:153
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:151
std::string GetGradientFuncName() const
Definition TFormula.h:128
static bool fIsCladRuntimeIncluded
Definition TFormula.h:111
std::vector< Double_t > CladStorage
Definition TFormula.h:183
Double_t GetVariable(const char *name) const
Returns variable value.
std::list< TFormulaFunction > fFuncs
!
Definition TFormula.h:143
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:137
void SetVectorized(Bool_t vectorized)
void FillVecFunctionsShurtCuts()
Fill the shortcuts for vectorized functions We will replace for example sin with vecCore::Mat::Sin.
Definition TFormula.cxx:988
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:144
CallFuncSignature fFuncPtr
! Function pointer, owned by the JIT.
Definition TFormula.h:107
void HandlePolN(TString &formula)
Handling Polynomial Notation (polN)
static Bool_t IsHexadecimal(const TString &formula, int ipos)
Definition TFormula.cxx: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:272
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:148
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:149
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:178
@ kLambda
Set to true if TFormula has been build with a lambda.
Definition TFormula.h:181
@ kLinear
Set to true if the TFormula is for linear fitting.
Definition TFormula.h:180
@ kNormalized
Set to true if the TFormula (ex gausn) is normalized.
Definition TFormula.h:179
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:150
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:145
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