ATLAS Offline Software
Loading...
Searching...
No Matches
CP::MakeSystematicsVector Class Reference

This class handles turning the list of systematics into the actual list of nuisance parameter points to evaluate. More...

#include <MakeSystematicsVector.h>

Collaboration diagram for CP::MakeSystematicsVector:

Classes

struct  GroupConfig
 the configuration for the given group More...

Public Member Functions

void testInvariant () const
 test the invariant of this object
 MakeSystematicsVector ()
 standard default constructor
const std::vector< SystematicSet > & result (const std::string &label) const
 the list of nuisance parameter points generated with the given label
void calc (const SystematicSet &sysList)
 fill in result
void addGroup (const std::string &val_label)
 finish configuration for this group and add a new one
void setPattern (const std::string &val_pattern)
 set the pattern for the current group
void setSigma (float val_sigma)
 set the number of sigmas to vary this group by
void setToys (unsigned val_toys)
 set the number of toys to run for this group
void useForNominal ()
 set this group as the default, i.e.

Private Member Functions

std::vector< std::map< std::string, std::vector< SystematicVariation > > > calcBaseSys (const SystematicSet &sysList)
 make the list of base systematics for calc

Private Attributes

std::map< std::string, std::vector< SystematicSet > > m_result
 the value of result
std::vector< GroupConfigm_config
 the configuration on a per-group basis
std::string m_useForNominal
 the group for which useForNominal was set

Detailed Description

This class handles turning the list of systematics into the actual list of nuisance parameter points to evaluate.

This is meant as a placeholder for a generic tool to be developed by the statistics forum (or as a prototype for it).

For now I decided to keep it as a single class, but there are other options, e.g. the member MakeSystematicsVector::Config could be made a class that the user configures directly and then passes in. However, for now this approach seems better, as it hides some of the mechanics from the user and gives me more freedom on the backend side.

Definition at line 33 of file MakeSystematicsVector.h.

Constructor & Destructor Documentation

◆ MakeSystematicsVector()

CP::MakeSystematicsVector::MakeSystematicsVector ( )

standard default constructor

Guarantee
no-fail

Definition at line 70 of file MakeSystematicsVector.cxx.

72 : m_config (1)
73 {
74 RCU_NEW_INVARIANT (this);
75 }
#define RCU_NEW_INVARIANT(x)
Definition Assert.h:221
std::vector< GroupConfig > m_config
the configuration on a per-group basis

Member Function Documentation

◆ addGroup()

void CP::MakeSystematicsVector::addGroup ( const std::string & val_label)

finish configuration for this group and add a new one

Parameters
val_labelthe label for the new group
Guarantee
strong
Failures
out of memory II

Definition at line 181 of file MakeSystematicsVector.cxx.

183 {
186 config.label = val_label;
187 m_config.push_back (std::move(config));
188 }
#define RCU_CHANGE_INVARIANT(x)
Definition Assert.h:219
the configuration for the given group

◆ calc()

void CP::MakeSystematicsVector::calc ( const SystematicSet & sysList)

fill in result

Parameters
sysListthe list of systematics to use, usually the list of recommended systematics from your CP tools, framework or the systematics registry
Guarantee
strong
Failures
out of memory II
configuration errors

Definition at line 92 of file MakeSystematicsVector.cxx.

