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