From 3db296fea17b0cc47f397d643b02d26f6c4a28a2 Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Tue, 30 Aug 2022 12:07:33 -0700 Subject: [PATCH 1/9] add mixin to docs api --- docs/api.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api.rst b/docs/api.rst index 9a4fa772f3..bf0e1c15eb 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -44,6 +44,7 @@ Making Models from PDFs ~pdf.Model ~pdf._ModelConfig + ~mixins._ChannelSummaryMixin ~workspace.Workspace ~patchset.PatchSet ~patchset.Patch From 4fd32950db8d84b96ae2088592869a43bb0f2a31 Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Tue, 30 Aug 2022 12:07:53 -0700 Subject: [PATCH 2/9] set as @property to show up in docs and type it up --- pyproject.toml | 1 - src/pyhf/mixins.py | 74 +++++++++++++++++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2708c44b7f..035defaab5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -136,7 +136,6 @@ module = [ 'pyhf.compat', 'pyhf.events', 'pyhf.utils', - 'pyhf.mixins', 'pyhf.constraints', 'pyhf.pdf', 'pyhf.simplemodels', diff --git a/src/pyhf/mixins.py b/src/pyhf/mixins.py index 30a0919a48..e42352f716 100644 --- a/src/pyhf/mixins.py +++ b/src/pyhf/mixins.py @@ -1,4 +1,7 @@ +from __future__ import annotations import logging +from typing import Sequence, Any +from pyhf.typing import Channel log = logging.getLogger(__name__) @@ -13,39 +16,74 @@ class _ChannelSummaryMixin: **channels: A list of channels to provide summary information about. Follows the `defs.json#/definitions/channel` schema. """ - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Sequence[Channel]): channels = kwargs.pop('channels') super().__init__(*args, **kwargs) - self.channels = [] - self.samples = [] - self.modifiers = [] + self._channels: list[str] = [] + self._samples: list[str] = [] + self._modifiers: list[tuple[str, str]] = [] # keep track of the width of each channel (how many bins) - self.channel_nbins = {} + self._channel_nbins: dict[str, int] = {} # need to keep track in which order we added the constraints # so that we can generate correctly-ordered data for channel in channels: - self.channels.append(channel['name']) - self.channel_nbins[channel['name']] = len(channel['samples'][0]['data']) + self._channels.append(channel['name']) + self._channel_nbins[channel['name']] = len(channel['samples'][0]['data']) for sample in channel['samples']: - self.samples.append(sample['name']) + self._samples.append(sample['name']) for modifier_def in sample['modifiers']: - self.modifiers.append( + self._modifiers.append( ( modifier_def['name'], # mod name modifier_def['type'], # mod type ) ) - self.channels = sorted(list(set(self.channels))) - self.samples = sorted(list(set(self.samples))) - self.modifiers = sorted(list(set(self.modifiers))) - self.channel_nbins = { - channel: self.channel_nbins[channel] for channel in self.channels + self._channels = sorted(list(set(self._channels))) + self._samples = sorted(list(set(self._samples))) + self._modifiers = sorted(list(set(self._modifiers))) + self._channel_nbins = { + channel: self._channel_nbins[channel] for channel in self._channels } - self.channel_slices = {} + self._channel_slices = {} begin = 0 - for c in self.channels: - end = begin + self.channel_nbins[c] - self.channel_slices[c] = slice(begin, end) + for c in self._channels: + end = begin + self._channel_nbins[c] + self._channel_slices[c] = slice(begin, end) begin = end + + @property + def channels(self) -> list[str]: + """ + Ordered list of channel names in the model. + """ + return self._channels + + @property + def samples(self) -> list[str]: + """ + Ordered list of sample names in the model. + """ + return self._samples + + @property + def modifiers(self) -> list[tuple[str, str]]: + """ + Ordered list of pairs of modifier name/type in the model. + """ + return self._modifiers + + @property + def channel_nbins(self) -> dict[str, int]: + """ + Dictionary mapping channel name to number of bins in the channel. + """ + return self._channel_nbins + + @property + def channel_slices(self) -> dict[str, slice]: + """ + Dictionary mapping channel name to the bin slices in the model. + """ + return self._channel_slices From 3b7a45c9c4e71bd0566a1e8e5bf8f8539ca7c0e7 Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Tue, 30 Aug 2022 13:33:24 -0700 Subject: [PATCH 3/9] fix test --- tests/test_workspace.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/test_workspace.py b/tests/test_workspace.py index 6f1f380307..f4f52ca096 100644 --- a/tests/test_workspace.py +++ b/tests/test_workspace.py @@ -170,11 +170,16 @@ def test_get_workspace_data(workspace_factory, include_auxdata): assert w.data(m, include_auxdata=include_auxdata) -def test_get_workspace_data_bad_model(workspace_factory, caplog): +def test_get_workspace_data_bad_model(workspace_factory, caplog, mocker): w = workspace_factory() m = w.model() # the iconic fragrance of an expected failure - m.config.channels = [c.replace('channel', 'chanel') for c in m.config.channels] + + mocker.patch( + 'pyhf.mixins._ChannelSummaryMixin.channels', + new_callable=mocker.PropertyMock, + return_value=['chanel'], + ) with caplog.at_level(logging.INFO, 'pyhf.pdf'): with pytest.raises(KeyError): assert w.data(m) From 397fe9fcff758d60f2ac9639581f439177907028 Mon Sep 17 00:00:00 2001 From: Matthew Feickert Date: Sat, 3 Sep 2022 00:35:56 -0500 Subject: [PATCH 4/9] isort --- src/pyhf/mixins.py | 4 +++- tests/test_workspace.py | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/pyhf/mixins.py b/src/pyhf/mixins.py index e42352f716..0314188cc1 100644 --- a/src/pyhf/mixins.py +++ b/src/pyhf/mixins.py @@ -1,6 +1,8 @@ from __future__ import annotations + import logging -from typing import Sequence, Any +from typing import Any, Sequence + from pyhf.typing import Channel log = logging.getLogger(__name__) diff --git a/tests/test_workspace.py b/tests/test_workspace.py index f4f52ca096..ae7fc10053 100644 --- a/tests/test_workspace.py +++ b/tests/test_workspace.py @@ -176,9 +176,9 @@ def test_get_workspace_data_bad_model(workspace_factory, caplog, mocker): # the iconic fragrance of an expected failure mocker.patch( - 'pyhf.mixins._ChannelSummaryMixin.channels', + "pyhf.mixins._ChannelSummaryMixin.channels", new_callable=mocker.PropertyMock, - return_value=['chanel'], + return_value=["chanel"], ) with caplog.at_level(logging.INFO, 'pyhf.pdf'): with pytest.raises(KeyError): From 260dfd31421462e288817ccc9d4697b7f458ddcb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 3 Sep 2022 05:37:17 +0000 Subject: [PATCH 5/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_workspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_workspace.py b/tests/test_workspace.py index ae7fc10053..0e3f5b21c1 100644 --- a/tests/test_workspace.py +++ b/tests/test_workspace.py @@ -178,7 +178,7 @@ def test_get_workspace_data_bad_model(workspace_factory, caplog, mocker): mocker.patch( "pyhf.mixins._ChannelSummaryMixin.channels", new_callable=mocker.PropertyMock, - return_value=["chanel"], + return_value=["channel"], ) with caplog.at_level(logging.INFO, 'pyhf.pdf'): with pytest.raises(KeyError): From bc59b37837e5c0120a6540e92f730b10c4322f7b Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Sat, 3 Sep 2022 07:50:55 -0700 Subject: [PATCH 6/9] document config and other parameters --- src/pyhf/pdf.py | 73 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/src/pyhf/pdf.py b/src/pyhf/pdf.py index b145a89721..db59fa1c70 100644 --- a/src/pyhf/pdf.py +++ b/src/pyhf/pdf.py @@ -220,13 +220,58 @@ def __init__(self, spec, **config_kwargs): f"Unsupported options were passed in: {list(config_kwargs.keys())}." ) + # prefixed with underscore are documented via @property + self._par_order = [] + self._poi_name = None + self._poi_index = None + self._nmaindata = sum(self.channel_nbins.values()) + self._auxdata = [] + + # these are not documented properties self.par_map = {} - self.par_order = [] - self.poi_name = None - self.poi_index = None - self.auxdata = [] self.auxdata_order = [] - self.nmaindata = sum(self.channel_nbins.values()) + + @property + def par_order(self): + """ + Return an ordered list of paramset names in the model. + """ + return self._par_order + + @property + def poi_name(self): + """ + Return the name of the POI parameter in the model. + """ + return self._poi_name + + @property + def poi_index(self): + """ + Return the index of the POI parameter in the model. + """ + return self._poi_index + + @property + def auxdata(self): + """ + Return the auxiliary data in the model. + """ + return self._auxdata + + @property + def nmaindata(self): + """ + Return the length of data in the main model. + """ + return self._nmaindata + + @property + def nauxdata(self): + """ + Return the length of data in the constraint model. + """ + return len(self._auxdata) def set_parameters(self, _required_paramsets): """ @@ -240,9 +285,8 @@ def set_auxinfo(self, auxdata, auxdata_order): """ Sets a group of configuration data for the constraint terms. """ - self.auxdata = auxdata + self._auxdata = auxdata self.auxdata_order = auxdata_order - self.nauxdata = len(self.auxdata) def suggested_init(self): """ @@ -400,8 +444,8 @@ def set_poi(self, name): ) s = self.par_slice(name) assert s.stop - s.start == 1 - self.poi_name = name - self.poi_index = s.start + self._poi_name = name + self._poi_index = s.start def _create_and_register_paramsets(self, required_paramsets): next_index = 0 @@ -415,7 +459,7 @@ def _create_and_register_paramsets(self, required_paramsets): sl = slice(next_index, next_index + paramset.n_parameters) next_index = next_index + paramset.n_parameters - self.par_order.append(param_name) + self._par_order.append(param_name) self.par_map[param_name] = {'slice': sl, 'paramset': paramset} @@ -700,7 +744,7 @@ def __init__( schema.validate(self.spec, self.schema, version=self.version) # build up our representation of the specification poi_name = config_kwargs.pop('poi_name', 'mu') - self.config = _ModelConfig(self.spec, **config_kwargs) + self._config = _ModelConfig(self.spec, **config_kwargs) modifiers, _nominal_rates = _nominal_and_modifiers_from_spec( modifier_set, self.config, self.spec, self.batch_size @@ -733,6 +777,13 @@ def __init__( sizes, ['main', 'aux'], self.batch_size ) + @property + def config(self): + """ + The :class:`_ModelConfig` instance for the model. + """ + return self._config + def expected_auxdata(self, pars): """ Compute the expected value of the auxiliary measurements. From 91e7e252b456309410011e7281ff871029b0f30b Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Sat, 3 Sep 2022 07:55:38 -0700 Subject: [PATCH 7/9] sigh --- tests/test_workspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_workspace.py b/tests/test_workspace.py index 0e3f5b21c1..428055d08b 100644 --- a/tests/test_workspace.py +++ b/tests/test_workspace.py @@ -178,7 +178,7 @@ def test_get_workspace_data_bad_model(workspace_factory, caplog, mocker): mocker.patch( "pyhf.mixins._ChannelSummaryMixin.channels", new_callable=mocker.PropertyMock, - return_value=["channel"], + return_value=["fakechannel"], ) with caplog.at_level(logging.INFO, 'pyhf.pdf'): with pytest.raises(KeyError): From f67179f2fa7e1418ce84279f9d442df9aca8b3f4 Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Sat, 3 Sep 2022 07:56:16 -0700 Subject: [PATCH 8/9] fix code reach --- tests/test_workspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_workspace.py b/tests/test_workspace.py index 428055d08b..3cea38fd21 100644 --- a/tests/test_workspace.py +++ b/tests/test_workspace.py @@ -183,7 +183,7 @@ def test_get_workspace_data_bad_model(workspace_factory, caplog, mocker): with caplog.at_level(logging.INFO, 'pyhf.pdf'): with pytest.raises(KeyError): assert w.data(m) - assert 'Invalid channel' in caplog.text + assert 'Invalid channel' in caplog.text def test_json_serializable(workspace_factory): From 18b3cde0e2d24b45d54aa8fe4cfc1c62a17b6690 Mon Sep 17 00:00:00 2001 From: Matthew Feickert Date: Sun, 4 Sep 2022 21:17:57 +0200 Subject: [PATCH 9/9] go --- tests/test_workspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_workspace.py b/tests/test_workspace.py index 3cea38fd21..aaa7a3b301 100644 --- a/tests/test_workspace.py +++ b/tests/test_workspace.py @@ -183,7 +183,7 @@ def test_get_workspace_data_bad_model(workspace_factory, caplog, mocker): with caplog.at_level(logging.INFO, 'pyhf.pdf'): with pytest.raises(KeyError): assert w.data(m) - assert 'Invalid channel' in caplog.text + assert "Invalid channel" in caplog.text def test_json_serializable(workspace_factory):