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 *
11 * *
12 * Redistribution and use in source and binary forms, *
13 * with or without modification, are permitted according to the terms *
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
72**/
73
74#include "RooCategory.h"
75
76#include "RooArgSet.h"
77#include "RooStreamParser.h"
78#include "RooMsgService.h"
79#include "RooTrace.h"
80#include "RooHelpers.h"
83
84#include "ROOT/StringUtils.hxx"
85
86#include "TBuffer.h"
87#include "TString.h"
88#include "TList.h"
89
90#include <cstdlib>
91#include <iostream>
92#include <memory>
93
94using namespace std;
95
97
98std::map<RooSharedProperties::UUID, std::weak_ptr<RooCategory::RangeMap_t>> RooCategory::_uuidToSharedRangeIOHelper; // Helper for restoring shared properties
99std::map<std::string, std::weak_ptr<RooCategory::RangeMap_t>> RooCategory::_sharedRangeIOHelper;
100
101
102////////////////////////////////////////////////////////////////////////////////
103
105{
107}
108
109
110
111////////////////////////////////////////////////////////////////////////////////
112/// Constructor. Types must be defined using defineType() before variable can be used
113RooCategory::RooCategory(const char *name, const char *title) :
115 _ranges(new RangeMap_t())
116{
117 setValueDirty() ;
118 setShapeDirty() ;
120}
121
122
123////////////////////////////////////////////////////////////////////////////////
124/// Create a new category and define allowed states.
125/// \param[in] name Name used to refer to this object.
126/// \param[in] title Title for e.g. plotting.
127/// \param[in] allowedStates Map of allowed states. Pass e.g. { {"0Lep", 0}, {"1Lep:, 1} }
128RooCategory::RooCategory(const char* name, const char* title, const std::map<std::string, int>& allowedStates) :
130 _ranges(new RangeMap_t())
131{
132 defineTypes(allowedStates);
133}
134
135
136
137////////////////////////////////////////////////////////////////////////////////
138/// Copy constructor
139
140RooCategory::RooCategory(const RooCategory& other, const char* name) :
142 _ranges(other._ranges)
143{
145}
146
147
148////////////////////////////////////////////////////////////////////////////////
149/// Destructor
150
152{
154}
155
156
157
158
159////////////////////////////////////////////////////////////////////////////////
160/// Set value by specifying the index code of the desired state.
161/// If printError is set, a message will be printed if
162/// the specified index does not represent a valid state.
163/// \return bool signalling if an error occurred.
164bool RooCategory::setIndex(Int_t index, bool printError)
165{
166 if (!hasIndex(index)) {
167 if (printError) {
168 coutE(InputArguments) << "RooCategory: Trying to set invalid state " << index << " for category " << GetName() << std::endl;
169 }
170 return true;
171 }
172
175
176 return false;
177}
178
179
180
181////////////////////////////////////////////////////////////////////////////////
182/// Set value by specifying the name of the desired state.
183/// If printError is set, a message will be printed if
184/// the specified label does not represent a valid state.
185/// \return false on success.
186bool RooCategory::setLabel(const char* label, bool printError)
187{
188 const auto item = stateNames().find(label);
189 if (item != stateNames().end()) {
190 _currentIndex = item->second;
192 return false;
193 }
194
195 if (printError) {
196 coutE(InputArguments) << "Trying to set invalid state label '" << label << "' for category " << GetName() << std::endl;
197 }
198
199 return true;
200}
201
202
203
204////////////////////////////////////////////////////////////////////////////////
205/// Define a state with given name.
206/// The lowest available positive integer is assigned as index. Category
207/// state labels may not contain semicolons.
208/// \return True in case of an error.
209bool RooCategory::defineType(const std::string& label)
210{
211 if (label.find(';') != std::string::npos) {
212 coutE(InputArguments) << "RooCategory::defineType(" << GetName()
213 << "): semicolons not allowed in label name" << endl ;
214 return true;
215 }
216
218}
219
220
221////////////////////////////////////////////////////////////////////////////////
222/// Define a state with given name and index. Category
223/// state labels may not contain semicolons.
224/// \return True in case of error.
225bool RooCategory::defineType(const std::string& label, Int_t index)
226{
227 if (label.find(';') != std::string::npos) {
228 coutE(InputArguments) << "RooCategory::defineType(" << GetName()
229 << "): semicolons not allowed in label name" << endl ;
230 return true;
231 }
232
234}
235
236
237////////////////////////////////////////////////////////////////////////////////
238/// Define multiple states in a single call. Use like:
239/// 
240/// myCat.defineTypes({ {"0Lep", 0}, {"1Lep", 1}, {"2Lep", 2}, {"3Lep", 3} });
241/// 
242/// Note: When labels or indices are defined multiple times, an error message is printed,
243/// and the corresponding state is ignored.
244void RooCategory::defineTypes(const std::map<std::string, int>& allowedStates) {
245 for (const auto& nameAndIdx : allowedStates) {
246 defineType(nameAndIdx.first, nameAndIdx.second);
247 }
248}
249
250
251////////////////////////////////////////////////////////////////////////////////
252/// Access a named state. If a state with this name doesn't exist yet, the state is
253/// assigned the next available positive integer.
254/// \param[in] stateName Name of the state to be accessed.
255/// \return Reference to the category index. If no state exists, it will be created on the fly.
258 if (stateNames().count(stateName) == 0) {
259 _insertionOrder.push_back(stateName);
260 return stateNames()[stateName] = nextAvailableStateIndex();
261
262 }
263
264 return stateNames()[stateName];
265}
266
267
268////////////////////////////////////////////////////////////////////////////////
269/// Return a reference to the map of state names to index states.
270/// This can be used to manipulate the category.
271/// \note Calling this function will **always** trigger recomputations of
272/// of **everything** that depends on this category, since in case the map gets
273/// manipulated, names or indices might change. Also, the order that states have
274/// been inserted in gets lost. This changes what is returned by getOrdinal().
275std::map<std::string, RooAbsCategory::value_type>& RooCategory::states() {
276 auto& theStates = stateNames();
279 _insertionOrder.clear();
280 return theStates;
281}
282
283
284////////////////////////////////////////////////////////////////////////////////
285/// Read object contents from given stream. If token is a decimal digit, try to
286/// find a corresponding state for it. If that succeeds, the state denoted by this
287/// index is used. Otherwise, interpret it as a label.
288bool RooCategory::readFromStream(istream& is, bool /*compact*/, bool verbose)
289{
291 RooStreamParser parser(is) ;
292 TString token = parser.readToken() ;
293
294 if (token.IsDec() && hasIndex(std::stoi(token.Data()))) {
295 return setIndex(std::stoi(token.Data()), verbose);
296 } else {
297 return setLabel(token,verbose) ;
298 }
299}
300
301
302
303////////////////////////////////////////////////////////////////////////////////
304/// compact only at the moment
305
306void RooCategory::writeToStream(ostream& os, bool compact) const
307{
308 if (compact) {
309 os << getCurrentIndex() ;
310 } else {
311 os << getCurrentLabel() ;
312 }
313}
314
315
316////////////////////////////////////////////////////////////////////////////////
317/// Clear the named range.
318/// \note This affects **all** copies of this category, because they are sharing
319/// range definitions. This ensures that categories inside a dataset and their
320/// counterparts on the outside will both see a modification of the range.
321void RooCategory::clearRange(const char* name, bool silent)
322{
323 std::map<std::string, std::vector<value_type>>::iterator item = _ranges->find(name);
324 if (item == _ranges->end()) {
325 if (!silent)
326 coutE(InputArguments) << "RooCategory::clearRange(" << GetName() << ") ERROR: must specify valid range name" << endl ;
327 return;
328 }
329
330 _ranges->erase(item);
331}
332
333
334////////////////////////////////////////////////////////////////////////////////
335
336void RooCategory::setRange(const char* name, const char* stateNameList)
337{
338 clearRange(name,true) ;
340}
341
342
343////////////////////////////////////////////////////////////////////////////////
344/// Add the given state to the given range.
345/// \note This creates or accesses a **shared** map with allowed ranges. All copies of this
346/// category will share this range such that a category inside a dataset and its
347/// counterpart on the outside will both see a modification of the range.
349 auto item = _ranges->find(name);
350 if (item == _ranges->end()) {
351 if (!name) {
352 coutE(Contents) << "RooCategory::addToRange(" << GetName()
353 << "): Need valid range name." << std::endl;
354 return;
355 }
356
357 item = _ranges->emplace(name, std::vector<value_type>()).first;
358 coutI(Contents) << "RooCategory::setRange(" << GetName()
359 << ") new range named '" << name << "' created for state " << stateIndex << endl ;
360 }
361
362 item->second.push_back(stateIndex);
363}
364
365
366////////////////////////////////////////////////////////////////////////////////
367/// Add the list of state names to the given range. State names can be separated
368/// with ','.
369/// \note This creates or accesses a **shared** map with allowed ranges. All copies of this
370/// category will share this range such that a category inside a dataset and its
371/// counterpart on the outside will both see a modification of the range.
372void RooCategory::addToRange(const char* name, const char* stateNameList)
373{
374 if (!stateNameList) {
375 coutE(InputArguments) << "RooCategory::setRange(" << GetName() << ") ERROR: must specify valid name and state name list" << endl ;
376 return;
377 }
378
379 // Parse list of state names, verify that each is valid and add them to the list
380 for (const auto& token : ROOT::Split(stateNameList, ",")) {
381 const value_type idx = lookupIndex(token);
382 if (idx != invalidCategory().second) {
384 } else {
385 coutW(InputArguments) << "RooCategory::setRange(" << GetName() << ") WARNING: Ignoring invalid state name '"
386 << token << "' in state name list" << endl ;
387 }
388 }
389}
390
391
392////////////////////////////////////////////////////////////////////////////////
393/// Check if the state is in the given range.
394/// If no range is specified either as argument or if no range has been defined for this category
395/// (*i.e.*, the default range is meant), all category states count as being in range.
396bool RooCategory::isStateInRange(const char* rangeName, RooAbsCategory::value_type stateIndex) const {
397 if (rangeName == nullptr || _ranges->empty())
398 return true;
399
400 const auto item = _ranges->find(rangeName);
401 if (item == _ranges->end())
402 return false;
403
404 const std::vector<value_type>& vec = item->second;
405 return std::find(vec.begin(), vec.end(), stateIndex) != vec.end();
406}
407
408
409////////////////////////////////////////////////////////////////////////////////
410/// Check if the state is in the given range.
411/// If no range is specified (*i.e.*, the default range), all category states count as being in range.
412/// This overload requires a name lookup. Recommend to use the category index with
413/// RooCategory::isStateInRange(const char*, RooAbsCategory::value_type) const.
414bool RooCategory::isStateInRange(const char* rangeName, const char* stateName) const
415{
416 // Check that both input arguments are not null pointers
417 if (!rangeName) {
418 return true;
419 }
420
421 if (!stateName) {
422 coutE(InputArguments) << "RooCategory::isStateInRange(" << GetName() << ") ERROR: must specify valid state name" << endl ;
423 return false;
424 }
425
426 return isStateInRange(rangeName, lookupIndex(stateName));
427}
428
429
430////////////////////////////////////////////////////////////////////////////////
431
433{
434 UInt_t R__s, R__c;
436
437 Version_t R__v = R__b.ReadVersion(&R__s, &R__c);
438
439 if (R__v==1) {
441
442 // In v1, properties were a direct pointer:
443 RooCategorySharedProperties* props = nullptr;
444 R__b >> props;
446 // props was allocated by I/O system, we cannot delete here in case it gets reused
447
448 } else if (R__v == 2) {
450
451 // In v2, properties were written directly into the class buffer
452 auto props = std::make_unique<RooCategorySharedProperties>();
453 props->Streamer(R__b);
454 installLegacySharedProp(props.get());
455
456 } else {
457 // Starting at v3, ranges are shared using a shared pointer, which cannot be read by ROOT's I/O.
458 // Instead, ranges are written as a normal pointer, and here we restore the sharing.
459 R__b.ReadClassBuffer(RooCategory::Class(), this, R__v, R__s, R__c);
460 installSharedRange(std::unique_ptr<RangeMap_t>(_rangesPointerForIO));
461 _rangesPointerForIO = nullptr;
462 }
463
464 R__b.CheckByteCount(R__s, R__c, RooCategory::IsA());
465
466 } else {
467 // Since we cannot write shared pointers yet, assign the shared ranges to a normal pointer,
468 // write, and restore.
469 if (_ranges)
471
473 _rangesPointerForIO = nullptr;
474 }
475}
476
477
478/// When reading old versions of the class, we get instances of shared properties.
479/// Since these only contain ranges with numbers, just convert to vectors of numbers.
481 if (props == nullptr || (*props == RooCategorySharedProperties("00000000-0000-0000-0000-000000000000")))
482 return;
483
484 auto& weakPtr = _uuidToSharedRangeIOHelper[props->uuid()];
485 if (auto existingObject = weakPtr.lock()) {
486 // We know this range, start sharing
487 _ranges = std::move(existingObject);
488 } else {
489 // This range is unknown, make a new object
490 _ranges = std::make_shared<std::map<std::string, std::vector<value_type>>>();
491 auto& rangesMap = *_ranges;
492
493 // Copy the data:
494 for (auto * olist : static_range_cast<TList*>(props->_altRanges)) {
495 std::vector<value_type>& vec = rangesMap[olist->GetName()];
496
497
498 for(auto * ctype : static_range_cast<RooCatType*>(*olist)) {
499 vec.push_back(ctype->getVal());
500 }
501 }
502
503 // Register the shared_ptr for future sharing
504 weakPtr = _ranges;
505 }
506}
507
508
509/// In current versions of the class, a map with ranges can be shared between instances.
510/// If an instance with the same name alreday uses the same map, the instances will start sharing.
511/// Otherwise, this instance will be registered, and future copies being read will share with this
512/// one.
513void RooCategory::installSharedRange(std::unique_ptr<RangeMap_t>&& rangeMap) {
514 if (rangeMap == nullptr)
515 return;
516
517 auto checkRangeMapsEqual = [](const RooCategory::RangeMap_t& a, const RooCategory::RangeMap_t& b) {
518 if (&a == &b)
519 return true;
520
521 if (a.size() != b.size())
522 return false;
523
524 auto vecsEqual = [](const std::vector<RooAbsCategory::value_type>& aa, const std::vector<RooAbsCategory::value_type>& bb) {
525 return aa.size() == bb.size() && std::equal(aa.begin(), aa.end(), bb.begin());
526 };
527
528 for (const auto& itemA : a) {
529 const auto itemB = b.find(itemA.first);
530 if (itemB == b.end())
531 return false;
532
533 if (!vecsEqual(itemA.second, itemB->second))
534 return false;
535 }
536
537 return true;
538 };
539
540
541 auto& weakPtr = _sharedRangeIOHelper[GetName()];
542 auto existingMap = weakPtr.lock();
543 if (existingMap && checkRangeMapsEqual(*rangeMap, *existingMap)) {
544 // We know this map, use the shared one.
545 _ranges = std::move(existingMap);
546 if (rangeMap.get() == _ranges.get()) {
547 // This happens when ROOT's IO has written the same pointer twice. We cannot delete now.
548 (void) rangeMap.release(); // NOLINT: clang-tidy is normally right that this leaks. Here, we need to leave the result unused, though.
549 }
550 } else {
551 // We don't know this map. Register for sharing.
552 _ranges = std::move(rangeMap);
553 weakPtr = _ranges;
554 }
555}
#define coutI(a)
Definition: RooMsgService.h:34
#define coutW(a)
Definition: RooMsgService.h:36
#define coutE(a)
Definition: RooMsgService.h:37
#define TRACE_DESTROY
Definition: RooTrace.h:24
#define TRACE_CREATE
Definition: RooTrace.h:23
short Version_t
Definition: RtypesCore.h:65
#define ClassImp(name)
Definition: Rtypes.h:375
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t b
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
char name[80]
Definition: TGX11.cxx:110
void setShapeDirty()
Notify that a shape-like property (e.g. binning) has changed.
Definition: RooAbsArg.h:498
void setValueDirty()
Mark the element dirty. This forces a re-evaluation when a value is requested.
Definition: RooAbsArg.h:493
RooAbsCategoryLValue is the common abstract base class for objects that represent a discrete value th...
void Streamer(TBuffer &) override
Stream an object of class TObject.
virtual const char * getCurrentLabel() const
Return label string of current state.
value_type _currentIndex
Current category state.
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.
std::vector< std::string > _insertionOrder
Keeps track in which order state numbers have been inserted. Make sure this is updated in recomputeSh...
const std::map< std::string, value_type > & stateNames() const
Access the map of state names to index numbers.
bool hasIndex(value_type index) const
Check if a state with index index exists.
static const decltype(_stateNames) ::value_type & invalidCategory()
A category state to signify an invalid category.
value_type lookupIndex(const std::string &stateName) const
Find the index number corresponding to the state name.
RooCategorySharedProperties is the container for all properties that are shared between instance of R...
Optional alternative ranges.
RooCategory is an object to represent discrete states.
Definition: RooCategory.h:28
RangeMap_t * _rangesPointerForIO
Pointer to the same object as _ranges, but not shared for I/O.
Definition: RooCategory.h:132
void addToRange(const char *rangeName, RooAbsCategory::value_type stateIndex)
Add the given state to the given range.
bool setIndex(Int_t index, bool printError=true) override
Set value by specifying the index code of the desired state.
void setRange(const char *rangeName, const char *stateNameList)
void defineTypes(const std::map< std::string, int > &allowedStates)
Define multiple states in a single call.
void writeToStream(std::ostream &os, bool compact) const override
compact only at the moment
static std::map< std::string, std::weak_ptr< RangeMap_t > > _sharedRangeIOHelper
Helper for restoring shared ranges from current versions of this class read from files....
Definition: RooCategory.h:139
TClass * IsA() const override
Definition: RooCategory.h:141
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.
void clearRange(const char *name, bool silent)
Clear the named range.
std::shared_ptr< RangeMap_t > _ranges
Map range names to allowed category states.
Definition: RooCategory.h:131
std::map< std::string, std::vector< value_type > > RangeMap_t
Definition: RooCategory.h:128
void Streamer(TBuffer &) override
Stream an object of class TObject.
static std::map< RooSharedProperties::UUID, std::weak_ptr< RangeMap_t > > _uuidToSharedRangeIOHelper
Helper for restoring shared ranges from old versions of this class read from files....
Definition: RooCategory.h:137
value_type & operator[](const std::string &stateName)
Access a named state.
bool readFromStream(std::istream &is, bool compact, bool verbose=false) override
Read object contents from given stream.
bool setLabel(const char *label, bool printError=true) override
Set value by specifying the name of the desired state.
std::map< std::string, RooAbsCategory::value_type > & states()
Return a reference to the map of state names to index states.
bool isStateInRange(const char *rangeName, RooAbsCategory::value_type stateIndex) const
Check if the state is in the given range.
value_type getCurrentIndex() const final
Return current index.
Definition: RooCategory.h:40
void installLegacySharedProp(const RooCategorySharedProperties *sp)
When reading old versions of the class, we get instances of shared properties.
~RooCategory() override
Destructor.
static TClass * Class()
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 Version_t ReadVersion(UInt_t *start=nullptr, UInt_t *bcnt=nullptr, const TClass *cl=nullptr)=0
virtual Int_t CheckByteCount(UInt_t startpos, UInt_t bcnt, const TClass *clss)=0
virtual Int_t ReadClassBuffer(const TClass *cl, void *pointer, const TClass *onfile_class=nullptr)=0
Definition: TBuffer.h:86
virtual Int_t WriteClassBuffer(const TClass *cl, void *pointer)=0
const char * GetName() const override
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:1919
const char * Data() const
Definition: TString.h:369
void(off) SmallVectorTemplateBase< T
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:63
static constexpr double second
Definition: civetweb.c:1856
TArc a
Definition: textangle.C:12