94 {
96
97 auto baseSys = calcBaseSys (sysList);
98
99 std::map<std::string,std::vector<SystematicSet>> myresult;
100 myresult[m_useForNominal].push_back (SystematicSet ());
101 for (std::size_t group = 0; group != m_config.size(); ++ group)
102 {
103 const auto& config = m_config[group];
104
105 // note: this is not just a short-cut, but also makes sure that
106 // we have an entry for each label, even if there are no
107 // systematics for the label
108 auto& subresult = myresult[config.label];
109
110 // this skips groups that don't match any requested systematics,
111 // which is mainly important for toy systematics as you wouldn't
112 // want to generate a bunch of empty systematics
113 if (baseSys[group].empty())
114 continue;
115
116 if (config.toys == 0)
117 {
118 for (auto sys : baseSys[group])
119 {
120 RCU_ASSERT (!sys.second.empty());
121 RCU_ASSERT (!sys.second.front().isToyEnsemble());
122 if (sys.second.front().isContinuousEnsemble())
123 {
124 // for continuous systematics
125 subresult.push_back(CP::SystematicSet());
126 subresult.back().insert (CP::SystematicVariation (sys.first, config.sigma));
127 subresult.push_back(CP::SystematicSet());
128 subresult.back().insert (CP::SystematicVariation (sys.first, -config.sigma));
129 } else if (sys.second.front().isEnsemble())
130 {
131 // we must have added a new kind of ensemble after I wrote
132 // this code
133 throw std::runtime_error ("unsupported ensemble systematic: " + sys.first);
134 } else
135 {
136 // otherwise just add all of them flat
137 for (const auto & mysys : sys.second)
138 {
139 subresult.push_back(CP::SystematicSet());
140 subresult.back().insert(mysys);
141 }
142 }
143 }
144 } else
145 {
146 std::vector<CP::SystematicSet> toys (config.toys);
147
148 for (auto sys : baseSys[group])
149 {
150 RCU_ASSERT (!sys.second.empty());
151 RCU_ASSERT (sys.second.front().isEnsemble());
152
153 if (sys.second.front().isContinuousEnsemble())
154 {
155 std::unique_ptr<TRandom3> random (new TRandom3);
156 random->SetSeed (hash_string (sys.first));
157
158 for (auto& toy : toys)
159 toy.insert (CP::SystematicVariation (sys.first, random->Gaus (0, config.sigma)));
160 } else if (sys.second.front().isToyEnsemble())
161 {
162 for (unsigned toy = 0; toy != config.toys; ++ toy)
163 toys[toy].insert (CP::SystematicVariation::makeToyVariation (sys.first, toy + 1, config.sigma));
164 } else
165 {
166 // we must have added a new kind of ensemble after I
167 // wrote this code
168 throw std::runtime_error ("unsupported ensemble systematic for toys: " + sys.first);
169 }
170 }
171 for (auto& toy : toys)
172 subresult.push_back (std::move (toy));
173 }
174 }
175
176 m_result = std::move(myresult);
177 }
#define RCU_ASSERT(x)
Definition Assert.h:210
static const Attributes_t empty
std::string m_useForNominal
the group for which useForNominal was set
std::vector< std::map< std::string, std::vector< SystematicVariation > > > calcBaseSys(const SystematicSet &sysList)
make the list of base systematics for calc
std::map< std::string, std::vector< SystematicSet > > m_result
the value of result
static SystematicVariation makeToyVariation(const std::string &basename, unsigned toyIndex, float toyScale)
constructor for toy systematics

◆ calcBaseSys()

std::vector< std::map< std::string, std::vector< SystematicVariation > > > CP::MakeSystematicsVector::calcBaseSys ( const SystematicSet & sysList)
private

make the list of base systematics for calc

Guarantee
strong
Failures
out of memory II

Definition at line 231 of file MakeSystematicsVector.cxx.

