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
21Object 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 "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 std::endl, std::istream, std::ostream;
95
96
97std::map<RooSharedProperties::UUID, std::weak_ptr<RooCategory::RangeMap_t>> RooCategory::_uuidToSharedRangeIOHelper; // Helper for restoring shared properties
98std::map<std::string, std::weak_ptr<RooCategory::RangeMap_t>> RooCategory::_sharedRangeIOHelper;
99
100
101////////////////////////////////////////////////////////////////////////////////
102
107
108
109
110////////////////////////////////////////////////////////////////////////////////
111/// Constructor. Types must be defined using defineType() before variable can be used
112RooCategory::RooCategory(const char *name, const char *title) :
114 _ranges{std::make_unique<RangeMap_t>()}
115{
116 setValueDirty() ;
117 setShapeDirty() ;
119}
120
121
122////////////////////////////////////////////////////////////////////////////////
123/// Create a new category and define allowed states.
124/// \param[in] name Name used to refer to this object.
125/// \param[in] title Title for e.g. plotting.
126/// \param[in] allowedStates Map of allowed states. Pass e.g. `{ {"0Lep", 0}, {"1Lep:, 1} }`
127RooCategory::RooCategory(const char* name, const char* title, const std::map<std::string, int>& allowedStates) :
129 _ranges{std::make_unique<RangeMap_t>()}
130{
132}
133
134
135
136////////////////////////////////////////////////////////////////////////////////
137/// Copy constructor
138
141 _ranges(other._ranges)
142{
144}
145
146
147////////////////////////////////////////////////////////////////////////////////
148/// Destructor
149
154
155
156
157
158////////////////////////////////////////////////////////////////////////////////
159/// Set value by specifying the index code of the desired state.
160/// If printError is set, a message will be printed if
161/// the specified index does not represent a valid state.
162/// \return bool signalling if an error occurred.
163bool RooCategory::setIndex(Int_t index, bool printError)
164{
165 if (!hasIndex(index)) {
166 if (printError) {
167 coutE(InputArguments) << "RooCategory: Trying to set invalid state " << index << " for category " << GetName() << std::endl;
168 }
169 return true;
170 }
171
174
175 return false;
176}
177
178
179
180////////////////////////////////////////////////////////////////////////////////
181/// Set value by specifying the name of the desired state.
182/// If printError is set, a message will be printed if
183/// the specified label does not represent a valid state.
184/// \return false on success.
185bool RooCategory::setLabel(const char* label, bool printError)
186{
187 const auto item = stateNames().find(label);
188 if (item != stateNames().end()) {
189 _currentIndex = item->second;
191 return false;
192 }
193
194 if (printError) {
195 coutE(InputArguments) << "Trying to set invalid state label '" << label << "' for category " << GetName() << std::endl;
196 }
197
198 return true;
199}
200
201
202
203////////////////////////////////////////////////////////////////////////////////
204/// Define a state with given name.
205/// The lowest available positive integer is assigned as index. Category
206/// state labels may not contain semicolons.
207/// \return True in case of an error.
208bool RooCategory::defineType(const std::string& label)
209{
210 if (label.find(';') != std::string::npos) {
211 coutE(InputArguments) << "RooCategory::defineType(" << GetName()
212 << "): semicolons not allowed in label name" << std::endl ;
213 return true;
214 }
215
217}
218
219
220////////////////////////////////////////////////////////////////////////////////
221/// Define a state with given name and index. Category
222/// state labels may not contain semicolons.
223/// \return True in case of error.
224bool RooCategory::defineType(const std::string& label, Int_t index)
225{
226 if (label.find(';') != std::string::npos) {
227 coutE(InputArguments) << "RooCategory::defineType(" << GetName()
228 << "): semicolons not allowed in label name" << std::endl ;
229 return true;
230 }
231
233}
234
235
236////////////////////////////////////////////////////////////////////////////////
237/// Define multiple states in a single call. Use like:
238/// ```
239/// myCat.defineTypes({ {"0Lep", 0}, {"1Lep", 1}, {"2Lep", 2}, {"3Lep", 3} });
240/// ```
241/// Note: When labels or indices are defined multiple times, an error message is printed,
242/// and the corresponding state is ignored.
243void RooCategory::defineTypes(const std::map<std::string, int>& allowedStates) {
244 for (const auto& nameAndIdx : allowedStates) {
245 defineType(nameAndIdx.first, nameAndIdx.second);
246 }
247}
248
249
250////////////////////////////////////////////////////////////////////////////////
251/// Access a named state. If a state with this name doesn't exist yet, the state is
252/// assigned the next available positive integer.
253/// \param[in] stateName Name of the state to be accessed.
254/// \return Reference to the category index. If no state exists, it will be created on the fly.
257 if (stateNames().count(stateName) == 0) {
258 _insertionOrder.push_back(stateName);
260
261 }
262
263 return stateNames()[stateName];
264}
265
266
267////////////////////////////////////////////////////////////////////////////////
268/// Return a reference to the map of state names to index states.
269/// This can be used to manipulate the category.
270/// \note Calling this function will **always** trigger recomputations of
271/// of **everything** that depends on this category, since in case the map gets
272/// manipulated, names or indices might change. Also, the order that states have
273/// been inserted in gets lost. This changes what is returned by getOrdinal().
274std::map<std::string, RooAbsCategory::value_type>& RooCategory::states() {
275 auto& theStates = stateNames();
278 _insertionOrder.clear();
279 return theStates;
280}
281
282
283////////////////////////////////////////////////////////////////////////////////
284/// Read object contents from given stream. If token is a decimal digit, try to
285/// find a corresponding state for it. If that succeeds, the state denoted by this
286/// index is used. Otherwise, interpret it as a label.
287bool RooCategory::readFromStream(istream& is, bool /*compact*/, bool verbose)
288{
289 // Read single token
290 RooStreamParser parser(is) ;
291 TString token = parser.readToken() ;
292
293 if (token.IsDec() && hasIndex(std::stoi(token.Data()))) {
294 return setIndex(std::stoi(token.Data()), verbose);
295 } else {
296 return setLabel(token,verbose) ;
297 }
298}
299
300
301
302////////////////////////////////////////////////////////////////////////////////
303/// compact only at the moment
304
305void RooCategory::writeToStream(ostream& os, bool compact) const
306{
307 if (compact) {
308 os << getCurrentIndex() ;
309 } else {
310 os << getCurrentLabel() ;
311 }
312}
313
314
315////////////////////////////////////////////////////////////////////////////////
316/// Clear the named range.
317/// \note This affects **all** copies of this category, because they are sharing
318/// range definitions. This ensures that categories inside a dataset and their
319/// counterparts on the outside will both see a modification of the range.
320void RooCategory::clearRange(const char* name, bool silent)
321{
322 std::map<std::string, std::vector<value_type>>::iterator item = _ranges->find(name);
323 if (item == _ranges->end()) {
324 if (!silent)
325 coutE(InputArguments) << "RooCategory::clearRange(" << GetName() << ") ERROR: must specify valid range name" << std::endl ;
326 return;
327 }
328
329 _ranges->erase(item);
330}
331
332
333////////////////////////////////////////////////////////////////////////////////
334
335void RooCategory::setRange(const char* name, const char* stateNameList)
336{
337 clearRange(name,true) ;
339}
340
341
342////////////////////////////////////////////////////////////////////////////////
343/// Add the given state to the given range.
344/// \note This creates or accesses a **shared** map with allowed ranges. All copies of this
345/// category will share this range such that a category inside a dataset and its
346/// counterpart on the outside will both see a modification of the range.
348 auto item = _ranges->find(name);
349 if (item == _ranges->end()) {
350 if (!name) {
351 coutE(Contents) << "RooCategory::addToRange(" << GetName()
352 << "): Need valid range name." << std::endl;
353 return;
354 }
355
356 item = _ranges->emplace(name, std::vector<value_type>()).first;
357 coutI(Contents) << "RooCategory::setRange(" << GetName()
358 << ") new range named '" << name << "' created for state " << stateIndex << std::endl ;
359 }
360
361 item->second.push_back(stateIndex);
362}
363
364
365////////////////////////////////////////////////////////////////////////////////
366/// Add the list of state names to the given range. State names can be separated
367/// with ','.
368/// \note This creates or accesses a **shared** map with allowed ranges. All copies of this
369/// category will share this range such that a category inside a dataset and its
370/// counterpart on the outside will both see a modification of the range.
371void RooCategory::addToRange(const char* name, const char* stateNameList)
372{
373 if (!stateNameList) {
374 coutE(InputArguments) << "RooCategory::setRange(" << GetName() << ") ERROR: must specify valid name and state name list" << std::endl ;
375 return;
376 }
377
378 // Parse list of state names, verify that each is valid and add them to the list
379 for (const auto& token : ROOT::Split(stateNameList, ",")) {
380 const value_type idx = lookupIndex(token);
381 if (idx != invalidCategory().second) {
382 addToRange(name, idx);
383 } else {
384 coutW(InputArguments) << "RooCategory::setRange(" << GetName() << ") WARNING: Ignoring invalid state name '"
385 << token << "' in state name list" << std::endl ;
386 }
387 }
388}
389
390
391////////////////////////////////////////////////////////////////////////////////
392/// Check if the state is in the given range.
393/// If no range is specified either as argument or if no range has been defined for this category
394/// (*i.e.*, the default range is meant), all category states count as being in range.
396 if (rangeName == nullptr || _ranges->empty())
397 return true;
398
399 const auto item = _ranges->find(rangeName);
400 if (item == _ranges->end())
401 return false;
402
403 const std::vector<value_type>& vec = item->second;
404 return std::find(vec.begin(), vec.end(), stateIndex) != vec.end();
405}
406
407
408////////////////////////////////////////////////////////////////////////////////
409/// Check if the state is in the given range.
410/// If no range is specified (*i.e.*, the default range), all category states count as being in range.
411/// This overload requires a name lookup. Recommend to use the category index with
412/// RooCategory::isStateInRange(const char*, RooAbsCategory::value_type) const.
413bool RooCategory::isStateInRange(const char* rangeName, const char* stateName) const
414{
415 // Check that both input arguments are not null pointers
416 if (!rangeName) {
417 return true;
418 }
419
420 if (!stateName) {
421 coutE(InputArguments) << "RooCategory::isStateInRange(" << GetName() << ") ERROR: must specify valid state name" << std::endl ;
422 return false;
423 }
424
425 return isStateInRange(rangeName, lookupIndex(stateName));
426}
427
428
429////////////////////////////////////////////////////////////////////////////////
430
432{
433 UInt_t R__s;
434 UInt_t R__c;
435 if (R__b.IsReading()) {
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:
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);
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
472 R__b.WriteClassBuffer(RooCategory::Class(), this);
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
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_unique<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
499 vec.push_back(ctype->getVal());
500 }
501 }
502
503 // Register the shared_ptr for future sharing
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 already 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
518 if (&a == &b)
519 return true;
520
521 if (a.size() != b.size())
522 return false;
523
524 for (const auto& itemA : a) {
525 const auto itemB = b.find(itemA.first);
526 if (itemB == b.end())
527 return false;
528
529 if (itemA.second != itemB->second)
530 return false;
531 }
532
533 return true;
534 };
535
536
538 auto existingMap = weakPtr.lock();
540 // We know this map, use the shared one.
541 _ranges = std::move(existingMap);
542 if (rangeMap.get() == _ranges.get()) {
543 // This happens when ROOT's IO has written the same pointer twice. We cannot delete now.
544 (void) rangeMap.release(); // NOLINT: clang-tidy is normally right that this leaks. Here, we need to leave the result unused, though.
545 }
546 } else {
547 // We don't know this map. Register for sharing.
548 _ranges = std::move(rangeMap);
550 }
551}
#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
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
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
const_iterator end() const
void setShapeDirty()
Notify that a shape-like property (e.g. binning) has changed.
Definition RooAbsArg.h:436
friend void RooRefArray::Streamer(TBuffer &)
void setValueDirty()
Mark the element dirty. This forces a re-evaluation when a value is requested.
Definition RooAbsArg.h:431
Abstract base class for objects that represent a discrete value that can be set from the outside,...
virtual const char * getCurrentLabel() const
Return label string of current state.
value_type _currentIndex
Current category state.
value_type nextAvailableStateIndex() const
static const decltype(_stateNames) ::value_type & invalidCategory()
A category state to signify an invalid category.
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.
value_type lookupIndex(const std::string &stateName) const
Find the index number corresponding to the state name.
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.
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....
TClass * IsA() const override
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.
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....
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
std::map< std::string, std::vector< value_type > > RangeMap_t
void installLegacySharedProp(const RooCategorySharedProperties *sp)
When reading old versions of the class, we get instances of shared properties.
~RooCategory() override
Destructor.
static TClass * Class()
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
const char * GetName() const override
Returns name of object.
Definition TNamed.h:47
Basic string class.
Definition TString.h:139
std::vector< std::string > Split(std::string_view str, std::string_view delims, bool skipEmpty=false)
Splits a string at each character in delims.