Logo ROOT  
Reference Guide
RooCategory.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 RooCategory
19\ingroup Roofitcore
20
21RooCategory is an object to represent discrete states.
22States have names and index numbers, and the index numbers can be written into datasets and
23used in calculations.
24A category is "fundamental", i.e., its value doesn't depend on the value of other objects.
25(Objects in datasets cannot depend on other objects' values, they need to be self-consistent.)
26
27A category object can be used to *e.g.* conduct a simultaneous fit of
28the same observable in multiple categories.
29
30### Setting up a category
311. A category can be set up like this:
32~~~{.cpp}
33RooCategory myCat("myCat", "Lepton multiplicity category", {
34 {"0Lep", 0},
35 {"1Lep", 1},
36 {"2Lep", 2},
37 {"3Lep", 3}
38});
39~~~
402. Like this:
41~~~{.cpp}
42RooCategory myCat("myCat", "Asymmetry");
43myCat["left"] = -1;
44myCat["right"] = 1;
45~~~
463. Or like this:
47~~~{.cpp}
48RooCategory myCat("myCat", "Asymmetry");
49myCat.defineType("left", -1);
50myCat.defineType("right", 1);
51~~~
52Inspect the pairs of state names and state numbers like this:
53~~~{.cpp}
54for (const auto& nameIdx : myCat) {
55 std::cout << nameIdx.first << " --> " << nameIdx.second << std::endl;
56}
57~~~
58
59### Changing category states
60Category states can be modified either by using the index state (faster) or state names.
61For example:
62~~~{.cpp}
63myCat.setIndex(5);
64myCat.setLabel("left");
65for (const auto& otherNameIdx : otherCat) {
66 myCat.setIndex(otherNameIdx);
67}
68~~~
69
70Also refer to \ref tutorial_roofit, especially rf404_categories.C for an introduction, and to rf405_realtocatfuncs.C and rf406_cattocatfuncs.C
71for advanced uses of categories.
72**/
73
74#include "RooCategory.h"
75
76#include "RooFit.h"
77#include "RooArgSet.h"
78#include "RooStreamParser.h"
79#include "RooMsgService.h"
80#include "RooTrace.h"
81#include "RooHelpers.h"
84
85#include "ROOT/StringUtils.hxx"
86
87#include "TBuffer.h"
88#include "TString.h"
89#include "TList.h"
90
91#include <cstdlib>
92#include <iostream>
93#include <memory>
94
95using namespace std;
96
98
99std::map<std::string, std::weak_ptr<RooCategory::RangeMap_t>> RooCategory::_uuidToSharedRangeIOHelper; // Helper for restoring shared properties
100std::map<std::string, std::weak_ptr<RooCategory::RangeMap_t>> RooCategory::_sharedRangeIOHelper;
101
102
103////////////////////////////////////////////////////////////////////////////////
104
106{
108}
109
110
111
112////////////////////////////////////////////////////////////////////////////////
113/// Constructor. Types must be defined using defineType() before variable can be used
114RooCategory::RooCategory(const char *name, const char *title) :
116 _ranges(new RangeMap_t())
117{
118 setValueDirty() ;
119 setShapeDirty() ;
121}
122
123
124////////////////////////////////////////////////////////////////////////////////
125/// Create a new category and define allowed states.
126/// \param[in] name Name used to refer to this object.
127/// \param[in] title Title for e.g. plotting.
128/// \param[in] allowedStates Map of allowed states. Pass e.g. `{ {"0Lep", 0}, {"1Lep:, 1} }`
129RooCategory::RooCategory(const char* name, const char* title, const std::map<std::string, int>& allowedStates) :
131 _ranges(new RangeMap_t())
132{
133 defineTypes(allowedStates);
134}
135
136
137
138////////////////////////////////////////////////////////////////////////////////
139/// Copy constructor
140
141RooCategory::RooCategory(const RooCategory& other, const char* name) :
143 _ranges(other._ranges)
144{
146}
147
148
149////////////////////////////////////////////////////////////////////////////////
150/// Destructor
151
153{
155}
156
157
158
159
160////////////////////////////////////////////////////////////////////////////////
161/// Set value by specifying the index code of the desired state.
162/// If printError is set, a message will be printed if
163/// the specified index does not represent a valid state.
164/// \return bool signalling if an error occurred.
166{
167 if (!hasIndex(index)) {
168 if (printError) {
169 coutE(InputArguments) << "RooCategory: Trying to set invalid state " << index << " for category " << GetName() << std::endl;
170 }
171 return true;
172 }
173
174 _currentIndex = index;
176
177 return false;
178}
179
180
181
182////////////////////////////////////////////////////////////////////////////////
183/// Set value by specifying the name of the desired state.
184/// If printError is set, a message will be printed if
185/// the specified label does not represent a valid state.
186/// \return false on success.
187Bool_t RooCategory::setLabel(const char* label, Bool_t printError)
188{
189 const auto item = stateNames().find(label);
190 if (item != stateNames().end()) {
191 _currentIndex = item->second;
193 return false;
194 }
195
196 if (printError) {
197 coutE(InputArguments) << "Trying to set invalid state label '" << label << "' for category " << GetName() << std::endl;
198 }
199
200 return true;
201}
202
203
204
205////////////////////////////////////////////////////////////////////////////////
206/// Define a state with given name.
207/// The lowest available positive integer is assigned as index. Category
208/// state labels may not contain semicolons.
209/// \return True in case of an error.
210bool RooCategory::defineType(const std::string& label)
211{
212 if (label.find(';') != std::string::npos) {
213 coutE(InputArguments) << "RooCategory::defineType(" << GetName()
214 << "): semicolons not allowed in label name" << endl ;
215 return true;
216 }
217
219}
220
221
222////////////////////////////////////////////////////////////////////////////////
223/// Define a state with given name and index. Category
224/// state labels may not contain semicolons.
225/// \return True in case of error.
226bool RooCategory::defineType(const std::string& label, Int_t index)
227{
228 if (label.find(';') != std::string::npos) {
229 coutE(InputArguments) << "RooCategory::defineType(" << GetName()
230 << "): semicolons not allowed in label name" << endl ;
231 return true;
232 }
233
234 return RooAbsCategory::defineState(label, index) == invalidCategory();
235}
236
237
238////////////////////////////////////////////////////////////////////////////////
239/// Define multiple states in a single call. Use like:
240/// ```
241/// myCat.defineTypes({ {"0Lep", 0}, {"1Lep", 1}, {"2Lep", 2}, {"3Lep", 3} });
242/// ```
243/// Note: When labels or indices are defined multiple times, an error message is printed,
244/// and the corresponding state is ignored.
245void RooCategory::defineTypes(const std::map<std::string, int>& allowedStates) {
246 for (const auto& nameAndIdx : allowedStates) {
247 defineType(nameAndIdx.first, nameAndIdx.second);
248 }
249}
250
251
252////////////////////////////////////////////////////////////////////////////////
253/// Access a named state. If a state with this name doesn't exist yet, the state is
254/// assigned the next available positive integer.
255/// \param[in] stateName Name of the state to be accessed.
256/// \return Reference to the category index. If no state exists, it will be created on the fly.
259 if (stateNames().count(stateName) == 0) {
260 _insertionOrder.push_back(stateName);
261 return stateNames()[stateName] = nextAvailableStateIndex();
262
263 }
264
265 return stateNames()[stateName];
266}
267
268
269////////////////////////////////////////////////////////////////////////////////
270/// Return a reference to the map of state names to index states.
271/// This can be used to manipulate the category.
272/// \note Calling this function will **always** trigger recomputations of
273/// of **everything** that depends on this category, since in case the map gets
274/// manipulated, names or indices might change. Also, the order that states have
275/// been inserted in gets lost. This changes what is returned by getOrdinal().
276std::map<std::string, RooAbsCategory::value_type>& RooCategory::states() {
277 auto& theStates = stateNames();
280 _insertionOrder.clear();
281 return theStates;
282}
283
284
285////////////////////////////////////////////////////////////////////////////////
286/// Read object contents from given stream. If token is a decimal digit, try to
287/// find a corresponding state for it. If that succeeds, the state denoted by this
288/// index is used. Otherwise, interpret it as a label.
290{
291 // Read single token
292 RooStreamParser parser(is) ;
293 TString token = parser.readToken() ;
294
295 if (token.IsDec() && hasIndex(std::stoi(token.Data()))) {
296 return setIndex(std::stoi(token.Data()), verbose);
297 } else {
298 return setLabel(token,verbose) ;
299 }
300}
301
302
303
304////////////////////////////////////////////////////////////////////////////////
305/// compact only at the moment
306
307void RooCategory::writeToStream(ostream& os, Bool_t compact) const
308{
309 if (compact) {
310 os << getCurrentIndex() ;
311 } else {
312 os << getCurrentLabel() ;
313 }
314}
315
316
317////////////////////////////////////////////////////////////////////////////////
318/// Clear the named range.
319/// \note This affects **all** copies of this category, because they are sharing
320/// range definitions. This ensures that categories inside a dataset and their
321/// counterparts on the outside will both see a modification of the range.
322void RooCategory::clearRange(const char* name, Bool_t silent)
323{
324 std::map<std::string, std::vector<value_type>>::iterator item = _ranges->find(name);
325 if (item == _ranges->end()) {
326 if (!silent)
327 coutE(InputArguments) << "RooCategory::clearRange(" << GetName() << ") ERROR: must specify valid range name" << endl ;
328 return;
329 }
330
331 _ranges->erase(item);
332}
333
334
335////////////////////////////////////////////////////////////////////////////////
336
337void RooCategory::setRange(const char* name, const char* stateNameList)
338{
340 addToRange(name,stateNameList) ;
341}
342
343
344////////////////////////////////////////////////////////////////////////////////
345/// Add the given state to the given range.
346/// \note This creates or accesses a **shared** map with allowed ranges. All copies of this
347/// category will share this range such that a category inside a dataset and its
348/// counterpart on the outside will both see a modification of the range.
350 auto item = _ranges->find(name);
351 if (item == _ranges->end()) {
352 if (!name) {
353 coutE(Contents) << "RooCategory::addToRange(" << GetName()
354 << "): Need valid range name." << std::endl;
355 return;
356 }
357
358 item = _ranges->emplace(name, std::vector<value_type>()).first;
359 coutI(Contents) << "RooCategory::setRange(" << GetName()
360 << ") new range named '" << name << "' created for state " << stateIndex << endl ;
361 }
362
363 item->second.push_back(stateIndex);
364}
365
366
367////////////////////////////////////////////////////////////////////////////////
368/// Add the list of state names to the given range. State names can be separated
369/// with ','.
370/// \note This creates or accesses a **shared** map with allowed ranges. All copies of this
371/// category will share this range such that a category inside a dataset and its
372/// counterpart on the outside will both see a modification of the range.
373void RooCategory::addToRange(const char* name, const char* stateNameList)
374{
375 if (!stateNameList) {
376 coutE(InputArguments) << "RooCategory::setRange(" << GetName() << ") ERROR: must specify valid name and state name list" << endl ;
377 return;
378 }
379
380 // Parse list of state names, verify that each is valid and add them to the list
381 for (const auto& token : ROOT::Split(stateNameList, ",")) {
382 const value_type idx = lookupIndex(token);
383 if (idx != invalidCategory().second) {
384 addToRange(name, idx);
385 } else {
386 coutW(InputArguments) << "RooCategory::setRange(" << GetName() << ") WARNING: Ignoring invalid state name '"
387 << token << "' in state name list" << endl ;
388 }
389 }
390}
391
392
393////////////////////////////////////////////////////////////////////////////////
394/// Check if the state is in the given range.
395/// If no range is specified either as argument or if no range has been defined for this category
396/// (*i.e.*, the default range is meant), all category states count as being in range.
397bool RooCategory::isStateInRange(const char* rangeName, RooAbsCategory::value_type stateIndex) const {
398 if (rangeName == nullptr || _ranges->empty())
399 return true;
400
401 const auto item = _ranges->find(rangeName);
402 if (item == _ranges->end())
403 return false;
404
405 const std::vector<value_type>& vec = item->second;
406 return std::find(vec.begin(), vec.end(), stateIndex) != vec.end();
407}
408
409
410////////////////////////////////////////////////////////////////////////////////
411/// Check if the state is in the given range.
412/// If no range is specified (*i.e.*, the default range), all category states count as being in range.
413/// This overload requires a name lookup. Recommend to use the category index with
414/// RooCategory::isStateInRange(const char*, RooAbsCategory::value_type) const.
415bool RooCategory::isStateInRange(const char* rangeName, const char* stateName) const
416{
417 // Check that both input arguments are not null pointers
418 if (!rangeName) {
419 return true;
420 }
421
422 if (!stateName) {
423 coutE(InputArguments) << "RooCategory::isStateInRange(" << GetName() << ") ERROR: must specify valid state name" << endl ;
424 return false;
425 }
426
427 return isStateInRange(rangeName, lookupIndex(stateName));
428}
429
430
431////////////////////////////////////////////////////////////////////////////////
432
433void RooCategory::Streamer(TBuffer &R__b)
434{
435 UInt_t R__s, R__c;
436 if (R__b.IsReading()) {
437
438 Version_t R__v = R__b.ReadVersion(&R__s, &R__c);
439
440 if (R__v==1) {
441 RooAbsCategoryLValue::Streamer(R__b);
442
443 // In v1, properties were a direct pointer:
444 RooCategorySharedProperties* props = nullptr;
445 R__b >> props;
447 // props was allocated by I/O system, we cannot delete here in case it gets reused
448
449 } else if (R__v == 2) {
450 RooAbsCategoryLValue::Streamer(R__b);
451
452 // In v2, properties were written directly into the class buffer
453 auto props = std::make_unique<RooCategorySharedProperties>();
454 props->Streamer(R__b);
455 installLegacySharedProp(props.get());
456
457 } else {
458 // Starting at v3, ranges are shared using a shared pointer, which cannot be read by ROOT's I/O.
459 // Instead, ranges are written as a normal pointer, and here we restore the sharing.
460 R__b.ReadClassBuffer(RooCategory::Class(), this, R__v, R__s, R__c);
461 installSharedRange(std::unique_ptr<RangeMap_t>(_rangesPointerForIO));
462 _rangesPointerForIO = nullptr;
463 }
464
465 R__b.CheckByteCount(R__s, R__c, RooCategory::IsA());
466
467 } else {
468 // Since we cannot write shared pointers yet, assign the shared ranges to a normal pointer,
469 // write, and restore.
470 if (_ranges)
472
474 _rangesPointerForIO = nullptr;
475 }
476}
477
478
479/// When reading old versions of the class, we get instances of shared properties.
480/// Since these only contain ranges with numbers, just convert to vectors of numbers.
482 if (props == nullptr || (*props == RooCategorySharedProperties("00000000-0000-0000-0000-000000000000")))
483 return;
484
485 auto& weakPtr = _uuidToSharedRangeIOHelper[props->asString().Data()];
486 if (auto existingObject = weakPtr.lock()) {
487 // We know this range, start sharing
488 _ranges = std::move(existingObject);
489 } else {
490 // This range is unknown, make a new object
491 _ranges = std::make_shared<std::map<std::string, std::vector<value_type>>>();
492 auto& rangesMap = *_ranges;
493
494 // Copy the data:
495 std::unique_ptr<TIterator> iter(props->_altRanges.MakeIterator());
496 while (TList* olist = (TList*)iter->Next()) {
497 std::vector<value_type>& vec = rangesMap[olist->GetName()];
498
499
500 std::unique_ptr<TIterator> citer(olist->MakeIterator());
501 while (RooCatType* ctype = (RooCatType*)citer->Next()) {
502 vec.push_back(ctype->getVal());
503 }
504 }
505
506 // Register the shared_ptr for future sharing
507 weakPtr = _ranges;
508 }
509}
510
511
512/// In current versions of the class, a map with ranges can be shared between instances.
513/// If an instance with the same name alreday uses the same map, the instances will start sharing.
514/// Otherwise, this instance will be registered, and future copies being read will share with this
515/// one.
516void RooCategory::installSharedRange(std::unique_ptr<RangeMap_t>&& rangeMap) {
517 if (rangeMap == nullptr)
518 return;
519
520 auto checkRangeMapsEqual = [](const RooCategory::RangeMap_t& a, const RooCategory::RangeMap_t& b) {
521 if (&a == &b)
522 return true;
523
524 if (a.size() != b.size())
525 return false;
526
527 auto vecsEqual = [](const std::vector<RooAbsCategory::value_type>& aa, const std::vector<RooAbsCategory::value_type>& bb) {
528 return aa.size() == bb.size() && std::equal(aa.begin(), aa.end(), bb.begin());
529 };
530
531 for (const auto& itemA : a) {
532 const auto itemB = b.find(itemA.first);
533 if (itemB == b.end())
534 return false;
535
536 if (!vecsEqual(itemA.second, itemB->second))
537 return false;
538 }
539
540 return true;
541 };
542
543
544 auto& weakPtr = _sharedRangeIOHelper[GetName()];
545 auto existingMap = weakPtr.lock();
546 if (existingMap && checkRangeMapsEqual(*rangeMap, *existingMap)) {
547 // We know this map, use the shared one.
548 _ranges = std::move(existingMap);
549 if (rangeMap.get() == _ranges.get()) {
550 // This happens when ROOT's IO has written the same pointer twice. We cannot delete now.
551 (void) rangeMap.release(); // NOLINT: clang-tidy is normally right that this leaks. Here, we need to leave the result unused, though.
552 }
553 } else {
554 // We don't know this map. Register for sharing.
555 _ranges = std::move(rangeMap);
556 weakPtr = _ranges;
557 }
558}
void Class()
Definition: Class.C:29
typedef void(GLAPIENTRYP _GLUfuncptr)(void)
#define b(i)
Definition: RSha256.hxx:100
#define coutI(a)
Definition: RooMsgService.h:30
#define coutW(a)
Definition: RooMsgService.h:32
#define coutE(a)
Definition: RooMsgService.h:33
#define TRACE_DESTROY
Definition: RooTrace.h:24
#define TRACE_CREATE
Definition: RooTrace.h:23
int Int_t
Definition: RtypesCore.h:45
short Version_t
Definition: RtypesCore.h:65
unsigned int UInt_t
Definition: RtypesCore.h:46
bool Bool_t
Definition: RtypesCore.h:63
const Bool_t kTRUE
Definition: RtypesCore.h:100
#define ClassImp(name)
Definition: Rtypes.h:364
char name[80]
Definition: TGX11.cxx:110
void setShapeDirty()
Notify that a shape-like property (e.g. binning) has changed.
Definition: RooAbsArg.h:496
void setValueDirty()
Mark the element dirty. This forces a re-evaluation when a value is requested.
Definition: RooAbsArg.h:491
RooAbsCategoryLValue is the common abstract base class for objects that represent a discrete value th...
virtual const char * getCurrentLabel() const
Return label string of current state.
value_type _currentIndex
value_type nextAvailableStateIndex() const
std::map< std::string, value_type >::const_iterator end() const
Iterator for category state names. Points to pairs of index and name.
virtual const std::map< std::string, RooAbsCategory::value_type >::value_type & defineState(const std::string &label)
Define a new state with given label.
int value_type
The type used to denote a specific category state.
static const decltype(_stateNames) ::value_type & invalidCategory()
Is this category attached to a tree?
bool hasIndex(value_type index) const
Check if a state with index index exists.
std::vector< std::string > _insertionOrder
Map state names to index numbers. Make sure state names are updated in recomputeShape().
const std::map< std::string, value_type > & stateNames() const
Access the map of state names to index numbers.
value_type lookupIndex(const std::string &stateName) const
Find the index number corresponding to the state name.
RooCatType is an auxilary class for RooAbsCategory and defines a a single category state.
RooCategorySharedProperties is the container for all properties that are shared between instance of R...
RooCategory is an object to represent discrete states.
Definition: RooCategory.h:27
RangeMap_t * _rangesPointerForIO
Definition: RooCategory.h:131
void addToRange(const char *rangeName, RooAbsCategory::value_type stateIndex)
Add the given state to the given range.
virtual void writeToStream(std::ostream &os, Bool_t compact) const override
compact only at the moment
void setRange(const char *rangeName, const char *stateNameList)
void defineTypes(const std::map< std::string, int > &allowedStates)
Define multiple states in a single call.
static std::map< std::string, std::weak_ptr< RangeMap_t > > _sharedRangeIOHelper
Definition: RooCategory.h:138
void installSharedRange(std::unique_ptr< RangeMap_t > &&rangeMap)
In current versions of the class, a map with ranges can be shared between instances.
bool defineType(const std::string &label)
Define a state with given name.
virtual ~RooCategory()
Destructor.
std::shared_ptr< RangeMap_t > _ranges
Map range names to allowed category states.
Definition: RooCategory.h:130
std::map< std::string, std::vector< value_type > > RangeMap_t
Definition: RooCategory.h:127
virtual Bool_t readFromStream(std::istream &is, Bool_t compact, Bool_t verbose=kFALSE) override
Read object contents from given stream.
virtual Bool_t setLabel(const char *label, bool printError=true) override
Set value by specifying the name of the desired state.
virtual value_type getCurrentIndex() const override final
Return current index.
Definition: RooCategory.h:39
value_type & operator[](const std::string &stateName)
Access a named state.
static std::map< std::string, std::weak_ptr< RangeMap_t > > _uuidToSharedRangeIOHelper
Definition: RooCategory.h:136
virtual Bool_t setIndex(Int_t index, bool printError=true) override
Set value by specifying the index code of the desired state.
void clearRange(const char *name, Bool_t silent)
Clear the named range.
std::map< std::string, RooAbsCategory::value_type > & states()
Return a reference to the map of state names to index states.
Bool_t isStateInRange(const char *rangeName, RooAbsCategory::value_type stateIndex) const
Check if the state is in the given range.
void installLegacySharedProp(const RooCategorySharedProperties *sp)
When reading old versions of the class, we get instances of shared properties.
TIterator * MakeIterator(Bool_t forward=kTRUE) const
Create a TIterator for this list.
TString asString() const
TString readToken()
Read one token separated by any of the know punctuation characters This function recognizes and handl...
Buffer base class used for serializing objects.
Definition: TBuffer.h:43
virtual Int_t ReadClassBuffer(const TClass *cl, void *pointer, const TClass *onfile_class=0)=0
virtual Version_t ReadVersion(UInt_t *start=0, UInt_t *bcnt=0, const TClass *cl=0)=0
virtual Int_t CheckByteCount(UInt_t startpos, UInt_t bcnt, const TClass *clss)=0
Bool_t IsReading() const
Definition: TBuffer.h:86
virtual Int_t WriteClassBuffer(const TClass *cl, void *pointer)=0
A doubly linked list.
Definition: TList.h:44
virtual const char * GetName() const
Returns name of object.
Definition: TNamed.h:47
Basic string class.
Definition: TString.h:136
Bool_t IsDec() const
Returns true if all characters in string are decimal digits (0-9).
Definition: TString.cxx:1898
const char * Data() const
Definition: TString.h:369
std::vector< std::string > Split(std::string_view str, std::string_view delims, bool skipEmpty=false)
Splits a string at each character in delims.
Definition: StringUtils.cxx:23
@ InputArguments
Definition: RooGlobalFunc.h:61
static constexpr double second
Definition: civetweb.c:2228
auto * a
Definition: textangle.C:12