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