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