233 {
234 std::map<std::string,std::vector<SystematicVariation> > basesys;
235 for (const auto & sys : sysList)
236 {
237 basesys[sys.basename()].push_back (sys);
238 }
239 std::vector<std::map<std::string,std::vector<SystematicVariation> >>
240 basesysList (m_config.size());
241 for (auto sys : basesys)
242 {
243 // extract the ensemble if we have one
244 SystematicVariation ensemble;
245 for (const auto & mysys : sys.second)
246 {
247 if (mysys.isEnsemble())
248 {
249 if (!ensemble.empty())
250 throw std::runtime_error ("inconsistent ensembles requested: " + ensemble.name() + " " + mysys.name());
251 ensemble = mysys;
252 }
253 }
254
255 // setting this beyond the valid groups in case none matches
256 std::size_t group = m_config.size();
257 for (std::size_t iter = 0; iter != m_config.size(); ++ iter)
258 {
259 if (m_config[iter].pattern.empty())
260 {
261 // only use empty patterns if no previous pattern already took this
262 if (group == m_config.size())
263 {
264 if (m_config[iter].toys > 0)
265 {
266 if (ensemble.isToyEnsemble())
267 group = iter;
268 } else
269 {
270 if (!ensemble.isToyEnsemble())
271 group = iter;
272 }
273 }
274 } else if (RCU::match_expr (std::regex (m_config[iter].pattern.c_str()), sys.first))
275 {
276 if (m_config[iter].toys > 0 && ensemble.empty())
277 throw std::runtime_error ("toys only supported for ensemble systematics");
278 group = iter;
279 }
280 }
281 if (group == m_config.size())
282 throw std::runtime_error ("no systematics group for systematic: " + sys.first);
283
284 if (!ensemble.empty())
285 {
286 basesysList[group][sys.first].push_back (std::move(ensemble));
287 } else
288 {
289 basesysList[group][sys.first] = std::move (sys.second);
290 }
291 }
292 return basesysList;
293 }
bool isToyEnsemble() const
whether this represents a toy ensemble
bool empty() const
returns: whether this is an empty systematic, i.e.
const std::string & name() const
description: the full systematics name, for use in strings, etc.
bool match_expr(const std::regex &expr, std::string_view str)
returns: whether we can match the entire string with the regular expression guarantee: strong failure...

◆ result()

const std::vector< SystematicSet > & CP::MakeSystematicsVector::result ( const std::string & label) const

the list of nuisance parameter points generated with the given label

Guarantee
strong
Failures
unknown label
Precondition
calculate() has been called

Definition at line 79 of file MakeSystematicsVector.cxx.

81 {
82 RCU_READ_INVARIANT (this);
83 RCU_REQUIRE2 (!m_result.empty(), "calculate() has been called");
84 auto iter = m_result.find (label);
85 if (iter == m_result.end())
86 throw std::runtime_error ("unknown systematics group: " + label);
87 return iter->second;
88 }
#define RCU_REQUIRE2(x, y)
Definition Assert.h:198
#define RCU_READ_INVARIANT(x)
Definition Assert.h:217
std::string label(const std::string &format, int i)
Definition label.h:19

◆ setPattern()

void CP::MakeSystematicsVector::setPattern ( const std::string & val_pattern)

set the pattern for the current group

Guarantee
strong
Failures
out of memory II

Definition at line 192 of file MakeSystematicsVector.cxx.

194 {
196 m_config.back().pattern = val_pattern;
197 }

◆ setSigma()

void CP::MakeSystematicsVector::setSigma ( float val_sigma)

set the number of sigmas to vary this group by

Normally we are using just +/-1 sigma variations, but if the systematics are very small that can get lost in the statistical jitter. For those cases it is better to do a multi-sigma variation and then scale it back to +/-1 sigma thereby reducing the statistical jitter introduced into the systematic. A traditional scaling factor for these cases is to scale by five sigma.

Please note that if you do this, you normally only want to do this for small systematics for which statistical jitter is an issue. For large systematics there is a legitimate concern that a 5 sigma variation won't be 5 times the size of a 1 sigma variation, introducing a different kind of bias. To that end, if you use this, you should normally put the small systematics into a separate group from your regular systematics (which then also makes it easier for you to know which one to scale down).

Guarantee
no-fail
Precondition
val_sigma > 0

Definition at line 201 of file MakeSystematicsVector.cxx.

