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 "RooTrace.h"
54#include "RooFormula.h"
55#include "RooAbsRealLValue.h"
56#include "RooAbsBinning.h"
57#include "RooCurve.h"
58#include "RooHelpers.h"
59
60#ifdef ROOFIT_LEGACY_EVAL_BACKEND
61#include "RooNLLVar.h"
62#include "RooChi2Var.h"
63#endif
64
65using std::ostream, std::istream, std::list;
66
67
69
74
75////////////////////////////////////////////////////////////////////////////////
76/// Constructor with formula expression and list of input variables.
77/// \param[in] name Name of the formula.
78/// \param[in] title Title of the formula.
79/// \param[in] inFormula Expression to be evaluated.
80/// \param[in] dependents Variables that should be passed to the formula.
81/// \param[in] checkVariables Check that all variables from `dependents` are used in the expression.
82RooFormulaVar::RooFormulaVar(const char *name, const char *title, const char* inFormula, const RooArgList& dependents,
83 bool checkVariables) :
84 RooAbsReal(name,title),
85 _actualVars("actualVars","Variables used by formula expression",this),
86 _formExpr(inFormula)
87{
88 if (dependents.empty()) {
89 _value = traceEval(nullptr);
90 } else {
92 _formExpr = _formula->formulaString().c_str();
93 _actualVars.add(_formula->actualDependents());
94 }
95}
96
97
98
99////////////////////////////////////////////////////////////////////////////////
100/// Constructor with formula expression, title and list of input variables.
101/// \param[in] name Name of the formula.
102/// \param[in] title Formula expression. Will also be used as the title.
103/// \param[in] dependents Variables that should be passed to the formula.
104/// \param[in] checkVariables Check that all variables from `dependents` are used in the expression.
105RooFormulaVar::RooFormulaVar(const char *name, const char *title, const RooArgList& dependents,
106 bool checkVariables) :
107 RooAbsReal(name,title),
108 _actualVars("actualVars","Variables used by formula expression",this),
109 _formExpr(title)
110{
111 if (dependents.empty()) {
112 _value = traceEval(nullptr);
113 } else {
115 _formExpr = _formula->formulaString().c_str();
116 _actualVars.add(_formula->actualDependents());
117 }
118}
119
120
121
122////////////////////////////////////////////////////////////////////////////////
123/// Copy constructor
124
127 _actualVars("actualVars",this,other._actualVars),
128 _formExpr(other._formExpr)
129{
130 for (auto const &item : other._binnings) {
131 _binnings[item.first] = std::unique_ptr<RooAbsBinning>{item.second->clone()};
132 }
133 if (other._formula && other._formula->ok()) {
134 _formula = new RooFormula(*other._formula);
135 _formExpr = _formula->formulaString().c_str();
136 }
137}
138
139
140////////////////////////////////////////////////////////////////////////////////
141/// Return reference to internal RooFormula object.
142/// If it doesn't exist, create it on the fly.
144{
145 if (!_formula) {
146 // After being read from file, the formula object might not exist, yet:
148 const_cast<TString&>(_formExpr) = _formula->formulaString().c_str();
149 }
150
151 return *_formula;
152}
153
154
155bool RooFormulaVar::ok() const { return getFormula().ok() ; }
156
157
158void RooFormulaVar::dumpFormula() { getFormula().printMultiline(std::cout, 0) ; }
159
160
161////////////////////////////////////////////////////////////////////////////////
162/// Calculate current value of object from internal formula
163
165{
166 return getFormula().eval(_actualVars.nset());
167}
168
169
171{
172 getFormula().doEval(_actualVars, ctx);
173}
174
175
176////////////////////////////////////////////////////////////////////////////////
177/// Propagate server change information to embedded RooFormula object
178
186
187
188
189////////////////////////////////////////////////////////////////////////////////
190/// Print info about this object to the specified stream.
191
192void RooFormulaVar::printMultiline(ostream& os, Int_t contents, bool verbose, TString indent) const
193{
194 RooAbsReal::printMultiline(os,contents,verbose,indent);
195 if(verbose) {
196 indent.Append(" ");
197 os << indent;
198 getFormula().printMultiline(os,contents,verbose,indent);
199 }
200}
201
202
203
204////////////////////////////////////////////////////////////////////////////////
205/// Add formula expression as meta argument in printing interface
206
207void RooFormulaVar::printMetaArgs(ostream& os) const
208{
209 os << "formula=\"" << _formExpr << "\" " ;
210}
211
212
213
214
215////////////////////////////////////////////////////////////////////////////////
216/// Read object contents from given stream
217
218bool RooFormulaVar::readFromStream(istream& /*is*/, bool /*compact*/, bool /*verbose*/)
219{
220 coutE(InputArguments) << "RooFormulaVar::readFromStream(" << GetName() << "): can't read" << std::endl ;
221 return true ;
222}
223
224
225
226////////////////////////////////////////////////////////////////////////////////
227/// Write object contents to given stream
228
229void RooFormulaVar::writeToStream(ostream& os, bool compact) const
230{
231 if (compact) {
232 std::cout << getVal() << std::endl ;
233 } else {
234 os << GetTitle() ;
235 }
236}
237
238////////////////////////////////////////////////////////////////////////////////
239/// Declare that this function is piecewise constant (flat) within the bins of
240/// the given `binning` of the observable `obs`, which must be one of the formula
241/// variables. The method can be called several times to set a binning for more
242/// than one observable. See RooGenericPdf::setBinning() for details.
243
245{
246 // Match the observable to a formula variable by name, so that a same-named
247 // stand-in for the actual server is accepted too.
248 const int idx = _actualVars.index(obs.GetName());
249 if (idx < 0) {
250 coutE(InputArguments) << "RooFormulaVar::setBinning(" << GetName() << ") the observable " << obs.GetName()
251 << " is not one of the formula variables of this function, nothing done." << std::endl;
252 return;
253 }
254
255 if (checkFlatness) {
256 // Sample the function by varying the actual formula variable (the server),
257 // which may be a different object than `obs` if `obs` is just a same-named
258 // stand-in: the function's value depends on the server, not on `obs`.
259 if (auto *serverObs = dynamic_cast<RooAbsRealLValue *>(_actualVars.at(idx))) {
260 std::span<const double> boundaries{binning.array(), static_cast<std::size_t>(binning.numBoundaries())};
261 if (!RooHelpers::isFunctionFlatInBins(*this, *serverObs, boundaries)) {
262 coutE(InputArguments) << "RooFormulaVar::setBinning(" << GetName() << ") the expression \"" << _formExpr
263 << "\" is not flat within the given bins of " << obs.GetName()
264 << ". The binning is not set. Pass checkFlatness=false to override this check."
265 << std::endl;
266 return;
267 }
268 }
269 }
270
271 // Key the binning by the observable's index in _actualVars (not its name), so
272 // that it survives a renaming of the variable or a server redirection.
273 _binnings[idx] = std::unique_ptr<RooAbsBinning>{binning.clone()};
274}
275
276////////////////////////////////////////////////////////////////////////////////
277/// Remove a binning previously declared with setBinning() for observable `obs`,
278/// reverting to the generic numeric integrator for it. Returns true if a binning
279/// was removed, false if none was set for `obs`.
280
282{
283 return _binnings.erase(_actualVars.index(obs.GetName())) > 0;
284}
285
286////////////////////////////////////////////////////////////////////////////////
287/// Return true if a binning was set with setBinning() for every
288/// observable in the integration set `obs`.
289
291{
292 if (obs.empty() || _binnings.empty()) {
293 return false;
294 }
295 for (RooAbsArg *o : obs) {
296 const int idx = _actualVars.index(o->GetName());
297 // Observables that are not formula variables of this function are ones we
298 // do not depend on: the function is constant (hence trivially binned) in
299 // them, so they must be ignored here. This matches the convention that
300 // composite functions like RooProduct rely on, where each component's
301 // isBinnedDistribution() is queried with the full observable set.
302 if (idx < 0) {
303 continue;
304 }
305 if (_binnings.find(idx) == _binnings.end()) {
306 return false;
307 }
308 }
309 return true;
310}
311
312////////////////////////////////////////////////////////////////////////////////
313/// Return the boundaries of the binning set with setBinning() that fall
314/// within [xlo, xhi]. If no binning was set for this observable, forward the bin
315/// boundaries from the server that defines the observable obs.
316
317std::list<double>* RooFormulaVar::binBoundaries(RooAbsRealLValue& obs, double xlo, double xhi) const
318{
319 auto found = _binnings.find(_actualVars.index(obs.GetName()));
320 if (found != _binnings.end()) {
321 const RooAbsBinning &binning = *found->second;
322 return RooHelpers::binBoundariesInRange({binning.array(), static_cast<std::size_t>(binning.numBoundaries())}, xlo,
323 xhi);
324 }
325
326 for (const auto par : _actualVars) {
327 auto func = static_cast<const RooAbsReal*>(par);
328 list<double>* binb = nullptr;
329
330 if (func && (binb = func->binBoundaries(obs,xlo,xhi)) ) {
331 return binb;
332 }
333 }
334
335 return nullptr;
336}
337
338////////////////////////////////////////////////////////////////////////////////
339/// Return sampling hints that draw the piecewise-flat shape exactly if a binning
340/// was set for this observable. Otherwise, forward the plot sampling hint from
341/// the server that defines the observable obs.
342
343std::list<double>* RooFormulaVar::plotSamplingHint(RooAbsRealLValue& obs, double xlo, double xhi) const
344{
345 auto found = _binnings.find(_actualVars.index(obs.GetName()));
346 if (found != _binnings.end()) {
347 const RooAbsBinning &binning = *found->second;
349 {binning.array(), static_cast<std::size_t>(binning.numBoundaries())}, xlo, xhi);
350 }
351
352 for (const auto par : _actualVars) {
353 auto func = dynamic_cast<const RooAbsReal*>(par);
354 list<double>* hint = nullptr;
355
356 if (func && (hint = func->plotSamplingHint(obs,xlo,xhi)) ) {
357 return hint;
358 }
359 }
360
361 return nullptr;
362}
363
364
365
366////////////////////////////////////////////////////////////////////////////////
367/// Return the default error level for MINUIT error analysis
368/// If the formula contains one or more RooNLLVars and
369/// no RooChi2Vars, return the defaultErrorLevel() of
370/// RooNLLVar. If the addition contains one ore more RooChi2Vars
371/// and no RooNLLVars, return the defaultErrorLevel() of
372/// RooChi2Var. If the addition contains neither or both
373/// issue a warning message and return a value of 1
374
376{
377 RooAbsReal* nllArg(nullptr) ;
378 RooAbsReal* chi2Arg(nullptr) ;
379
380#ifdef ROOFIT_LEGACY_EVAL_BACKEND
381 for (const auto arg : _actualVars) {
382 if (dynamic_cast<RooNLLVar*>(arg)) {
383 nllArg = static_cast<RooAbsReal*>(arg) ;
384 }
385 if (dynamic_cast<RooChi2Var*>(arg)) {
386 chi2Arg = static_cast<RooAbsReal*>(arg) ;
387 }
388 }
389#endif
390
391 if (nllArg && !chi2Arg) {
392 coutI(Minimization) << "RooFormulaVar::defaultErrorLevel(" << GetName()
393 << ") Formula contains a RooNLLVar, using its error level" << std::endl ;
394 return nllArg->defaultErrorLevel() ;
395 } else if (chi2Arg && !nllArg) {
396 coutI(Minimization) << "RooFormulaVar::defaultErrorLevel(" << GetName()
397 << ") Formula contains a RooChi2Var, using its error level" << std::endl ;
398 return chi2Arg->defaultErrorLevel() ;
399 } else if (!nllArg && !chi2Arg) {
400 coutI(Minimization) << "RooFormulaVar::defaultErrorLevel(" << GetName() << ") WARNING: "
401 << "Formula contains neither RooNLLVar nor RooChi2Var server, using default level of 1.0" << std::endl ;
402 } else {
403 coutI(Minimization) << "RooFormulaVar::defaultErrorLevel(" << GetName() << ") WARNING: "
404 << "Formula contains BOTH RooNLLVar and RooChi2Var server, using default level of 1.0" << std::endl ;
405 }
406
407 return 1.0 ;
408}
409
411{
412 return getFormula().getTFormula()->GetUniqueFuncName().Data();
413}
414
415std::unique_ptr<RooAbsArg>
417{
418 // Some users exploit unnormalized RooAbsPdfs as inputs for RooFormulaVars,
419 // relying on what the pdf returns from RooAbsPdf::evaluate(). This is in
420 // principle not allowed because every pdf needs to be evaluated with a
421 // normalization set, but it's so common in user code that we need to
422 // support it. To make this work, we need to make sure that the no
423 // normalization over non-dependents is happening at this point, reducing
424 // the normalization set to the subset of actual dependents.
425 // See also the "PdfAsFunctionInFormulaVar" test in testRooAbsPdf.
428 auto newArg = std::unique_ptr<RooAbsArg>{static_cast<RooAbsArg *>(Clone())};
431 return newArg;
432}
#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:539
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.
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...
std::list< double > * binBoundariesInRange(std::span< const double > boundaries, double xlo, double xhi)
Return a newly allocated list with the subset of boundaries that lies strictly inside [xlo,...