Logo ROOT  
Reference Guide
RooFormula.cxx
Go to the documentation of this file.
1/*****************************************************************************
2 * Project: RooFit *
3 * Package: RooFitCore *
4 * @(#)root/roofitcore:$Id$
5 * Authors: *
6 * WV, Wouter Verkerke, UC Santa Barbara, verkerke@slac.stanford.edu *
7 * DK, David Kirkby, UC Irvine, dkirkby@uci.edu *
8 * *
9 * Copyright (c) 2000-2005, Regents of the University of California *
10 * and Stanford University. All rights reserved. *
11 * *
12 * Redistribution and use in source and binary forms, *
13 * with or without modification, are permitted according to the terms *
14 * listed in LICENSE (http://roofit.sourceforge.net/license.txt) *
15 *****************************************************************************/
16
17/**
18\file RooFormula.cxx
19\class RooFormula
20\ingroup Roofitcore
21
22RooFormula internally uses ROOT's TFormula to compute user-defined expressions
23of RooAbsArgs.
24
25The string expression can be any valid TFormula expression referring to the
26listed servers either by name or by their ordinal list position. These three are
27forms equivalent:
28```
29 RooFormula("formula", "x*y", RooArgList(x,y)) or
30 RooFormula("formula", "@0*@1", RooArgList(x,y))
31 RooFormula("formula", "x[0]*x[1]", RooArgList(x,y))
32```
33Note that `x[i]` is an expression reserved for TFormula. If a variable with
34the name `x` is given, the RooFormula interprets `x` as a variable name,
35but `x[i]` as an index in the list of variables.
36
37### Category expressions
38State information of RooAbsCategories can be accessed using the '::' operator,
39*i.e.*, `tagCat::Kaon` will resolve to the numerical value of
40the `Kaon` state of the RooAbsCategory object named `tagCat`.
41
42A formula to switch between lepton categories could look like this:
43```
44 RooFormula("formulaWithCat",
45 "x * (leptonMulti == leptonMulti::one) + y * (leptonMulti == leptonMulti::two)",
46 RooArgList(x, y, leptonMulti));
47```
48
49### Debugging a formula that won't compile
50When the formula is preprocessed, RooFit can print information in the debug stream.
51These can be retrieved by activating the RooFit::MsgLevel `RooFit::DEBUG`
52and the RooFit::MsgTopic `RooFit::InputArguments`.
53Check the tutorial rf506_msgservice.C for details.
54**/
55
56#include "RooFormula.h"
57#include "BracketAdapters.h"
58#include "RooAbsReal.h"
59#include "RooAbsCategory.h"
60#include "RooArgList.h"
61#include "RooMsgService.h"
62#include "RunContext.h"
63#include "RooBatchCompute.h"
64
65#include "TObjString.h"
66#include "TClass.h"
67
68#include <memory>
69#include <regex>
70#include <sstream>
71#include <cctype>
72
73using namespace std;
74
76
77namespace {
78
79////////////////////////////////////////////////////////////////////////////////
80/// Find all input arguments which are categories, and save this information in
81/// with the names of the variables that are being used to evaluate it.
82std::vector<bool> findCategoryServers(const RooAbsCollection& collection) {
83 std::vector<bool> output;
84 output.reserve(collection.size());
85
86 for (unsigned int i = 0; i < collection.size(); ++i) {
87 output.push_back(collection[i]->InheritsFrom(RooAbsCategory::Class()));
88 }
89
90 return output;
91}
92
93/// Convert `@i`-style references to `x[i]`.
94void convertArobaseReferences(std::string &formula)
95{
96 bool match = false;
97 for (std::size_t i = 0; i < formula.size(); ++i) {
98 if (match && !isdigit(formula[i])) {
99 formula.insert(formula.begin() + i, ']');
100 i += 1;
101 match = false;
102 } else if (!match && formula[i] == '@') {
103 formula[i] = 'x';
104 formula.insert(formula.begin() + i + 1, '[');
105 i += 1;
106 match = true;
107 }
108 }
109 if (match)
110 formula += ']';
111}
112
113/// Replace all occurences of `what` with `with` inside of `inout`.
114void replaceAll(std::string &inout, std::string_view what, std::string_view with)
115{
116 for (std::string::size_type pos{}; inout.npos != (pos = inout.find(what.data(), pos, what.length()));
117 pos += with.length()) {
118 inout.replace(pos, what.length(), with.data(), with.length());
119 }
120}
121
122/// Find the word boundaries with a static std::regex and return a bool vector
123/// flagging their positions. The end of the string is considered a word
124/// boundary.
125std::vector<bool> getWordBoundaryFlags(std::string const &s)
126{
127 static const std::regex r{"\\b"};
128 std::vector<bool> out(s.size() + 1);
129
130 for (auto i = std::sregex_iterator(s.begin(), s.end(), r); i != std::sregex_iterator(); ++i) {
131 std::smatch m = *i;
132 out[m.position()] = true;
133 }
134
135 // The end of a string is also a word boundary
136 out[s.size()] = true;
137
138 return out;
139}
140
141/// Replace all named references with "x[i]"-style.
142void replaceVarNamesWithIndexStyle(std::string &formula, RooArgList const &varList)
143{
144 std::vector<bool> isWordBoundary = getWordBoundaryFlags(formula);
145 for (unsigned int i = 0; i < varList.size(); ++i) {
146 std::string_view varName = varList[i].GetName();
147
148 std::stringstream replacementStream;
149 replacementStream << "x[" << i << "]";
150 std::string replacement = replacementStream.str();
151
152 for (std::string::size_type pos{}; formula.npos != (pos = formula.find(varName.data(), pos, varName.length()));
153 pos += replacement.size()) {
154
155 std::string::size_type next = pos + varName.length();
156
157 // The matched variable name has to be surrounded by word boundaries
158 // std::cout << pos << " " << next << std::endl;
159 if (!isWordBoundary[pos] || !isWordBoundary[next])
160 continue;
161
162 // Veto '[' and ']' as next characters. If the variable is called `x`
163 // or `0`, this might otherwise replace `x[0]`.
164 if (next < formula.size() && (formula[next] == '[' || formula[next] == ']')) {
165 continue;
166 }
167
168 // As we replace substrings in the middle of the string, we also have
169 // to update the word boundary flag vector. Note that we don't care
170 // the word boundaries in the `x[i]` are correct, as it has already
171 // been replaced.
172 std::size_t nOld = varName.length();
173 std::size_t nNew = replacement.size();
174 auto wbIter = isWordBoundary.begin() + pos;
175 if (nNew > nOld)
176 isWordBoundary.insert(wbIter + nOld, nNew - nOld, false);
177 else if (nNew < nOld)
178 isWordBoundary.erase(wbIter + nNew, wbIter + nOld);
179
180 // Do the actual replacment
181 formula.replace(pos, varName.length(), replacement);
182 }
183
184 oocxcoutD(static_cast<TObject *>(nullptr), InputArguments)
185 << "Preprocessing formula: replace named references: " << varName << " --> " << replacement << "\n\t"
186 << formula << endl;
187 }
188}
189
190}
191
192////////////////////////////////////////////////////////////////////////////////
193/// Default constructor
194/// coverity[UNINIT_CTOR]
195
197{
198}
199
200
201////////////////////////////////////////////////////////////////////////////////
202/// Construct a new formula.
203/// \param[in] name Name of the formula.
204/// \param[in] formula Formula to be evaluated. Parameters/observables are identified by name
205/// or ordinal position in `varList`.
206/// \param[in] varList List of variables to be passed to the formula.
207/// \param[in] checkVariables Check that the variables being passed in the `varList` are used in
208/// the formula expression.
209RooFormula::RooFormula(const char* name, const char* formula, const RooArgList& varList,
210 bool checkVariables) :
211 TNamed(name, formula), _tFormula{nullptr}
212{
213 _origList.add(varList);
214 _isCategory = findCategoryServers(_origList);
215
216 installFormulaOrThrow(formula);
217
218 RooArgList useList = usedVariables();
219 if (checkVariables && _origList.size() != useList.size()) {
220 coutI(InputArguments) << "The formula " << GetName() << " claims to use the variables " << _origList
221 << " but only " << useList << " seem to be in use."
222 << "\n inputs: " << formula << std::endl;
223 }
224}
225
226
227
228////////////////////////////////////////////////////////////////////////////////
229/// Copy constructor
230RooFormula::RooFormula(const RooFormula& other, const char* name) :
231 TNamed(name ? name : other.GetName(), other.GetTitle()), RooPrintable(other)
232{
233 _origList.add(other._origList);
234 _isCategory = findCategoryServers(_origList);
235
236 TFormula* newTF = nullptr;
237 if (other._tFormula) {
238 newTF = new TFormula(*other._tFormula);
239 newTF->SetName(GetName());
240 }
241
242 _tFormula.reset(newTF);
243}
244
245
246#ifndef _MSC_VER
247#if !defined(__GNUC__) || defined(__clang__) || (__GNUC__ > 4) || ( __GNUC__ == 4 && __GNUC_MINOR__ > 8)
248#define ROOFORMULA_HAVE_STD_REGEX
249#endif //GCC < 4.9 Check
250#endif //_MSC_VER
251
252#ifdef ROOFORMULA_HAVE_STD_REGEX
253////////////////////////////////////////////////////////////////////////////////
254/// Process given formula by replacing all ordinal and name references by
255/// `x[i]`, where `i` matches the position of the argument in `_origList`.
256/// Further, references to category states such as `leptonMulti:one` are replaced
257/// by the category index.
258std::string RooFormula::processFormula(std::string formula) const {
259
260 // WARNING to developers: people use RooFormula a lot via RooGenericPdf and
261 // RooFormulaVar! Performance matters here. Avoid non-static std::regex,
262 // because constructing these can become a bottleneck because of the regex
263 // compilation.
264
265 cxcoutD(InputArguments) << "Preprocessing formula step 1: find category tags (catName::catState) in "
266 << formula << endl;
267
268 // Step 1: Find all category tags and the corresponding index numbers
269 static const std::regex categoryReg("(\\w+)::(\\w+)");
270 std::map<std::string, int> categoryStates;
271 for (sregex_iterator matchIt = sregex_iterator(formula.begin(), formula.end(), categoryReg);
272 matchIt != sregex_iterator(); ++matchIt) {
273 assert(matchIt->size() == 3);
274 const std::string fullMatch = (*matchIt)[0];
275 const std::string catName = (*matchIt)[1];
276 const std::string catState = (*matchIt)[2];
277
278 const auto catVariable = dynamic_cast<const RooAbsCategory*>(_origList.find(catName.c_str()));
279 if (!catVariable) {
280 cxcoutD(InputArguments) << "Formula " << GetName() << " uses '::' to reference a category state as '" << fullMatch
281 << "' but a category '" << catName << "' cannot be found in the input variables." << endl;
282 continue;
283 }
284
285 if (!catVariable->hasLabel(catState)) {
286 coutE(InputArguments) << "Formula " << GetName() << " uses '::' to reference a category state as '" << fullMatch
287 << "' but the category '" << catName << "' does not seem to have the state '" << catState << "'." << endl;
288 throw std::invalid_argument(formula);
289 }
290 const int catNum = catVariable->lookupIndex(catState);
291
292 categoryStates[fullMatch] = catNum;
293 cxcoutD(InputArguments) << "\n\t" << fullMatch << "\tname=" << catName << "\tstate=" << catState << "=" << catNum;
294 }
295 cxcoutD(InputArguments) << "-- End of category tags --"<< endl;
296
297 // Step 2: Replace all category tags
298 for (const auto& catState : categoryStates) {
299 replaceAll(formula, catState.first, std::to_string(catState.second));
300 }
301
302 cxcoutD(InputArguments) << "Preprocessing formula step 2: replace category tags\n\t" << formula << endl;
303
304 // Step 3: Convert `@i`-style references to `x[i]`
305 convertArobaseReferences(formula);
306
307 cxcoutD(InputArguments) << "Preprocessing formula step 3: replace '@'-references\n\t" << formula << endl;
308
309 // Step 4: Replace all named references with "x[i]"-style
310 replaceVarNamesWithIndexStyle(formula, _origList);
311
312 cxcoutD(InputArguments) << "Final formula:\n\t" << formula << endl;
313
314 return formula;
315}
316
317
318////////////////////////////////////////////////////////////////////////////////
319/// Analyse internal formula to find out which variables are actually in use.
321 RooArgList useList;
322 if (_tFormula == nullptr)
323 return useList;
324
325 const std::string formula(_tFormula->GetTitle());
326
327 std::set<unsigned int> matchedOrdinals;
328 static const std::regex newOrdinalRegex("\\bx\\[([0-9]+)\\]");
329 for (sregex_iterator matchIt = sregex_iterator(formula.begin(), formula.end(), newOrdinalRegex);
330 matchIt != sregex_iterator(); ++matchIt) {
331 assert(matchIt->size() == 2);
332 std::stringstream matchString((*matchIt)[1]);
333 unsigned int i;
334 matchString >> i;
335
336 matchedOrdinals.insert(i);
337 }
338
339 for (unsigned int i : matchedOrdinals) {
340 useList.add(_origList[i]);
341 }
342
343 return useList;
344}
345
346
347////////////////////////////////////////////////////////////////////////////////
348/// From the internal representation, construct a formula by replacing all index place holders
349/// with the names of the variables that are being used to evaluate it.
350std::string RooFormula::reconstructFormula(std::string internalRepr) const {
351 for (unsigned int i = 0; i < _origList.size(); ++i) {
352 const auto& var = _origList[i];
353 std::stringstream regexStr;
354 regexStr << "x\\[" << i << "\\]|@" << i;
355 std::regex regex(regexStr.str());
356
357 std::string replacement = std::string("[") + var.GetName() + "]";
358 internalRepr = std::regex_replace(internalRepr, regex, replacement);
359 }
360
361 return internalRepr;
362}
363#endif //ROOFORMULA_HAVE_STD_REGEX
364
365
366
367
368////////////////////////////////////////////////////////////////////////////////
369/// Recompile formula with new expression. In case of error, the old formula is
370/// retained.
371bool RooFormula::reCompile(const char* newFormula)
372{
373 try {
374 installFormulaOrThrow(newFormula);
375 } catch (std::runtime_error& e) {
376 coutE(InputArguments) << __func__ << ": new equation doesn't compile, formula unchanged."
377 << "\n" << e.what() << endl;
378 return true;
379 }
380
381 SetTitle(newFormula);
382 return false;
383}
384
386{
387 printMultiline(std::cout, 0);
388}
389
390
391////////////////////////////////////////////////////////////////////////////////
392/// Change used variables to those with the same name in given list.
393/// \param[in] newDeps New dependents to replace the old ones.
394/// \param[in] mustReplaceAll Will yield an error if one dependent does not have a replacement.
395/// \param[in] nameChange Passed down to RooAbsArg::findNewServer(const RooAbsCollection&, bool) const.
396bool RooFormula::changeDependents(const RooAbsCollection& newDeps, bool mustReplaceAll, bool nameChange)
397{
398 //Change current servers to new servers with the same name given in list
399 bool errorStat = false;
400
401 for (const auto arg : _origList) {
402 RooAbsReal* replace = (RooAbsReal*) arg->findNewServer(newDeps,nameChange) ;
403 if (replace) {
404 _origList.replace(*arg, *replace);
405
406 if (arg->getStringAttribute("origName")) {
407 replace->setStringAttribute("origName",arg->getStringAttribute("origName")) ;
408 } else {
409 replace->setStringAttribute("origName",arg->GetName()) ;
410 }
411
412 } else if (mustReplaceAll) {
413 coutE(LinkStateMgmt) << __func__ << ": cannot find replacement for " << arg->GetName() << endl;
414 errorStat = true;
415 }
416 }
417
418 _isCategory = findCategoryServers(_origList);
419
420 return errorStat;
421}
422
423
424
425////////////////////////////////////////////////////////////////////////////////
426/// Evaluate the internal TFormula.
427///
428/// First, all variables serving this instance are evaluated given the normalisation set,
429/// and then the formula is evaluated.
430/// \param[in] nset Normalisation set passed to evaluation of arguments serving values.
431/// \return The result of the evaluation.
432double RooFormula::eval(const RooArgSet* nset) const
433{
434 if (!_tFormula) {
435 coutF(Eval) << __func__ << " (" << GetName() << "): Formula didn't compile: " << GetTitle() << endl;
436 std::string what = "Formula ";
437 what += GetTitle();
438 what += " didn't compile.";
439 throw std::runtime_error(what);
440 }
441
442 std::vector<double> pars;
443 pars.reserve(_origList.size());
444 for (unsigned int i = 0; i < _origList.size(); ++i) {
445 if (_isCategory[i]) {
446 const auto& cat = static_cast<RooAbsCategory&>(_origList[i]);
447 pars.push_back(cat.getCurrentIndex());
448 } else {
449 const auto& real = static_cast<RooAbsReal&>(_origList[i]);
450 pars.push_back(real.getVal(nset));
451 }
452 }
453
454 return _tFormula->EvalPar(pars.data());
455}
456
457
459 if (!_tFormula) {
460 coutF(Eval) << __func__ << " (" << GetName() << "): Formula didn't compile: " << GetTitle() << endl;
461 std::string what = "Formula ";
462 what += GetTitle();
463 what += " didn't compile.";
464 throw std::runtime_error(what);
465 }
466
467 std::vector<RooBatchCompute::BracketAdapterWithMask> valueAdapters;
468 std::vector<RooSpan<const double>> inputSpans;
469 size_t nData=1;
470 for (const auto arg : _origList) {
471 auto realArg = static_cast<const RooAbsReal*>(arg);
472 auto batch = realArg->getValues(inputData, nset);
473 assert(!batch.empty());
474 nData = std::max(nData, batch.size());
475 valueAdapters.emplace_back(batch[0], batch);
476 inputSpans.push_back(std::move(batch));
477 }
478
479 auto output = inputData.makeBatch(dataOwner, nData);
480 std::vector<double> pars(_origList.size());
481
482
483 for (std::size_t i=0; i < nData; ++i) {
484 for (unsigned int j=0; j < _origList.size(); ++j) {
485 if (_isCategory[j]) {
486 // TODO: As long as category states cannot be passed in the RunContext,
487 // the current state has to be used.
488 const auto& cat = static_cast<RooAbsCategory&>(_origList[j]);
489 pars[j] = cat.getCurrentIndex();
490 } else {
491 pars[j] = valueAdapters[j][i];
492 }
493 }
494
495 output[i] = _tFormula->EvalPar(pars.data());
496 }
497
498 return output;
499}
500
501void RooFormula::computeBatch(cudaStream_t*, double* output, size_t nEvents, RooFit::Detail::DataMap const& dataMap) const
502{
503 const int nPars=_origList.size();
504 std::vector<RooSpan<const double>> inputSpans(nPars);
505 for (int i=0; i<nPars; i++)
506 inputSpans[i] = dataMap.at( static_cast<const RooAbsReal*>(&_origList[i]) );
507
508 std::vector<double> pars(nPars);
509 for (size_t i=0; i<nEvents; i++)
510 {
511 for (int j=0; j<nPars; j++) pars[j] = inputSpans[j].size()>1 ? inputSpans[j][i] : inputSpans[j][0];
512 output[i] = _tFormula->EvalPar( pars.data() );
513 }
514}
515
516////////////////////////////////////////////////////////////////////////////////
517/// Printing interface
518
519void RooFormula::printMultiline(ostream& os, Int_t /*contents*/, bool /*verbose*/, TString indent) const
520{
521 os << indent << "--- RooFormula ---" << endl;
522 os << indent << " Formula: '" << GetTitle() << "'" << endl;
523 os << indent << " Interpretation: '" << reconstructFormula(GetTitle()) << "'" << endl;
524 indent.Append(" ");
525 os << indent << "Servers: " << _origList << "\n";
526 os << indent << "In use : " << actualDependents() << endl;
527}
528
529
530////////////////////////////////////////////////////////////////////////////////
531/// Print value of formula
532
533void RooFormula::printValue(ostream& os) const
534{
535 os << const_cast<RooFormula*>(this)->eval(0) ;
536}
537
538
539////////////////////////////////////////////////////////////////////////////////
540/// Print name of formula
541
542void RooFormula::printName(ostream& os) const
543{
544 os << GetName() ;
545}
546
547
548////////////////////////////////////////////////////////////////////////////////
549/// Print title of formula
550
551void RooFormula::printTitle(ostream& os) const
552{
553 os << GetTitle() ;
554}
555
556
557////////////////////////////////////////////////////////////////////////////////
558/// Print class name of formula
559
560void RooFormula::printClassName(ostream& os) const
561{
562 os << IsA()->GetName() ;
563}
564
565
566////////////////////////////////////////////////////////////////////////////////
567/// Print arguments of formula, i.e. dependents that are actually used
568
569void RooFormula::printArgs(ostream& os) const
570{
571 os << "[ actualVars=";
572 for (const auto arg : usedVariables()) {
573 os << " " << arg->GetName();
574 }
575 os << " ]";
576}
577
578
579////////////////////////////////////////////////////////////////////////////////
580/// Check that the formula compiles, and also fulfills the assumptions.
581///
582void RooFormula::installFormulaOrThrow(const std::string& formula) {
583 const std::string processedFormula = processFormula(formula);
584
585 cxcoutD(InputArguments) << "RooFormula '" << GetName() << "' will be compiled as "
586 << "\n\t" << processedFormula
587 << "\n and used as"
588 << "\n\t" << reconstructFormula(processedFormula)
589 << "\n with the parameters " << _origList << endl;
590
591 auto theFormula = std::make_unique<TFormula>(GetName(), processedFormula.c_str(), false);
592
593 if (!theFormula || !theFormula->IsValid()) {
594 std::stringstream msg;
595 msg << "RooFormula '" << GetName() << "' did not compile or is invalid."
596 << "\nInput:\n\t" << formula
597 << "\nPassed over to TFormula:\n\t" << processedFormula << std::endl;
598 coutF(InputArguments) << msg.str();
599 throw std::runtime_error(msg.str());
600 }
601
602 if (theFormula && theFormula->GetNdim() != 1) {
603 // TFormula thinks that we have a multi-dimensional formula, e.g. with variables x,y,z,t.
604 // We have to check now that this is not the case, as RooFit only uses the syntax x[0], x[1], x[2], ...
605 bool haveProblem = false;
606 std::stringstream msg;
607 msg << "TFormula interprets the formula " << formula << " as " << theFormula->GetNdim() << "-dimensional with the variable(s) {";
608 for (int i=1; i < theFormula->GetNdim(); ++i) {
609 const TString varName = theFormula->GetVarName(i);
610 if (varName.BeginsWith("x[") && varName[varName.Length()-1] == ']')
611 continue;
612
613 haveProblem = true;
614 msg << theFormula->GetVarName(i) << ",";
615 }
616 if (haveProblem) {
617 msg << "}, which could not be supplied by RooFit."
618 << "\nThe formula must be modified, or those variables must be supplied in the list of variables." << std::endl;
619 coutF(InputArguments) << msg.str();
620 throw std::invalid_argument(msg.str());
621 }
622 }
623
624 _tFormula = std::move(theFormula);
625}
626
627
628#ifndef ROOFORMULA_HAVE_STD_REGEX
629/*
630 * g++ 4.8 doesn't support the std::regex. It has headers, but no implementations of the standard, leading to linker
631 * errors. As long as centos 7 needs to be supported, this forces us to have a legacy implementation.
632 */
633
634#include "TPRegexp.h"
635
636////////////////////////////////////////////////////////////////////////////////
637/// Process given formula by replacing all ordinal and name references by
638/// `x[i]`, where `i` matches the position of the argument in `_origList`.
639/// Further, references to category states such as `leptonMulti:one` are replaced
640/// by the category index.
641std::string RooFormula::processFormula(std::string formula) const {
642 TString formulaTString = formula.c_str();
643
644 cxcoutD(InputArguments) << "Preprocessing formula step 1: find category tags (catName::catState) in "
645 << formulaTString.Data() << endl;
646
647 // Step 1: Find all category tags and the corresponding index numbers
648 TPRegexp categoryReg("(\\w+)::(\\w+)");
649 std::map<std::string, int> categoryStates;
650 int offset = 0;
651 do {
652 std::unique_ptr<TObjArray> matches(categoryReg.MatchS(formulaTString, "", offset, 3));
653 if (matches->GetEntries() == 0)
654 break;
655
656 std::string fullMatch = static_cast<TObjString*>(matches->At(0))->GetString().Data();
657 std::string catName = static_cast<TObjString*>(matches->At(1))->GetString().Data();
658 std::string catState = static_cast<TObjString*>(matches->At(2))->GetString().Data();
659 offset = formulaTString.Index(categoryReg, offset) + fullMatch.size();
660
661 const auto catVariable = dynamic_cast<const RooAbsCategory*>(_origList.find(catName.c_str()));
662 if (!catVariable) {
663 cxcoutD(InputArguments) << "Formula " << GetName() << " uses '::' to reference a category state as '" << fullMatch
664 << "' but a category '" << catName << "' cannot be found in the input variables." << endl;
665 continue;
666 }
667
668 const RooCatType* catType = catVariable->lookupType(catState.c_str(), false);
669 if (!catType) {
670 coutE(InputArguments) << "Formula " << GetName() << " uses '::' to reference a category state as '" << fullMatch
671 << "' but the category '" << catName << "' does not seem to have the state '" << catState << "'." << endl;
672 throw std::invalid_argument(formula);
673 }
674 const int catNum = catType->getVal();
675
676 categoryStates[fullMatch] = catNum;
677 cxcoutD(InputArguments) << "\n\t" << fullMatch << "\tname=" << catName << "\tstate=" << catState << "=" << catNum;
678 } while (offset != -1);
679 cxcoutD(InputArguments) << "-- End of category tags --"<< endl;
680
681 // Step 2: Replace all category tags
682 for (const auto& catState : categoryStates) {
683 std::stringstream replacement;
684 replacement << catState.second;
685 formulaTString.ReplaceAll(catState.first.c_str(), replacement.str().c_str());
686 }
687
688 cxcoutD(InputArguments) << "Preprocessing formula step 2: replace category tags\n\t" << formulaTString.Data() << endl;
689
690 // Step 3: Convert `@i`-style references to `x[i]`
691 TPRegexp ordinalRegex("@([0-9]+)");
692 int nsub = 0;
693 do {
694 nsub = ordinalRegex.Substitute(formulaTString, "x[$1]");
695 } while (nsub > 0);
696
697 cxcoutD(InputArguments) << "Preprocessing formula step 3: replace '@'-references\n\t" << formulaTString.Data() << endl;
698
699 // Step 4: Replace all named references with "x[i]"-style
700 for (unsigned int i = 0; i < _origList.size(); ++i) {
701 const auto& var = _origList[i];
702 TString regex = "\\b";
703 regex += var.GetName();
704 regex += "\\b([^\\[\\]]|$)"; //Negative lookahead. If the variable is called `x` or `0`, this might otherwise replace `x[0]`.
705 TPRegexp findParameterRegex(regex);
706
707 std::stringstream replacement;
708 replacement << "x[" << i << "]$1";
709 int nsub2 = 0;
710 do {
711 nsub2 = findParameterRegex.Substitute(formulaTString, replacement.str().c_str());
712 } while (nsub2 > 0);
713
714 cxcoutD(InputArguments) << "Preprocessing formula step 4: replace named references: "
715 << var.GetName() << " --> " << replacement.str()
716 << "\n\t" << formulaTString.Data() << endl;
717 }
718
719 cxcoutD(InputArguments) << "Final formula:\n\t" << formulaTString << endl;
720
721 return formulaTString.Data();
722}
723
724
725
726////////////////////////////////////////////////////////////////////////////////
727/// Analyse internal formula to find out which variables are actually in use.
729 RooArgList useList;
730 if (_tFormula == nullptr)
731 return useList;
732
733 const TString formulaTString = _tFormula->GetTitle();
734
735 std::set<unsigned int> matchedOrdinals;
736 TPRegexp newOrdinalRegex("\\bx\\[([0-9]+)\\]");
737 int offset = 0;
738 do {
739 std::unique_ptr<TObjArray> matches(newOrdinalRegex.MatchS(formulaTString, "", offset, 2));
740 if (matches->GetEntries() == 0)
741 break;
742
743 std::string fullMatch = static_cast<TObjString*>(matches->At(0))->GetString().Data();
744 std::string ordinal = static_cast<TObjString*>(matches->At(1))->GetString().Data();
745 offset = formulaTString.Index(newOrdinalRegex, offset) + fullMatch.size();
746
747 std::stringstream matchString(ordinal.c_str());
748 unsigned int i;
749 matchString >> i;
750
751 matchedOrdinals.insert(i);
752 } while (offset != -1);
753
754 for (unsigned int i : matchedOrdinals) {
755 useList.add(_origList[i]);
756 }
757
758 return useList;
759}
760
761
762////////////////////////////////////////////////////////////////////////////////
763/// From the internal representation, construct a formula by replacing all index place holders
764/// with the names of the variables that are being used to evaluate it.
765std::string RooFormula::reconstructFormula(std::string internalRepr) const {
766 TString internalReprT = internalRepr.c_str();
767
768 for (unsigned int i = 0; i < _origList.size(); ++i) {
769 const auto& var = _origList[i];
770 std::stringstream regexStr;
771 regexStr << "x\\[" << i << "\\]|@" << i;
772 TPRegexp regex(regexStr.str().c_str());
773
774 std::string replacement = std::string("[") + var.GetName() + "]";
775 regex.Substitute(internalReprT, replacement.c_str());
776 }
777
778 return internalReprT.Data();
779}
780#endif //ROOFORMULA_HAVE_STD_REGEX
#define e(i)
Definition: RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
#define coutI(a)
Definition: RooMsgService.h:34
#define cxcoutD(a)
Definition: RooMsgService.h:85
#define oocxcoutD(o, a)
Definition: RooMsgService.h:87
#define coutF(a)
Definition: RooMsgService.h:38
#define coutE(a)
Definition: RooMsgService.h:37
#define ClassImp(name)
Definition: Rtypes.h:375
static void indent(ostringstream &buf, int indent_level)
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
char name[80]
Definition: TGX11.cxx:110
void setStringAttribute(const Text_t *key, const Text_t *value)
Associate string 'value' to this object under key 'key'.
Definition: RooAbsArg.cxx:314
RooAbsArg * findNewServer(const RooAbsCollection &newSet, bool nameChange) const
Find the new server in the specified set that matches the old server.
Definition: RooAbsArg.cxx:1150
A space to attach TBranches.
virtual value_type getCurrentIndex() const
Return index number of current state.
static TClass * Class()
RooAbsCollection is an 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
virtual bool replace(const RooAbsArg &var1, const RooAbsArg &var2)
Replace var1 with var2 and return true for success.
RooAbsArg * find(const char *name) const
Find object with given name in list.
RooAbsReal is the common abstract base class for objects that represent a real value and implements f...
Definition: RooAbsReal.h:64
virtual RooSpan< const double > getValues(RooBatchCompute::RunContext &evalData, const RooArgSet *normSet=nullptr) const
Definition: RooAbsReal.cxx:311
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition: RooArgList.h:22
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition: RooArgSet.h:57
RooCatType is an auxilary class for RooAbsCategory and defines a a single category state.
Int_t getVal() const
RooFormula internally uses ROOT's TFormula to compute user-defined expressions of RooAbsArgs.
Definition: RooFormula.h:33
void printTitle(std::ostream &os) const override
Print title of formula.
Definition: RooFormula.cxx:551
void printMultiline(std::ostream &os, Int_t contents, bool verbose=false, TString indent="") const override
Printing interface.
Definition: RooFormula.cxx:519
double eval(const RooArgSet *nset=0) const
Evalute all parameters/observables, and then evaluate formula.
Definition: RooFormula.cxx:432
void computeBatch(cudaStream_t *, double *output, size_t nEvents, RooFit::Detail::DataMap const &) const
Definition: RooFormula.cxx:501
RooArgSet actualDependents() const
Return list of arguments which are used in the formula.
Definition: RooFormula.h:43
TClass * IsA() const override
Definition: RooFormula.h:96
RooArgList usedVariables() const
Analyse internal formula to find out which variables are actually in use.
Definition: RooFormula.cxx:320
RooSpan< double > evaluateSpan(const RooAbsReal *dataOwner, RooBatchCompute::RunContext &inputData, const RooArgSet *nset=nullptr) const
Definition: RooFormula.cxx:458
bool reCompile(const char *newFormula)
Recompile formula with new expression.
Definition: RooFormula.cxx:371
RooFormula()
Default constructor coverity[UNINIT_CTOR].
Definition: RooFormula.cxx:196
void printValue(std::ostream &os) const override
Print value of formula.
Definition: RooFormula.cxx:533
void printName(std::ostream &os) const override
Print name of formula.
Definition: RooFormula.cxx:542
void installFormulaOrThrow(const std::string &formulaa)
Check that the formula compiles, and also fulfills the assumptions.
Definition: RooFormula.cxx:582
void dump() const
DEBUG: Dump state information.
Definition: RooFormula.cxx:385
std::vector< bool > _isCategory
! Whether an element of the _origList is a category.
Definition: RooFormula.h:93
std::string processFormula(std::string origFormula) const
Process given formula by replacing all ordinal and name references by x[i], where i matches the posit...
Definition: RooFormula.cxx:258
bool changeDependents(const RooAbsCollection &newDeps, bool mustReplaceAll, bool nameChange)
Change used variables to those with the same name in given list.
Definition: RooFormula.cxx:396
std::string reconstructFormula(std::string internalRepr) const
From the internal representation, construct a formula by replacing all index place holders with the n...
Definition: RooFormula.cxx:350
void printClassName(std::ostream &os) const override
Print class name of formula.
Definition: RooFormula.cxx:560
RooArgList _origList
! Original list of dependents
Definition: RooFormula.h:92
std::unique_ptr< TFormula > _tFormula
! The formula used to compute values
Definition: RooFormula.h:94
void printArgs(std::ostream &os) const override
Print arguments of formula, i.e. dependents that are actually used.
Definition: RooFormula.cxx:569
RooPlotable is a 'mix-in' base class that define the standard RooFit plotting and printing methods.
Definition: RooPrintable.h:25
A simple container to hold a batch of data values.
Definition: RooSpan.h:34
The Formula class.
Definition: TFormula.h:87
void SetName(const char *name) override
Set the name of the formula.
Definition: TFormula.cxx:2637
The TNamed class is the base class for all named ROOT classes.
Definition: TNamed.h:29
virtual void SetTitle(const char *title="")
Set the title of the TNamed.
Definition: TNamed.cxx:164
const char * GetName() const override
Returns name of object.
Definition: TNamed.h:47
const char * GetTitle() const override
Returns title of object.
Definition: TNamed.h:48
Collectable string class.
Definition: TObjString.h:28
Mother of all ROOT objects.
Definition: TObject.h:37
Basic string class.
Definition: TString.h:136
Ssiz_t Length() const
Definition: TString.h:410
const char * Data() const
Definition: TString.h:369
TString & ReplaceAll(const TString &s1, const TString &s2)
Definition: TString.h:692
Bool_t BeginsWith(const char *s, ECaseCompare cmp=kExact) const
Definition: TString.h:615
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition: TString.h:639
basic_string_view< char > string_view
std::map< DataKey, RooSpan< const double > > DataMap
Definition: DataMap.h:59
@ InputArguments
Definition: RooGlobalFunc.h:64
@ LinkStateMgmt
Definition: RooGlobalFunc.h:63
static constexpr double s
static const char * what
Definition: stlLoader.cc:6
This struct enables passing computation data around between elements of a computation graph.
Definition: RunContext.h:31
RooSpan< double > makeBatch(const RooAbsArg *owner, std::size_t size)
Create a writable batch.
Definition: RunContext.cxx:89
auto * m
Definition: textangle.C:8
static void output()