def __init__(self, minimum, maximum, name=None, latex_label=None, unit=None, boundary=None): """Log-Uniform prior with bounds Parameters ---------- minimum: float See superclass maximum: float See superclass name: str See superclass latex_label: str See superclass unit: str See superclass boundary: str See superclass """ super(LogUniform, self).__init__(name=name, latex_label=latex_label, unit=unit, minimum=minimum, maximum=maximum, alpha=-1, boundary=boundary) if self.minimum <= 0: logger.warning( 'You specified a uniform-in-log prior with minimum={}'.format( self.minimum))
def sample(self, size=1, **kwargs): """ Draw a sample from the prior. Parameters ---------- size: int, float (defaults to 1) number of samples to draw kwargs: dict kwargs passed to the dist.sample method Returns ------- float: A sample from the prior paramter. """ if self.name in self.dist.sampled_parameters: logger.warning("You have already drawn a sample from parameter " "'{}'. The same sample will be " "returned".format(self.name)) if len(self.dist.current_sample) == 0: # generate a sample self.dist.sample(size=size, **kwargs) sample = self.dist.current_sample[self.name] if self.name not in self.dist.sampled_parameters: self.dist.sampled_parameters.append(self.name) if len(self.dist.sampled_parameters) == len(self.dist): # reset samples self.dist.reset_sampled() self.least_recently_sampled = sample return sample
def from_dictionary(self, dictionary): eval_dict = dict(inf=np.inf) for key, val in iteritems(dictionary): if isinstance(val, Prior): continue elif isinstance(val, (int, float)): dictionary[key] = DeltaFunction(peak=val) elif isinstance(val, str): cls = val.split('(')[0] args = '('.join(val.split('(')[1:])[:-1] try: dictionary[key] = DeltaFunction(peak=float(cls)) logger.debug("{} converted to DeltaFunction prior".format(key)) continue except ValueError: pass if "." in cls: module = '.'.join(cls.split('.')[:-1]) cls = cls.split('.')[-1] else: module = __name__.replace( '.' + os.path.basename(__file__).replace('.py', ''), '' ) cls = getattr(import_module(module), cls, cls) if key.lower() in ["conversion_function", "condition_func"]: setattr(self, key, cls) elif isinstance(cls, str): if "(" in val: raise TypeError("Unable to parse prior class {}".format(cls)) else: continue elif (cls.__name__ in ['MultivariateGaussianDist', 'MultivariateNormalDist']): if key not in eval_dict: eval_dict[key] = eval(val, None, eval_dict) elif (cls.__name__ in ['MultivariateGaussian', 'MultivariateNormal']): dictionary[key] = eval(val, None, eval_dict) else: try: dictionary[key] = cls.from_repr(args) except TypeError as e: raise TypeError( "Unable to parse prior, bad entry: {} " "= {}. Error message {}".format(key, val, e) ) elif isinstance(val, dict): logger.warning( 'Cannot convert {} into a prior object. ' 'Leaving as dictionary.'.format(key)) else: raise TypeError( "Unable to parse prior, bad entry: {} " "= {} of type {}".format(key, val, type(val)) ) self.update(dictionary)
def from_dictionary(self, dictionary): for key, val in iteritems(dictionary): if isinstance(val, str): try: prior = eval(val) if isinstance(prior, (Prior, float, int, str)): val = prior except (NameError, SyntaxError, TypeError): logger.debug( "Failed to load dictionary value {} correctly".format( key)) pass elif isinstance(val, dict): logger.warning('Cannot convert {} into a prior object. ' 'Leaving as dictionary.'.format(key)) self[key] = val
def fill_priors(self, likelihood, default_priors_file=None): """ Fill dictionary of priors based on required parameters of likelihood Any floats in prior will be converted to delta function prior. Any required, non-specified parameters will use the default. Note: if `likelihood` has `non_standard_sampling_parameter_keys`, then this will set-up default priors for those as well. Parameters ---------- likelihood: bilby.likelihood.GravitationalWaveTransient instance Used to infer the set of parameters to fill the prior with default_priors_file: str, optional If given, a file containing the default priors. Returns ------- prior: dict The filled prior dictionary """ self.convert_floats_to_delta_functions() missing_keys = set(likelihood.parameters) - set(self.keys()) for missing_key in missing_keys: if not self.test_redundancy(missing_key): default_prior = create_default_prior(missing_key, default_priors_file) if default_prior is None: set_val = likelihood.parameters[missing_key] logger.warning( "Parameter {} has no default prior and is set to {}, this" " will not be sampled and may cause an error.".format( missing_key, set_val)) else: self[missing_key] = default_prior for key in self: self.test_redundancy(key)
def test_has_redundant_keys(self): """ Test whether there are redundant keys in self. Return ------ bool: Whether there are redundancies or not """ redundant = False for key in self: if isinstance(self[key], Constraint): continue temp = self.copy() del temp[key] if temp.test_redundancy(key, disable_logging=True): logger.warning('{} is a redundant key in this {}.' .format(key, self.__class__.__name__)) redundant = True return redundant
def __init__(self, file_name, minimum=None, maximum=None, name=None, latex_label=None, unit=None, boundary=None): """Creates an interpolated prior function from arrays of xx and yy=p(xx) extracted from a file Parameters ---------- file_name: str Name of the file containing the xx and yy arrays minimum: float See superclass maximum: float See superclass name: str See superclass latex_label: str See superclass unit: str See superclass boundary: str See superclass """ try: self.id = file_name xx, yy = np.genfromtxt(self.id).T super(FromFile, self).__init__(xx=xx, yy=yy, minimum=minimum, maximum=maximum, name=name, latex_label=latex_label, unit=unit, boundary=boundary) except IOError: logger.warning("Can't load {}.".format(self.id)) logger.warning("Format should be:") logger.warning(r"x\tp(x)")
def __init__(self, posteriors, hyper_prior, sampling_prior=None, ln_evidences=None, max_samples=1e100, selection_function=lambda args: 1, conversion_function=lambda args: (args, None), cupy=True): """ Parameters ---------- posteriors: list An list of pandas data frames of samples sets of samples. Each set may have a different size. These can contain a `prior` column containing the original prior values. hyper_prior: `bilby.hyper.model.Model` The population model, this can alternatively be a function. sampling_prior: `bilby.hyper.model.Model` *DEPRECATED* The sampling prior, this can alternatively be a function. ln_evidences: list, optional Log evidences for single runs to ensure proper normalisation of the hyperparameter likelihood. If not provided, the original evidences will be set to 0. This produces a Bayes factor between the sampling power_prior and the hyperparameterised model. selection_function: func Function which evaluates your population selection function. conversion_function: func Function which converts a dictionary of sampled parameter to a dictionary of parameters of the population model. max_samples: int, optional Maximum number of samples to use from each set. cupy: bool If True and a compatible CUDA environment is available, cupy will be used for performance. Note: this requires setting up your hyper_prior properly. """ if cupy and not CUPY_LOADED: logger.warning('Cannot import cupy, falling back to numpy.') self.samples_per_posterior = max_samples self.data = self.resample_posteriors(posteriors, max_samples=max_samples) if not isinstance(hyper_prior, Model): hyper_prior = Model([hyper_prior]) self.hyper_prior = hyper_prior Likelihood.__init__(self, hyper_prior.parameters) if sampling_prior is not None: logger.warning('Passing a sampling_prior is deprecated. This ' 'should be passed as a column in the posteriors.') if not isinstance(sampling_prior, Model): sampling_prior = Model([sampling_prior]) self.sampling_prior = sampling_prior.prob(self.data) elif 'prior' in self.data: self.sampling_prior = self.data.pop('prior') else: logger.info('No prior values provided, defaulting to 1.') self.sampling_prior = 1 if ln_evidences is not None: self.total_noise_evidence = np.sum(ln_evidences) else: self.total_noise_evidence = np.nan self.conversion_function = conversion_function self.selection_function = selection_function self.n_posteriors = len(posteriors) self.samples_factor =\ - self.n_posteriors * np.log(self.samples_per_posterior)
def __init__(self, dictionary=None, filename=None): """ DEPRECATED: USE PriorDict INSTEAD""" logger.warning("The name 'PriorSet' is deprecated use 'PriorDict' instead") super(PriorSet, self).__init__(dictionary, filename)
def __init__( self, posteriors, hyper_prior, sampling_prior=None, ln_evidences=None, max_samples=1e100, selection_function=lambda args: 1, conversion_function=lambda args: (args, None), cupy=True, ): """ Parameters ---------- posteriors: list An list of pandas data frames of samples sets of samples. Each set may have a different size. These can contain a `prior` column containing the original prior values. hyper_prior: `bilby.hyper.model.Model` The population model, this can alternatively be a function. sampling_prior: array-like *DEPRECATED* The sampling prior, this can alternatively be a function. THIS WILL BE REMOVED IN THE NEXT RELEASE. ln_evidences: list, optional Log evidences for single runs to ensure proper normalisation of the hyperparameter likelihood. If not provided, the original evidences will be set to 0. This produces a Bayes factor between the sampling power_prior and the hyperparameterised model. selection_function: func Function which evaluates your population selection function. conversion_function: func Function which converts a dictionary of sampled parameter to a dictionary of parameters of the population model. max_samples: int, optional Maximum number of samples to use from each set. cupy: bool If True and a compatible CUDA environment is available, cupy will be used for performance. Note: this requires setting up your hyper_prior properly. """ if cupy and not CUPY_LOADED: logger.warning("Cannot import cupy, falling back to numpy.") self.samples_per_posterior = max_samples self.data = self.resample_posteriors(posteriors, max_samples=max_samples) if isinstance(hyper_prior, types.FunctionType): hyper_prior = Model([hyper_prior]) elif not (hasattr(hyper_prior, 'parameters') and callable(getattr(hyper_prior, 'prob'))): raise AttributeError( "hyper_prior must either be a function, " "or a class with attribute 'parameters' and method 'prob'") self.hyper_prior = hyper_prior Likelihood.__init__(self, hyper_prior.parameters) if sampling_prior is not None: raise ValueError( "Passing a sampling_prior is deprecated and will be removed " "in the next release. This should be passed as a 'prior' " "column in the posteriors.") elif "prior" in self.data: self.sampling_prior = self.data.pop("prior") else: logger.info("No prior values provided, defaulting to 1.") self.sampling_prior = 1 if ln_evidences is not None: self.total_noise_evidence = np.sum(ln_evidences) else: self.total_noise_evidence = np.nan self.conversion_function = conversion_function self.selection_function = selection_function self.n_posteriors = len(posteriors)
def __init__(self, names, nmodes=1, mus=None, sigmas=None, corrcoefs=None, covs=None, weights=None, bounds=None): """ A class defining a multi-variate Gaussian, allowing multiple modes for a Gaussian mixture model. Note: if using a multivariate Gaussian prior, with bounds, this can lead to biases in the marginal likelihood estimate and posterior estimate for nested samplers routines that rely on sampling from a unit hypercube and having a prior transform, e.g., nestle, dynesty and MultiNest. Parameters ---------- names: list A list of the parameter names in the multivariate Gaussian. The listed parameters must have the same order that they appear in the lists of means, standard deviations, and the correlation coefficient, or covariance, matrices. nmodes: int The number of modes for the mixture model. This defaults to 1, which will be checked against the shape of the other inputs. mus: array_like A list of lists of means of each mode in a multivariate Gaussian mixture model. A single list can be given for a single mode. If this is None then means at zero will be assumed. sigmas: array_like A list of lists of the standard deviations of each mode of the multivariate Gaussian. If supplying a correlation coefficient matrix rather than a covariance matrix these values must be given. If this is None unit variances will be assumed. corrcoefs: array A list of square matrices containing the correlation coefficients of the parameters for each mode. If this is None it will be assumed that the parameters are uncorrelated. covs: array A list of square matrices containing the covariance matrix of the multivariate Gaussian. weights: list A list of weights (relative probabilities) for each mode of the multivariate Gaussian. This will default to equal weights for each mode. bounds: list A list of bounds on each parameter. The defaults are for bounds at +/- infinity. """ super(MultivariateGaussianDist, self).__init__(names=names, bounds=bounds) for name in self.names: bound = self.bounds[name] if bound[0] != -np.inf or bound[1] != np.inf: logger.warning("If using bounded ranges on the multivariate " "Gaussian this will lead to biased posteriors " "for nested sampling routines that require " "a prior transform.") self.distname = 'mvg' self.mus = [] self.covs = [] self.corrcoefs = [] self.sigmas = [] self.weights = [] self.eigvalues = [] self.eigvectors = [] self.sqeigvalues = [] # square root of the eigenvalues self.mvn = [] # list of multivariate normal distributions # put values in lists if required if nmodes == 1: if mus is not None: if len(np.shape(mus)) == 1: mus = [mus] elif len(np.shape(mus)) == 0: raise ValueError("Must supply a list of means") if sigmas is not None: if len(np.shape(sigmas)) == 1: sigmas = [sigmas] elif len(np.shape(sigmas)) == 0: raise ValueError("Must supply a list of standard " "deviations") if covs is not None: if isinstance(covs, np.ndarray): covs = [covs] elif isinstance(covs, list): if len(np.shape(covs)) == 2: covs = [np.array(covs)] elif len(np.shape(covs)) != 3: raise TypeError("List of covariances the wrong shape") else: raise TypeError("Must pass a list of covariances") if corrcoefs is not None: if isinstance(corrcoefs, np.ndarray): corrcoefs = [corrcoefs] elif isinstance(corrcoefs, list): if len(np.shape(corrcoefs)) == 2: corrcoefs = [np.array(corrcoefs)] elif len(np.shape(corrcoefs)) != 3: raise TypeError("List of correlation coefficients the wrong shape") elif not isinstance(corrcoefs, list): raise TypeError("Must pass a list of correlation " "coefficients") if weights is not None: if isinstance(weights, (int, float)): weights = [weights] elif isinstance(weights, list): if len(weights) != 1: raise ValueError("Wrong number of weights given") for val in [mus, sigmas, covs, corrcoefs, weights]: if val is not None and not isinstance(val, list): raise TypeError("Value must be a list") else: if val is not None and len(val) != nmodes: raise ValueError("Wrong number of modes given") # add the modes self.nmodes = 0 for i in range(nmodes): mu = mus[i] if mus is not None else None sigma = sigmas[i] if sigmas is not None else None corrcoef = corrcoefs[i] if corrcoefs is not None else None cov = covs[i] if covs is not None else None weight = weights[i] if weights is not None else 1. self.add_mode(mu, sigma, corrcoef, cov, weight)
def __init__(self, names, bounds=None): """ A class defining JointPriorDist that will be overwritten with child classes defining the joint prior distribtuions between given parameters, Parameters ---------- names: list (required) A list of the parameter names in the JointPriorDist. The listed parameters must have the same order that they appear in the lists of statistical parameters that may be passed in child class bounds: list (optional) A list of bounds on each parameter. The defaults are for bounds at +/- infinity. """ self.distname = 'joint_dist' if not isinstance(names, list): self.names = [names] else: self.names = names self.num_vars = len(self.names) # set the bounds for each parameter if isinstance(bounds, list): if len(bounds) != len(self): raise ValueError("Wrong number of parameter bounds") # check bounds for bound in bounds: if isinstance(bounds, (list, tuple, np.ndarray)): if len(bound) != 2: raise ValueError("Bounds must contain an upper and " "lower value.") else: if bound[1] <= bound[0]: raise ValueError("Bounds are not properly set") else: raise TypeError("Bound must be a list") logger.warning("If using bounded ranges on the multivariate " "Gaussian this will lead to biased posteriors " "for nested sampling routines that require " "a prior transform.") else: bounds = [(-np.inf, np.inf) for _ in self.names] self.bounds = {name: val for name, val in zip(self.names, bounds)} self._current_sample = {} # initialise empty sample self._uncorrelated = None self._current_lnprob = None # a dictionary of the parameters as requested by the prior self.requested_parameters = dict() self.reset_request() # a dictionary of the rescaled parameters self.rescale_parameters = dict() self.reset_rescale() # a list of sampled parameters self.reset_sampled()