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
28#include <ROOT/StringUtils.hxx>
29#include <TClass.h>
30
31namespace RooHelpers {
32
34 unsigned int extraTopics, unsigned int removeTopics, bool overrideExternalLevel) {
35 auto& msg = RooMsgService::instance();
36 fOldKillBelow = msg.globalKillBelow();
37 if (overrideExternalLevel) msg.setGlobalKillBelow(lvl);
38
39 for (int i = 0; i < msg.numStreams(); ++i) {
40 fOldConf.push_back(msg.getStream(i));
41 if (overrideExternalLevel) msg.getStream(i).minLevel = lvl;
42 msg.getStream(i).removeTopic(static_cast<RooFit::MsgTopic>(removeTopics));
43 msg.setStreamStatus(i, true);
44 }
45
46 if (extraTopics != 0) {
47 fExtraStream = msg.addStream(lvl);
48 msg.getStream(fExtraStream).addTopic(static_cast<RooFit::MsgTopic>(extraTopics));
49 }
50}
51
53 auto& msg = RooMsgService::instance();
54 msg.setGlobalKillBelow(fOldKillBelow);
55 for (int i=0; i < msg.numStreams(); ++i) {
56 if (i < static_cast<int>(fOldConf.size()))
57 msg.getStream(i) = fOldConf[i];
58 }
59
60 if (fExtraStream > 0)
61 msg.deleteStream(fExtraStream);
62}
63
64
65/// Hijack all messages with given level and topics while this object is alive.
66/// \param[in] level Minimum level to hijack. Higher levels also get captured.
67/// \param[in] topics Topics to hijack. Use `|` to combine different topics, and cast to `RooFit::MsgTopic` if necessary.
68/// \param[in] objectName Only hijack messages from an object with the given name. Defaults to any object.
70{
71 auto& msg = RooMsgService::instance();
72 _oldKillBelow = msg.globalKillBelow();
73 if (_oldKillBelow > level)
74 msg.setGlobalKillBelow(level);
75
76 std::vector<RooMsgService::StreamConfig> tmpStreams;
77 for (int i = 0; i < msg.numStreams(); ++i) {
78 _oldConf.push_back(msg.getStream(i));
79 if (msg.getStream(i).match(level, topics, static_cast<RooAbsArg*>(nullptr))) {
80 tmpStreams.push_back(msg.getStream(i));
81 msg.setStreamStatus(i, false);
82 }
83 }
84
85 _thisStream = msg.addStream(level,
86 RooFit::Topic(topics),
88 objectName ? RooFit::ObjectName(objectName) : RooCmdArg());
89
90 for (RooMsgService::StreamConfig& st : tmpStreams) {
91 msg.addStream(st.minLevel,
92 RooFit::Topic(st.topic),
94 RooFit::ObjectName(st.objectName.c_str()),
95 RooFit::ClassName(st.className.c_str()),
96 RooFit::BaseClassName(st.baseClassName.c_str()),
97 RooFit::TagName(st.tagName.c_str()));
98 }
99}
100
101/// Deregister the hijacked stream and restore the stream state of all previous streams.
103 auto& msg = RooMsgService::instance();
104 msg.setGlobalKillBelow(_oldKillBelow);
105 for (unsigned int i = 0; i < _oldConf.size(); ++i) {
106 msg.getStream(i) = _oldConf[i];
107 }
108
109 while (_thisStream < msg.numStreams()) {
110 msg.deleteStream(_thisStream);
111 }
112}
113
114
115/// \param[in] callingClass Class that's calling. Needed to include name and type name of the class in error message.
116/// \param[in] pars List of all parameters to be checked.
117/// \param[in] min Minimum of allowed range. `min` itself counts as disallowed.
118/// \param[in] max Maximum of allowed range. `max` itself counts as disallowed.
119/// \param[in] limitsInAllowedRange If true, the limits passed as parameters are part of the allowed range.
120/// \param[in] extraMessage Message that should be appended to the warning.
121void checkRangeOfParameters(const RooAbsReal* callingClass, std::initializer_list<const RooAbsReal*> pars,
122 double min, double max, bool limitsInAllowedRange, std::string const& extraMessage) {
123 const char openBr = limitsInAllowedRange ? '[' : '(';
124 const char closeBr = limitsInAllowedRange ? ']' : ')';
125
126 for (auto parameter : pars) {
127 auto par = dynamic_cast<const RooAbsRealLValue*>(parameter);
128 if (par && (
129 (par->getMin() < min || par->getMax() > max)
130 || (!limitsInAllowedRange && (par->getMin() == min || par->getMax() == max)) )) {
131 std::stringstream rangeMsg;
132 rangeMsg << openBr;
133 if (min > -std::numeric_limits<double>::max())
134 rangeMsg << min << ", ";
135 else
136 rangeMsg << "-inf, ";
137
138 if (max < std::numeric_limits<double>::max())
139 rangeMsg << max << closeBr;
140 else
141 rangeMsg << "inf" << closeBr;
142
143 oocoutW(callingClass, InputArguments) << "The parameter '" << par->GetName() << "' with range [" << par->getMin("") << ", "
144 << par->getMax() << "] of the " << callingClass->IsA()->GetName() << " '" << callingClass->GetName()
145 << "' exceeds the safe range of " << rangeMsg.str() << ". Advise to limit its range."
146 << (!extraMessage.empty() ? "\n" : "") << extraMessage << std::endl;
147 }
148 }
149}
150
151
152namespace {
153 std::pair<double, double> getBinningInterval(RooAbsBinning const& binning) {
154 if (!binning.isParameterized()) {
155 return {binning.lowBound(), binning.highBound()};
156 } else {
157 return {binning.lowBoundFunc()->getVal(), binning.highBoundFunc()->getVal()};
158 }
159 }
160} // namespace
161
162
163/// Get the lower and upper bound of parameter range if arg can be casted to RooAbsRealLValue.
164/// If no range with rangeName is defined for the argument, this will check if a binning of the
165/// same name exists and return the interval covered by the binning.
166/// Returns `{-infinity, infinity}` if agument can't be casted to RooAbsRealLValue* or if no
167/// range or binning with the requested name exists.
168/// \param[in] arg RooAbsArg for which to get the range.
169/// \param[in] rangeName The name of the range.
170std::pair<double, double> getRangeOrBinningInterval(RooAbsArg const* arg, const char* rangeName) {
171 auto rlv = dynamic_cast<RooAbsRealLValue const*>(arg);
172 if (rlv) {
173 if (rangeName && rlv->hasRange(rangeName)) {
174 return {rlv->getMin(rangeName), rlv->getMax(rangeName)};
175 } else if (auto binning = rlv->getBinningPtr(rangeName)) {
176 return getBinningInterval(*binning);
177 }
178 }
179 return {-std::numeric_limits<double>::infinity(), +std::numeric_limits<double>::infinity()};
180}
181
182
183/// Check if there is any overlap when a list of ranges is applied to a set of observables.
184/// \param[in] pdf the PDF
185/// \param[in] data RooAbsCollection with the observables to check for overlap.
186/// \param[in] rangeNames The names of the ranges.
187/// \param[in] splitRange If `true`, each component of a RooSimultaneous will
188/// be checked individually for overlaps, with the range
189/// names in that component suffixed by `_<cat_label>`.
190/// See the `SplitRange()` command argument of
191/// RooAbsPdf::fitTo()` to understand where this is used.
193 RooAbsData const& data,
194 std::vector<std::string> const& rangeNames,
195 bool splitRange)
196{
197 // If the PDF is a RooSimultaneous and the `splitRange` option is set, we
198 // have to check each component PDF with a different set of rangeNames, each
199 // suffixed by the category name.
200 if(splitRange && dynamic_cast<RooSimultaneous const*>(&pdf)) {
201 auto const& simPdf = static_cast<RooSimultaneous const&>(pdf);
202 bool hasOverlap = false;
203 std::vector<std::string> rangeNamesSplit;
204 for (const auto& catState : simPdf.indexCat()) {
205 const std::string& catName = catState.first;
206 for(std::string const& rangeName : rangeNames) {
207 rangeNamesSplit.emplace_back(rangeName + "_" + catName);
208 }
209 hasOverlap |= checkIfRangesOverlap(*simPdf.getPdf(catName.c_str()), data, rangeNamesSplit, false);
210
211 rangeNamesSplit.clear();
212 }
213
214 return hasOverlap;
215 }
216
217 auto observables = *pdf.getObservables(data);
218
219 auto getLimits = [&](RooAbsRealLValue const& rlv, const char* rangeName) {
220
221 // RooDataHistCase
222 if(dynamic_cast<RooDataHist const*>(&data)) {
223 if (auto binning = rlv.getBinningPtr(rangeName)) {
224 return getBinningInterval(*binning);
225 } else {
226 // default binning if range is not defined
227 return getBinningInterval(*rlv.getBinningPtr(nullptr));
228 }
229 }
230
231 // RooDataSet and other cases
232 if (rlv.hasRange(rangeName)) {
233 return std::pair<double, double>{rlv.getMin(rangeName), rlv.getMax(rangeName)};
234 }
235 // default range if range with given name is not defined
236 return std::pair<double, double>{rlv.getMin(), rlv.getMax()};
237 };
238
239 // cache the range limits in a flat vector
240 std::vector<std::pair<double,double>> limits;
241 limits.reserve(rangeNames.size() * observables.size());
242
243 for (auto const& range : rangeNames) {
244 for (auto const& obs : observables) {
245 if(dynamic_cast<RooAbsCategory const*>(obs)) {
246 // Nothing to be done for category observables
247 } else if(auto * rlv = dynamic_cast<RooAbsRealLValue const*>(obs)) {
248 limits.push_back(getLimits(*rlv, range.c_str()));
249 } else {
250 throw std::logic_error("Classes that represent observables are expected to inherit from RooAbsRealLValue or RooAbsCategory!");
251 }
252 }
253 }
254
255 auto nRanges = rangeNames.size();
256 auto nObs = limits.size() / nRanges; // number of observables that are not categories
257
258 // loop over pairs of ranges
259 for(size_t ir1 = 0; ir1 < nRanges; ++ir1) {
260 for(size_t ir2 = ir1 + 1; ir2 < nRanges; ++ir2) {
261
262 // Loop over observables. If all observables have overlapping limits for
263 // these ranges, the hypercubes defining the range are overlapping and we
264 // can return `true`.
265 size_t overlaps = 0;
266 for(size_t io1 = 0; io1 < nObs; ++io1) {
267 auto r1 = limits[ir1 * nObs + io1];
268 auto r2 = limits[ir2 * nObs + io1];
269 overlaps += (r1.second > r2.first && r1.first < r2.second)
270 || (r2.second > r1.first && r2.first < r1.second);
271 }
272 if(overlaps == nObs) return true;
273 }
274 }
275
276 return false;
277}
278
279
280/// Create a string with all sorted names of RooArgSet elements separated by colons.
281/// \param[in] arg argSet The input RooArgSet.
282std::string getColonSeparatedNameString(RooArgSet const& argSet) {
283
284 RooArgList tmp(argSet);
285 tmp.sort();
286
287 std::string content;
288 for(auto const& arg : tmp) {
289 content += arg->GetName();
290 content += ":";
291 }
292 if(!content.empty()) {
293 content.pop_back();
294 }
295 return content;
296}
297
298
299/// Construct a RooArgSet of objects in a RooArgSet whose names match to those
300/// in the names string.
301/// \param[in] arg argSet The input RooArgSet.
302/// \param[in] arg names The names of the objects to select in a colon-separated string.
303RooArgSet selectFromArgSet(RooArgSet const& argSet, std::string const& names) {
305 for(auto const& name : ROOT::Split(names, ":")) {
306 if(auto arg = argSet.find(name.c_str())) output.add(*arg);
307 }
308 return output;
309}
310
311
312}
#define oocoutW(o, a)
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:69
RooArgSet * getObservables(const RooArgSet &set, Bool_t valueOnly=kTRUE) const
Given a set of possible observables, return the observables that this PDF depends on.
Definition RooAbsArg.h:309
RooAbsBinning is the abstract base class for RooRealVar binning definitions.
virtual Bool_t isParameterized() const
Interface function.
virtual Double_t highBound() const =0
virtual RooAbsReal * highBoundFunc() const
Return pointer to RooAbsReal parameterized upper bound, if any.
virtual Double_t lowBound() const =0
virtual RooAbsReal * lowBoundFunc() const
Return pointer to RooAbsReal parameterized lower bound, if any.
RooAbsCategory is the base class for objects that represent a discrete value with a finite number of ...
void sort(Bool_t reverse=false)
Sort collection using std::sort and name comparison.
virtual Bool_t add(const RooAbsArg &var, Bool_t silent=kFALSE)
Add the specified argument to list.
RooAbsArg * find(const char *name) const
Find object with given name in list.
RooAbsData is the common abstract base class for binned and unbinned datasets.
Definition RooAbsData.h:82
RooAbsRealLValue is the common abstract base class for objects that represent a real value that may a...
virtual Double_t getMax(const char *name=0) const
Get maximum of currently defined range.
virtual Bool_t hasRange(const char *name) const
Check if variable has a binning with given name.
virtual const RooAbsBinning * getBinningPtr(const char *rangeName) const
virtual Double_t getMin(const char *name=0) const
Get miniminum of currently defined range.
RooAbsReal is the common abstract base class for objects that represent a real value and implements f...
Definition RooAbsReal.h:64
Double_t getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
Definition RooAbsReal.h:94
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:35
RooCmdArg is a named container for two doubles, two integers two object points and three string point...
Definition RooCmdArg.h:27
The RooDataHist is a container class to hold N-dimensional binned data.
Definition RooDataHist.h:45
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.
RooSimultaneous facilitates simultaneous fitting of multiple PDFs to subsets of a given dataset.
virtual const char * GetName() const
Returns name of object.
Definition TNamed.h:47
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.
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.
bool checkIfRangesOverlap(RooAbsPdf const &pdf, RooAbsData const &data, std::vector< std::string > const &rangeNames, bool splitRange)
Check if there is any overlap when a list of ranges is applied to a set of observables.
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.
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(int code)
Definition gifencode.c:226