Skip to content

Commit 843a11b

Browse files
Merge branch 'master' into feat/workspace-poiless
2 parents 49f2704 + 6f4ca1a commit 843a11b

22 files changed

Lines changed: 706 additions & 752 deletions

docs/examples/notebooks/binderexample/StatisticalAnalysis.ipynb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@
148148
}
149149
],
150150
"source": [
151-
"print(f'Samples:\\n {workspace.samples}')\n",
152-
"print(f'Parameters:\\n {workspace.parameters}')"
151+
"print(f'Samples:\\n {pdf.config.samples}')\n",
152+
"print(f'Parameters:\\n {pdf.config.parameters}')"
153153
]
154154
},
155155
{

src/pyhf/cli/spec.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from pyhf.workspace import Workspace
88
from pyhf import modifiers
9+
from pyhf import parameters
910
from pyhf import utils
1011

1112
log = logging.getLogger(__name__)
@@ -72,14 +73,16 @@ def inspect(workspace, output_file, measurement):
7273
]
7374
result['modifiers'] = dict(ws.modifiers)
7475

76+
parset_descr = {
77+
parameters.paramsets.unconstrained: 'unconstrained',
78+
parameters.paramsets.constrained_by_normal: 'constrained_by_normal',
79+
parameters.paramsets.constrained_by_poisson: 'constrained_by_poisson',
80+
}
81+
82+
model = ws.model()
83+
7584
result['parameters'] = sorted(
76-
(
77-
parname,
78-
modifiers.registry[result['modifiers'][parname]]
79-
.required_parset([], [])['paramset_type']
80-
.__name__,
81-
)
82-
for parname in ws.parameters
85+
(k, parset_descr[type(v['paramset'])]) for k, v in model.config.par_map.items()
8386
)
8487
result['systematics'] = [
8588
(
@@ -97,7 +100,7 @@ def inspect(workspace, output_file, measurement):
97100

98101
maxlen_channels = max(map(len, ws.channels))
99102
maxlen_samples = max(map(len, ws.samples))
100-
maxlen_parameters = max(map(len, ws.parameters))
103+
maxlen_parameters = max(map(len, [p for p, _ in result['parameters']]))
101104
maxlen_measurements = max(map(lambda x: len(x[0]), result['measurements']))
102105
maxlen = max(
103106
[maxlen_channels, maxlen_samples, maxlen_parameters, maxlen_measurements]
@@ -174,7 +177,7 @@ def inspect(workspace, output_file, measurement):
174177
'--modifier-type',
175178
default=[],
176179
multiple=True,
177-
type=click.Choice(modifiers.uncombined.keys()),
180+
type=click.Choice(modifiers.histfactory_set.keys()),
178181
)
179182
@click.option('--measurement', default=[], multiple=True, metavar='<MEASUREMENT>...')
180183
def prune(

src/pyhf/infer/calculators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ def expected_value(self, nsigma):
628628
>>> samples = normal.sample((100,))
629629
>>> dist = pyhf.infer.calculators.EmpiricalDistribution(samples)
630630
>>> dist.expected_value(nsigma=1)
631-
6.15094381209505
631+
6.15094381209...
632632
633633
>>> import pyhf
634634
>>> import numpy.random as random

src/pyhf/mixins.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ def __init__(self, *args, **kwargs):
1818
super().__init__(*args, **kwargs)
1919
self.channels = []
2020
self.samples = []
21-
self.parameters = []
2221
self.modifiers = []
2322
# keep track of the width of each channel (how many bins)
2423
self.channel_nbins = {}
@@ -30,7 +29,6 @@ def __init__(self, *args, **kwargs):
3029
for sample in channel['samples']:
3130
self.samples.append(sample['name'])
3231
for modifier_def in sample['modifiers']:
33-
self.parameters.append(modifier_def['name'])
3432
self.modifiers.append(
3533
(
3634
modifier_def['name'], # mod name
@@ -40,7 +38,6 @@ def __init__(self, *args, **kwargs):
4038

4139
self.channels = sorted(list(set(self.channels)))
4240
self.samples = sorted(list(set(self.samples)))
43-
self.parameters = sorted(list(set(self.parameters)))
4441
self.modifiers = sorted(list(set(self.modifiers)))
4542
self.channel_nbins = {
4643
channel: self.channel_nbins[channel] for channel in self.channels

src/pyhf/modifiers/__init__.py

Lines changed: 26 additions & 193 deletions
Original file line numberDiff line numberDiff line change
@@ -1,214 +1,47 @@
1-
import logging
2-
3-
from pyhf import exceptions
4-
from pyhf import get_backend
5-
6-
log = logging.getLogger(__name__)
7-
8-
registry = {}
9-
10-
11-
def validate_modifier_structure(modifier):
12-
"""
13-
Check if given object contains the right structure for modifiers
14-
"""
15-
required_methods = ['required_parset']
16-
17-
for method in required_methods:
18-
if not hasattr(modifier, method):
19-
raise exceptions.InvalidModifier(
20-
f'Expected {method:s} method on modifier {modifier.__name__:s}'
21-
)
22-
return True
23-
24-
25-
def add_to_registry(
26-
cls, cls_name=None, constrained=False, pdf_type='normal', op_code='addition'
27-
):
28-
"""
29-
Consistent add_to_registry() function that handles actually adding thing to the registry.
30-
31-
Raises an error if the name to register for the modifier already exists in the registry,
32-
or if the modifier does not have the right structure.
33-
"""
34-
global registry
35-
cls_name = cls_name or cls.__name__
36-
if cls_name in registry:
37-
raise KeyError(f'The modifier name "{cls_name:s}" is already taken.')
38-
# validate the structure
39-
validate_modifier_structure(cls)
40-
# set is_constrained
41-
cls.is_constrained = constrained
42-
if constrained:
43-
tensorlib, _ = get_backend()
44-
if not hasattr(tensorlib, pdf_type):
45-
raise exceptions.InvalidModifier(
46-
f'The specified pdf_type "{pdf_type:s}" is not valid for {cls_name:s}({cls.__name__:s}). See pyhf.tensor documentation for available pdfs.'
47-
)
48-
cls.pdf_type = pdf_type
49-
else:
50-
cls.pdf_type = None
51-
52-
if op_code not in ['addition', 'multiplication']:
53-
raise exceptions.InvalidModifier(
54-
f'The specified op_code "{op_code:s}" is not valid for {cls_name:s}({cls.__name__:s}). See pyhf.modifier documentation for available operation codes.'
55-
)
56-
cls.op_code = op_code
57-
58-
registry[cls_name] = cls
59-
60-
61-
def modifier(*args, **kwargs):
62-
"""
63-
Decorator for registering modifiers. To flag the modifier as a constrained modifier, add `constrained=True`.
64-
65-
66-
Args:
67-
name (:obj:`str`): the name of the modifier to use. Use the class name by default. (default: None)
68-
constrained (:obj:`bool`): whether the modifier is constrained or not. (default: False)
69-
pdf_type (:obj:`str): the name of the pdf to use from tensorlib if constrained. (default: normal)
70-
op_code (:obj:`str`): the name of the operation the modifier performs on the data (e.g. addition, multiplication)
71-
72-
Returns:
73-
modifier
74-
75-
Raises:
76-
ValueError: too many keyword arguments, or too many arguments, or wrong arguments
77-
TypeError: provided name is not a string
78-
pyhf.exceptions.InvalidModifier: object does not have necessary modifier structure
79-
"""
80-
#
81-
# Examples:
82-
#
83-
# >>> @modifiers.modifier
84-
# >>> ... class myCustomModifier(object):
85-
# >>> ... @classmethod
86-
# >>> ... def required_parset(cls, sample_data, modifier_data): pass
87-
#
88-
# >>> @modifiers.modifier(name='myCustomNamer')
89-
# >>> ... class myCustomModifier(object):
90-
# >>> ... @classmethod
91-
# >>> ... def required_parset(cls, sample_data, modifier_data): pass
92-
#
93-
# >>> @modifiers.modifier(constrained=False)
94-
# >>> ... class myUnconstrainedModifier(object):
95-
# >>> ... @classmethod
96-
# >>> ... def required_parset(cls, sample_data, modifier_data): pass
97-
# >>> ...
98-
# >>> myUnconstrainedModifier.pdf_type
99-
# None
100-
#
101-
# >>> @modifiers.modifier(constrained=True, pdf_type='poisson')
102-
# >>> ... class myConstrainedCustomPoissonModifier(object):
103-
# >>> ... @classmethod
104-
# >>> ... def required_parset(cls, sample_data, modifier_data): pass
105-
# >>> ...
106-
# >>> myConstrainedCustomGaussianModifier.pdf_type
107-
# 'poisson'
108-
#
109-
# >>> @modifiers.modifier(constrained=True)
110-
# >>> ... class myCustomModifier(object):
111-
# >>> ... @classmethod
112-
# >>> ... def required_parset(cls, sample_data, modifier_data): pass
113-
#
114-
# >>> @modifiers.modifier(op_code='multiplication')
115-
# >>> ... class myMultiplierModifier(object):
116-
# >>> ... @classmethod
117-
# >>> ... def required_parset(cls, sample_data, modifier_data): pass
118-
# >>> ...
119-
# >>> myMultiplierModifier.op_code
120-
# 'multiplication'
121-
122-
def _modifier(name, constrained, pdf_type, op_code):
123-
def wrapper(cls):
124-
add_to_registry(
125-
cls,
126-
cls_name=name,
127-
constrained=constrained,
128-
pdf_type=pdf_type,
129-
op_code=op_code,
130-
)
131-
return cls
132-
133-
return wrapper
134-
135-
name = kwargs.pop('name', None)
136-
constrained = bool(kwargs.pop('constrained', False))
137-
pdf_type = str(kwargs.pop('pdf_type', 'normal'))
138-
op_code = str(kwargs.pop('op_code', 'addition'))
139-
# check for unparsed keyword arguments
140-
if kwargs:
141-
raise ValueError(f'Unparsed keyword arguments {kwargs.keys()}')
142-
# check to make sure the given name is a string, if passed in one
143-
if not isinstance(name, str) and name is not None:
144-
raise TypeError(f'@modifier must be given a string. You gave it {type(name)}')
145-
146-
if not args:
147-
# called like @modifier(name='foo', constrained=False, pdf_type='normal', op_code='addition')
148-
return _modifier(name, constrained, pdf_type, op_code)
149-
if len(args) == 1:
150-
# called like @modifier
151-
if not callable(args[0]):
152-
raise ValueError('You must decorate a callable python object')
153-
add_to_registry(
154-
args[0],
155-
cls_name=name,
156-
constrained=constrained,
157-
pdf_type=pdf_type,
158-
op_code=op_code,
159-
)
160-
return args[0]
161-
raise ValueError(
162-
f'@modifier must be called with only keyword arguments, @modifier(name=\'foo\'), or no arguments, @modifier; ({len(args):d} given)'
163-
)
164-
165-
166-
from pyhf.modifiers.histosys import histosys, histosys_combined
167-
from pyhf.modifiers.lumi import lumi, lumi_combined
168-
from pyhf.modifiers.normfactor import normfactor, normfactor_combined
169-
from pyhf.modifiers.normsys import normsys, normsys_combined
170-
from pyhf.modifiers.shapefactor import shapefactor, shapefactor_combined
171-
from pyhf.modifiers.shapesys import shapesys, shapesys_combined
172-
from pyhf.modifiers.staterror import staterror, staterror_combined
173-
174-
uncombined = {
175-
'histosys': histosys,
176-
'lumi': lumi,
177-
'normfactor': normfactor,
178-
'normsys': normsys,
179-
'shapefactor': shapefactor,
180-
'shapesys': shapesys,
181-
'staterror': staterror,
182-
}
183-
184-
combined = {
185-
'histosys': histosys_combined,
186-
'lumi': lumi_combined,
187-
'normfactor': normfactor_combined,
188-
'normsys': normsys_combined,
189-
'shapefactor': shapefactor_combined,
190-
'shapesys': shapesys_combined,
191-
'staterror': staterror_combined,
192-
}
1+
from pyhf.modifiers.histosys import histosys_builder, histosys_combined
2+
from pyhf.modifiers.lumi import lumi_builder, lumi_combined
3+
from pyhf.modifiers.normfactor import normfactor_builder, normfactor_combined
4+
from pyhf.modifiers.normsys import normsys_builder, normsys_combined
5+
from pyhf.modifiers.shapefactor import shapefactor_builder, shapefactor_combined
6+
from pyhf.modifiers.shapesys import shapesys_builder, shapesys_combined
7+
from pyhf.modifiers.staterror import staterror_builder, staterror_combined
1938

1949
__all__ = [
195-
"combined",
10+
"histfactory_set",
19611
"histosys",
12+
"histosys_builder",
19713
"histosys_combined",
19814
"lumi",
15+
"lumi_builder",
19916
"lumi_combined",
20017
"normfactor",
18+
"normfactor_builder",
20119
"normfactor_combined",
20220
"normsys",
21+
"normsys_builder",
20322
"normsys_combined",
20423
"shapefactor",
24+
"shapefactor_builder",
20525
"shapefactor_combined",
20626
"shapesys",
27+
"shapesys_builder",
20728
"shapesys_combined",
20829
"staterror",
30+
"staterror_builder",
20931
"staterror_combined",
21032
]
21133

21234

21335
def __dir__():
21436
return __all__
37+
38+
39+
histfactory_set = {
40+
"histosys": (histosys_builder, histosys_combined),
41+
"lumi": (lumi_builder, lumi_combined),
42+
"normfactor": (normfactor_builder, normfactor_combined),
43+
"normsys": (normsys_builder, normsys_combined),
44+
"shapefactor": (shapefactor_builder, shapefactor_combined),
45+
"shapesys": (shapesys_builder, shapesys_combined),
46+
"staterror": (staterror_builder, staterror_combined),
47+
}

0 commit comments

Comments
 (0)