66from pyhf .exceptions import InvalidModifier
77from pyhf .parameters import ParamViewer
88from pyhf .tensor .manager import get_backend
9+ from typing import Optional
910
1011log = logging .getLogger (__name__ )
1112
1213
13- def required_parset (sigmas , fixed : List [bool ]):
14+ def required_parset (sigmas , fixed : List [bool ], constraint : Optional [ str ] = "gaussian" ):
1415 n_parameters = len (sigmas )
1516 return {
16- 'paramset_type' : 'constrained_by_normal' ,
17+ 'paramset_type' : 'constrained_by_normal'
18+ if constraint == "gaussian"
19+ else 'constrained_by_poisson' ,
1720 'n_parameters' : n_parameters ,
1821 'is_scalar' : False ,
1922 'inits' : (1.0 ,) * n_parameters ,
2023 'bounds' : ((1e-10 , 10.0 ),) * n_parameters ,
2124 'fixed' : tuple (fixed ),
2225 'auxdata' : (1.0 ,) * n_parameters ,
23- 'sigmas' : tuple (sigmas ),
26+ 'sigmas' if constraint == "gaussian" else 'factors' : tuple (sigmas ),
2427 }
2528
2629
@@ -37,11 +40,12 @@ def __init__(self, config):
3740 def collect (self , thismod , nom ):
3841 uncrt = thismod ['data' ] if thismod else [0.0 ] * len (nom )
3942 mask = [True if thismod else False ] * len (nom )
40- return {'mask' : mask , 'nom_data' : nom , 'uncrt' : uncrt }
43+ constraint = thismod .get ('constraint' , 'gaussian' ) if thismod else 'gaussian'
44+ return {'mask' : mask , 'nom_data' : nom , 'uncrt' : uncrt , 'constraint' : constraint }
4145
4246 def append (self , key , channel , sample , thismod , defined_samp ):
4347 self .builder_data .setdefault (key , {}).setdefault (sample , {}).setdefault (
44- 'data' , {'uncrt' : [], 'nom_data' : [], 'mask' : []}
48+ 'data' , {'uncrt' : [], 'nom_data' : [], 'mask' : [], 'constraint' : [] }
4549 )
4650 nom = (
4751 defined_samp ['data' ]
@@ -52,6 +56,9 @@ def append(self, key, channel, sample, thismod, defined_samp):
5256 self .builder_data [key ][sample ]['data' ]['mask' ].append (moddata ['mask' ])
5357 self .builder_data [key ][sample ]['data' ]['uncrt' ].append (moddata ['uncrt' ])
5458 self .builder_data [key ][sample ]['data' ]['nom_data' ].append (moddata ['nom_data' ])
59+ self .builder_data [key ][sample ]['data' ]['constraint' ].append (
60+ moddata ['constraint' ]
61+ )
5562
5663 def finalize (self ):
5764 default_backend = pyhf .default_backend
@@ -118,6 +125,9 @@ def finalize(self):
118125 else :
119126 assert (mask_this_sample == masks [modname ]).all ()
120127
128+ for modifier_data in self .builder_data [modname ].values ():
129+ modifier_data ['data' ]['mask' ] = masks [modname ]
130+
121131 # extract sigmas using this modifiers mask
122132 sigmas = relerrs [masks [modname ]]
123133
@@ -127,7 +137,16 @@ def finalize(self):
127137 # non-Nan constraint term, but in a future PR need to remove constraints
128138 # for these
129139 sigmas [fixed ] = 1.0
130- self .required_parsets .setdefault (parname , [required_parset (sigmas , fixed )])
140+
141+ constraint = [
142+ i
143+ for i , v in zip (modifier_data ['data' ]['constraint' ], masks [modname ])
144+ if v
145+ ]
146+ assert all (constraint [0 ] == element for element in constraint )
147+ self .required_parsets .setdefault (
148+ parname , [required_parset (sigmas , fixed , constraint )]
149+ )
131150 return self .builder_data
132151
133152
0 commit comments