203 {
205 RCU_REQUIRE (val_sigma > 0);
206 m_config.back().sigma = val_sigma;
207 }
#define RCU_REQUIRE(x)
Definition Assert.h:196

◆ setToys()

void CP::MakeSystematicsVector::setToys ( unsigned val_toys)

set the number of toys to run for this group

This is a specialized mechanism pioneered for the e/gamma and muon scale factors. Instead of evaluating a large number of systematics separately, it allows to vary all of them at the same time repeatedly, so that instead of hundreds of systematic variations you only have to perform maybe 10 or 20 "toy" variations. You then just take the spread of the variations as the overall systematic uncertainty from the toys. There are reports that compared to the "old" method this can yield a factor five reduction in systematic uncertainty without increasing the number of systematic variations to evaluate.

Please note that this approach requires a more expert handling than "regular" systematics. The main point here is that the "toy" variations need a different post-processing than regular variations, i.e. you need to look at the spread between the output histograms for the different "toys" and use that to construct the combined systematic. The e/gamma group currently (Oct 15) provides a tool for that. For Bayesian marginalization you may alternatively consider to integrate the "toy" variations directly into the integral over nuisance parameter space to extract more information than you could with a single systematic.

A general concern for all methods that reduce the number of nuisance parameters (including the "toy" approach) is if you use profiling and are able to constrain the "toy" systematic, in which case we generally assume that such an approach is invalid. Similar precautions should be taken when used with Bayesian marginalization. This is typically of little practical concern as long as the systematic in question is small, but it is something that you should check for and that you should include in your supporting documentation.

The "toy" approach to systematics evaluation introduces an additional statistical jitter into your systematics, due to the sampling fluctuations of your "toy" variations. Naturally this uncertainty decreases if you use a larger number of "toy" variations. We currently (Oct 15) provide no recommendations for evaluating the size of that uncertainty or for determining whether your chosen number of "toys" is sufficient. Anecdotal evidence from the e/gamma group suggests that for evaluating their scale factor systematic as little as 10 or 20 "toys" may be sufficient. However, this will vary depending on your analysis and the systematics you use the "toys" for.

If you decide to go for a large number of "toy" variations it may be better to go for a "traditional" evaluation of your systematics instead, as that can cut down on the aforementioned statistical jitter by interpolating between the variations. The number of variations needed for the traditional approach to be better (or even feasible) will depend on the interpolation algorithm used and is likely to improve as we implement better interpolation algorithms.

Guarantee
no-fail

Definition at line 211 of file MakeSystematicsVector.cxx.

213 {
215 RCU_REQUIRE (val_toys > 0);
216 m_config.back().toys = val_toys;
217 }

◆ testInvariant()

void CP::MakeSystematicsVector::testInvariant ( ) const

test the invariant of this object

Guarantee
no-fail

Definition at line 62 of file MakeSystematicsVector.cxx.

64 {
65 RCU_INVARIANT (!m_config.empty());
66 }
#define RCU_INVARIANT(x)
Definition Assert.h:187

◆ useForNominal()

void CP::MakeSystematicsVector::useForNominal ( )

set this group as the default, i.e.

the group containing the nominal variation

Guarantee
no-fail

Definition at line 221 of file MakeSystematicsVector.cxx.

223 {
225 m_useForNominal = m_config.back().label;
226 }

Member Data Documentation

◆ m_config

std::vector<GroupConfig> CP::MakeSystematicsVector::m_config
private

the configuration on a per-group basis

Definition at line 229 of file MakeSystematicsVector.h.

◆ m_result

std::map<std::string,std::vector<SystematicSet> > CP::MakeSystematicsVector::m_result
private

the value of result

Definition at line 202 of file MakeSystematicsVector.h.

◆ m_useForNominal

std::string CP::MakeSystematicsVector::m_useForNominal
private

the group for which useForNominal was set

Definition at line 234 of file MakeSystematicsVector.h.


The documentation for this class was generated from the following files: