Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RooHelpers.cxx
Go to the documentation of this file.
1// Author: Stephan Hageboeck, CERN 01/2019
2
3/*****************************************************************************
4 * RooFit
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-2019, 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#include <RooHelpers.h>
18
19#include <RooAbsCategory.h>
20#include <RooAbsData.h>
21#include <RooAbsPdf.h>
22#include <RooAbsRealLValue.h>
23#include <RooArgList.h>
24#include <RooDataHist.h>
25#include <RooDataSet.h>
26#include <RooSimultaneous.h>
27#include <RooProdPdf.h>
28#include <RooRealSumPdf.h>
29
30#include <ROOT/StringUtils.hxx>
31#include <TClass.h>
32
33#include <unordered_map>
34
35namespace RooHelpers {
36
38 unsigned int extraTopics, unsigned int removeTopics, bool overrideExternalLevel) {
39 auto& msg = RooMsgService::instance();
40 fOldKillBelow = msg.globalKillBelow();
41 if (overrideExternalLevel) msg.setGlobalKillBelow(lvl);
42
43 for (int i = 0; i < msg.numStreams(); ++i) {
44 fOldConf.push_back(msg.getStream(i));
45 if (overrideExternalLevel) msg.getStream(i).minLevel = lvl;
46 msg.getStream(i).removeTopic(static_cast<RooFit::MsgTopic>(removeTopics));
47 msg.setStreamStatus(i, true);
48 }
49
50 if (extraTopics != 0) {
51 fExtraStream = msg.addStream(lvl);
52 msg.getStream(fExtraStream).addTopic(static_cast<RooFit::MsgTopic>(extraTopics));
53 }
54}
55
57 auto& msg = RooMsgService::instance();
58 msg.setGlobalKillBelow(fOldKillBelow);
59 for (int i=0; i < msg.numStreams(); ++i) {
60 if (i < static_cast<int>(fOldConf.size()))
61 msg.getStream(i) = fOldConf[i];
62 }
63
64 if (fExtraStream > 0)
65 msg.deleteStream(fExtraStream);
66}
67
68
69/// Hijack all messages with given level and topics while this object is alive.
70/// \param[in] level Minimum level to hijack. Higher levels also get captured.
71/// \param[in] topics Topics to hijack. Use `|` to combine different topics, and cast to `RooFit::MsgTopic` if necessary.
72/// \param[in] objectName Only hijack messages from an object with the given name. Defaults to any object.
74{
75 auto& msg = RooMsgService::instance();
76 _oldKillBelow = msg.globalKillBelow();
77 if (_oldKillBelow > level)
78 msg.setGlobalKillBelow(level);
79
80 std::vector<RooMsgService::StreamConfig> tmpStreams;
81 for (int i = 0; i < msg.numStreams(); ++i) {
82 _oldConf.push_back(msg.getStream(i));
83 if (msg.getStream(i).match(level, topics, static_cast<RooAbsArg*>(nullptr))) {
84 tmpStreams.push_back(msg.getStream(i));
85 msg.setStreamStatus(i, false);
86 }
87 }
88
89 _thisStream = msg.addStream(level,
90 RooFit::Topic(topics),
92 objectName ? RooFit::ObjectName(objectName) : RooCmdArg());
93
94 for (RooMsgService::StreamConfig& st : tmpStreams) {
95 msg.addStream(st.minLevel,
96 RooFit::Topic(st.topic),
98 RooFit::ObjectName(st.objectName.c_str()),
99 RooFit::ClassName(st.className.c_str()),
100 RooFit::BaseClassName(st.baseClassName.c_str()),
101 RooFit::TagName(st.tagName.c_str()));
102 }
103}
104
105/// Deregister the hijacked stream and restore the stream state of all previous streams.
107 auto& msg = RooMsgService::instance();
108 msg.setGlobalKillBelow(_oldKillBelow);
109 for (unsigned int i = 0; i < _oldConf.size(); ++i) {
110 msg.getStream(i) = _oldConf[i];
111 }
112
113 while (_thisStream < msg.numStreams()) {
114 msg.deleteStream(_thisStream);
115 }
116}
117
118
119/// \param[in] callingClass Class that's calling. Needed to include name and type name of the class in error message.
120/// \param[in] pars List of all parameters to be checked.
121/// \param[in] min Minimum of allowed range. `min` itself counts as disallowed.
122/// \param[in] max Maximum of allowed range. `max` itself counts as disallowed.
123/// \param[in] limitsInAllowedRange If true, the limits passed as parameters are part of the allowed range.
124/// \param[in] extraMessage Message that should be appended to the warning.
125void checkRangeOfParameters(const RooAbsReal* callingClass, std::initializer_list<const RooAbsReal*> pars,
126 double min, double max, bool limitsInAllowedRange, std::string const& extraMessage) {
127 const char openBr = limitsInAllowedRange ? '[' : '(';
128 const char closeBr = limitsInAllowedRange ? ']' : ')';
129
130 for (auto parameter : pars) {
131 auto par = dynamic_cast<const RooAbsRealLValue*>(parameter);
132 if (par && (
133 (par->getMin() < min || par->getMax() > max)
134 || (!limitsInAllowedRange && (par->getMin() == min || par->getMax() == max)) )) {
135 std::stringstream rangeMsg;
136 rangeMsg << openBr;
137 if (min > -std::numeric_limits<double>::max())
138 rangeMsg << min << ", ";
139 else
140 rangeMsg << "-inf, ";
141
142 if (max < std::numeric_limits<double>::max())
143 rangeMsg << max << closeBr;
144 else
145 rangeMsg << "inf" << closeBr;
146
147 oocoutW(callingClass, InputArguments) << "The parameter '" << par->GetName() << "' with range [" << par->getMin("") << ", "
148 << par->getMax() << "] of the " << callingClass->ClassName() << " '" << callingClass->GetName()
149 << "' exceeds the safe range of " << rangeMsg.str() << ". Advise to limit its range."
150 << (!extraMessage.empty() ? "\n" : "") << extraMessage << std::endl;
151 }
152 }
153}
154
155
156namespace {
157 std::pair<double, double> getBinningInterval(RooAbsBinning const& binning) {
158 if (!binning.isParameterized()) {
159 return {binning.lowBound(), binning.highBound()};
160 } else {
161 return {binning.lowBoundFunc()->getVal(), binning.highBoundFunc()->getVal()};
162 }
163 }
164} // namespace
165
166
167/// Get the lower and upper bound of parameter range if arg can be casted to RooAbsRealLValue.
168/// If no range with rangeName is defined for the argument, this will check if a binning of the
169/// same name exists and return the interval covered by the binning.
170/// Returns `{-infinity, infinity}` if agument can't be casted to RooAbsRealLValue* or if no
171/// range or binning with the requested name exists.
172/// \param[in] arg RooAbsArg for which to get the range.
173/// \param[in] rangeName The name of the range.
174std::pair<double, double> getRangeOrBinningInterval(RooAbsArg const* arg, const char* rangeName) {
175 auto rlv = dynamic_cast<RooAbsRealLValue const*>(arg);
176 if (rlv) {
177 if (rangeName && rlv->hasRange(rangeName)) {
178 return {rlv->getMin(rangeName), rlv->getMax(rangeName)};
179 } else if (auto binning = rlv->getBinningPtr(rangeName)) {
180 return getBinningInterval(*binning);
181 }
182 }
183 return {-std::numeric_limits<double>::infinity(), +std::numeric_limits<double>::infinity()};
184}
185
186
187/// Check if there is any overlap when a list of ranges is applied to a set of observables.
188/// \param[in] observables The observables to check for overlap
189/// \param[in] rangeNames The names of the ranges.
190bool checkIfRangesOverlap(RooArgSet const& observables, std::vector<std::string> const& rangeNames)
191{
192 // cache the range limits in a flat vector
193 std::vector<std::pair<double,double>> limits;
194 limits.reserve(rangeNames.size() * observables.size());
195
196 for (auto const& range : rangeNames) {
197 for (auto const& obs : observables) {
198 if(dynamic_cast<RooAbsCategory const*>(obs)) {
199 // Nothing to be done for category observables
200 } else if(auto * rlv = dynamic_cast<RooAbsRealLValue const*>(obs)) {
201 limits.emplace_back(rlv->getMin(range.c_str()), rlv->getMax(range.c_str()));
202 } else {
203 throw std::logic_error("Classes that represent observables are expected to inherit from RooAbsRealLValue or RooAbsCategory!");
204 }
205 }
206 }
207
208 auto nRanges = rangeNames.size();
209 auto nObs = limits.size() / nRanges; // number of observables that are not categories
210
211 // loop over pairs of ranges
212 for(size_t ir1 = 0; ir1 < nRanges; ++ir1) {
213 for(size_t ir2 = ir1 + 1; ir2 < nRanges; ++ir2) {
214
215 // Loop over observables. If all observables have overlapping limits for
216 // these ranges, the hypercubes defining the range are overlapping and we
217 // can return `true`.
218 size_t overlaps = 0;
219 for(size_t io1 = 0; io1 < nObs; ++io1) {
220 auto r1 = limits[ir1 * nObs + io1];
221 auto r2 = limits[ir2 * nObs + io1];
222 overlaps += (r1.second > r2.first && r1.first < r2.second)
223 || (r2.second > r1.first && r2.first < r1.second);
224 }
225 if(overlaps == nObs) return true;
226 }
227 }
228
229 return false;
230}
231
232
233/// Create a string with all sorted names of RooArgSet elements separated by colons.
234/// \param[in] argSet The input RooArgSet.
235std::string getColonSeparatedNameString(RooArgSet const& argSet) {
236
237 RooArgList tmp(argSet);
238 tmp.sort();
239
240 std::string content;
241 for(auto const& arg : tmp) {
242 content += arg->GetName();
243 content += ":";
244 }
245 if(!content.empty()) {
246 content.pop_back();
247 }
248 return content;
249}
250
251
252/// Construct a RooArgSet of objects in a RooArgSet whose names match to those
253/// in the names string.
254/// \param[in] argSet The input RooArgSet.
255/// \param[in] names The names of the objects to select in a colon-separated string.
256RooArgSet selectFromArgSet(RooArgSet const& argSet, std::string const& names) {
258 for(auto const& name : ROOT::Split(names, ":")) {
259 if(auto arg = argSet.find(name.c_str())) output.add(*arg);
260 }
261 return output;
262}
263
264
265std::string getRangeNameForSimComponent(std::string const& rangeName, bool splitRange, std::string const& catName)
266{
267 if (splitRange && !rangeName.empty()) {
268 std::string out;
269 auto tokens = ROOT::Split(rangeName, ",");
270 for(std::string const& token : tokens) {
271 out += token + "_" + catName + ",";
272 }
273 out.pop_back(); // to remove the last comma
274 return out;
275 }
276
277 return rangeName;
278}
279
281{
282 if (pdf.getAttribute("BinnedLikelihood") && pdf.IsA()->InheritsFrom(RooRealSumPdf::Class())) {
283 // Simplest case: top-level of component is a RooRealSumPdf
284 return {&pdf, true};
285 } else if (pdf.IsA()->InheritsFrom(RooProdPdf::Class())) {
286 // Default case: top-level pdf is a product of RooRealSumPdf and other pdfs
287 for (RooAbsArg *component : static_cast<RooProdPdf &>(pdf).pdfList()) {
288 if (component->getAttribute("BinnedLikelihood") && component->IsA()->InheritsFrom(RooRealSumPdf::Class())) {
289 return {static_cast<RooAbsPdf *>(component), true};
290 }
291 if (component->getAttribute("MAIN_MEASUREMENT")) {
292 // not really a binned pdf, but this prevents a (potentially) long list of subsidiary measurements to be passed to the slave calculator
293 return {static_cast<RooAbsPdf *>(component), false};
294 }
295 }
296 }
297 return {nullptr, false};
298}
299
300/// Get the topologically-sorted list of all nodes in the computation graph.
302{
303 // Get the set of nodes in the computation graph. Do the detour via
304 // RooArgList to avoid deduplication done after adding each element.
305 RooArgList serverList;
306 func.treeNodeServerList(&serverList, nullptr, true, true, false, true);
307 // If we fill the servers in reverse order, they are approximately in
308 // topological order so we save a bit of work in sortTopologically().
309 out.add(serverList.rbegin(), serverList.rend(), /*silent=*/true);
310 // Sort nodes topologically: the servers of any node will be before that
311 // node in the collection.
312 out.sortTopologically();
313}
314
315namespace Detail {
316
317namespace {
318
319using ToCloneList = std::vector<RooAbsArg const *>;
320using ToCloneMap = std::unordered_map<TNamed const *, RooAbsArg const *>;
321
322// Add clones of servers of given argument to end of list
323void addServerClonesToList(const RooAbsArg &var, ToCloneList &outlist, ToCloneMap &outmap, bool deepCopy,
324 RooArgSet const *observables)
325{
326 if (outmap.find(var.namePtr()) != outmap.end()) {
327 return;
328 }
329
330 if (observables && var.isFundamental() && !observables->find(var)) {
331 return;
332 }
333
334 outmap[var.namePtr()] = &var;
335 outlist.push_back(&var);
336
337 if (deepCopy) {
338 for (const auto server : var.servers()) {
339 addServerClonesToList(*server, outlist, outmap, deepCopy, observables);
340 }
341 }
342}
343
344} // namespace
345
346/// Implementation of RooAbsCollection::snapshot() with some extra parameters.
347/// to be used in other RooHelpers functions.
348/// param[in] input The input collection.
349/// param[in] output The output collection.
350/// param[in] deepCopy If the whole computation graph should be cloned recursivly.
351/// param[in] observables If this is not a nullptr, only the fundamental
352/// variables that are in observables are deep cloned.
353bool snapshotImpl(RooAbsCollection const &input, RooAbsCollection &output, bool deepCopy, RooArgSet const *observables)
354{
355 // Figure out what needs to be cloned
356 ToCloneList toCloneList;
357 ToCloneMap toCloneMap;
358 for (RooAbsArg *orig : input) {
359 addServerClonesToList(*orig, toCloneList, toCloneMap, deepCopy, observables);
360 }
361
362 // Actually do the cloning
363 output.reserve(toCloneList.size());
364 for (RooAbsArg const *arg : toCloneList) {
365 std::unique_ptr<RooAbsArg> serverClone{static_cast<RooAbsArg *>(arg->Clone())};
366 serverClone->setAttribute("SnapShot_ExtRefClone");
367 output.addOwned(std::move(serverClone));
368 }
369
370 // Redirect all server connections to internal list members
371 for (RooAbsArg *var : output) {
372 var->redirectServers(output, deepCopy && !observables);
373 }
374
375 return false;
376}
377
379{
380 // Clone tree using snapshot
381 RooArgSet clonedNodes;
382 snapshotImpl(RooArgSet(arg), clonedNodes, true, observables);
383
384 // Find the head node in the cloneSet
385 RooAbsArg *head = clonedNodes.find(arg);
386 assert(head);
387
388 // We better to release the ownership before removing the "head". Otherwise,
389 // "head" might also be deleted as the clonedNodes collection owns it.
390 // (Actually this does not happen because even an owning collection doesn't
391 // delete the element when removed by pointer lookup, but it's better not to
392 // rely on this unexpected fact).
393 clonedNodes.releaseOwnership();
394
395 // Remove the head node from the cloneSet
396 // To release it from the set ownership
397 clonedNodes.remove(*head);
398
399 // Add the set as owned component of the head
400 head->addOwnedComponents(std::move(clonedNodes));
401
402 return head;
403}
404
405} // namespace Detail
406
407}
#define oocoutW(o, a)
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void input
char name[80]
Definition TGX11.cxx:110
RooAbsArg is the common abstract base class for objects that represent a value and a "shape" in RooFi...
Definition RooAbsArg.h:78
const TNamed * namePtr() const
De-duplicated pointer to this object's name.
Definition RooAbsArg.h:572
bool addOwnedComponents(const RooAbsCollection &comps)
Take ownership of the contents of 'comps'.
bool redirectServers(const RooAbsCollection &newServerList, bool mustReplaceAll=false, bool nameChange=false, bool isRecursionStep=false)
Replace all direct servers of this object with the new servers in newServerList.
bool getAttribute(const Text_t *name) const
Check if a named attribute is set. By default, all attributes are unset.
void setAttribute(const Text_t *name, bool value=true)
Set (default) or clear a named boolean attribute of this object.
virtual bool isFundamental() const
Is this object a fundamental type that can be added to a dataset? Fundamental-type subclasses overrid...
Definition RooAbsArg.h:254
void treeNodeServerList(RooAbsCollection *list, const RooAbsArg *arg=nullptr, bool doBranch=true, bool doLeaf=true, bool valueOnly=false, bool recurseNonDerived=false) const
Fill supplied list with nodes of the arg tree, following all server links, starting with ourself as t...
RooAbsBinning is the abstract base class for RooRealVar binning definitions.
virtual bool isParameterized() const
Interface function.
virtual double highBound() const =0
virtual double lowBound() const =0
virtual RooAbsReal * highBoundFunc() const
Return pointer to RooAbsReal parameterized upper bound, if any.
virtual RooAbsReal * lowBoundFunc() const
Return pointer to RooAbsReal parameterized lower bound, if any.
A space to attach TBranches.
RooAbsCollection is an abstract container object that can hold multiple RooAbsArg objects.
virtual bool remove(const RooAbsArg &var, bool silent=false, bool matchByNameOnly=false)
Remove the specified argument from our list.
void sortTopologically()
Sort collection topologically: the servers of any RooAbsArg will be before that RooAbsArg in the coll...
Storage_t::const_reverse_iterator rend() const
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
Storage_t::const_reverse_iterator rbegin() const
Storage_t::size_type size() const
void sort(bool reverse=false)
Sort collection using std::sort and name comparison.
RooAbsArg * find(const char *name) const
Find object with given name in list.
TClass * IsA() const override
Definition RooAbsPdf.h:400
RooAbsRealLValue is the common abstract base class for objects that represent a real value that may a...
virtual double getMin(const char *name=nullptr) const
Get minimum of currently defined range.
RooAbsReal is the common abstract base class for objects that represent a real value and implements f...
Definition RooAbsReal.h:61
double getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
Definition RooAbsReal.h:105
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
RooCmdArg is a named container for two doubles, two integers two object points and three string point...
Definition RooCmdArg.h:26
RooFit::MsgLevel _oldKillBelow
Definition RooHelpers.h:88
HijackMessageStream(RooFit::MsgLevel level, RooFit::MsgTopic topics, const char *objectName=nullptr)
Hijack all messages with given level and topics while this object is alive.
std::vector< RooMsgService::StreamConfig > _oldConf
Definition RooHelpers.h:89
~HijackMessageStream()
Deregister the hijacked stream and restore the stream state of all previous streams.
RooFit::MsgLevel fOldKillBelow
Definition RooHelpers.h:57
LocalChangeMsgLevel(RooFit::MsgLevel lvl=RooFit::DEBUG, unsigned int extraTopics=0u, unsigned int removeTopics=0u, bool overrideExternalLevel=true)
Change message level (and topics) while this object is alive, reset when it goes out of scope.
std::vector< RooMsgService::StreamConfig > fOldConf
Definition RooHelpers.h:58
static RooMsgService & instance()
Return reference to singleton instance.
RooProdPdf is an efficient implementation of a product of PDFs of the form.
Definition RooProdPdf.h:33
static TClass * Class()
static TClass * Class()
Bool_t InheritsFrom(const char *cl) const override
Return kTRUE if this class inherits from a class with name "classname".
Definition TClass.cxx:4874
const char * GetName() const override
Returns name of object.
Definition TNamed.h:47
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:207
RooCmdArg ClassName(const char *name)
RooCmdArg OutputStream(std::ostream &os)
RooCmdArg Topic(Int_t topic)
RooCmdArg TagName(const char *name)
RooCmdArg BaseClassName(const char *name)
RooCmdArg ObjectName(const char *name)
std::vector< std::string > Split(std::string_view str, std::string_view delims, bool skipEmpty=false)
Splits a string at each character in delims.
MsgLevel
Verbosity level for RooMsgService::StreamConfig in RooMsgService.
MsgTopic
Topics for a RooMsgService::StreamConfig in RooMsgService.
RooAbsArg * cloneTreeWithSameParametersImpl(RooAbsArg const &arg, RooArgSet const *observables)
bool snapshotImpl(RooAbsCollection const &input, RooAbsCollection &output, bool deepCopy, RooArgSet const *observables)
Implementation of RooAbsCollection::snapshot() with some extra parameters.
void checkRangeOfParameters(const RooAbsReal *callingClass, std::initializer_list< const RooAbsReal * > pars, double min=-std::numeric_limits< double >::max(), double max=std::numeric_limits< double >::max(), bool limitsInAllowedRange=false, std::string const &extraMessage="")
Check if the parameters have a range, and warn if the range extends below / above the set limits.
BinnedLOutput getBinnedL(RooAbsPdf &pdf)
std::string getRangeNameForSimComponent(std::string const &rangeName, bool splitRange, std::string const &catName)
RooArgSet selectFromArgSet(RooArgSet const &, std::string const &names)
Construct a RooArgSet of objects in a RooArgSet whose names match to those in the names string.
void getSortedComputationGraph(RooAbsArg const &func, RooArgSet &out)
Get the topologically-sorted list of all nodes in the computation graph.
bool checkIfRangesOverlap(RooArgSet const &observables, std::vector< std::string > const &rangeNames)
Check if there is any overlap when a list of ranges is applied to a set of observables.
std::pair< double, double > getRangeOrBinningInterval(RooAbsArg const *arg, const char *rangeName)
Get the lower and upper bound of parameter range if arg can be casted to RooAbsRealLValue.
std::string getColonSeparatedNameString(RooArgSet const &argSet)
Create a string with all sorted names of RooArgSet elements separated by colons.
static void output()