Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RooFitImplHelpers.cxx
Go to the documentation of this file.
1/// \cond ROOFIT_INTERNAL
2
3/*
4 * Project: RooFit
5 * Author:
6 * Jonas Rembser, CERN 2023
7 *
8 * Copyright (c) 2023, CERN
9 *
10 * Redistribution and use in source and binary forms,
11 * with or without modification, are permitted according to the terms
12 * listed in LICENSE (http://roofit.sourceforge.net/license.txt)
13 */
14
15#include <RooFitImplHelpers.h>
16
17#include <RooAbsCategory.h>
18#include <RooAbsData.h>
19#include <RooAbsPdf.h>
20#include <RooAbsRealLValue.h>
21#include <RooArgList.h>
22#include <RooDataHist.h>
23#include <RooDataSet.h>
24#include <RooProdPdf.h>
25#include <RooRealSumPdf.h>
26#include <RooSimultaneous.h>
27
28#include <ROOT/StringUtils.hxx>
29#include <TClass.h>
30
31#include <unordered_map>
32
33namespace RooHelpers {
34
35namespace {
36std::pair<double, double> getBinningInterval(RooAbsBinning const &binning)
37{
38 if (!binning.isParameterized()) {
39 return {binning.lowBound(), binning.highBound()};
40 } else {
41 return {binning.lowBoundFunc()->getVal(), binning.highBoundFunc()->getVal()};
42 }
43}
44} // namespace
45
46/// Get the lower and upper bound of parameter range if arg can be casted to RooAbsRealLValue.
47/// If no range with rangeName is defined for the argument, this will check if a binning of the
48/// same name exists and return the interval covered by the binning.
49/// Returns `{-infinity, infinity}` if argument can't be casted to RooAbsRealLValue* or if no
50/// range or binning with the requested name exists.
51/// \param[in] arg RooAbsArg for which to get the range.
52/// \param[in] rangeName The name of the range.
53std::pair<double, double> getRangeOrBinningInterval(RooAbsArg const *arg, const char *rangeName)
54{
55 auto rlv = dynamic_cast<RooAbsRealLValue const *>(arg);
56 if (rlv) {
57 if (rangeName && rlv->hasRange(rangeName)) {
58 return {rlv->getMin(rangeName), rlv->getMax(rangeName)};
59 } else if (auto binning = rlv->getBinningPtr(rangeName)) {
60 return getBinningInterval(*binning);
61 }
62 }
63 return {-std::numeric_limits<double>::infinity(), +std::numeric_limits<double>::infinity()};
64}
65
66/// Check if there is any overlap when a list of ranges is applied to a set of observables.
67/// \param[in] observables The observables to check for overlap
68/// \param[in] rangeNames The names of the ranges.
69bool checkIfRangesOverlap(RooArgSet const &observables, std::vector<std::string> const &rangeNames)
70{
71 // cache the range limits in a flat vector
72 std::vector<std::pair<double, double>> limits;
73 limits.reserve(rangeNames.size() * observables.size());
74
75 for (auto const &range : rangeNames) {
76 for (auto const &obs : observables) {
77 if (dynamic_cast<RooAbsCategory const *>(obs)) {
78 // Nothing to be done for category observables
79 } else if (auto *rlv = dynamic_cast<RooAbsRealLValue const *>(obs)) {
80 limits.emplace_back(rlv->getMin(range.c_str()), rlv->getMax(range.c_str()));
81 } else {
82 throw std::logic_error(
83 "Classes that represent observables are expected to inherit from RooAbsRealLValue or RooAbsCategory!");
84 }
85 }
86 }
87
88 auto nRanges = rangeNames.size();
89 auto nObs = limits.size() / nRanges; // number of observables that are not categories
90
91 // loop over pairs of ranges
92 for (size_t ir1 = 0; ir1 < nRanges; ++ir1) {
93 for (size_t ir2 = ir1 + 1; ir2 < nRanges; ++ir2) {
94
95 // Loop over observables. If all observables have overlapping limits for
96 // these ranges, the hypercubes defining the range are overlapping and we
97 // can return `true`.
98 size_t overlaps = 0;
99 for (size_t io1 = 0; io1 < nObs; ++io1) {
100 auto r1 = limits[ir1 * nObs + io1];
101 auto r2 = limits[ir2 * nObs + io1];
102 overlaps +=
103 (r1.second > r2.first && r1.first < r2.second) || (r2.second > r1.first && r2.first < r1.second);
104 }
105 if (overlaps == nObs)
106 return true;
107 }
108 }
109
110 return false;
111}
112
113/// Create a string with all sorted names of RooArgSet elements separated by delimiters.
114/// \param[in] argSet The input RooArgSet.
115std::string getColonSeparatedNameString(RooArgSet const &argSet, char delim)
116{
117
118 RooArgList tmp(argSet);
119 tmp.sort();
120
121 std::string content;
122 for (auto const &arg : tmp) {
123 content += arg->GetName();
124 content += delim;
125 }
126 if (!content.empty()) {
127 content.pop_back();
128 }
129 return content;
130}
131
132/// Construct a RooArgSet of objects in a RooArgSet whose names match to those
133/// in the names string.
134/// \param[in] argSet The input RooArgSet.
135/// \param[in] names The names of the objects to select in a colon-separated string.
136RooArgSet selectFromArgSet(RooArgSet const &argSet, std::string const &names)
137{
139 for (auto const &name : ROOT::Split(names, ":")) {
140 if (auto arg = argSet.find(name.c_str()))
141 output.add(*arg);
142 }
143 return output;
144}
145
146std::string getRangeNameForSimComponent(std::string const &rangeName, bool splitRange, std::string const &catName)
147{
148 if (splitRange && !rangeName.empty()) {
149 std::string out;
150 auto tokens = ROOT::Split(rangeName, ",");
151 for (std::string const &token : tokens) {
152 out += token + "_" + catName + ",";
153 }
154 out.pop_back(); // to remove the last comma
155 return out;
156 }
157
158 return rangeName;
159}
160
161BinnedLOutput getBinnedL(RooAbsPdf const &pdf)
162{
163 if (pdf.getAttribute("BinnedLikelihood") && pdf.IsA()->InheritsFrom(RooRealSumPdf::Class())) {
164 // Simplest case: top-level of component is a RooRealSumPdf
165 return {const_cast<RooAbsPdf *>(&pdf), true};
166 } else if (pdf.IsA()->InheritsFrom(RooProdPdf::Class())) {
167 // Default case: top-level pdf is a product of RooRealSumPdf and other pdfs
168 for (RooAbsArg *component : static_cast<RooProdPdf const &>(pdf).pdfList()) {
169 if (component->getAttribute("BinnedLikelihood") && component->IsA()->InheritsFrom(RooRealSumPdf::Class())) {
170 return {static_cast<RooAbsPdf *>(component), true};
171 }
172 if (component->getAttribute("MAIN_MEASUREMENT")) {
173 // not really a binned pdf, but this prevents a (potentially) long list of subsidiary measurements to be
174 // passed to the slave calculator
175 return {static_cast<RooAbsPdf *>(component), false};
176 }
177 }
178 }
179 return {nullptr, false};
180}
181
182/// Get the topologically-sorted list of all nodes in the computation graph.
183void getSortedComputationGraph(RooAbsArg const &func, RooArgSet &out)
184{
185 // Get the set of nodes in the computation graph. Do the detour via
186 // RooArgList to avoid deduplication done after adding each element.
187 RooArgList serverList;
188 func.treeNodeServerList(&serverList, nullptr, true, true, false, true);
189 // If we fill the servers in reverse order, they are approximately in
190 // topological order so we save a bit of work in sortTopologically().
191 out.add(serverList.rbegin(), serverList.rend(), /*silent=*/true);
192 // Sort nodes topologically: the servers of any node will be before that
193 // node in the collection.
194 out.sortTopologically();
195}
196
197namespace Detail {
198
199namespace {
200
201using ToCloneList = std::vector<RooAbsArg const *>;
202using ToCloneMap = std::unordered_map<TNamed const *, RooAbsArg const *>;
203
204// Add clones of servers of given argument to end of list
205void addServerClonesToList(const RooAbsArg &var, ToCloneList &outlist, ToCloneMap &outmap, bool deepCopy,
206 RooArgSet const *observables)
207{
208 if (outmap.find(var.namePtr()) != outmap.end()) {
209 return;
210 }
211
212 if (observables && var.isFundamental() && !observables->find(var)) {
213 return;
214 }
215
216 outmap[var.namePtr()] = &var;
217 outlist.push_back(&var);
218
219 if (deepCopy) {
220 for (const auto server : var.servers()) {
221 addServerClonesToList(*server, outlist, outmap, deepCopy, observables);
222 }
223 }
224}
225
226} // namespace
227
228/// Implementation of RooAbsCollection::snapshot() with some extra parameters.
229/// to be used in other RooHelpers functions.
230/// param[in] input The input collection.
231/// param[in] output The output collection.
232/// param[in] deepCopy If the whole computation graph should be cloned recursively.
233/// param[in] observables If this is not a nullptr, only the fundamental
234/// variables that are in observables are deep cloned.
235bool snapshotImpl(RooAbsCollection const &input, RooAbsCollection &output, bool deepCopy, RooArgSet const *observables)
236{
237 // Figure out what needs to be cloned
238 ToCloneList toCloneList;
239 ToCloneMap toCloneMap;
240 for (RooAbsArg *orig : input) {
241 addServerClonesToList(*orig, toCloneList, toCloneMap, deepCopy, observables);
242 }
243
244 // Actually do the cloning
245 output.reserve(toCloneList.size());
246 for (RooAbsArg const *arg : toCloneList) {
247 std::unique_ptr<RooAbsArg> serverClone{static_cast<RooAbsArg *>(arg->Clone())};
248 serverClone->setAttribute("SnapShot_ExtRefClone");
249 output.addOwned(std::move(serverClone));
250 }
251
252 // Redirect all server connections to internal list members
253 for (RooAbsArg *var : output) {
254 var->redirectServers(output, deepCopy && !observables);
255 }
256
257 return false;
258}
259
260RooAbsArg *cloneTreeWithSameParametersImpl(RooAbsArg const &arg, RooArgSet const *observables)
261{
262 // Clone tree using snapshot
263 RooArgSet clonedNodes;
264 snapshotImpl(RooArgSet(arg), clonedNodes, true, observables);
265
266 // Find the head node in the cloneSet
267 RooAbsArg *head = clonedNodes.find(arg);
268 assert(head);
269
270 // We better to release the ownership before removing the "head". Otherwise,
271 // "head" might also be deleted as the clonedNodes collection owns it.
272 // (Actually this does not happen because even an owning collection doesn't
273 // delete the element when removed by pointer lookup, but it's better not to
274 // rely on this unexpected fact).
275 clonedNodes.releaseOwnership();
276
277 // Remove the head node from the cloneSet
278 // To release it from the set ownership
279 clonedNodes.remove(*head);
280
281 // Add the set as owned component of the head
282 head->addOwnedComponents(std::move(clonedNodes));
283
284 return head;
285}
286
287} // namespace Detail
288
289} // namespace RooHelpers
290
291namespace RooFit {
292namespace Detail {
293
294/// Transform a string into a valid C++ variable name by replacing forbidden
295/// characters with underscores.
296/// @param in The input string.
297/// @return A new string valid variable name.
298std::string makeValidVarName(std::string const &in)
299{
300 std::string out;
301 if (std::isdigit(in[0])) {
302 out += '_';
303 }
304 out += in;
305 std::transform(out.begin(), out.end(), out.begin(), [](char c) { return std::isalnum(c) ? c : '_'; });
306 return out;
307}
308
309} // namespace Detail
310} // namespace RooFit
311
312/// \endcond
#define c(i)
Definition RSha256.hxx:101
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
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:77
const TNamed * namePtr() const
De-duplicated pointer to this object's name.
Definition RooAbsArg.h:561
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.
TObject * Clone(const char *newname=nullptr) const override
Make a clone of an object using the Streamer facility.
Definition RooAbsArg.h:89
virtual bool isFundamental() const
Is this object a fundamental type that can be added to a dataset? Fundamental-type subclasses overrid...
Definition RooAbsArg.h:249
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...
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.
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.
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
RooAbsArg * find(const char *name) const
Find object with given name in list.
Abstract interface for all probability density functions.
Definition RooAbsPdf.h:40
TClass * IsA() const override
Definition RooAbsPdf.h:352
Abstract base class for objects that represent a real value that may appear on the left hand side of ...
virtual double getMin(const char *name=nullptr) const
Get minimum of currently defined range.
double getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
Definition RooAbsReal.h:103
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
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
This file contains a specialised ROOT message handler to test for diagnostic in unit tests.
std::vector< std::string > Split(std::string_view str, std::string_view delims, bool skipEmpty=false)
Splits a string at each character in delims.
std::string makeValidVarName(std::string const &in)
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition JSONIO.h:26
RooAbsArg * cloneTreeWithSameParametersImpl(RooAbsArg const &arg, RooArgSet const *observables)
bool snapshotImpl(RooAbsCollection const &input, RooAbsCollection &output, bool deepCopy, RooArgSet const *observables)
RooArgSet selectFromArgSet(RooArgSet const &, std::string const &names)
bool checkIfRangesOverlap(RooArgSet const &observables, std::vector< std::string > const &rangeNames)
std::string getRangeNameForSimComponent(std::string const &rangeName, bool splitRange, std::string const &catName)
void getSortedComputationGraph(RooAbsArg const &func, RooArgSet &out)
BinnedLOutput getBinnedL(RooAbsPdf const &pdf)
std::string getColonSeparatedNameString(RooArgSet const &argSet, char delim=':')
std::pair< double, double > getRangeOrBinningInterval(RooAbsArg const *arg, const char *rangeName)
static void output()