Module flavio.statistics.likelihood
Classes
class FastLikelihood (name,
par_obj=<flavio.classes.ParameterConstraints object>,
fit_parameters=None,
nuisance_parameters='all',
observables=None,
exclude_measurements=None,
include_measurements=None)-
Expand source code
class FastLikelihood(NamedInstanceClass, iio.YAMLLoadable): """A variant (but not subclass) of `Likelihood` where some or all of the parameters have been "integrated out" and their theoretical uncertainties combined with the experimental uncertainties into a multivariate Gaussian "pseudo measurement". The pseuo measurement is generated by calling the method `make_measurement`. This is done by generating random samples of the nuisance parameters and evaluating all observables within the Standard Model many times (100 by default). Then, the covariance of all predictions is extracted. Similarly, a covariance matrix for all experimental measurements is determined. Both covariance matrices are added and the resulting multivariate Gaussian treated as a single measurement. This approach has the advantage that two-dimensional plots of the likelihood can be produced without the need for sampling or profiling the other dimensions. However, several strong assumptions go into this method, most importantly, - all uncertainties - experimental and theoretical - are treated as Gaussian - the theoretical uncertainties in the presence of new physics are assumed to be equal to the ones in the SM Methods: - `make_measurement`: Generate the pseudo measurement - `log_likelihood`: The log-likelihood function Important attributes/properties: - `likelihood`: the `Likelihood` instance based on the pseudo measurement - `full_measurement_likelihood`: the `MeasurementLikelihood` instance based on the original measurements - `sm_covariance`: the `SMCovariance` instance (that can be used to load and save the SM covariance matrix) - `exp_covariance`: the `MeasurementCovariance` instance (that can be used to load and save the experimental covariance matrix) Instances can be imported and exported from/to YAML using the `load` and `dump` methods. """ _input_schema_dict = { 'name': str, 'par_obj': vol.All([dict], iio.coerce_par_obj), 'fit_parameters': vol.Any(None, [str]), 'nuisance_parameters': vol.Any(None, [str]), 'observables': vol.All([iio.coerce_observable_tuple], iio.list_deduplicate), 'exclude_measurements': vol.Any(None, [str]), 'include_measurements': vol.Any(None, [str]), } _output_schema_dict = { 'name': str, 'par_obj': iio.get_par_diff, 'fit_parameters': vol.Any(iio.ensurelist, [str]), 'nuisance_parameters': vol.Any(iio.ensurelist, [str]), 'observables': [iio.coerce_observable_dict], 'exclude_measurements': vol.Any(iio.ensurelist, [str]), 'include_measurements': vol.Any(iio.ensurelist, [str]), } def __init__(self, name, par_obj=flavio.default_parameters, fit_parameters=None, nuisance_parameters='all', observables=None, exclude_measurements=None, include_measurements=None, ): self.par_obj = par_obj self.parameters_central = self.par_obj.get_central_all() self.fit_parameters = fit_parameters or [] if nuisance_parameters == 'all': self.nuisance_parameters = self.par_obj.all_parameters else: self.nuisance_parameters = nuisance_parameters or [] self.observables = observables self.exclude_measurements = exclude_measurements self.include_measurements = include_measurements self.full_measurement_likelihood = MeasurementLikelihood( self.observables, exclude_measurements=self.exclude_measurements, include_measurements=self.include_measurements) self.sm_covariance = SMCovariance(self.observables, vary_parameters=self.nuisance_parameters, par_obj=self.par_obj) self.exp_covariance = MeasurementCovariance( self.full_measurement_likelihood) self.pseudo_measurement = None self._likelihood = None NamedInstanceClass.__init__(self, name) def make_measurement(self, N=100, Nexp=5000, threads=1, force=False, force_exp=False): """Initialize the likelihood by producing a pseudo-measurement containing both experimental uncertainties as well as theory uncertainties stemming from nuisance parameters. Optional parameters: - `N`: number of random computations for the SM covariance (computing time is proportional to it; more means less random fluctuations.) - `Nexp`: number of random computations for the experimental covariance. This is much less expensive than the theory covariance, so a large number can be afforded (default: 5000). - `threads`: number of parallel threads for the SM covariance computation. Defaults to 1 (no parallelization). - `force`: if True, will recompute SM covariance even if it already has been computed. Defaults to False. - `force_exp`: if True, will recompute experimental central values and covariance even if they have already been computed. Defaults to False. """ central_exp, cov_exp = self.exp_covariance.get(Nexp, force=force_exp) cov_sm = self.sm_covariance.get(N, force=force, threads=threads) covariance = cov_exp + cov_sm # add the Pseudo-measurement m = flavio.classes.Measurement('Pseudo-measurement for FastLikelihood instance: ' + self.name) if np.asarray(central_exp).ndim == 0 or len(central_exp) <= 1: # for a 1D (or 0D) array m.add_constraint(self.observables, NormalDistribution(central_exp, np.sqrt(covariance))) else: m.add_constraint(self.observables, MultivariateNormalDistribution(central_exp, covariance)) self.pseudo_measurement = m self._likelihood = Likelihood( par_obj=self.par_obj, fit_parameters=self.fit_parameters, observables=self.observables, include_measurements=[m.name], # only include our pseudo-meas. include_pseudo_measurements=True, # force including the pseudo-meas. ) @property def likelihood(self): if self._likelihood is None: raise ValueError("You need to call `make_measurement` first.") return self._likelihood def log_likelihood(self, par_dict, wc_obj, exclude_observables=None, delta=False): """Log-likelihood function. Parameters: - `par_dict`: a dictionary of parameter values - `wc_obj`: an instance of `WilsonCoefficients` or `wilson.Wilson` """ return self.likelihood.log_likelihood_exp(par_dict, wc_obj, exclude_observables=exclude_observables, delta=delta)A variant (but not subclass) of
Likelihoodwhere some or all of the parameters have been "integrated out" and their theoretical uncertainties combined with the experimental uncertainties into a multivariate Gaussian "pseudo measurement".The pseuo measurement is generated by calling the method
make_measurement. This is done by generating random samples of the nuisance parameters and evaluating all observables within the Standard Model many times (100 by default). Then, the covariance of all predictions is extracted. Similarly, a covariance matrix for all experimental measurements is determined. Both covariance matrices are added and the resulting multivariate Gaussian treated as a single measurement.This approach has the advantage that two-dimensional plots of the likelihood can be produced without the need for sampling or profiling the other dimensions. However, several strong assumptions go into this method, most importantly,
- all uncertainties - experimental and theoretical - are treated as Gaussian
- the theoretical uncertainties in the presence of new physics are assumed to be equal to the ones in the SM
Methods:
make_measurement: Generate the pseudo measurementlog_likelihood: The log-likelihood function
Important attributes/properties:
likelihood: theLikelihoodinstance based on the pseudo measurementfull_measurement_likelihood: theMeasurementLikelihoodinstance based on the original measurementssm_covariance: theSMCovarianceinstance (that can be used to load and save the SM covariance matrix)exp_covariance: theMeasurementCovarianceinstance (that can be used to load and save the experimental covariance matrix)
Instances can be imported and exported from/to YAML using the
loadanddumpmethods.Ancestors
Instance variables
prop likelihood-
Expand source code
@property def likelihood(self): if self._likelihood is None: raise ValueError("You need to call `make_measurement` first.") return self._likelihood
Methods
def log_likelihood(self, par_dict, wc_obj, exclude_observables=None, delta=False)-
Expand source code
def log_likelihood(self, par_dict, wc_obj, exclude_observables=None, delta=False): """Log-likelihood function. Parameters: - `par_dict`: a dictionary of parameter values - `wc_obj`: an instance of `WilsonCoefficients` or `wilson.Wilson` """ return self.likelihood.log_likelihood_exp(par_dict, wc_obj, exclude_observables=exclude_observables, delta=delta)Log-likelihood function.
Parameters: -
par_dict: a dictionary of parameter values -wc_obj: an instance ofWilsonCoefficientsorwilson.Wilson def make_measurement(self, N=100, Nexp=5000, threads=1, force=False, force_exp=False)-
Expand source code
def make_measurement(self, N=100, Nexp=5000, threads=1, force=False, force_exp=False): """Initialize the likelihood by producing a pseudo-measurement containing both experimental uncertainties as well as theory uncertainties stemming from nuisance parameters. Optional parameters: - `N`: number of random computations for the SM covariance (computing time is proportional to it; more means less random fluctuations.) - `Nexp`: number of random computations for the experimental covariance. This is much less expensive than the theory covariance, so a large number can be afforded (default: 5000). - `threads`: number of parallel threads for the SM covariance computation. Defaults to 1 (no parallelization). - `force`: if True, will recompute SM covariance even if it already has been computed. Defaults to False. - `force_exp`: if True, will recompute experimental central values and covariance even if they have already been computed. Defaults to False. """ central_exp, cov_exp = self.exp_covariance.get(Nexp, force=force_exp) cov_sm = self.sm_covariance.get(N, force=force, threads=threads) covariance = cov_exp + cov_sm # add the Pseudo-measurement m = flavio.classes.Measurement('Pseudo-measurement for FastLikelihood instance: ' + self.name) if np.asarray(central_exp).ndim == 0 or len(central_exp) <= 1: # for a 1D (or 0D) array m.add_constraint(self.observables, NormalDistribution(central_exp, np.sqrt(covariance))) else: m.add_constraint(self.observables, MultivariateNormalDistribution(central_exp, covariance)) self.pseudo_measurement = m self._likelihood = Likelihood( par_obj=self.par_obj, fit_parameters=self.fit_parameters, observables=self.observables, include_measurements=[m.name], # only include our pseudo-meas. include_pseudo_measurements=True, # force including the pseudo-meas. )Initialize the likelihood by producing a pseudo-measurement containing both experimental uncertainties as well as theory uncertainties stemming from nuisance parameters.
Optional parameters:
N: number of random computations for the SM covariance (computing time is proportional to it; more means less random fluctuations.)Nexp: number of random computations for the experimental covariance. This is much less expensive than the theory covariance, so a large number can be afforded (default: 5000).threads: number of parallel threads for the SM covariance computation. Defaults to 1 (no parallelization).force: if True, will recompute SM covariance even if it already has been computed. Defaults to False.force_exp: if True, will recompute experimental central values and covariance even if they have already been computed. Defaults to False.
Inherited members
class Likelihood (par_obj=<flavio.classes.ParameterConstraints object>,
fit_parameters=None,
observables=None,
exclude_measurements=None,
include_measurements=None,
include_pseudo_measurements=False)-
Expand source code
class Likelihood(iio.YAMLLoadable): """A `Likelihood` provides a likelihood function consisting of a contribution from experimental measurements and a contribution from parameters. Methods: - `log_prior_fit_parameters`: The parameter contribution to the log-likelihood - `log_likelihood_exp`: The experimental contribution to the log-likelihood - `log_likelihood`: The total log-likelihood that is the sum of the parameter and the experimental contribution Instances can be imported and exported from/to YAML using the `load` and `dump` methods. """ _input_schema_dict = { 'par_obj': vol.All([dict], iio.coerce_par_obj), 'fit_parameters': vol.Any(iio.ensurelist, [str]), 'observables': vol.All([iio.coerce_observable_tuple], iio.list_deduplicate), 'exclude_measurements': vol.Any(iio.ensurelist, [str]), 'include_measurements': vol.Any(iio.ensurelist, [str]), } _output_schema_dict = { 'par_obj': vol.All([dict], iio.coerce_par_obj), 'fit_parameters': iio.get_par_diff, 'observables': [iio.coerce_observable_dict], 'exclude_measurements': vol.Any(iio.ensurelist, [str]), 'include_measurements': vol.Any(iio.ensurelist, [str]), } def __init__(self, par_obj=flavio.default_parameters, fit_parameters=None, observables=None, exclude_measurements=None, include_measurements=None, include_pseudo_measurements=False, ): self.par_obj = par_obj self.parameters_central = self.par_obj.get_central_all() self.fit_parameters = fit_parameters or [] self.observables = observables self.exclude_measurements = exclude_measurements self.include_measurements = include_measurements self.measurement_likelihood = MeasurementLikelihood( observables, exclude_measurements=exclude_measurements, include_measurements=include_measurements, include_pseudo_measurements=include_pseudo_measurements,) self.parameter_likelihood = ParameterLikelihood( par_obj=par_obj, parameters=fit_parameters) def log_prior_fit_parameters(self, par_dict, delta=False): """Parameter contribution to the log-likelihood.""" if not self.fit_parameters: return 0 # nothing to do return self.parameter_likelihood.log_likelihood_par(par_dict, delta=delta) def log_likelihood_exp(self, par_dict, wc_obj, exclude_observables=None, delta=False): """Experimental contribution to the log-likelihood.""" return self.measurement_likelihood.log_likelihood_par(par_dict, wc_obj, exclude_observables=exclude_observables, delta=delta) def log_likelihood(self, par_dict, wc_obj, exclude_observables=None, delta=False): """Total log-likelihood. Parameters: - `par_dict`: a dictionary of parameter values - `wc_obj`: an instance of `WilsonCoefficients` or `wilson.Wilson` """ return self.log_prior_fit_parameters(par_dict, delta=delta) + self.log_likelihood_exp(par_dict, wc_obj, exclude_observables=exclude_observables, delta=delta)A
Likelihoodprovides a likelihood function consisting of a contribution from experimental measurements and a contribution from parameters.Methods:
log_prior_fit_parameters: The parameter contribution to the log-likelihoodlog_likelihood_exp: The experimental contribution to the log-likelihoodlog_likelihood: The total log-likelihood that is the sum of the parameter and the experimental contribution
Instances can be imported and exported from/to YAML using the
loadanddumpmethods.Ancestors
Methods
def log_likelihood(self, par_dict, wc_obj, exclude_observables=None, delta=False)-
Expand source code
def log_likelihood(self, par_dict, wc_obj, exclude_observables=None, delta=False): """Total log-likelihood. Parameters: - `par_dict`: a dictionary of parameter values - `wc_obj`: an instance of `WilsonCoefficients` or `wilson.Wilson` """ return self.log_prior_fit_parameters(par_dict, delta=delta) + self.log_likelihood_exp(par_dict, wc_obj, exclude_observables=exclude_observables, delta=delta)Total log-likelihood.
Parameters: -
par_dict: a dictionary of parameter values -wc_obj: an instance ofWilsonCoefficientsorwilson.Wilson def log_likelihood_exp(self, par_dict, wc_obj, exclude_observables=None, delta=False)-
Expand source code
def log_likelihood_exp(self, par_dict, wc_obj, exclude_observables=None, delta=False): """Experimental contribution to the log-likelihood.""" return self.measurement_likelihood.log_likelihood_par(par_dict, wc_obj, exclude_observables=exclude_observables, delta=delta)Experimental contribution to the log-likelihood.
def log_prior_fit_parameters(self, par_dict, delta=False)-
Expand source code
def log_prior_fit_parameters(self, par_dict, delta=False): """Parameter contribution to the log-likelihood.""" if not self.fit_parameters: return 0 # nothing to do return self.parameter_likelihood.log_likelihood_par(par_dict, delta=delta)Parameter contribution to the log-likelihood.
Inherited members
class MeasurementCovariance (measurement_likelihood)-
Expand source code
class MeasurementCovariance(object): """Class to compute, save, and load a covariance matrix and the central values of experimental measurements. Methods: - `compute`: Compute the covariance - `get`: Compute the covariance if necessary, otherwise return cached one - `save`: Save the covariance to a file - `load`: Load the covariance from a file - `load_dict`: Load the covariance from a dictionary """ def __init__(self, measurement_likelihood): """Initialize the class. Parameters: - `measurement_likelihood`: an instance of `MeasurementLikelihood` """ self.measurement_likelihood = measurement_likelihood self._central_cov = None def compute(self, N): """Compute the covariance for `N` random values.""" ml = self.measurement_likelihood means = [] covariances = [] for measurement in ml.get_measurements: m_obj = flavio.Measurement[measurement] # obs. included in the fit and constrained by this measurement our_obs = set(m_obj.all_parameters).intersection(ml.observables) # construct a dict. containing a vector of N random values for # each of these observables random_dict = m_obj.get_random_all(size=N) random_arr = np.zeros((len(ml.observables), N)) for i, obs in enumerate(ml.observables): if obs in our_obs: random_arr[i] = random_dict[obs] mean = np.mean(random_arr, axis=1) covariance = np.cov(random_arr) for i, obs in enumerate(ml.observables): if obs not in our_obs: covariance[:,i] = 0 covariance[i, :] = 0 covariance[i, i] = np.inf means.append(mean) covariances.append(covariance) # if there is only a single measuement if len(means) == 1: if len(means[0]) == 1: # if there is only a single observable return means[0][0], covariances[0] else: return means[0], covariances[0] # if there are severeal measurements, perform a weighted average else: # covariances: [Sigma_1, Sigma_2, ...] # means: [x_1, x_2, ...] # weights_ [W_1, W_2, ...] where W_i = (Sigma_i)^(-1) # weighted covariance is (W_1 + W_2 + ...)^(-1) = Sigma # weigted mean is Sigma.(W_1.x_1 + W_2.x_2 + ...) = x if len(ml.observables) == 1: weights = np.array([1/c for c in covariances]) weighted_covariance = 1/np.sum(weights, axis=0) weighted_mean = weighted_covariance * np.sum( [np.dot(weights[i], means[i]) for i in range(len(means))]) else: weights = [np.linalg.inv(c) for c in covariances] weighted_covariance = np.linalg.inv(np.sum(weights, axis=0)) weighted_mean = np.dot(weighted_covariance, np.sum( [np.dot(weights[i], means[i]) for i in range(len(means))], axis=0)) return weighted_mean, weighted_covariance def get(self, N=5000, force=True): """Compute the covariance for `N` random values (default: 5000). If `force` is False, return a cached version if it exists. """ if self._central_cov is None or force: self._central_cov = self.compute(N=N) elif N != 5000: warnings.warn("Argument N={} ignored ".format(N) + \ "as experimental covariance has already been " + \ "computed. Recompute using `force=True`.") return self._central_cov def save(self, filename): """Save the experimental central values and the covariance to a pickle file. The central values and the covariance must have been computed before using `get` or `compute`.""" if self._central_cov is None: raise ValueError("Call `get` or `compute` first.") with open(filename, 'wb') as f: data = dict(central=self._central_cov[0], covariance=self._central_cov[1], observables=self.measurement_likelihood.observables) pickle.dump(data, f) def load(self, filename): """Load the experimental central values and the covriance from a pickle file.""" with open(filename, 'rb') as f: data = pickle.load(f) self.load_dict(d=data) def load_dict(self, d): """Load the the experimental central values and the covariance from a dictionary. It must have the form `{'observables': [...], 'central': [...], 'covariance': [[...]]}` where 'central' is a vector of central values and 'covariance' is a covariance matrix, both in the basis of observables given by 'observables' which must at least contain all the observables involved in the fit. Additional observables will be ignored; the ordering is arbitrary.""" ml = self.measurement_likelihood obs = d['observables'] try: permutation = [obs.index(o) for o in ml.observables] except ValueError: raise ValueError("Covariance matrix does not contain all necessary entries") assert len(permutation) == len(ml.observables), \ "Covariance matrix does not contain all necessary entries" if np.isscalar(d['central']): self._central_cov = ( d['central'], d['covariance'] ) else: self._central_cov = ( d['central'][permutation], d['covariance'][permutation][:, permutation], )Class to compute, save, and load a covariance matrix and the central values of experimental measurements.
Methods:
compute: Compute the covarianceget: Compute the covariance if necessary, otherwise return cached onesave: Save the covariance to a fileload: Load the covariance from a fileload_dict: Load the covariance from a dictionary
Initialize the class.
Parameters: -
measurement_likelihood: an instance ofMeasurementLikelihoodMethods
def compute(self, N)-
Expand source code
def compute(self, N): """Compute the covariance for `N` random values.""" ml = self.measurement_likelihood means = [] covariances = [] for measurement in ml.get_measurements: m_obj = flavio.Measurement[measurement] # obs. included in the fit and constrained by this measurement our_obs = set(m_obj.all_parameters).intersection(ml.observables) # construct a dict. containing a vector of N random values for # each of these observables random_dict = m_obj.get_random_all(size=N) random_arr = np.zeros((len(ml.observables), N)) for i, obs in enumerate(ml.observables): if obs in our_obs: random_arr[i] = random_dict[obs] mean = np.mean(random_arr, axis=1) covariance = np.cov(random_arr) for i, obs in enumerate(ml.observables): if obs not in our_obs: covariance[:,i] = 0 covariance[i, :] = 0 covariance[i, i] = np.inf means.append(mean) covariances.append(covariance) # if there is only a single measuement if len(means) == 1: if len(means[0]) == 1: # if there is only a single observable return means[0][0], covariances[0] else: return means[0], covariances[0] # if there are severeal measurements, perform a weighted average else: # covariances: [Sigma_1, Sigma_2, ...] # means: [x_1, x_2, ...] # weights_ [W_1, W_2, ...] where W_i = (Sigma_i)^(-1) # weighted covariance is (W_1 + W_2 + ...)^(-1) = Sigma # weigted mean is Sigma.(W_1.x_1 + W_2.x_2 + ...) = x if len(ml.observables) == 1: weights = np.array([1/c for c in covariances]) weighted_covariance = 1/np.sum(weights, axis=0) weighted_mean = weighted_covariance * np.sum( [np.dot(weights[i], means[i]) for i in range(len(means))]) else: weights = [np.linalg.inv(c) for c in covariances] weighted_covariance = np.linalg.inv(np.sum(weights, axis=0)) weighted_mean = np.dot(weighted_covariance, np.sum( [np.dot(weights[i], means[i]) for i in range(len(means))], axis=0)) return weighted_mean, weighted_covarianceCompute the covariance for
Nrandom values. def get(self, N=5000, force=True)-
Expand source code
def get(self, N=5000, force=True): """Compute the covariance for `N` random values (default: 5000). If `force` is False, return a cached version if it exists. """ if self._central_cov is None or force: self._central_cov = self.compute(N=N) elif N != 5000: warnings.warn("Argument N={} ignored ".format(N) + \ "as experimental covariance has already been " + \ "computed. Recompute using `force=True`.") return self._central_covCompute the covariance for
Nrandom values (default: 5000).If
forceis False, return a cached version if it exists. def load(self, filename)-
Expand source code
def load(self, filename): """Load the experimental central values and the covriance from a pickle file.""" with open(filename, 'rb') as f: data = pickle.load(f) self.load_dict(d=data)Load the experimental central values and the covriance from a pickle file.
def load_dict(self, d)-
Expand source code
def load_dict(self, d): """Load the the experimental central values and the covariance from a dictionary. It must have the form `{'observables': [...], 'central': [...], 'covariance': [[...]]}` where 'central' is a vector of central values and 'covariance' is a covariance matrix, both in the basis of observables given by 'observables' which must at least contain all the observables involved in the fit. Additional observables will be ignored; the ordering is arbitrary.""" ml = self.measurement_likelihood obs = d['observables'] try: permutation = [obs.index(o) for o in ml.observables] except ValueError: raise ValueError("Covariance matrix does not contain all necessary entries") assert len(permutation) == len(ml.observables), \ "Covariance matrix does not contain all necessary entries" if np.isscalar(d['central']): self._central_cov = ( d['central'], d['covariance'] ) else: self._central_cov = ( d['central'][permutation], d['covariance'][permutation][:, permutation], )Load the the experimental central values and the covariance from a dictionary.
It must have the form
{'observables': [...], 'central': [...], 'covariance': [[...]]}where 'central' is a vector of central values and 'covariance' is a covariance matrix, both in the basis of observables given by 'observables' which must at least contain all the observables involved in the fit. Additional observables will be ignored; the ordering is arbitrary. def save(self, filename)-
Expand source code
def save(self, filename): """Save the experimental central values and the covariance to a pickle file. The central values and the covariance must have been computed before using `get` or `compute`.""" if self._central_cov is None: raise ValueError("Call `get` or `compute` first.") with open(filename, 'wb') as f: data = dict(central=self._central_cov[0], covariance=self._central_cov[1], observables=self.measurement_likelihood.observables) pickle.dump(data, f)Save the experimental central values and the covariance to a pickle file.
The central values and the covariance must have been computed before using
getorcompute.
class MeasurementLikelihood (observables,
*,
exclude_measurements=None,
include_measurements=None,
include_pseudo_measurements=False)-
Expand source code
class MeasurementLikelihood(iio.YAMLLoadable): """A `MeasurementLikelihood` provides a likelihood function from experimental measurements. Methods: - `get_predictions_par`: Return a dictionary of SM predictions for the observables of interest - `log_likelihood_pred`: The likelihood as a function of the predictions - `log_likelihood_par`: The likelihood as a function of parameters and Wilson coefficients Instances can be imported and exported from/to YAML using the `load` and `dump` methods. """ _input_schema_dict = { 'observables': vol.All([iio.coerce_observable_tuple], iio.list_deduplicate), 'exclude_measurements': vol.Any(None, [str]), 'include_measurements': vol.Any(None, [str]), } _output_schema_dict = { 'observables': [iio.coerce_observable_dict], 'exclude_measurements': vol.All(iio.ensurelist, [str]), 'include_measurements': vol.All(iio.ensurelist, [str]), } def __init__(self, observables, *, exclude_measurements=None, include_measurements=None, include_pseudo_measurements=False): """Initialize the instance. Parameters: - `observables`: list of observables (tuples or strings) - `exclude_measurements`: list of measurement names to exclude (default: none ar excluded) - `include_measurements`: list of measurement names to include (default: all are included) Only one of `exclude_measurements` or `include_measurements` should be specified. By default, all existing instances of `flavio.Measurements` are included as constraints (except if they carry 'Pseudo-measurement' in their name). """ super().__init__() self.observables = observables self.exclude_measurements = exclude_measurements self.include_measurements = include_measurements self.include_pseudo_measurements = include_pseudo_measurements self._predictions_cache_hash = None self._predictions_cache_values = None if exclude_measurements and include_measurements: raise ValueError("The options exclude_measurements and include_measurements must not be specified simultaneously") # check that observables are constrained _obs_measured = set() for m_name in self.get_measurements: m_obj = flavio.Measurement[m_name] _obs_measured.update(m_obj.all_parameters) missing_obs = set(observables) - set(_obs_measured).intersection(set(observables)) assert missing_obs == set(), "No measurement found for the observables: " + str(missing_obs) self._warn_meas_corr() def _warn_meas_corr(self): """Warn the user if the fit contains multiple correlated measurements of an observable that is not included in the fit parameters, as this will lead to inconsistent results.""" corr_with = {} # iterate over all measurements constraining at least one fit obs. for name in self.get_measurements: m = flavio.classes.Measurement[name] # iterate over all fit obs. constrained by this measurement for obs in set(self.observables) & set(m.all_parameters): # the constraint on this fit obs. constraint = m._parameters[obs][1] # find all the other obs. constrained by this constraint for c, p in m._constraints: if c == constraint: par = p break for p in par: # if the other obs. are not fit obs., append them to the list if p not in self.observables: if p not in corr_with: corr_with[p] = [obs] else: corr_with[p].append(obs) # replace list by a Counter corr_with = {k: Counter(v) for k, v in corr_with.items() if v} # warn for all counts > 1 for obs1, counter in corr_with.items(): for obs2, count in counter.items(): if count > 1: warnings.warn(("{} of the measurements in the likelihood " "constrain both '{}' and '{}', but only the " "latter is included among the fit " "observables. This can lead to inconsistent " "results as the former is profiled over." ).format(count, obs1, obs2)) return corr_with @property def get_measurements(self): """Return a list of all the measurements currently defined that constrain any of the fit observables.""" all_measurements = [] for m_name, m_obj in flavio.classes.Measurement.instances.items(): if m_name.split(' ')[0] == 'Pseudo-measurement' and not self.include_pseudo_measurements: # skip pseudo measurements generated by FastFit instances continue if set(m_obj.all_parameters).isdisjoint(self.observables): # if set of all observables constrained by measurement is disjoint # with fit observables, do nothing continue else: # else, add measurement name to output list all_measurements.append(m_name) if self.exclude_measurements is None and self.include_measurements is None: return all_measurements elif self.exclude_measurements is not None: return list(set(all_measurements) - set(self.exclude_measurements)) elif self.include_measurements is not None: return list(set(all_measurements) & set(self.include_measurements)) def get_predictions_par(self, par_dict, wc_obj): """Compute the predictions for all observables as functions of a parameter dictionary `par_dict`and WilsonCoefficient instance `wc_obj`. The latest computed values are cached and returned if the function is called successively with the same arguments. """ # compute a hash from the function's arguments used for caching arg_hash = hash((frozenset(par_dict.items()),wc_obj)) if arg_hash != self._predictions_cache_hash: all_predictions = {} for observable in self.observables: obs = flavio.classes.Observable.argument_format(observable, 'dict') name = obs.pop('name') _inst = flavio.classes.Observable[name] all_predictions[observable] = _inst.prediction_par(par_dict, wc_obj, **obs) self._predictions_cache_values = all_predictions self._predictions_cache_hash = arg_hash return self._predictions_cache_values def get_number_observations(self, exclude_observables=None): """Get the number of observations, defined as individual measurements of observables.""" nobs = 0 for measurement in self.get_measurements: m_obj = flavio.Measurement[measurement] m_obs = set(m_obj.all_parameters) exclude_observables = set(exclude_observables or []) our_obs = set(self.observables) - exclude_observables # add the number of observables contained in the measurement # and in our instance (except the excluded ones) nobs += len(m_obs & our_obs) return nobs def log_likelihood_pred(self, pred_dict, exclude_observables=None, delta=False): """Return the logarithm of the likelihood function as a function of a dictionary of observable predictions `pred_dict`""" ll = 0. for measurement in self.get_measurements: m_obj = flavio.Measurement[measurement] m_obs = set(m_obj.all_parameters) exclude_observables = set(exclude_observables or []) # exclude the observables not contained in the likelihood # as well as the ones explicitly excluded by `exclude_observables` exclude_parameters = m_obs - set(self.observables) | exclude_observables if m_obs == exclude_parameters: continue # if all measured observables are excluded: nothing to do prob_dict = m_obj.get_logprobability_all(pred_dict, exclude_parameters=exclude_parameters, delta=delta) ll += sum(prob_dict.values()) return ll def log_likelihood_par(self, par_dict, wc_obj, exclude_observables=None, delta=False): """Return the logarithm of the likelihood function as a function of a parameter dictionary `par_dict` and WilsonCoefficient instance `wc_obj`""" predictions = self.get_predictions_par(par_dict, wc_obj) return self.log_likelihood_pred(predictions, exclude_observables=exclude_observables, delta=delta)A
MeasurementLikelihoodprovides a likelihood function from experimental measurements.Methods:
get_predictions_par: Return a dictionary of SM predictions for the observables of interestlog_likelihood_pred: The likelihood as a function of the predictionslog_likelihood_par: The likelihood as a function of parameters and Wilson coefficients
Instances can be imported and exported from/to YAML using the
loadanddumpmethods.Initialize the instance.
Parameters: -
observables: list of observables (tuples or strings) -exclude_measurements: list of measurement names to exclude (default: none ar excluded) -include_measurements: list of measurement names to include (default: all are included)Only one of
exclude_measurementsorinclude_measurementsshould be specified. By default, all existing instances offlavio.Measurementsare included as constraints (except if they carry 'Pseudo-measurement' in their name).Ancestors
Instance variables
prop get_measurements-
Expand source code
@property def get_measurements(self): """Return a list of all the measurements currently defined that constrain any of the fit observables.""" all_measurements = [] for m_name, m_obj in flavio.classes.Measurement.instances.items(): if m_name.split(' ')[0] == 'Pseudo-measurement' and not self.include_pseudo_measurements: # skip pseudo measurements generated by FastFit instances continue if set(m_obj.all_parameters).isdisjoint(self.observables): # if set of all observables constrained by measurement is disjoint # with fit observables, do nothing continue else: # else, add measurement name to output list all_measurements.append(m_name) if self.exclude_measurements is None and self.include_measurements is None: return all_measurements elif self.exclude_measurements is not None: return list(set(all_measurements) - set(self.exclude_measurements)) elif self.include_measurements is not None: return list(set(all_measurements) & set(self.include_measurements))Return a list of all the measurements currently defined that constrain any of the fit observables.
Methods
def get_number_observations(self, exclude_observables=None)-
Expand source code
def get_number_observations(self, exclude_observables=None): """Get the number of observations, defined as individual measurements of observables.""" nobs = 0 for measurement in self.get_measurements: m_obj = flavio.Measurement[measurement] m_obs = set(m_obj.all_parameters) exclude_observables = set(exclude_observables or []) our_obs = set(self.observables) - exclude_observables # add the number of observables contained in the measurement # and in our instance (except the excluded ones) nobs += len(m_obs & our_obs) return nobsGet the number of observations, defined as individual measurements of observables.
def get_predictions_par(self, par_dict, wc_obj)-
Expand source code
def get_predictions_par(self, par_dict, wc_obj): """Compute the predictions for all observables as functions of a parameter dictionary `par_dict`and WilsonCoefficient instance `wc_obj`. The latest computed values are cached and returned if the function is called successively with the same arguments. """ # compute a hash from the function's arguments used for caching arg_hash = hash((frozenset(par_dict.items()),wc_obj)) if arg_hash != self._predictions_cache_hash: all_predictions = {} for observable in self.observables: obs = flavio.classes.Observable.argument_format(observable, 'dict') name = obs.pop('name') _inst = flavio.classes.Observable[name] all_predictions[observable] = _inst.prediction_par(par_dict, wc_obj, **obs) self._predictions_cache_values = all_predictions self._predictions_cache_hash = arg_hash return self._predictions_cache_valuesCompute the predictions for all observables as functions of a parameter dictionary
par_dictand WilsonCoefficient instancewc_obj. The latest computed values are cached and returned if the function is called successively with the same arguments. def log_likelihood_par(self, par_dict, wc_obj, exclude_observables=None, delta=False)-
Expand source code
def log_likelihood_par(self, par_dict, wc_obj, exclude_observables=None, delta=False): """Return the logarithm of the likelihood function as a function of a parameter dictionary `par_dict` and WilsonCoefficient instance `wc_obj`""" predictions = self.get_predictions_par(par_dict, wc_obj) return self.log_likelihood_pred(predictions, exclude_observables=exclude_observables, delta=delta)Return the logarithm of the likelihood function as a function of a parameter dictionary
par_dictand WilsonCoefficient instancewc_obj def log_likelihood_pred(self, pred_dict, exclude_observables=None, delta=False)-
Expand source code
def log_likelihood_pred(self, pred_dict, exclude_observables=None, delta=False): """Return the logarithm of the likelihood function as a function of a dictionary of observable predictions `pred_dict`""" ll = 0. for measurement in self.get_measurements: m_obj = flavio.Measurement[measurement] m_obs = set(m_obj.all_parameters) exclude_observables = set(exclude_observables or []) # exclude the observables not contained in the likelihood # as well as the ones explicitly excluded by `exclude_observables` exclude_parameters = m_obs - set(self.observables) | exclude_observables if m_obs == exclude_parameters: continue # if all measured observables are excluded: nothing to do prob_dict = m_obj.get_logprobability_all(pred_dict, exclude_parameters=exclude_parameters, delta=delta) ll += sum(prob_dict.values()) return llReturn the logarithm of the likelihood function as a function of a dictionary of observable predictions
pred_dict
Inherited members
class ParameterLikelihood (par_obj=<flavio.classes.ParameterConstraints object>, parameters=None)-
Expand source code
class ParameterLikelihood(iio.YAMLLoadable): """A `ParameterLikelihood` provides a likelihood function in terms of parameters. Methods: - `log_likelihood_par`: The likelihood as a function of the parameters - `get_central`: get an array with the parameters' central values - `get_random`: get an array with random values for the parameters Instances can be imported and exported from/to YAML using the `load` and `dump` methods. """ _input_schema_dict = { 'par_obj': vol.All([dict], iio.coerce_par_obj), 'parameters': vol.Any(None, [str]), } _output_schema_dict = { 'par_obj': iio.get_par_diff, 'parameters': vol.Any(iio.ensurelist, [str]), } def __init__(self, par_obj=flavio.default_parameters, parameters=None): """Initialize the instance. Parameters: - `par_obj`: an instance of `ParameterConstraints` (defaults to `flavio.default_parameters`) - parameters: a list of parameters whose constraints should be taken into account in the likelihood. """ self.par_obj = par_obj self.parameters = parameters self.parameters_central = self.par_obj.get_central_all() def log_likelihood_par(self, par_dict, delta=False): """Return the prior probability for all parameters. Note that only the parameters in `self.parameters` will give a contribution to the likelihood.""" exclude_parameters = list(set(self.par_obj._parameters.keys())-set(self.parameters)) prob_dict = self.par_obj.get_logprobability_all(par_dict, exclude_parameters=exclude_parameters, delta=delta) return sum([p for obj, p in prob_dict.items()]) @property def get_central(self): """Return a numpy array with the central values of all parameters.""" return np.asarray([self.parameters_central[p] for p in self.parameters]) @property def get_random(self): """Return a numpy array with random values for all parameters.""" all_random = self.par_obj.get_random_all() return np.asarray([all_random[p] for p in self.parameters])A
ParameterLikelihoodprovides a likelihood function in terms of parameters.Methods:
log_likelihood_par: The likelihood as a function of the parametersget_central: get an array with the parameters' central valuesget_random: get an array with random values for the parameters
Instances can be imported and exported from/to YAML using the
loadanddumpmethods.Initialize the instance.
Parameters:
par_obj: an instance ofParameterConstraints(defaults toflavio.default_parameters)- parameters: a list of parameters whose constraints should be taken into account in the likelihood.
Ancestors
Instance variables
prop get_central-
Expand source code
@property def get_central(self): """Return a numpy array with the central values of all parameters.""" return np.asarray([self.parameters_central[p] for p in self.parameters])Return a numpy array with the central values of all parameters.
prop get_random-
Expand source code
@property def get_random(self): """Return a numpy array with random values for all parameters.""" all_random = self.par_obj.get_random_all() return np.asarray([all_random[p] for p in self.parameters])Return a numpy array with random values for all parameters.
Methods
def log_likelihood_par(self, par_dict, delta=False)-
Expand source code
def log_likelihood_par(self, par_dict, delta=False): """Return the prior probability for all parameters. Note that only the parameters in `self.parameters` will give a contribution to the likelihood.""" exclude_parameters = list(set(self.par_obj._parameters.keys())-set(self.parameters)) prob_dict = self.par_obj.get_logprobability_all(par_dict, exclude_parameters=exclude_parameters, delta=delta) return sum([p for obj, p in prob_dict.items()])Return the prior probability for all parameters.
Note that only the parameters in
self.parameterswill give a contribution to the likelihood.
Inherited members
class SMCovariance (observables, *, vary_parameters='all', par_obj=None)-
Expand source code
class SMCovariance(object): """Class to compute, save, and load a covariance matrix of SM predictions. Methods: - `compute`: Compute the covariance - `get`: Compute the covariance if necessary, otherwise return cached one - `save`: Save the covariance to a file - `load`: Load the covariance from a file - `load_dict`: Load the covariance from a dictionary """ def __init__(self, observables, *, vary_parameters='all', par_obj=None): """Initialize the class. Parameters: - `observables`: list of observables - `vary_parameters`: parameters to vary. Defaults to 'all'. - `par_obj`: instance of ParameterConstraints. Defaults to flavio.default_parameters. """ self.observables = observables self.vary_parameters = vary_parameters self.par_obj = par_obj or flavio.default_parameters self._cov = None def compute(self, N, threads): """Compute the covariance for `N` random values, using `threads` CPU threads.""" return flavio.sm_covariance(obs_list=self.observables, N=N, par_vary=self.vary_parameters, par_obj=self.par_obj, threads=threads) def get(self, N=100, threads=1, force=True): """Compute the covariance for `N` random values (default: 100), using `threads` CPU threads (default: 1). If `force` is False, return a cached version if it exists. """ if self._cov is None or force: self._cov = self.compute(N=N, threads=threads) elif N != 100: warnings.warn("Argument N={} ignored ".format(N) + \ "as covariance has already been " + \ "computed. Recompute using `force=True`.") return self._cov def save(self, filename): """Save the SM covariance to a pickle file. The covariance must have been computed before using `get` or `compute`. """ if self._cov is None: raise ValueError("Call `get` or `compute` first.") with open(filename, 'wb') as f: data = dict(covariance=self._cov, observables=self.observables) pickle.dump(data, f) def load(self, filename): """Load the SM covariance from a pickle file.""" with open(filename, 'rb') as f: data = pickle.load(f) self.load_dict(d=data) def load_dict(self, d): """Load the SM covariance from a dictionary. It must have the form `{'observables': [...], 'covariance': [[...]]}` where 'covariance' is a covariance matrix in the basis of observables given by 'observables' which must at least contain all the observables involved in the fit. Additional observables will be ignored; the ordering is arbitrary. """ obs = d['observables'] try: permutation = [obs.index(o) for o in self.observables] except ValueError: raise ValueError("Covariance matrix does not contain all necessary entries") assert len(permutation) == len(self.observables), \ "Covariance matrix does not contain all necessary entries" if len(permutation) == 1: if d['covariance'].shape == (): self._cov = d['covariance'] else: self._cov = d['covariance'][permutation][:,permutation][0,0] else: self._cov = d['covariance'][permutation][:,permutation]Class to compute, save, and load a covariance matrix of SM predictions.
Methods:
compute: Compute the covarianceget: Compute the covariance if necessary, otherwise return cached onesave: Save the covariance to a fileload: Load the covariance from a fileload_dict: Load the covariance from a dictionary
Initialize the class.
Parameters: -
observables: list of observables -vary_parameters: parameters to vary. Defaults to 'all'. -par_obj: instance of ParameterConstraints. Defaults to flavio.default_parameters.Methods
def compute(self, N, threads)-
Expand source code
def compute(self, N, threads): """Compute the covariance for `N` random values, using `threads` CPU threads.""" return flavio.sm_covariance(obs_list=self.observables, N=N, par_vary=self.vary_parameters, par_obj=self.par_obj, threads=threads)Compute the covariance for
Nrandom values, usingthreadsCPU threads. def get(self, N=100, threads=1, force=True)-
Expand source code
def get(self, N=100, threads=1, force=True): """Compute the covariance for `N` random values (default: 100), using `threads` CPU threads (default: 1). If `force` is False, return a cached version if it exists. """ if self._cov is None or force: self._cov = self.compute(N=N, threads=threads) elif N != 100: warnings.warn("Argument N={} ignored ".format(N) + \ "as covariance has already been " + \ "computed. Recompute using `force=True`.") return self._covCompute the covariance for
Nrandom values (default: 100), usingthreadsCPU threads (default: 1).If
forceis False, return a cached version if it exists. def load(self, filename)-
Expand source code
def load(self, filename): """Load the SM covariance from a pickle file.""" with open(filename, 'rb') as f: data = pickle.load(f) self.load_dict(d=data)Load the SM covariance from a pickle file.
def load_dict(self, d)-
Expand source code
def load_dict(self, d): """Load the SM covariance from a dictionary. It must have the form `{'observables': [...], 'covariance': [[...]]}` where 'covariance' is a covariance matrix in the basis of observables given by 'observables' which must at least contain all the observables involved in the fit. Additional observables will be ignored; the ordering is arbitrary. """ obs = d['observables'] try: permutation = [obs.index(o) for o in self.observables] except ValueError: raise ValueError("Covariance matrix does not contain all necessary entries") assert len(permutation) == len(self.observables), \ "Covariance matrix does not contain all necessary entries" if len(permutation) == 1: if d['covariance'].shape == (): self._cov = d['covariance'] else: self._cov = d['covariance'][permutation][:,permutation][0,0] else: self._cov = d['covariance'][permutation][:,permutation]Load the SM covariance from a dictionary.
It must have the form
{'observables': [...], 'covariance': [[...]]}where 'covariance' is a covariance matrix in the basis of observables given by 'observables' which must at least contain all the observables involved in the fit. Additional observables will be ignored; the ordering is arbitrary. def save(self, filename)-
Expand source code
def save(self, filename): """Save the SM covariance to a pickle file. The covariance must have been computed before using `get` or `compute`. """ if self._cov is None: raise ValueError("Call `get` or `compute` first.") with open(filename, 'wb') as f: data = dict(covariance=self._cov, observables=self.observables) pickle.dump(data, f)Save the SM covariance to a pickle file.
The covariance must have been computed before using
getorcompute.