Skip to content

Commit b700ed8

Browse files
authored
[RF] correctly detect undefined variables in RooArgList
[RF] correctly detect undefined variables in rooarglist Fixes #18013
1 parent f6220f6 commit b700ed8

File tree

3 files changed

+64
-50
lines changed

3 files changed

+64
-50
lines changed

Diff for: roofit/roofitcore/src/RooFormula.cxx

+51-48
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,41 @@ void replaceVarNamesWithIndexStyle(std::string &formula, RooArgList const &varLi
184184
}
185185
}
186186

187+
////////////////////////////////////////////////////////////////////////////////
188+
/// From the internal representation, construct a formula by replacing all index place holders
189+
/// with the names of the variables that are being used to evaluate it, and return it as string.
190+
std::string reconstructFormula(std::string internalRepr, RooArgList const& args) {
191+
const auto nArgs = args.size();
192+
for (unsigned int i = 0; i < nArgs; ++i) {
193+
const auto& var = args[i];
194+
std::stringstream regexStr;
195+
regexStr << "x\\[" << i << "\\]|@" << i;
196+
std::regex regex(regexStr.str());
197+
198+
std::string replacement = std::string("[") + var.GetName() + "]";
199+
internalRepr = std::regex_replace(internalRepr, regex, replacement);
200+
}
201+
202+
return internalRepr;
203+
}
204+
205+
////////////////////////////////////////////////////////////////////////////////
206+
/// From the internal representation, construct a null-formula by replacing all
207+
/// index place holders with zeroes, and return it as string
208+
std::string reconstructNullFormula(std::string internalRepr, RooArgList const& args) {
209+
const auto nArgs = args.size();
210+
for (unsigned int i = 0; i < nArgs; ++i) {
211+
std::stringstream regexStr;
212+
regexStr << "x\\[" << i << "\\]|@" << i;
213+
std::regex regex(regexStr.str());
214+
215+
std::string replacement = "1e-18";
216+
internalRepr = std::regex_replace(internalRepr, regex, replacement);
217+
}
218+
219+
return internalRepr;
220+
}
221+
187222
}
188223

189224
////////////////////////////////////////////////////////////////////////////////
@@ -219,7 +254,6 @@ RooFormula::RooFormula(const RooFormula& other, const char* name) :
219254
_tFormula = std::move(newTF);
220255
}
221256

222-
223257
////////////////////////////////////////////////////////////////////////////////
224258
/// Process given formula by replacing all ordinal and name references by
225259
/// `x[i]`, where `i` matches the position of the argument in `_origList`.
@@ -284,7 +318,6 @@ std::string RooFormula::processFormula(std::string formula) const {
284318
return formula;
285319
}
286320

