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