71using std::sregex_iterator, std::ostream;
76void convertArobaseReferences(std::string &formula)
79 for (std::size_t i = 0; i < formula.size(); ++i) {
80 if (match && !isdigit(formula[i])) {
81 formula.insert(formula.begin() + i,
']');
84 }
else if (!match && formula[i] ==
'@') {
86 formula.insert(formula.begin() + i + 1,
'[');
96void replaceAll(std::string &inOut, std::string_view
what, std::string_view with)
98 for (std::string::size_type pos{}; inOut.npos != (
pos = inOut.find(
what.data(), pos,
what.length()));
99 pos += with.length()) {
100 inOut.replace(pos,
what.length(), with.data(), with.length());
107std::vector<bool> getWordBoundaryFlags(std::string
const &s)
109 static const std::regex
r{
"\\b"};
110 std::vector<bool> out(s.size() + 1);
112 for (
auto i = std::sregex_iterator(s.begin(), s.end(),
r); i != std::sregex_iterator(); ++i) {
114 out[
m.position()] =
true;
118 out[s.size()] =
true;
129bool isNumericNameValid(
RooAbsArg &_rooAbsArg)
132 std::stringstream ss;
134 double nameValue, actualValue;
136 nameValue = std::stod(_rooAbsArg.
GetName());
137 actualValue = std::stod(ss.str());
138 }
catch (
const std::invalid_argument &) {
139 std::stringstream ssExc;
140 ssExc <<
"RooConstVar named " << _rooAbsArg.
GetName() <<
" has name or value that "
141 <<
"cannot be converted to number";
142 throw std::invalid_argument(ssExc.str());
143 }
catch (
const std::out_of_range &) {
144 std::stringstream ssExc;
145 ssExc <<
"RooConstVar named " << _rooAbsArg.
GetName() <<
" has numeric name or value that "
146 <<
"gets out of a double range";
147 throw std::out_of_range(ssExc.str());
150 return nameValue == actualValue;
154void replaceVarNamesWithIndexStyle(std::string &formula,
RooArgList const &varList)
156 std::vector<bool> isWordBoundary = getWordBoundaryFlags(formula);
157 for (
unsigned int i = 0; i < varList.
size(); ++i) {
158 std::string_view varName = varList[i].
GetName();
161 std::string varNameStr{varName};
162 static const std::regex pureNumberNameRegex(
"^\\s*\\d+(\\.\\d+)?\\s*$");
163 if (std::regex_match(varNameStr, pureNumberNameRegex)) {
165 std::stringstream classNameSs;
169 if (classNameSs.str() ==
"RooConstVar" && isNumericNameValid(varList[i])) {
172 std::stringstream exceptionSs;
173 exceptionSs <<
"Variable '" << varName <<
"' is not a valid argument for RooFormulaVar. "
174 <<
"Variables with a name that is a number can only be of type RooConstVar "
175 <<
"and have value equal to the name";
176 throw std::invalid_argument(exceptionSs.str());
180 std::stringstream replacementStream;
181 replacementStream <<
"x[" << i <<
"]";
182 std::string replacement = replacementStream.str();
184 for (std::string::size_type pos{}; formula.npos != (
pos = formula.find(varName.data(), pos, varName.length()));
185 pos += replacement.size()) {
187 std::string::size_type next =
pos + varName.length();
191 if (!isWordBoundary[pos] || !isWordBoundary[next])
196 if (next < formula.size() && (formula[next] ==
'[' || formula[next] ==
']')) {
204 std::size_t nOld = varName.length();
205 std::size_t nNew = replacement.size();
206 auto wbIter = isWordBoundary.begin() +
pos;
208 isWordBoundary.insert(wbIter + nOld, nNew - nOld,
false);
209 }
else if (nNew < nOld) {
210 isWordBoundary.erase(wbIter + nNew, wbIter + nOld);
214 formula.replace(pos, varName.length(), replacement);
218 <<
"Preprocessing formula: replace named references: " << varName <<
" --> " << replacement <<
"\n\t"
219 << formula << std::endl;
226std::string reconstructFormula(std::string internalRepr,
RooArgList const& args) {
227 const auto nArgs = args.
size();
228 for (
unsigned int i = 0; i < nArgs; ++i) {
229 const auto& var = args[i];
230 std::stringstream regexStr;
231 regexStr <<
"x\\[" << i <<
"\\]|@" << i;
232 std::regex regex(regexStr.str());
234 std::string replacement = std::string(
"[") + var.GetName() +
"]";
235 internalRepr = std::regex_replace(internalRepr, regex, replacement);
244std::string reconstructNullFormula(std::string internalRepr,
RooArgList const& args) {
245 const auto nArgs = args.
size();
246 for (
unsigned int i = 0; i < nArgs; ++i) {
247 std::stringstream regexStr;
248 regexStr <<
"x\\[" << i <<
"\\]|@" << i;
249 std::regex regex(regexStr.str());
251 std::string replacement =
"1e-18";
252 internalRepr = std::regex_replace(internalRepr, regex, replacement);
267RooFormula::RooFormula(
const char *
name,
const char *formula,
const RooArgList &varList,
bool )
270 _origList.add(varList);
271 _varIsUsed.resize(varList.
size());
273 installFormulaOrThrow(formula);
275 if (_tFormula ==
nullptr)
278 const std::string processedFormula(_tFormula->GetTitle());
280 std::set<unsigned int> matchedOrdinals;
281 static const std::regex newOrdinalRegex(
"\\bx\\[([0-9]+)\\]");
282 for (sregex_iterator matchIt = sregex_iterator(processedFormula.begin(), processedFormula.end(), newOrdinalRegex);
283 matchIt != sregex_iterator(); ++matchIt) {
284 assert(matchIt->size() == 2);
285 std::stringstream matchString((*matchIt)[1]);
289 matchedOrdinals.insert(i);
292 for (
unsigned int i : matchedOrdinals) {
293 _varIsUsed[i] =
true;
299RooFormula::RooFormula(
const RooFormula& other,
const char*
name) :
301 _varIsUsed{other._varIsUsed}
303 _origList.add(other._origList);
305 std::unique_ptr<TFormula> newTF;
306 if (other._tFormula) {
307 newTF = std::make_unique<TFormula>(*other._tFormula);
308 newTF->SetName(GetName());
311 _tFormula = std::move(newTF);
319std::string RooFormula::processFormula(std::string formula)
const {
326 cxcoutD(InputArguments) <<
"Preprocessing formula step 1: find category tags (catName::catState) in "
327 << formula << std::endl;
330 static const std::regex categoryReg(
"(\\w+)::(\\w+)");
331 std::map<std::string, int> categoryStates;
332 for (sregex_iterator matchIt = sregex_iterator(formula.begin(), formula.end(), categoryReg);
333 matchIt != sregex_iterator(); ++matchIt) {
334 assert(matchIt->size() == 3);
335 const std::string fullMatch = (*matchIt)[0];
336 const std::string catName = (*matchIt)[1];
337 const std::string catState = (*matchIt)[2];
339 const auto catVariable =
dynamic_cast<const RooAbsCategory*
>(_origList.find(catName.c_str()));
341 cxcoutD(InputArguments) <<
"Formula " << GetName() <<
" uses '::' to reference a category state as '" << fullMatch
342 <<
"' but a category '" << catName <<
"' cannot be found in the input variables." << std::endl;
346 if (!catVariable->hasLabel(catState)) {
347 coutE(InputArguments) <<
"Formula " << GetName() <<
" uses '::' to reference a category state as '" << fullMatch
348 <<
"' but the category '" << catName <<
"' does not seem to have the state '" << catState <<
"'." << std::endl;
349 throw std::invalid_argument(formula);
351 const int catNum = catVariable->
lookupIndex(catState);
353 categoryStates[fullMatch] = catNum;
354 cxcoutD(InputArguments) <<
"\n\t" << fullMatch <<
"\tname=" << catName <<
"\tstate=" << catState <<
"=" << catNum;
356 cxcoutD(InputArguments) <<
"-- End of category tags --"<< std::endl;
359 for (
const auto& catState : categoryStates) {
360 replaceAll(formula, catState.first, std::to_string(catState.second));
363 cxcoutD(InputArguments) <<
"Preprocessing formula step 2: replace category tags\n\t" << formula << std::endl;
366 convertArobaseReferences(formula);
368 cxcoutD(InputArguments) <<
"Preprocessing formula step 3: replace '@'-references\n\t" << formula << std::endl;
371 replaceVarNamesWithIndexStyle(formula, _origList);
373 cxcoutD(InputArguments) <<
"Final formula:\n\t" << formula << std::endl;
383 for (std::size_t i = 0; i < _varIsUsed.size(); ++i) {
385 useList.
add(_origList[i]);
397bool RooFormula::changeDependents(
const RooAbsCollection& newDeps,
bool mustReplaceAll,
bool nameChange)
400 bool errorStat =
false;
404 for (
const auto arg : usedVariables()) {
407 _origList.replace(*arg, *replace);
409 if (arg->getStringAttribute(
"origName")) {
415 }
else if (mustReplaceAll) {
416 coutE(LinkStateMgmt) << __func__ <<
": cannot find replacement for " << arg->GetName() << std::endl;
431double RooFormula::eval(
const RooArgSet* nset)
const
434 coutF(Eval) << __func__ <<
" (" << GetName() <<
"): Formula didn't compile: " << GetTitle() << std::endl;
435 std::string
what =
"Formula ";
437 what +=
" didn't compile.";
438 throw std::runtime_error(
what);
441 std::vector<double> pars;
442 pars.reserve(_origList.size());
443 for (
unsigned int i = 0; i < _origList.size(); ++i) {
444 if (_origList[i].isCategory()) {
446 pars.push_back(cat.getCurrentIndex());
448 const auto& real =
static_cast<RooAbsReal&
>(_origList[i]);
449 pars.push_back(real.getVal(nset));
453 return _tFormula->EvalPar(pars.data());
458 std::span<double> output = ctx.
output();
460 const int nPars = _origList.size();
461 std::vector<std::span<const double>> inputSpans(nPars);
463 for (
int i = 0; i < nPars; i++) {
465 std::span<const double> rhs = ctx.
at(
static_cast<const RooAbsReal *
>(&actualVars[iActual]));
471 std::vector<double> pars(nPars);
472 for (
size_t i = 0; i < output.size(); i++) {
473 for (
int j = 0; j < nPars; j++) {
474 pars[j] = inputSpans[j].size() > 1 ? inputSpans[j][i] : inputSpans[j][0];
476 output[i] = _tFormula->EvalPar(pars.data());
485 os <<
indent <<
"--- RooFormula ---" << std::endl;
486 os <<
indent <<
" Formula: '" << GetTitle() <<
"'" << std::endl;
487 os <<
indent <<
" Interpretation: '" << reconstructFormula(GetTitle(), _origList) <<
"'" << std::endl;
489 os <<
indent <<
"Servers: " << _origList <<
"\n";
490 os <<
indent <<
"In use : " << actualDependents() << std::endl;
496void RooFormula::installFormulaOrThrow(
const std::string& formula) {
497 const std::string processedFormula = processFormula(formula);
499 cxcoutD(InputArguments) <<
"RooFormula '" << GetName() <<
"' will be compiled as "
500 <<
"\n\t" << processedFormula
502 <<
"\n\t" << reconstructFormula(processedFormula, _origList)
503 <<
"\n with the parameters " << _origList << std::endl;
505 auto theFormula = std::make_unique<TFormula>(GetName(), processedFormula.c_str(),
false);
507 if (!theFormula || !theFormula->IsValid()) {
508 std::stringstream msg;
509 msg <<
"RooFormula '" << GetName() <<
"' did not compile or is invalid."
510 <<
"\nInput:\n\t" << formula
511 <<
"\nPassed over to TFormula:\n\t" << processedFormula << std::endl;
512 coutF(InputArguments) << msg.str();
513 throw std::runtime_error(msg.str());
516 if (theFormula && theFormula->GetNdim() != 0) {
517 TFormula nullFormula{
"nullFormula", reconstructNullFormula(processedFormula, _origList).c_str(),
false};
518 const auto nullDim = nullFormula.
GetNdim();
524 std::stringstream msg;
525 msg <<
"TFormula interprets the formula " << formula <<
" as " << theFormula->GetNdim()+nullDim <<
"-dimensional with undefined variable(s) {";
526 for (
auto i=0; i < nullDim; ++i) {
529 msg <<
"}, which could not be supplied by RooFit."
530 <<
"\nThe formula must be modified, or those variables must be supplied in the list of variables." << std::endl;
531 coutF(InputArguments) << msg.str();
532 throw std::invalid_argument(msg.str());
536 _tFormula = std::move(theFormula);
int Int_t
Signed integer 4 bytes (int).
static void indent(ostringstream &buf, int indent_level)
Common abstract base class for objects that represent a value and a "shape" in RooFit.
void setStringAttribute(const Text_t *key, const Text_t *value)
Associate string 'value' to this object under key 'key'.
A space to attach TBranches.
value_type lookupIndex(const std::string &stateName) const
Find the index number corresponding to the state name.
Abstract container object that can hold multiple RooAbsArg objects.
const char * GetName() const override
Returns name of object.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
Storage_t::size_type size() const
void printClassName(std::ostream &os) const override
Return collection class name.
Abstract base class for objects that represent a real value and implements functionality common to al...
RooArgList is a container object that can hold multiple RooAbsArg objects.
RooArgSet is a container object that can hold multiple RooAbsArg objects.
std::span< const double > at(RooAbsArg const *arg, RooAbsArg const *caller=nullptr)
std::span< double > output()
The TNamed class is the base class for all named ROOT classes.
const char * GetName() const override
Returns name of object.
Mother of all ROOT objects.
void replaceAll(std::string &inOut, std::string_view what, std::string_view with)