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