Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
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.
289Bool_t RooCategory::readFromStream(istream& is, Bool_t /*compact*/, Bool_t verbose)
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
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) {
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) {
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
473 R__b.WriteClassBuffer(RooCategory::Class(), this);
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 for (auto * olist : static_range_cast<TList*>(props->_altRanges)) {
496 std::vector<value_type>& vec = rangesMap[olist->GetName()];
497
498
499 std::unique_ptr<TIterator> citer(olist->MakeIterator());
500 while (RooCatType* ctype = (RooCatType*)citer->Next()) {
501 vec.push_back(ctype->getVal());
502 }
503 }
504
505 // Register the shared_ptr for future sharing
506 weakPtr = _ranges;
507 }
508}
509
510
511/// In current versions of the class, a map with ranges can be shared between instances.
512/// If an instance with the same name alreday uses the same map, the instances will start sharing.
513/// Otherwise, this instance will be registered, and future copies being read will share with this
514/// one.
515void RooCategory::installSharedRange(std::unique_ptr<RangeMap_t>&& rangeMap) {
516 if (rangeMap == nullptr)
517 return;
518
519 auto checkRangeMapsEqual = [](const RooCategory::RangeMap_t& a, const RooCategory::RangeMap_t& b) {
520 if (&a == &b)
521 return true;
522
523 if (a.size() != b.size())
524 return false;
525
526 auto vecsEqual = [](const std::vector<RooAbsCategory::value_type>& aa, const std::vector<RooAbsCategory::value_type>& bb) {
527 return aa.size() == bb.size() && std::equal(aa.begin(), aa.end(), bb.begin());
528 };
529
530 for (const auto& itemA : a) {
531 const auto itemB = b.find(itemA.first);
532 if (itemB == b.end())
533 return false;
534
535 if (!vecsEqual(itemA.second, itemB->second))
536 return false;
537 }
538
539 return true;
540 };
541
542
543 auto& weakPtr = _sharedRangeIOHelper[GetName()];
544 auto existingMap = weakPtr.lock();
545 if (existingMap && checkRangeMapsEqual(*rangeMap, *existingMap)) {
546 // We know this map, use the shared one.
547 _ranges = std::move(existingMap);
548 if (rangeMap.get() == _ranges.get()) {
549 // This happens when ROOT's IO has written the same pointer twice. We cannot delete now.
550 (void) rangeMap.release(); // NOLINT: clang-tidy is normally right that this leaks. Here, we need to leave the result unused, though.
551 }
552 } else {
553 // We don't know this map. Register for sharing.
554 _ranges = std::move(rangeMap);
555 weakPtr = _ranges;
556 }
557}
typedef void(GLAPIENTRYP _GLUfuncptr)(void)
#define b(i)
Definition RSha256.hxx:100
#define a(i)
Definition RSha256.hxx:99
#define coutI(a)
#define coutW(a)
#define coutE(a)
#define TRACE_DESTROY
Definition RooTrace.h:24
#define TRACE_CREATE
Definition RooTrace.h:23
short Version_t
Definition RtypesCore.h:65
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:510
friend void RooRefArray::Streamer(TBuffer &)
void setValueDirty()
Mark the element dirty. This forces a re-evaluation when a value is requested.
Definition RooAbsArg.h:505
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.
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.
static const decltype(_stateNames) ::value_type & invalidCategory()
Is this category attached to a tree?
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
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
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.
std::map< std::string, std::vector< value_type > > RangeMap_t
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
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.
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
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.