287-
288321
////////////////////////////////////////////////////////////////////////////////
289322
/// Analyse internal formula to find out which variables are actually in use.
290323
RooArgList RooFormula::usedVariables() const {
@@ -313,31 +346,11 @@ RooArgList RooFormula::usedVariables() const {
313346
return useList;
314347
}
315348

316-
317-
////////////////////////////////////////////////////////////////////////////////
318-
/// From the internal representation, construct a formula by replacing all index place holders
319-
/// with the names of the variables that are being used to evaluate it.
320-
std::string RooFormula::reconstructFormula(std::string internalRepr) const {
321-
for (unsigned int i = 0; i < _origList.size(); ++i) {
322-
const auto& var = _origList[i];
323-
std::stringstream regexStr;
324-
regexStr << "x\\[" << i << "\\]|@" << i;
325-
std::regex regex(regexStr.str());
326-
327-
std::string replacement = std::string("[") + var.GetName() + "]";
328-
internalRepr = std::regex_replace(internalRepr, regex, replacement);
329-
}
330-
331-
return internalRepr;
332-
}
333-
334-
335349
void RooFormula::dump() const
336350
{
337351
printMultiline(std::cout, 0);
338352
}
339353

340-
341354
////////////////////////////////////////////////////////////////////////////////
342355
/// Change used variables to those with the same name in given list.
343356
/// \param[in] newDeps New dependents to replace the old ones.
@@ -372,8 +385,6 @@ bool RooFormula::changeDependents(const RooAbsCollection& newDeps, bool mustRepl
372385
return errorStat;
373386
}
374387

375-
376-
377388
////////////////////////////////////////////////////////////////////////////////
378389
/// Evaluate the internal TFormula.
379390
///
@@ -433,13 +444,12 @@ void RooFormula::printMultiline(ostream& os, Int_t /*contents*/, bool /*verbose*
433444
{
434445
os << indent << "--- RooFormula ---" << std::endl;
435446
os << indent << " Formula: '" << GetTitle() << "'" << std::endl;
436-
os << indent << " Interpretation: '" << reconstructFormula(GetTitle()) << "'" << std::endl;
447+
os << indent << " Interpretation: '" << reconstructFormula(GetTitle(), _origList) << "'" << std::endl;
437448
indent.Append(" ");
438449
os << indent << "Servers: " << _origList << "\n";
439450
os << indent << "In use : " << actualDependents() << std::endl;
440451
}
441452

442-
443453
////////////////////////////////////////////////////////////////////////////////
444454
/// Print value of formula
445455

@@ -448,7 +458,6 @@ void RooFormula::printValue(ostream& os) const
448458
os << const_cast<RooFormula*>(this)->eval(nullptr) ;
449459
}
450460

451-
452461
////////////////////////////////////////////////////////////////////////////////
453462
/// Print name of formula
454463

@@ -457,7 +466,6 @@ void RooFormula::printName(ostream& os) const
457466
os << GetName() ;
458467
}
459468

460-
461469
////////////////////////////////////////////////////////////////////////////////
462470
/// Print title of formula
463471

@@ -466,7 +474,6 @@ void RooFormula::printTitle(ostream& os) const
466474
os << GetTitle() ;
467475
}
468476

469-
470477
////////////////////////////////////////////////////////////////////////////////
471478
/// Print class name of formula
472479

@@ -475,7 +482,6 @@ void RooFormula::printClassName(ostream& os) const
475482
os << ClassName() ;
476483
}
477484

478-
479485
////////////////////////////////////////////////////////////////////////////////
480486
/// Print arguments of formula, i.e. dependents that are actually used
481487

@@ -488,7 +494,6 @@ void RooFormula::printArgs(ostream& os) const
488494
os << " ]";
489495
}
490496

491-
492497
////////////////////////////////////////////////////////////////////////////////
493498
/// Check that the formula compiles, and also fulfills the assumptions.
494499
///
@@ -498,10 +503,10 @@ void RooFormula::installFormulaOrThrow(const std::string& formula) {
498503
cxcoutD(InputArguments) << "RooFormula '" << GetName() << "' will be compiled as "
499504
<< "\n\t" << processedFormula
500505
<< "\n and used as"
501-
<< "\n\t" << reconstructFormula(processedFormula)
506+
<< "\n\t" << reconstructFormula(processedFormula, _origList)
502507
<< "\n with the parameters " << _origList << std::endl;
503508

504-
auto theFormula = std::make_unique<TFormula>(GetName(), processedFormula.c_str(), false);
509+
auto theFormula = std::make_unique<TFormula>(GetName(), processedFormula.c_str(), /*addToGlobList=*/false);
505510

506511
if (!theFormula || !theFormula->IsValid()) {
507512
std::stringstream msg;
@@ -512,21 +517,19 @@ void RooFormula::installFormulaOrThrow(const std::string& formula) {
512517
throw std::runtime_error(msg.str());
513518
}
514519

515-
if (theFormula && theFormula->GetNdim() != 1) {
516-
// TFormula thinks that we have a multi-dimensional formula, e.g. with variables x,y,z,t.
517-
// We have to check now that this is not the case, as RooFit only uses the syntax x[0], x[1], x[2], ...
518-
bool haveProblem = false;
519-
std::stringstream msg;
520-
msg << "TFormula interprets the formula " << formula << " as " << theFormula->GetNdim() << "-dimensional with the variable(s) {";
521-
for (int i=1; i < theFormula->GetNdim(); ++i) {
522-
const TString varName = theFormula->GetVarName(i);
523-
if (varName.BeginsWith("x[") && varName[varName.Length()-1] == ']')
524-
continue;
525-
526-
haveProblem = true;
527-
msg << theFormula->GetVarName(i) << ",";
528-
}
529-
if (haveProblem) {
520+
if (theFormula && theFormula->GetNdim() != 0) {
521+
TFormula nullFormula{"nullFormula", reconstructNullFormula(processedFormula, _origList).c_str(), /*addToGlobList=*/false};
522+
const auto nullDim = nullFormula.GetNdim();
523+
if (nullDim != 0) {
524+
// TFormula thinks that we have an n-dimensional formula (n>0), but it shouldn't, as
525+
// these vars should have been replaced by zeroes in reconstructNullFormula
526+
// since RooFit only uses the syntax x[0], x[1], x[2], ...
527+
// This can happen e.g. with variables x,y,z,t that were not supplied in arglist.
528+
std::stringstream msg;
529+
msg << "TFormula interprets the formula " << formula << " as " << theFormula->GetNdim()+nullDim << "-dimensional with undefined variable(s) {";
530+
for (auto i=0; i < nullDim; ++i) {
531+
msg << nullFormula.GetVarName(i) << ",";
532+
}
530533
msg << "}, which could not be supplied by RooFit."
531534
<< "\nThe formula must be modified, or those variables must be supplied in the list of variables." << std::endl;
532535
coutF(InputArguments) << msg.str();

Diff for: roofit/roofitcore/src/RooFormula.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,7 @@ class RooFormula : public TNamed, public RooPrintable {
7676
private:
7777
std::string processFormula(std::string origFormula) const;
7878
RooArgList usedVariables() const;
79-
std::string reconstructFormula(std::string internalRepr) const;
80-
void installFormulaOrThrow(const std::string &formulaa);
79+
void installFormulaOrThrow(const std::string &formula);
8180

8281
RooArgList _origList; ///<! Original list of dependents
8382
std::vector<bool> _isCategory; ///<! Whether an element of the _origList is a category.

Diff for: roofit/roofitcore/test/testRooFormula.cxx

+12
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,15 @@ TEST(RooFormula, UnusedVariables)
7777
// formula.
7878
EXPECT_EQ(func.servers().size(), 2);
7979
}
80+
81+
TEST(RooFormula, UndefinedVariables)
82+
{
83+
RooRealVar B("B", "", 0.516952);
84+
RooRealVar r("r", "", 0.214107);
85+
RooRealVar x("x", "", 0.2, 1);
86+
RooRealVar y("y", "", 0.2, 1);
87+
88+
ASSERT_ANY_THROW(RooFormulaVar f1("f1", "r + B + x", {r, B})) << "Formulae with missing x in arg list cannot work.";
89+
ASSERT_ANY_THROW(RooFormulaVar f2("f2", "r + B + y", {r, B})) << "Formulae with missing (x,)y in arg list cannot work.";
90+
ASSERT_NO_THROW(RooFormulaVar f2("f2", "r + B + y", {r, B, y})) << "Formula with specified y must work.";
91+
}

0 commit comments

Comments
 (0)