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