Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RooFormulaVar.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/// \class RooFormulaVar
19///
20/// A RooFormulaVar is a generic implementation of a real-valued object,
21/// which takes a RooArgList of servers and a C++ expression string defining how
22/// its value should be calculated from the given list of servers.
23/// RooFormulaVar uses a RooFormula object to perform the expression evaluation.
24///
25/// If RooAbsPdf objects are supplied to RooFormulaVar as servers, their
26/// raw (unnormalized) values will be evaluated. Use RooGenericPdf, which
27/// constructs generic PDF functions, to access their properly normalized
28/// values.
29///
30/// The string expression can be any valid TFormula expression referring to the
31/// listed servers either by name or by their ordinal list position. These three are
32/// equivalent:
33/// ```
34/// RooFormulaVar("gen", "x*y", RooArgList(x,y)) // reference by name
35/// RooFormulaVar("gen", "@0*@1", RooArgList(x,y)) // reference by ordinal with @
36/// RooFormulaVar("gen", "x[0]*x[1]", RooArgList(x,y)) // TFormula-builtin reference by ordinal
37/// ```
38/// Note that `x[i]` is an expression reserved for TFormula. All variable references
39/// are automatically converted to the TFormula-native format. If a variable with
40/// the name `x` is given, the RooFormula interprets `x[i]` as a list position,
41/// but `x` without brackets as the name of a RooFit object.
42///
43/// The last two versions, while slightly less readable, are more versatile because
44/// the names of the arguments are not hard coded.
45///
46
47
48#include "Riostream.h"
49
50#include "RooFormulaVar.h"
51#include "RooStreamParser.h"
52#include "RooMsgService.h"
53#include "RooFormula.h"
54#include "RooAbsRealLValue.h"
55#include "RooAbsBinning.h"
56#include "RooCurve.h"
57#include "RooFitImplHelpers.h"
58
59#ifdef ROOFIT_LEGACY_EVAL_BACKEND
60#include "RooNLLVar.h"
61#include "RooChi2Var.h"
62#endif
63
64using std::ostream, std::istream, std::list;
65
66
68
73
74////////////////////////////////////////////////////////////////////////////////
75/// Constructor with formula expression and list of input variables.
76/// \param[in] name Name of the formula.
77/// \param[in] title Title of the formula.
78/// \param[in] inFormula Expression to be evaluated.
79/// \param[in] dependents Variables that should be passed to the formula.
80/// \param[in] checkVariables Check that all variables from `dependents` are used in the expression.
81RooFormulaVar::RooFormulaVar(const char *name, const char *title, const char* inFormula, const RooArgList& dependents,
82 bool checkVariables) :
83 RooAbsReal(name,title),
84 _actualVars("actualVars","Variables used by formula expression",this),
85 _formExpr(inFormula)
86{
87 if (dependents.empty()) {
88 _value = traceEval(nullptr);
89 } else {
91 _formExpr = _formula->reindexedFormulaForUsedVars().c_str();
92 _actualVars.add(_formula->actualDependents());
93 }
94}
95
96
97
98////////////////////////////////////////////////////////////////////////////////
99/// Constructor with formula expression, title and list of input variables.
100/// \param[in] name Name of the formula.
101/// \param[in] title Formula expression. Will also be used as the title.
102/// \param[in] dependents Variables that should be passed to the formula.
103/// \param[in] checkVariables Check that all variables from `dependents` are used in the expression.
104RooFormulaVar::RooFormulaVar(const char *name, const char *title, const RooArgList& dependents,
105 bool checkVariables) :
106 RooAbsReal(name,title),
107 _actualVars("actualVars","Variables used by formula expression",this),
108 _formExpr(title)
109{
110 if (dependents.empty()) {
111 _value = traceEval(nullptr);
112 } else {
114 _formExpr = _formula->reindexedFormulaForUsedVars().c_str();
115 _actualVars.add(_formula->actualDependents());
116 }
117}
118
119
120
121////////////////////////////////////////////////////////////////////////////////
122/// Copy constructor
123
126 _actualVars("actualVars",this,other._actualVars),
127 _formExpr(other._formExpr)
128{
129 for (auto const &item : other._binnings) {
130 _binnings[item.first] = std::unique_ptr<RooAbsBinning>{item.second->clone()};
131 }
132 if (other._formula && other._formula->ok()) {
133 _formula = new RooFormula(*other._formula);
134 _formExpr = _formula->reindexedFormulaForUsedVars().c_str();
135 }
136}
137
138
139////////////////////////////////////////////////////////////////////////////////
140/// Return reference to internal RooFormula object.
141/// If it doesn't exist, create it on the fly.
143{
144 if (!_formula) {
145 // After being read from file, the formula object might not exist, yet:
147 const_cast<TString &>(_formExpr) = _formula->reindexedFormulaForUsedVars().c_str();
148 }
149
150 return *_formula;
151}
152
153
154bool RooFormulaVar::ok() const { return getFormula().ok() ; }
155
156
157void RooFormulaVar::dumpFormula() { getFormula().printMultiline(std::cout, 0) ; }
158
159
160////////////////////////////////////////////////////////////////////////////////
161/// Calculate current value of object from internal formula
162
164{
165 return getFormula().eval(_actualVars.nset());
166}
167
168
170{
171 getFormula().doEval(_actualVars, ctx);
172}
173
174
175////////////////////////////////////////////////////////////////////////////////
176/// Propagate server change information to embedded RooFormula object
177
179{
180 bool error = getFormula().changeDependents(newServerList,mustReplaceAll,nameChange);
181
182 _formExpr = getFormula().reindexedFormulaForUsedVars().c_str();
184}
185
186
187
188////////////////////////////////////////////////////////////////////////////////
189/// Print info about this object to the specified stream.
190
191void RooFormulaVar::printMultiline(ostream& os, Int_t contents, bool verbose, TString indent) const
192{
193 RooAbsReal::printMultiline(os,contents,verbose,indent);
194 if(verbose) {
195 indent.Append(" ");
196 os << indent;
197 getFormula().printMultiline(os,contents,verbose,indent);
198 }
199}
200
201
202
203////////////////////////////////////////////////////////////////////////////////
204/// Add formula expression as meta argument in printing interface
205
206void RooFormulaVar::printMetaArgs(ostream& os) const
207{
208 os << "formula=\"" << _formExpr << "\" " ;
209}
210
211
212
213
214////////////////////////////////////////////////////////////////////////////////
215/// Read object contents from given stream
216
217bool RooFormulaVar::readFromStream(istream& /*is*/, bool /*compact*/, bool /*verbose*/)
218{
219 coutE(InputArguments) << "RooFormulaVar::readFromStream(" << GetName() << "): can't read" << std::endl ;
220 return true ;
221}
222
223
224
225////////////////////////////////////////////////////////////////////////////////
226/// Write object contents to given stream
227
228void RooFormulaVar::writeToStream(ostream& os, bool compact) const
229{
230 if (compact) {
231 std::cout << getVal() << std::endl ;
232 } else {
233 os << GetTitle() ;
234 }
235}
236
237////////////////////////////////////////////////////////////////////////////////
238/// Declare that this function is piecewise constant (flat) within the bins of
239/// the given `binning` of the observable `obs`, which must be one of the formula
240/// variables. The method can be called several times to set a binning for more
241/// than one observable. See RooGenericPdf::setBinning() for details.
242
244{
245 // Match the observable to a formula variable by name, so that a same-named
246 // stand-in for the actual server is accepted too.
247 const int idx = _actualVars.index(obs.GetName());
248 if (idx < 0) {
249 coutE(InputArguments) << "RooFormulaVar::setBinning(" << GetName() << ") the observable " << obs.GetName()
250 << " is not one of the formula variables of this function, nothing done." << std::endl;
251 return;
252 }
253
254 if (checkFlatness) {
255 // Sample the function by varying the actual formula variable (the server),
256 // which may be a different object than `obs` if `obs` is just a same-named
257 // stand-in: the function's value depends on the server, not on `obs`.
258 if (auto *serverObs = dynamic_cast<RooAbsRealLValue *>(_actualVars.at(idx))) {
259 std::span<const double> boundaries{binning.array(), static_cast<std::size_t>(binning.numBoundaries())};
260 if (!RooHelpers::isFunctionFlatInBins(*this, *serverObs, boundaries)) {
261 coutE(InputArguments) << "RooFormulaVar::setBinning(" << GetName() << ") the expression \"" << _formExpr
262 << "\" is not flat within the given bins of " << obs.GetName()
263 << ". The binning is not set. Pass checkFlatness=false to override this check."
264 << std::endl;
265 return;
266 }
267 }
268 }
269
270 // Key the binning by the observable's index in _actualVars (not its name), so
271 // that it survives a renaming of the variable or a server redirection.
272 _binnings[idx] = std::unique_ptr<RooAbsBinning>{binning.clone()};
273}
274
275////////////////////////////////////////////////////////////////////////////////
276/// Return the binning previously declared with setBinning() for observable
277/// `obs`, or nullptr if no binning was declared. This reports only binnings
278/// owned by this formula, not binning hints forwarded by its servers.
279
281{
282 auto found = _binnings.find(_actualVars.index(obs.GetName()));
283 return found != _binnings.end() ? found->second.get() : nullptr;
284}
285
286////////////////////////////////////////////////////////////////////////////////
287/// Remove a binning previously declared with setBinning() for observable `obs`,
288/// reverting to the generic numeric integrator for it. Returns true if a binning
289/// was removed, false if none was set for `obs`.
290
292{
293 return _binnings.erase(_actualVars.index(obs.GetName())) > 0;
294}
295
296////////////////////////////////////////////////////////////////////////////////
297/// Return true if a binning was set with setBinning() for every
298/// observable in the integration set `obs`.
299
301{
302 if (obs.empty() || _binnings.empty()) {
303 return false;
304 }
305 for (RooAbsArg *o : obs) {
306 const int idx = _actualVars.index(o->GetName());
307 // Observables that are not formula variables of this function are ones we
308 // do not depend on: the function is constant (hence trivially binned) in
309 // them, so they must be ignored here. This matches the convention that
310 // composite functions like RooProduct rely on, where each component's
311 // isBinnedDistribution() is queried with the full observable set.
312 if (idx < 0) {
313 continue;
314 }
315 if (_binnings.find(idx) == _binnings.end()) {
316 return false;
317 }
318 }
319 return true;
320}
321
322////////////////////////////////////////////////////////////////////////////////
323/// Return the boundaries of the binning set with setBinning() that fall
324/// within [xlo, xhi]. If no binning was set for this observable, forward the bin
325/// boundaries from the server that defines the observable obs.
326
327std::list<double>* RooFormulaVar::binBoundaries(RooAbsRealLValue& obs, double xlo, double xhi) const
328{
329 auto found = _binnings.find(_actualVars.index(obs.GetName()));
330 if (found != _binnings.end()) {
331 const RooAbsBinning &binning = *found->second;
332 auto hint = new std::list<double>;
333 for (int i = 0; i < binning.numBoundaries(); ++i) {
334 const double boundary = binning.array()[i];
335 if (boundary >= xlo && boundary <= xhi) {
336 hint->push_back(boundary);
337 }
338 }
339 return hint;
340 }
341
342 for (const auto par : _actualVars) {
343 auto func = static_cast<const RooAbsReal*>(par);
344 list<double>* binb = nullptr;
345
346 if (func && (binb = func->binBoundaries(obs,xlo,xhi)) ) {
347 return binb;
348 }
349 }
350
351 return nullptr;
352}
353
354////////////////////////////////////////////////////////////////////////////////
355/// Return sampling hints that draw the piecewise-flat shape exactly if a binning
356/// was set for this observable. Otherwise, forward the plot sampling hint from
357/// the server that defines the observable obs.
358
359std::list<double>* RooFormulaVar::plotSamplingHint(RooAbsRealLValue& obs, double xlo, double xhi) const
360{
361 if (const RooAbsBinning *binning = getBinning(obs)) {
363 {binning->array(), static_cast<std::size_t>(binning->numBoundaries())}, xlo, xhi);
364 }
365
366 for (const auto par : _actualVars) {
367 auto func = dynamic_cast<const RooAbsReal*>(par);
368 list<double>* hint = nullptr;
369
370 if (func && (hint = func->plotSamplingHint(obs,xlo,xhi)) ) {
371 return hint;
372 }
373 }
374
375 return nullptr;
376}
377
378
379
380////////////////////////////////////////////////////////////////////////////////
381/// Return the default error level for MINUIT error analysis
382/// If the formula contains one or more RooNLLVars and
383/// no RooChi2Vars, return the defaultErrorLevel() of
384/// RooNLLVar. If the addition contains one ore more RooChi2Vars
385/// and no RooNLLVars, return the defaultErrorLevel() of
386/// RooChi2Var. If the addition contains neither or both
387/// issue a warning message and return a value of 1
388
390{
391 RooAbsReal* nllArg(nullptr) ;
392 RooAbsReal* chi2Arg(nullptr) ;
393
394#ifdef ROOFIT_LEGACY_EVAL_BACKEND
395 for (const auto arg : _actualVars) {
396 if (dynamic_cast<RooNLLVar*>(arg)) {
397 nllArg = static_cast<RooAbsReal*>(arg) ;
398 }
399 if (dynamic_cast<RooChi2Var*>(arg)) {
400 chi2Arg = static_cast<RooAbsReal*>(arg) ;
401 }
402 }
403#endif
404
405 if (nllArg && !chi2Arg) {
406 coutI(Minimization) << "RooFormulaVar::defaultErrorLevel(" << GetName()
407 << ") Formula contains a RooNLLVar, using its error level" << std::endl ;
408 return nllArg->defaultErrorLevel() ;
409 } else if (chi2Arg && !nllArg) {
410 coutI(Minimization) << "RooFormulaVar::defaultErrorLevel(" << GetName()
411 << ") Formula contains a RooChi2Var, using its error level" << std::endl ;
412 return chi2Arg->defaultErrorLevel() ;
413 } else if (!nllArg && !chi2Arg) {
414 coutI(Minimization) << "RooFormulaVar::defaultErrorLevel(" << GetName() << ") WARNING: "
415 << "Formula contains neither RooNLLVar nor RooChi2Var server, using default level of 1.0" << std::endl ;
416 } else {
417 coutI(Minimization) << "RooFormulaVar::defaultErrorLevel(" << GetName() << ") WARNING: "
418 << "Formula contains BOTH RooNLLVar and RooChi2Var server, using default level of 1.0" << std::endl ;
419 }
420
421 return 1.0 ;
422}
423
425{
426 return getFormula().getTFormula()->GetUniqueFuncName().Data();
427}
428
429std::unique_ptr<RooAbsArg>
431{
432 // Some users exploit unnormalized RooAbsPdfs as inputs for RooFormulaVars,
433 // relying on what the pdf returns from RooAbsPdf::evaluate(). This is in
434 // principle not allowed because every pdf needs to be evaluated with a
435 // normalization set, but it's so common in user code that we need to
436 // support it. To make this work, we need to make sure that the no
437 // normalization over non-dependents is happening at this point, reducing
438 // the normalization set to the subset of actual dependents.
439 // See also the "PdfAsFunctionInFormulaVar" test in testRooAbsPdf.
442 auto newArg = std::unique_ptr<RooAbsArg>{static_cast<RooAbsArg *>(Clone())};
445 return newArg;
446}
#define coutI(a)
#define coutE(a)
static void indent(ostringstream &buf, int indent_level)
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
char name[80]
Definition TGX11.cxx:148
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:76
RooFit::OwningPtr< RooArgSet > getObservables(const RooArgSet &set, bool valueOnly=true) const
Given a set of possible observables, return the observables that this PDF depends on.
TObject * Clone(const char *newname=nullptr) const override
Make a clone of an object using the Streamer facility.
Definition RooAbsArg.h:88
Abstract base class for RooRealVar binning definitions.
virtual Int_t numBoundaries() const =0
virtual double * array() const =0
virtual RooAbsBinning * clone(const char *name=nullptr) const =0
Abstract container object that can hold multiple RooAbsArg objects.
Int_t index(const RooAbsArg *arg) const
Returns index of given arg, or -1 if arg is not in the collection.
const RooArgSet * nset() const
Definition RooAbsProxy.h:52
Abstract base class for objects that represent a real value that may appear on the left hand side of ...
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:63
double getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
Definition RooAbsReal.h:107
void printMultiline(std::ostream &os, Int_t contents, bool verbose=false, TString indent="") const override
Structure printing.
bool redirectServersHook(const RooAbsCollection &newServerList, bool mustReplaceAll, bool nameChange, bool isRecursiveStep) override
Function that is called at the end of redirectServers().
double _value
Cache for current value of object.
Definition RooAbsReal.h:542
double traceEval(const RooArgSet *set) const
Calculate current value of object, with error tracing wrapper.
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooAbsArg * at(Int_t idx) const
Return object at given index, or nullptr if index is out of range.
Definition RooArgList.h:110
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:24
bool add(const RooAbsArg &var, bool valueServer, bool shapeServer, bool silent)
Overloaded RooCollection_t::add() method insert object into set and registers object as server to own...
static std::list< double > * plotSamplingHintForBinBoundaries(std::span< const double > boundaries, double xlo, double xhi)
Returns sampling hints for a histogram with given boundaries.
Definition RooCurve.cxx:897
void markAsCompiled(RooAbsArg &arg) const
void compileServers(RooAbsArg &arg, RooArgSet const &normSet)
A RooFormulaVar is a generic implementation of a real-valued object, which takes a RooArgList of serv...
~RooFormulaVar() override
std::map< int, std::unique_ptr< RooAbsBinning > > _binnings
User-defined binnings, keyed by the observable's index in _actualVars, for a piecewise-flat distribut...
RooListProxy _actualVars
Actual parameters used by formula engine.
std::list< double > * binBoundaries(RooAbsRealLValue &obs, double xlo, double xhi) const override
Return the boundaries of the binning set with setBinning() that fall within [xlo, xhi].
bool isBinnedDistribution(const RooArgSet &obs) const override
Return true if a binning was set with setBinning() for every observable in the integration set obs.
RooFormula & getFormula() const
Return reference to internal RooFormula object.
RooFormula * _formula
! Formula engine
void doEval(RooFit::EvalContext &ctx) const override
Base function for computing multiple values of a RooAbsReal.
void dumpFormula()
Dump the formula to stdout.
double defaultErrorLevel() const override
Return the default error level for MINUIT error analysis If the formula contains one or more RooNLLVa...
std::unique_ptr< RooAbsArg > compileForNormSet(RooArgSet const &normSet, RooFit::Detail::CompileContext &ctx) const override
std::list< double > * plotSamplingHint(RooAbsRealLValue &obs, double xlo, double xhi) const override
Return sampling hints that draw the piecewise-flat shape exactly if a binning was set for this observ...
bool redirectServersHook(const RooAbsCollection &newServerList, bool mustReplaceAll, bool nameChange, bool isRecursive) override
Propagate server change information to embedded RooFormula object.
void setBinning(const RooAbsRealLValue &obs, const RooAbsBinning &binning, bool checkFlatness=true)
Declare that this function is piecewise constant (flat) within the bins of the given binning of the o...
bool ok() const
const RooArgList & dependents() const
bool removeBinning(const RooAbsRealLValue &obs)
Remove a binning previously declared with setBinning() for observable obs, reverting to the generic n...
bool readFromStream(std::istream &is, bool compact, bool verbose=false) override
Read object contents from given stream.
const RooAbsBinning * getBinning(const RooAbsRealLValue &obs) const
Return the binning previously declared with setBinning() for observable obs, or nullptr if no binning...
TString _formExpr
Formula expression string.
void printMultiline(std::ostream &os, Int_t contents, bool verbose=false, TString indent="") const override
Print info about this object to the specified stream.
std::string getUniqueFuncName() const
double evaluate() const override
Calculate current value of object from internal formula.
void writeToStream(std::ostream &os, bool compact) const override
Write object contents to given stream.
void printMetaArgs(std::ostream &os) const override
Add formula expression as meta argument in printing interface.
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
const char * GetTitle() const override
Returns title of object.
Definition TNamed.h:50
Basic string class.
Definition TString.h:138
bool isFunctionFlatInBins(const RooAbsReal &function, RooAbsRealLValue &obs, std::span< const double > boundaries, double relTol=1e-9)
Check that function is constant (flat) inside each bin defined by the sorted boundaries when scanning...