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