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