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