Performing a Combination¶
We’ll demonstrate how a combination works by combining everything we’ve learned so far.
Loading the Workspace¶
To do so, we’ll use a simple workspace to demonstrate functionality of combinations.
import json
spec = json.load(open("data/2-bin_1-channel.json"))
import pyhf
workspace = pyhf.Workspace(spec)
Combine Workspaces¶
Let’s just try to combine naively right now.
pyhf.Workspace.combine(workspace, workspace)
---------------------------------------------------------------------------
InvalidWorkspaceOperation Traceback (most recent call last)
<ipython-input-3-4480a7c227be> in <module>
----> 1 pyhf.Workspace.combine(workspace, workspace)
/opt/hostedtoolcache/Python/3.8.5/x64/lib/python3.8/site-packages/pyhf/workspace.py in combine(cls, left, right, join)
701
702 new_version = _join_versions(join, left['version'], right['version'])
--> 703 new_channels = _join_channels(join, left['channels'], right['channels'])
704 new_observations = _join_observations(
705 join, left['observations'], right['observations']
/opt/hostedtoolcache/Python/3.8.5/x64/lib/python3.8/site-packages/pyhf/workspace.py in _join_channels(join, left_channels, right_channels)
98 )
99 if common_channels:
--> 100 raise exceptions.InvalidWorkspaceOperation(
101 f"Workspaces cannot have any channels in common with the same name: {common_channels}. You can also try a different join operation: {Workspace.valid_joins}."
102 )
InvalidWorkspaceOperation: Workspaces cannot have any channels in common with the same name: {'singlechannel'}. You can also try a different join operation: ['none', 'outer', 'left outer', 'right outer'].
As we can see, we can’t just combine a workspace with itself if it has some channel names in common. We try very hard in pyhf
to make sure a combination “makes sense”.
Let’s go ahead and rename the channel (as well as the measurement). Then try to combine.
other_workspace = workspace.rename(channels={'singlechannel': 'othersinglechannel'},
modifiers={'uncorr_bkguncrt': 'otheruncorr_bkguncrt'},
measurements={'Measurement': 'OtherMeasurement'})
combined_workspace = pyhf.Workspace.combine(workspace, other_workspace)
And did we combine?
print(f' channels: {combined_workspace.channels}')
print(f' nbins: {combined_workspace.channel_nbins}')
print(f' samples: {combined_workspace.samples}')
print(f' modifiers: {combined_workspace.modifiers}')
print(f' parameters: {combined_workspace.parameters}')
print(f'measurements: {combined_workspace.measurement_names}')
channels: ['othersinglechannel', 'singlechannel']
nbins: {'singlechannel': 2, 'othersinglechannel': 2}
samples: ['background', 'signal']
modifiers: [('mu', 'normfactor'), ('otheruncorr_bkguncrt', 'shapesys'), ('uncorr_bkguncrt', 'shapesys')]
parameters: ['mu', 'otheruncorr_bkguncrt', 'uncorr_bkguncrt']
measurements: ['Measurement', 'OtherMeasurement']
Indeed. And at this point, we can just use all the same functionality we expect of pyhf, such as performing a fit:
model = workspace.model()
data = workspace.data(model)
pyhf.infer.hypotest(1.0, data, model, qtilde=True)
array(0.48563995)
other_model = other_workspace.model()
other_data = other_workspace.data(other_model)
pyhf.infer.hypotest(1.0, other_data, other_model, qtilde=True)
array(0.48563995)
combined_model = combined_workspace.model()
combined_data = combined_workspace.data(combined_model)
pyhf.infer.hypotest(1.0, combined_data, combined_model, qtilde=True)
multiple measurements defined. Taking the first measurement.
array(0.35661112)