flavio.statistics.likelihood module
import flavio from collections import Counter import warnings import numpy as np import voluptuous as vol import pickle from flavio.classes import NamedInstanceClass from flavio.statistics.probability import NormalDistribution, MultivariateNormalDistribution from flavio.io import instanceio as iio 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) 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]) 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) 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 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 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)
Classes
class FastLikelihood
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 measurementlog_likelihood
: The log-likelihood function
Important attributes/properties:
likelihood
: theLikelihood
instance based on the pseudo measurementfull_measurement_likelihood
: theMeasurementLikelihood
instance based on the original measurementssm_covariance
: theSMCovariance
instance (that can be used to load and save the SM covariance matrix)exp_covariance
: theMeasurementCovariance
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.
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)
Ancestors (in MRO)
- FastLikelihood
- flavio.classes.NamedInstanceClass
- flavio.io.instanceio.YAMLLoadable
- builtins.object
Static methods
def __init__(
self, name, par_obj=<flavio.classes.ParameterConstraints object at 0x7f066f862c70>, fit_parameters=None, nuisance_parameters='all', observables=None, exclude_measurements=None, include_measurements=None)
Initialize self. See help(type(self)) for accurate signature.
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 dump(
self, stream=None, **kwargs)
Dump the objectto a YAML string or stream.
def dump(self, stream=None, **kwargs): """Dump the objectto a YAML string or stream.""" d = self.get_yaml_dict(**kwargs) return yaml.dump(d, stream=stream, **kwargs)
def get_yaml_dict(
self)
Dump the object to a YAML dictionary.
def get_yaml_dict(self): """Dump the object to a YAML dictionary.""" d = self.__dict__.copy() schema = self.output_schema() d = schema(d) # remove NoneTypes and empty lists d = {k: v for k, v in d.items() if v is not None and v != []} return d
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
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)
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.
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. )
def set_description(
self, description)
def set_description(self, description): self.description = description
Instance variables
var exclude_measurements
var exp_covariance
var fit_parameters
var full_measurement_likelihood
var include_measurements
var likelihood
var observables
var par_obj
var parameters_central
var pseudo_measurement
var sm_covariance
Methods
def clear_all(
cls)
Delete all instances.
@classmethod def clear_all(cls): """Delete all instances.""" cls.instances = OrderedDict()
def del_instance(
cls, name)
@classmethod def del_instance(cls, name): del cls.instances[name]
def find(
cls, regex)
Find all instance names matching the regular expression regex
.
@classmethod def find(cls, regex): """Find all instance names matching the regular expression `regex`.""" rc = re.compile(regex) return list(filter(rc.search, cls.instances))
def get_instance(
cls, name)
@classmethod def get_instance(cls, name): return cls.instances[name]
def input_schema(
cls)
@classmethod def input_schema(cls): return vol.Schema(cls._input_schema_dict, extra=vol.ALLOW_EXTRA)
def load(
cls, f, **kwargs)
Instantiate an object from a YAML string or stream.
@classmethod def load(cls, f, **kwargs): """Instantiate an object from a YAML string or stream.""" d = flavio.io.yaml.load_include(f) return cls.load_dict(d, **kwargs)
def load_dict(
cls, d, **kwargs)
Instantiate an object from a YAML dictionary.
@classmethod def load_dict(cls, d, **kwargs): """Instantiate an object from a YAML dictionary.""" schema = cls.input_schema() return cls(**schema(d), **kwargs)
def output_schema(
cls)
@classmethod def output_schema(cls): return vol.Schema(cls._output_schema_dict, extra=vol.REMOVE_EXTRA)
class Likelihood
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-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 load
and dump
methods.
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)
Ancestors (in MRO)
- Likelihood
- flavio.io.instanceio.YAMLLoadable
- builtins.object
Static methods
def __init__(
self, par_obj=<flavio.classes.ParameterConstraints object at 0x7f066f862c70>, fit_parameters=None, observables=None, exclude_measurements=None, include_measurements=None, include_pseudo_measurements=False)
Initialize self. See help(type(self)) for accurate signature.
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 dump(
self, stream=None, **kwargs)
Dump the objectto a YAML string or stream.
def dump(self, stream=None, **kwargs): """Dump the objectto a YAML string or stream.""" d = self.get_yaml_dict(**kwargs) return yaml.dump(d, stream=stream, **kwargs)
def get_yaml_dict(
self)
Dump the object to a YAML dictionary.
def get_yaml_dict(self): """Dump the object to a YAML dictionary.""" d = self.__dict__.copy() schema = self.output_schema() d = schema(d) # remove NoneTypes and empty lists d = {k: v for k, v in d.items() if v is not None and v != []} return d
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
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)
def log_likelihood_exp(
self, par_dict, wc_obj, exclude_observables=None, delta=False)
Experimental contribution to the log-likelihood.
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_prior_fit_parameters(
self, par_dict, delta=False)
Parameter contribution to the log-likelihood.
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)
Instance variables
var exclude_measurements
var fit_parameters
var include_measurements
var measurement_likelihood
var observables
var par_obj
var parameter_likelihood
var parameters_central
Methods
def input_schema(
cls)
@classmethod def input_schema(cls): return vol.Schema(cls._input_schema_dict, extra=vol.ALLOW_EXTRA)
def load(
cls, f, **kwargs)
Instantiate an object from a YAML string or stream.
@classmethod def load(cls, f, **kwargs): """Instantiate an object from a YAML string or stream.""" d = flavio.io.yaml.load_include(f) return cls.load_dict(d, **kwargs)
def load_dict(
cls, d, **kwargs)
Instantiate an object from a YAML dictionary.
@classmethod def load_dict(cls, d, **kwargs): """Instantiate an object from a YAML dictionary.""" schema = cls.input_schema() return cls(**schema(d), **kwargs)
def output_schema(
cls)
@classmethod def output_schema(cls): return vol.Schema(cls._output_schema_dict, extra=vol.REMOVE_EXTRA)
class MeasurementCovariance
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
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], )
Ancestors (in MRO)
- MeasurementCovariance
- builtins.object
Static methods
def __init__(
self, measurement_likelihood)
Initialize the class.
Parameters:
- measurement_likelihood
: an instance of MeasurementLikelihood
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.
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.
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 load(
self, filename)
Load the experimental central values and the covriance from a pickle file.
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.
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], )
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
.
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)
Instance variables
var measurement_likelihood
class MeasurementLikelihood
A MeasurementLikelihood
provides 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 load
and dump
methods.
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)
Ancestors (in MRO)
- MeasurementLikelihood
- flavio.io.instanceio.YAMLLoadable
- builtins.object
Static methods
def __init__(
self, observables)
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).
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 dump(
self, stream=None, **kwargs)
Dump the objectto a YAML string or stream.
def dump(self, stream=None, **kwargs): """Dump the objectto a YAML string or stream.""" d = self.get_yaml_dict(**kwargs) return yaml.dump(d, stream=stream, **kwargs)
def get_number_observations(
self, exclude_observables=None)
Get the number of observations, defined as individual measurements of observables.
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 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.
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_yaml_dict(
self)
Dump the object to a YAML dictionary.
def get_yaml_dict(self): """Dump the object to a YAML dictionary.""" d = self.__dict__.copy() schema = self.output_schema() d = schema(d) # remove NoneTypes and empty lists d = {k: v for k, v in d.items() if v is not None and v != []} return d
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
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)
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
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
Instance variables
var exclude_measurements
var get_measurements
Return a list of all the measurements currently defined that constrain any of the fit observables.
var include_measurements
var include_pseudo_measurements
var observables
Methods
def input_schema(
cls)
@classmethod def input_schema(cls): return vol.Schema(cls._input_schema_dict, extra=vol.ALLOW_EXTRA)
def load(
cls, f, **kwargs)
Instantiate an object from a YAML string or stream.
@classmethod def load(cls, f, **kwargs): """Instantiate an object from a YAML string or stream.""" d = flavio.io.yaml.load_include(f) return cls.load_dict(d, **kwargs)
def load_dict(
cls, d, **kwargs)
Instantiate an object from a YAML dictionary.
@classmethod def load_dict(cls, d, **kwargs): """Instantiate an object from a YAML dictionary.""" schema = cls.input_schema() return cls(**schema(d), **kwargs)
def output_schema(
cls)
@classmethod def output_schema(cls): return vol.Schema(cls._output_schema_dict, extra=vol.REMOVE_EXTRA)
class ParameterLikelihood
A ParameterLikelihood
provides 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 load
and dump
methods.
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])
Ancestors (in MRO)
- ParameterLikelihood
- flavio.io.instanceio.YAMLLoadable
- builtins.object
Static methods
def __init__(
self, par_obj=<flavio.classes.ParameterConstraints object at 0x7f066f862c70>, parameters=None)
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.
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 dump(
self, stream=None, **kwargs)
Dump the objectto a YAML string or stream.
def dump(self, stream=None, **kwargs): """Dump the objectto a YAML string or stream.""" d = self.get_yaml_dict(**kwargs) return yaml.dump(d, stream=stream, **kwargs)
def get_yaml_dict(
self)
Dump the object to a YAML dictionary.
def get_yaml_dict(self): """Dump the object to a YAML dictionary.""" d = self.__dict__.copy() schema = self.output_schema() d = schema(d) # remove NoneTypes and empty lists d = {k: v for k, v in d.items() if v is not None and v != []} return d
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.
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()])
Instance variables
var get_central
Return a numpy array with the central values of all parameters.
var get_random
Return a numpy array with random values for all parameters.
var par_obj
var parameters
var parameters_central
Methods
def input_schema(
cls)
@classmethod def input_schema(cls): return vol.Schema(cls._input_schema_dict, extra=vol.ALLOW_EXTRA)
def load(
cls, f, **kwargs)
Instantiate an object from a YAML string or stream.
@classmethod def load(cls, f, **kwargs): """Instantiate an object from a YAML string or stream.""" d = flavio.io.yaml.load_include(f) return cls.load_dict(d, **kwargs)
def load_dict(
cls, d, **kwargs)
Instantiate an object from a YAML dictionary.
@classmethod def load_dict(cls, d, **kwargs): """Instantiate an object from a YAML dictionary.""" schema = cls.input_schema() return cls(**schema(d), **kwargs)
def output_schema(
cls)
@classmethod def output_schema(cls): return vol.Schema(cls._output_schema_dict, extra=vol.REMOVE_EXTRA)
class SMCovariance
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
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]
Ancestors (in MRO)
- SMCovariance
- builtins.object
Static methods
def __init__(
self, observables)
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.
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.
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.
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 load(
self, filename)
Load the SM covariance from a pickle file.
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.
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]
def save(
self, filename)
Save the SM covariance to a pickle file.
The covariance must have been computed before using get
or
compute
.
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)
Instance variables
var observables
var par_obj
var vary_parameters