Skip to content

Commit a2bf5bb

Browse files
lukasheinrichkratsg
authored andcommitted
Update interpolators and add patch functionality to json2xml (#505)
- interpolator codes can be configurable for histosys similarly to normsys - pyhf cls is updated to use the ROOT default - remove extra (duplicate) interpolator code that got added [codex == code4p] - pyhf json2xml now allows for patch files (#507) - remove extraneous print statements in tests
1 parent e7e297d commit a2bf5bb

File tree

6 files changed

+41
-140
lines changed

6 files changed

+41
-140
lines changed

pyhf/commandline.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import click
44
import json
55
import os
6+
import jsonpatch
67

78
from .utils import hypotest, EqDelimStringParamType
89
from .pdf import Workspace
@@ -70,7 +71,8 @@ def xml2json(entrypoint_xml, basedir, output_file, track_progress):
7071
@click.option('--specroot', default='config')
7172
@click.option('--dataroot', default='data')
7273
@click.option('--resultprefix', default='FitConfig')
73-
def json2xml(workspace, output_dir, specroot, dataroot, resultprefix):
74+
@click.option('-p', '--patch', multiple=True)
75+
def json2xml(workspace, output_dir, specroot, dataroot, resultprefix, patch):
7476
try:
7577
import uproot
7678

@@ -86,6 +88,9 @@ def json2xml(workspace, output_dir, specroot, dataroot, resultprefix):
8688
ensure_dirs(output_dir)
8789
with click.open_file(workspace, 'r') as specstream:
8890
d = json.load(specstream)
91+
for pfile in patch:
92+
patch = json.loads(click.open_file(pfile, 'r').read())
93+
d = jsonpatch.JsonPatch(patch).apply(d)
8994
ensure_dirs(os.path.join(output_dir, specroot))
9095
ensure_dirs(os.path.join(output_dir, dataroot))
9196
with click.open_file(
@@ -238,7 +243,10 @@ def cls(
238243
p = w.model(
239244
measurement_name=measurement,
240245
patches=patches,
241-
modifier_settings={'normsys': {'interpcode': 'code4'}},
246+
modifier_settings={
247+
'normsys': {'interpcode': 'code4'},
248+
'histosys': {'interpcode': 'code4p'},
249+
},
242250
)
243251

244252
optconf = {k: v for item in optconf for k, v in item.items()}

pyhf/interpolators/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,4 @@ def get(interpcode, do_tensorized_calc=True):
3939
raise exceptions.InvalidInterpCode
4040

4141

42-
__all__ = ['code0', 'code1', 'code2']
42+
__all__ = ['code0', 'code1', 'code2', 'code4', 'code4p']

pyhf/interpolators/codex.py

Lines changed: 0 additions & 132 deletions
This file was deleted.

pyhf/modifiers/histosys.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ def required_parset(cls, n_parameters):
2525

2626

2727
class histosys_combined(object):
28-
def __init__(self, histosys_mods, pdfconfig, mega_mods):
28+
def __init__(self, histosys_mods, pdfconfig, mega_mods, interpcode='code0'):
2929
self._parindices = list(range(len(pdfconfig.suggested_init())))
30+
self.interpcode = interpcode
31+
assert self.interpcode in ['code0', 'code2', 'code4p']
3032

3133
pnames = [pname for pname, _ in histosys_mods]
3234
keys = ['{}/{}'.format(mtype, m) for m, mtype in histosys_mods]
@@ -48,7 +50,9 @@ def __init__(self, histosys_mods, pdfconfig, mega_mods):
4850
]
4951

5052
if len(histosys_mods):
51-
self.interpolator = interpolators.code0(self._histosys_histoset)
53+
self.interpolator = getattr(interpolators, self.interpcode)(
54+
self._histosys_histoset
55+
)
5256

5357
self._precompute()
5458
events.subscribe('tensorlib_changed')(self._precompute)

pyhf/pdf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,9 +333,9 @@ def _create_nominal_and_modifiers(self):
333333
self.thenom = default_backend.reshape(
334334
thenom,
335335
(
336-
1,
336+
1, # modifier dimension.. thenom is the base
337337
len(self.config.samples),
338-
1,
338+
1, # alphaset dimension
339339
sum(list(self.config.channel_nbins.values())),
340340
),
341341
)

tests/test_scripts.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,13 @@ def test_patch(tmpdir, script_runner):
130130
command = 'pyhf cls {0:s} --patch {1:s}'.format(temp.strpath, patch.strpath)
131131
ret = script_runner.run(*shlex.split(command))
132132
assert ret.success
133+
134+
command = 'pyhf json2xml {0:s} --output-dir {1:s} --patch {2:s}'.format(
135+
temp.strpath, tmpdir.mkdir('output_1').strpath, patch.strpath
136+
)
137+
ret = script_runner.run(*shlex.split(command))
138+
assert ret.success
139+
133140
import io
134141

135142
command = 'pyhf cls {0:s} --patch -'.format(temp.strpath, patch.strpath)
@@ -138,7 +145,15 @@ def test_patch(tmpdir, script_runner):
138145
patchcontent
139146
) # python 2.7 pytest-files are not file-like enough
140147
ret = script_runner.run(*shlex.split(command), stdin=pipefile)
141-
print(ret.stderr)
148+
assert ret.success
149+
150+
command = 'pyhf json2xml {0:s} --output-dir {1:s} --patch -'.format(
151+
temp.strpath, tmpdir.mkdir('output_2').strpath, patch.strpath
152+
)
153+
pipefile = io.StringIO(
154+
patchcontent
155+
) # python 2.7 pytest-files are not file-like enough
156+
ret = script_runner.run(*shlex.split(command), stdin=pipefile)
142157
assert ret.success
143158

144159

@@ -157,6 +172,12 @@ def test_patch_fail(tmpdir, script_runner):
157172
ret = script_runner.run(*shlex.split(command))
158173
assert not ret.success
159174

175+
command = 'pyhf json2xml {0:s} --output-dir {1:s} --patch {2:s}'.format(
176+
temp.strpath, tmpdir.mkdir('output').strpath, patch.strpath
177+
)
178+
ret = script_runner.run(*shlex.split(command))
179+
assert not ret.success
180+
160181

161182
def test_bad_measurement_name(tmpdir, script_runner):
162183
temp = tmpdir.join("parsed_output.json")

0 commit comments

Comments
 (0)