Source code for contur.factories.likelihood_point

import contur.config.config as cfg
import contur.data.static_db as cdb
import contur.factories.likelihood as lh


[docs] class LikelihoodPoint(object): """ Save the statistical information about a model parameter point in a run, which can then be manipulated to sort them, calculate a full likelihood result, exclusions result, test b result, test s+b result with related stat_type and a parameter point dictionary If instantiated with a valid parameter dictionary this will be added as a property If instantiated with a valid YodaFactory, its likelihood blocks will be associated with this likelihood point If these are not provided, a blank point will be created which can be populated later (e.g. from a results database) Note that in general those likelihood blocks (ie the lists of likelihood objects) will not be present, since a result database does not store them. The statistics info can be retrieved from the relevant dictionaries, but not recalculated from scratch since this signal/background info won't be available. This class corresponds most closely to the run table in the results database, although each row of that table has a unique stat_type (which should maybe be changed in future. """ def __init__(self, paramPoint={}, yodaFactory=None): """ :param paramPoint: Dictionary of model parameter: value pairs. :type paramPoint: ``dict`` :param yodaFactory: String of filesystem location to write out the pickle of this instance to :type yodaFactory: ``contur.factories.yoda_factories.YodaFactory`` """ self.param_point = paramPoint self.pool_exclusion_dict = {} self.pool_histos_dict = {} self.pool_ts_b = {} self.pool_ts_s_b = {} self.combined_exclusion_dict = {} self.likelihood_blocks = None self.obs_excl_dict = {} self.run_point = "" self._sorted_likelihood_blocks = {} self._full_likelihood = {} # number of generated event used self.num_events = 0 # any additional arguments self.args = None # set up four versions of the full likelihood, one for each type of stat calculation. for stat_type in cfg.stat_types: self._full_likelihood[stat_type] = lh.CombinedLikelihood(stat_type) if yodaFactory is not None: self.num_events = yodaFactory.num_events self.likelihood_blocks = yodaFactory._likelihood_blocks for stat_type in cfg.stat_types: if self.get_sorted_likelihood_blocks(stat_type) is not None: self.fill_pool_dict(stat_type)
[docs] def fill_pool_dict(self,stat_type): pool_dict = {} pool_histos = {} try: self.combined_exclusion_dict[stat_type] = {'CLs':self.get_full_likelihood(stat_type).getCLs(stat_type), 'mu_lower_limit':self.get_full_likelihood(stat_type).get_mu_lower_limit(stat_type), 'mu_upper_limit':self.get_full_likelihood(stat_type).get_mu_upper_limit(stat_type), 'mu_hat':self.get_full_likelihood(stat_type).get_mu_hat(stat_type)} for p in self.get_sorted_likelihood_blocks(stat_type): pool_dict[p.pools] = {'CLs':p.getCLs(stat_type), 'mu_lower_limit':p.get_mu_lower_limit(stat_type), 'mu_upper_limit':p.get_mu_upper_limit(stat_type), 'mu_hat':p.get_mu_hat(stat_type)} pool_histos[p.pools] = p.tags except AttributeError: self.combined_exclusion_dict[stat_type] = None self.pool_exclusion_dict[stat_type] = pool_dict self.pool_histos_dict[stat_type] = pool_histos
[docs] def resort_blocks(self,stat_type,omitted_pools=""): """ Function to sort the :attr:`sorted_likelihood_blocks` list. Used for resorting after a merging exclusively. :Keyword Arguments: * *stat_type* (``string``) -- which statisic type (default, SM background, expected or hlexpected) is being sorted by. """ cfg.contur_log.debug('Calling resort blocks ({})'.format(stat_type)) try: self._sorted_likelihood_blocks[stat_type] = lh.sort_blocks(self._sorted_likelihood_blocks[stat_type],stat_type,omitted_pools="") except ValueError as ve: cfg.contur_log.warning("Unable to sort likelihoods for {}. Exception: {}".format(stat_type,ve)) self._full_likelihood[stat_type] = lh.build_full_likelihood(self.get_sorted_likelihood_blocks(stat_type),stat_type) # update the combined exclusion dictionary try: self.combined_exclusion_dict[stat_type] = self.get_full_likelihood(stat_type).getCLs(stat_type) except AttributeError: self.combined_exclusion_dict[stat_type] = None # cleanup some bulk we don't need @TODO make this a separate cleanup function. if hasattr(self, '_likelihood_blocks'): del self._likelihood_blocks if hasattr(self, 'yodaFilePath'): del self.yodaFilePath
# set runpoint info with beam and scan point, e.g. 13TeV/0003
[docs] def set_run_point(self,run_point): self.run_point = run_point
# get runpoint info with beam and scan point, e.g. 13TeV/0003
[docs] def get_run_point(self): return self.run_point
[docs] def get_sorted_likelihood_blocks(self,stat_type=None): """ The list of reduced component likelihood blocks extracted from the result file, sorted according the test statisitic of type `stat_type`. If stat_type is None, return the whole dictionary. **type** ( ``list`` [ :class:`~contur.factories.likelihood.Likelihood` ]) """ try: if stat_type is None: return self._sorted_likelihood_blocks if stat_type in self._sorted_likelihood_blocks.keys(): return self._sorted_likelihood_blocks[stat_type] else: # print(self._sorted_likelihood_blocks) # print(self._likelihood_blocks) # print("HERE2") return None except: raise cfg.ConturError("Likelihood point has no likelhood blocks.")
[docs] def set_sorted_likelihood_blocks(self, value, stat_type): self._sorted_likelihood_blocks[stat_type] = value
[docs] def get_dominant_pool(self,stat_type): ''' return the name of the dominant pool for this point ''' pools = self.pool_exclusion_dict[stat_type] return max(pools, key=lambda pool: pools[pool]['CLs'])
[docs] def get_dominant_analysis(self,stat_type,poolid=None,cls_cut=0.0): """ return the analysis object which has the biggest exclusion for this point. """ analysis = None maximum = cls_cut maximum_analysis = None for histname, stats in self.obs_excl_dict.items(): if stats[stat_type]['CLs'] is not None: analysis, lumi, lumi_fb, this_poolid, subpoolid = cdb.obsFinder(histname) if (poolid == this_poolid or poolid is None) and stats[stat_type]['CLs']>maximum: maximum_analysis = analysis maximum = stats[stat_type]['CLs'] return maximum_analysis
[docs] def store_point_info(self, statType, combinedExclusion, poolExclusion, poolHistos, poolTestb, poolTestsb, obs_excl_dict, yoda_files, num_events): """ :param statType: string, represent the point type :type combinedExclusion: ``string`` :param combinedExclusion: full likelihood for a parameter point :type combinedExclusion: ``float`` :param poolExclusion: **key** ``string`` pool name : **value** ``double`` :type poolExclusion: ``dict`` :param poolHistos: **key** ``string`` pool name : **value** ``string`` :type poolHistos: ``dict`` :param poolTestb: **key** ``string`` pool name : **value** ``double`` :type poolTestb: ``dict`` :param poolTestsb: **key** ``string`` pool name : **value** ``double`` :type poolTestsb: ``dict`` """ self.combined_exclusion_dict[statType] = combinedExclusion self.pool_exclusion_dict[statType] = poolExclusion self.pool_histos_dict[statType] = poolHistos self.pool_ts_b[statType] = poolTestb self.pool_ts_s_b[statType] = poolTestsb self.obs_excl_dict = obs_excl_dict self.yoda_files = yoda_files self.num_events = num_events
[docs] def store_param_point(self, paramPoint): """ :param paramPoint: **key** ``string`` param name : **value** ``float`` :type paramPoint: ``dict`` """ self.param_point = paramPoint
[docs] def recalculate_CLs(self, stat_type, omitted_pools=""): """ recalculate the combined exclusion after excluding the omitted pool in the class :param omitted_pools: string, the name of the pool to ignore :type omiited_pools: ``string`` """ if omitted_pools in self.pool_ts_b[stat_type].keys(): self.pool_ts_b[stat_type].pop(omitted_pools) self.pool_ts_s_b[stat_type].pop(omitted_pools) sum_ts_b = 0 sum_ts_s_b = 0 for pool in self.pool_ts_b[stat_type]: sum_ts_b += self.pool_ts_b[stat_type][pool] for pool in self.pool_ts_s_b[stat_type]: sum_ts_s_b += self.pool_ts_s_b[stat_type][pool] cls = ts_to_cls([(sum_ts_b, sum_ts_s_b)])[0] self.combined_exclusion_dict[stat_type] = cls return self.combined_exclusion_dict[stat_type]
@property def likelihood_blocks(self): """The list of all component likelihood blocks extracted from the result file This attribute is the total information in the result` file, but does not account for potential correlation/ overlap between the members of the list **type** ( ``list`` [ :class:`~contur.factories.likelihood.Likelihood` ]) """ return self._likelihood_blocks @likelihood_blocks.setter def likelihood_blocks(self, value): self._likelihood_blocks = value
[docs] def get_full_likelihood(self,stat_type=None): """ The full likelihood representing the result file in it's entirety. If stat_type is specified, return to entry for it. Else return the dict of all of them. **type** (:class:`~contur.factories.likelihood.CombinedLikelihood`) """ if stat_type is None: return self._full_likelihood else: return self._full_likelihood[stat_type]
[docs] def set_full_likelihood(self, stat_type, value): self._full_likelihood[stat_type] = value
def __repr__(self): try: return "%s with %s blocks, holding %s" % (self.__class__.__name__, len(self.likelihood_blocks), self._full_likelihood) except: return "%s with %s blocks, holding %s" % (self.__class__.__name__, len(self._sorted_likelihood_blocks), self._full_likelihood)