diff --git a/pesummary/cli/summarycombine_posteriors.py b/pesummary/cli/summarycombine_posteriors.py index c8cdb937157241fd2975c51ec667d862b0724b8b..399d3eebc5eea3710545f0af4500b26691d9390a 100644 --- a/pesummary/cli/summarycombine_posteriors.py +++ b/pesummary/cli/summarycombine_posteriors.py @@ -76,7 +76,7 @@ def main(args=None): label: samples for label, samples in zip(opts.labels, opts.samples) } mydict = MultiAnalysisSamplesDict.from_files( - samples, disable_prior=True, disable_injection_conversion=True + samples, disable_prior_sampling=True, disable_injection_conversion=True ) combined = mydict.combine( use_all=opts.use_all, weights=opts.weights, labels=opts.labels, diff --git a/pesummary/cli/summaryextract.py b/pesummary/cli/summaryextract.py index 8ffd02071820e9418775c1e971aaedde0ae1d059..fec7be039cee660cbd404723034a27216ce24848 100644 --- a/pesummary/cli/summaryextract.py +++ b/pesummary/cli/summaryextract.py @@ -51,7 +51,8 @@ def main(args=None): opts, unknown = _parser.parse_known_args(args=args) logger.info("Loading file: '{}'".format(opts.samples)) f = read( - opts.samples, disable_prior=True, disable_injection_conversion=True + opts.samples, disable_prior_sampling=True, + disable_injection_conversion=True ) posterior_samples = f.samples_dict logger.info("Writing analysis: '{}' to file".format(opts.label)) diff --git a/pesummary/cli/summaryjscompare.py b/pesummary/cli/summaryjscompare.py index fe40ba13412ea7e17f924791b9fdad424f8a8b2e..f3348f62a3d2aa14700b549dae8ecac920b5fdfd 100644 --- a/pesummary/cli/summaryjscompare.py +++ b/pesummary/cli/summaryjscompare.py @@ -36,7 +36,7 @@ _check_latex_install() def load_data(data_file): """ Read in a data file and return samples dictionary """ - f = read(data_file, package="gw", disable_prior=True) + f = read(data_file, package="gw", disable_prior_sampling=True) return f.samples_dict diff --git a/pesummary/core/file/formats/base_read.py b/pesummary/core/file/formats/base_read.py index 43f209b19d108ad7f92a20455e8db2f0df6a3fb9..6322d7280426e4bf8629f7fe74fdaec82506c4c1 100644 --- a/pesummary/core/file/formats/base_read.py +++ b/pesummary/core/file/formats/base_read.py @@ -317,6 +317,11 @@ class Read(object): "Unable to find a posterior samples table in '{}'".format(path) ) + def convert(self, *args, **kwargs): + """Empty function + """ + pass + def generate_all_posterior_samples(self, **kwargs): """Empty function """ diff --git a/pesummary/core/file/formats/bilby.py b/pesummary/core/file/formats/bilby.py index 6c588926244e1f1c381ff804ae448182a19f7a9d..9b3d15a6ee784f63914e34141126834f32725678 100644 --- a/pesummary/core/file/formats/bilby.py +++ b/pesummary/core/file/formats/bilby.py @@ -6,6 +6,7 @@ from pesummary.core.file.formats.base_read import SingleAnalysisRead from pesummary.core.plots.latex_labels import latex_labels from pesummary import conf from pesummary.utils.utils import logger +from pesummary.utils.decorators import deprecated_kwargs __author__ = ["Charlie Hoy "] @@ -22,9 +23,11 @@ def _load_bilby(path): return read_in_result(filename=path) +@deprecated_kwargs({"disable_prior": "disable_prior_sampling"}) def read_bilby( - path, disable_prior=False, complex_params=[], latex_dict=latex_labels, - nsamples_for_prior=None, _bilby_class=None, **kwargs + path, disable_prior_sampling=False, complex_params=[], + latex_dict=latex_labels, nsamples_for_prior=None, _bilby_class=None, + **kwargs ): """Grab the parameters and samples in a bilby file @@ -32,7 +35,7 @@ def read_bilby( ---------- path: str path to the result file you wish to read in - disable_prior: Bool, optional + disable_prior_sampling: Bool, optional if True, do not collect prior samples from the `bilby` result file. Default False complex_params: list, optional @@ -107,7 +110,7 @@ def read_bilby( data["config"] = { "config": bilby_object.meta_data["command_line_args"] } - if not disable_prior: + if not disable_prior_sampling: logger.debug("Drawing prior samples from bilby result file") if nsamples_for_prior is None: nsamples_for_prior = len(samples) @@ -336,7 +339,7 @@ class Bilby(SingleAnalysisRead): ---------- path_to_results_file: str path to the results file that you wish to read in with `bilby`. - disable_prior: Bool, optional + disable_prior_sampling: Bool, optional if True, do not collect prior samples from the `bilby` result file. Default False @@ -399,10 +402,20 @@ class Bilby(SingleAnalysisRead): return kwargs @staticmethod - def _grab_data_from_bilby_file(path, **kwargs): + def _grab_data_from_bilby_file(path, function=read_bilby, **kwargs): """Load the results file using the `bilby` library """ - return read_bilby(path, **kwargs) + try: + return function(path, **kwargs) + except Exception as e: + raise Exception( + "Although the file looks like it was produced with bilby, we " + "were unable to load the file with the " + "'bilby.core.result.read_in_result' function because {}. This " + "is likely a result of a bilby version incompatability".format( + e + ) + ) def add_marginalized_parameters_from_config_file(self, config_file): """Search the configuration file and add the marginalized parameters diff --git a/pesummary/core/file/read.py b/pesummary/core/file/read.py index 5ba83cee332ace9057a904f7039fb27c1ea1e93b..d56a322f3f83a27a918d2fbde1eeaedf97628136 100644 --- a/pesummary/core/file/read.py +++ b/pesummary/core/file/read.py @@ -10,26 +10,45 @@ import os __author__ = ["Charlie Hoy "] -def is_bilby_hdf5_file(path): - """Determine if the results file is a bilby hdf5 results file +def _check_bilby_file(f): + """Determine if the results file is a bilby results file Parameters ---------- - path: str - path to the results file + f: dict-like + loaded result file """ - import h5py try: - f = h5py.File(path, "r") if "bilby" in f["version"]: return True elif "bilby" in str(f["version"][0]): return True else: + standard_keys = [ + "fixed_parameter_keys", "label", "posterior", "meta_data", + "outdir", "parameter_labels" + ] + if all(key in f.keys() for key in standard_keys): + logger.info( + "This file looks like a bilby result file but unable " + "to find the string 'bilby' in the version information." + ) return False except Exception: return False - return False + + +def is_bilby_hdf5_file(path): + """Determine if the results file is a bilby hdf5 results file + + Parameters + ---------- + path: str + path to the results file + """ + import h5py + f = h5py.File(path, "r") + return _check_bilby_file(f) def is_bilby_json_file(path): @@ -43,15 +62,7 @@ def is_bilby_json_file(path): import json with open(path, "r") as f: data = json.load(f) - try: - if "bilby" in data["version"]: - return True - elif "bilby" in data["version"][0]: - return True - else: - return False - except Exception: - return False + return _check_bilby_file(data) def _is_pesummary_hdf5_file(path, check_function): diff --git a/pesummary/core/inputs.py b/pesummary/core/inputs.py index 8198ebc123d43595173fbdb6fde398428d6e4750..3278e5d3443c62fdcf4aff831fff0e19b818497a 100644 --- a/pesummary/core/inputs.py +++ b/pesummary/core/inputs.py @@ -126,7 +126,7 @@ class _Input(object): labels = compare if not f.mcmc_samples: - f.generate_all_posterior_samples(labels=labels, **kwargs) + f.convert("all", labels=labels, **kwargs) parameters = f.parameters if not f.mcmc_samples: @@ -244,7 +244,8 @@ class _Input(object): `generate_all_posterior_samples` method """ f = read_function( - file, file_format=file_format, disable_prior=disable_prior_sampling, + file, file_format=file_format, + disable_prior_sampling=disable_prior_sampling, nsamples_for_prior=nsamples_for_prior, path_to_samples=path_to_samples ) if config is not None: @@ -252,7 +253,7 @@ class _Input(object): if nsamples is not None: f.downsample(nsamples) - f.generate_all_posterior_samples(**kwargs) + f.convert("all", **kwargs) if injection: f.add_injection_parameters_from_file( injection, conversion_kwargs=kwargs diff --git a/pesummary/gw/file/formats/base_read.py b/pesummary/gw/file/formats/base_read.py index 487631cef4f9418c9da913fa1ae1286a2f0343db..76ed8ac207af1fb45fa29bf0202f4241ea0969c8 100644 --- a/pesummary/gw/file/formats/base_read.py +++ b/pesummary/gw/file/formats/base_read.py @@ -77,7 +77,7 @@ def _add_log_likelihood(parameters, samples): def convert_injection_parameters( data, extra_kwargs={"sampler": {}, "meta_data": {}}, disable_convert=False, - sampled_parameters=None + sampled_parameters=None, **kwargs ): """Apply the conversion module to the injection data @@ -92,12 +92,18 @@ def convert_injection_parameters( sampled_parameters: list, optional optional list of sampled parameters. If there is no injection value for a given sampled parameter, add a 'nan' + **kwargs: dict, optional + all kwargs passed to the pesummary.gw.conversions.convert function """ import math if disable_convert: return data if all(math.isnan(data[i]) for i in data.keys()): + if sampled_parameters is not None: + for i in sampled_parameters: + if i not in list(data.keys()): + data[i] = float("nan") return data parameters = list(data.keys()) samples = [[data[i] for i in parameters]] @@ -112,7 +118,10 @@ def convert_injection_parameters( for i in nan_inds[::-1]: parameters.remove(parameters[i]) samples[0].remove(samples[0][i]) - inj_samples = convert(parameters, samples, extra_kwargs=extra_kwargs) + kwargs["return_kwargs"] = False + inj_samples = convert( + parameters, samples, extra_kwargs=extra_kwargs, **kwargs + ) if sampled_parameters is not None: for i in sampled_parameters: if i not in list(inj_samples.keys()): @@ -158,6 +167,15 @@ class GWRead(Read): generate_all_posterior_samples: generate all posterior distributions that may be derived from sampled distributions + generate_all_prior_samples: + generate all prior distributions that may be derived from the + stored prior distributions + generate_all_injection_samples: + generate all injection values that may be derived from the stored + injection values + convert: + convert either the posterior distributions, prior distribution and/or + the injection samples. """ def __init__(self, path_to_results_file, **kwargs): super(GWRead, self).__init__(path_to_results_file, **kwargs) @@ -198,36 +216,30 @@ class GWRead(Read): } ) super(GWRead, self).load(function, _data=data, **kwargs) - if self.injection_parameters is not None: - self.injection_parameters = self.convert_injection_parameters( - self.injection_parameters, extra_kwargs=self.extra_kwargs, - disable_convert=kwargs.get("disable_injection_conversion", False) - ) - if self.priors is not None and len(self.priors): - if self.priors["samples"] != {}: - priors = self.priors["samples"] - self.priors["samples"] = self.convert_and_translate_prior_samples( - priors, disable_convert=kwargs.get( - "disable_prior_conversion", False - ) - ) - def convert_and_translate_prior_samples(self, priors, disable_convert=False): - """ + def _convert(self, parameters, samples, **kwargs): + """Convert a set of posterior samples via the conversion module + + Parameters + ---------- + parameters: list + list of parameter names + samples: 2d list + 2d list of samples where the columns correspond to the posterior + samples for parameters + **kwargs: dict + all kwargs passed to the conversion module """ - default_parameters = list(priors.keys()) - default_samples = [ - [priors[parameter][i] for parameter in default_parameters] for i - in range(len(priors[default_parameters[0]])) - ] - parameters, samples = self.translate_parameters( - default_parameters, default_samples - ) - if not disable_convert: - return convert( - parameters, samples, extra_kwargs=self.extra_kwargs - ) - return SamplesDict(parameters, samples) + from pesummary.gw.conversions import convert + + if "no_conversion" in kwargs.keys(): + no_conversion = kwargs.pop("no_conversion") + else: + no_conversion = False + if not no_conversion: + data = convert(parameters, samples, **kwargs) + return data + return def write(self, package="core", **kwargs): """Save the data to file @@ -264,6 +276,85 @@ class GWRead(Read): """ pass + def generate_all_prior_samples(self, priors, extra_kwargs, **kwargs): + """Generate all prior samples via the conversion module + + Parameters + ---------- + **kwargs: dict + all kwargs passed to the conversion module + """ + default_parameters = list(priors.keys()) + default_samples = np.array( + [priors[key] for key in default_parameters] + ).T.tolist() + parameters, samples = self.translate_parameters( + default_parameters, default_samples + ) + if "disable_prior_conversion" in kwargs.keys(): + kwargs["no_conversion"] = kwargs.get( + "disable_prior_conversion" + ) + if hasattr(self, "disable_prior_conversion"): + if self.disable_prior_conversion: + kwargs["no_conversion"] = kwargs.get( + "disable_prior_conversion" + ) + kwargs["return_kwargs"] = False + if kwargs.get("no_conversion", False): + return SamplesDict(parameters, samples) + data = self._convert( + parameters, samples, extra_kwargs=extra_kwargs, + **kwargs + ) + return data + + def generate_all_posterior_samples( + self, parameters, samples, extra_kwargs, **kwargs + ): + """Generate all posterior samples via the conversion module + + Parameters + ---------- + **kwargs: dict + all kwargs passed to the conversion module + """ + kwargs.update({"return_dict": False}) + data = self._convert( + parameters, samples, extra_kwargs=extra_kwargs, **kwargs + ) + return data + + def convert(self, quantity, **kwargs): + """Convert the stored samples + + Parameters + ---------- + quantity: str + The quantity you wish to convert. This can be 'posterior', 'prior', + 'injection' or 'all' + **kwargs: dict + all kwargs passed to the conversion module + """ + if quantity.lower() == "posterior": + logger.info("Converting the posterior samples") + self.generate_all_posterior_samples(**kwargs) + elif quantity.lower() == "prior": + logger.info("Converting the prior samples") + self.generate_all_prior_samples(**kwargs) + elif quantity.lower() == "injection": + logger.info("Converting the injection samples") + self.generate_all_injection_samples(**kwargs) + elif quantity.lower() == "all": + for _quantity in ["posterior", "prior", "injection"]: + self.convert(_quantity, **kwargs) + else: + raise ValueError( + "Unrecognised quantity '{}'. Please select either " + "'posterior', 'prior', 'injection' or 'all'".format(quantity) + ) + return + def interpolate_calibration_spline_posterior(self, **kwargs): from pesummary.gw.file.calibration import Calibration from pesummary.utils.utils import iterator @@ -430,6 +521,12 @@ class GWSingleAnalysisRead(GWRead, SingleAnalysisRead): generate_all_posterior_samples: generate all posterior distributions that may be derived from sampled distributions + generate_all_prior_samples: + generate all prior distributions that may be derived from the + stored prior distributions + generate_all_injection_samples: + generate all injection values that may be derived from the stored + injection values """ def __init__(self, *args, **kwargs): super(GWSingleAnalysisRead, self).__init__(*args, **kwargs) @@ -459,6 +556,23 @@ class GWSingleAnalysisRead(GWRead, SingleAnalysisRead): """ return _add_log_likelihood(parameters, samples) + def generate_all_prior_samples(self, **kwargs): + """Generate all prior samples via the conversion module + + Parameters + ---------- + **kwargs: dict + all kwargs passed to the conversion module + """ + if self.priors is not None and len(self.priors): + if self.priors["samples"] != {}: + data = super(GWSingleAnalysisRead, self).generate_all_prior_samples( + self.priors["samples"], self.extra_kwargs, **kwargs + ) + if data is not None: + self.priors["samples"] = data + return + def generate_all_posterior_samples(self, **kwargs): """Generate all posterior samples via the conversion module @@ -467,41 +581,34 @@ class GWSingleAnalysisRead(GWRead, SingleAnalysisRead): **kwargs: dict all kwargs passed to the conversion module """ - if "no_conversion" in kwargs.keys(): - no_conversion = kwargs.pop("no_conversion") - else: - no_conversion = False - if not no_conversion: - from pesummary.gw.conversions import convert - - data = convert( - self.parameters, self.samples, extra_kwargs=self.extra_kwargs, - return_dict=False, **kwargs - ) + data = super(GWSingleAnalysisRead, self).generate_all_posterior_samples( + self.parameters, self.samples, self.extra_kwargs, **kwargs + ) + if data is not None: self.parameters = data[0] self.converted_parameters = self.parameters.added self.samples = data[1] if kwargs.get("return_kwargs", False): self.extra_kwargs = data[2] - def convert_injection_parameters( - self, data, extra_kwargs={"sampler": {}, "meta_data": {}}, - disable_convert=False - ): - """Apply the conversion module to the injection data + def generate_all_injection_samples(self, **kwargs): + """Generate all injection samples via the conversion module Parameters ---------- - data: dict - dictionary of injection data keyed by the parameter - extra_kwargs: dict, optional - optional kwargs to pass to the conversion module - disable_convert: Bool, optional - if True, do not convert injection parameters + **kwargs: dict + all kwargs passed to the conversion module """ - return convert_injection_parameters( - data, extra_kwargs=extra_kwargs, disable_convert=disable_convert, - sampled_parameters=self.parameters + if "disable_injection_conversion" in kwargs.keys(): + kwargs["no_conversion"] = kwargs.get( + "disable_injection_conversion" + ) + if self.injection_parameters is None: + return + self.injection_parameters = convert_injection_parameters( + self.injection_parameters, extra_kwargs=self.extra_kwargs, + disable_convert=kwargs.get("no_conversion", False), + **kwargs ) def to_lalinference(self, **kwargs): @@ -591,31 +698,6 @@ class GWMultiAnalysisRead(GWRead, MultiAnalysisRead): except (KeyError, AttributeError): pass - def convert_and_translate_prior_samples(self, priors, disable_convert=False): - """ - """ - from pesummary.utils.samples_dict import MultiAnalysisSamplesDict - - mydict = {} - for num, label in enumerate(self.labels): - if label in priors.keys() and len(priors[label]): - default_parameters = list(priors[label].keys()) - default_samples = np.array( - [priors[label][_param] for _param in default_parameters] - ).T - parameters, samples = self.translate_parameters( - [default_parameters], [default_samples] - ) - if not disable_convert: - mydict[label] = convert( - parameters[0], samples[0], extra_kwargs=self.extra_kwargs[num] - ) - else: - mydict[label] = SamplesDict(parameters[0], samples[0]) - else: - mydict[label] = {} - return MultiAnalysisSamplesDict(mydict) - def check_for_log_likelihood(self, parameters): if all("log_likelihood" in p for p in parameters): return True @@ -649,14 +731,52 @@ class GWMultiAnalysisRead(GWRead, MultiAnalysisRead): samples_logl.append(ss) return parameters_logl, samples_logl + def generate_all_prior_samples(self, labels=None, **kwargs): + """Apply the conversion module to the prior samples + + Parameters + ---------- + labels: list, optional + list of analyses to consider + **kwargs: dict + all kwargs passed to the conversion module + """ + from pesummary.utils.samples_dict import MultiAnalysisSamplesDict + + mydict = {} + for num, label in enumerate(self.labels): + _kwargs = kwargs.copy() + if labels is not None and label not in labels: + _kwargs["no_conversion"] = True + if label not in self.priors.keys() or not len(self.priors[label]): + mydict[label] = {} + continue + mydict[label] = ( + super(GWMultiAnalysisRead, self).generate_all_prior_samples( + self.priors[label]["samples"], self.extra_kwargs[num], + **_kwargs + ) + ) + elif label in self.priors.keys() and len(self.priors[label]): + mydict[label] = ( + super(GWMultiAnalysisRead, self).generate_all_prior_samples( + self.priors[label]["samples"], self.extra_kwargs[num], + **kwargs + ) + ) + else: + mydict[label] = {} + return MultiAnalysisSamplesDict(mydict) + def generate_all_posterior_samples(self, labels=None, **conversion_kwargs): + """Apply the conversion module to the posterior distributions + """ if "no_conversion" in conversion_kwargs.keys(): no_conversion = conversion_kwargs.pop("no_conversion") else: no_conversion = False if no_conversion: return - from pesummary.gw.conversions import convert converted_params, converted_samples, converted_kwargs = [], [], [] _converted_params = [] @@ -677,9 +797,8 @@ class GWMultiAnalysisRead(GWRead, MultiAnalysisRead): if _conversion_kwargs.get("evolve_spins", False): if not _conversion_kwargs.get("return_kwargs", False): _conversion_kwargs["return_kwargs"] = True - data = convert( - param, samples, extra_kwargs=kwargs, return_dict=False, - **_conversion_kwargs + data = super(GWMultiAnalysisRead, self).generate_all_posterior_samples( + param, samples, kwargs, **_conversion_kwargs ) converted_params.append(data[0]) _converted_params.append(data[0].added) @@ -696,22 +815,36 @@ class GWMultiAnalysisRead(GWRead, MultiAnalysisRead): ) } - def convert_injection_parameters( - self, data, extra_kwargs={"sampler": {}, "meta_data": {}}, - disable_convert=False - ): + def generate_all_injection_samples(self, labels=None, **kwargs): """Apply the conversion module to the injection data + + Parameters + ---------- + labels: list, optional + list of analyses to consider + **kwargs: dict + all kwargs passed to the conversion module """ + if "disable_injection_conversion" in kwargs.keys(): + kwargs["no_conversion"] = kwargs.get( + "disable_injection_conversion" + ) + data = self.injection_parameters for num, label in enumerate(self.labels): + _kwargs = kwargs.copy() + if labels is not None and label not in labels: + _kwargs["no_conversion"] = True _identifier = label - if isinstance(data, dict): - _data = data[label] + if isinstance(self.injection_parameters, dict): + _data = self.injection_parameters[label] else: - _data = data[num] + _data = self.injection_parameters[num] _identifier = num + if _data is None: + continue data[_identifier] = convert_injection_parameters( - _data, extra_kwargs=extra_kwargs[num], - disable_convert=disable_convert, + _data, extra_kwargs=self.extra_kwargs[num], + disable_convert=_kwargs.get("no_conversion"), sampled_parameters=self.parameters[num] ) - return data + self.injection_parameters = data diff --git a/pesummary/gw/file/formats/bilby.py b/pesummary/gw/file/formats/bilby.py index de8a2931284ef1d0c42f2ce2a10114d7ddfba3f4..4a9b26a9e1b4979ad2c414ae07d09390df1d5f9a 100644 --- a/pesummary/gw/file/formats/bilby.py +++ b/pesummary/gw/file/formats/bilby.py @@ -11,7 +11,7 @@ __author__ = ["Charlie Hoy "] def read_bilby( - path, disable_prior=False, latex_dict=GWlatex_labels, + path, disable_prior_sampling=False, latex_dict=GWlatex_labels, complex_params=["matched_filter_snr", "optimal_snr"], **kwargs ): """Grab the parameters and samples in a bilby file @@ -20,7 +20,7 @@ def read_bilby( ---------- path: str path to the result file you wish to read in - disable_prior: Bool, optional + disable_prior_sampling: Bool, optional if True, do not collect prior samples from the `bilby` result file. Default False complex_params: list, optional @@ -35,8 +35,9 @@ def read_bilby( ) return _read_bilby( - path, disable_prior=disable_prior, latex_dict=latex_dict, - complex_params=complex_params, _bilby_class=Bilby, **kwargs + path, disable_prior_sampling=disable_prior_sampling, + latex_dict=latex_dict, complex_params=complex_params, + _bilby_class=Bilby, **kwargs ) @@ -72,7 +73,7 @@ class Bilby(GWSingleAnalysisRead): ---------- path_to_results_file: str path to the results file that you wish to read in with `bilby`. - disable_prior: Bool, optional + disable_prior_sampling: Bool, optional if True, do not collect prior samples from the `bilby` result file. Default False disable_prior_conversion: Bool, optional @@ -80,6 +81,18 @@ class Bilby(GWSingleAnalysisRead): distributions. Default False pe_algorithm: str name of the algorithm used to generate the posterior samples + disable_injection_conversion: Bool, optional + if True, disable the conversion module from deriving alternative + injection parameters + add_zero_likelihood: Bool, optional + if True assign 'log_likelihood=0' to every sample if no 'log_likelihood' + samples can be found. Default True + injection_conversion_kwargs: dict, optional + optional kwargs to pass to the pesummary.gw.conversions.convert function + for the injection parameters + prior_conversion_kwargs: dict, optional + optional kwargs to pass to the pesummary.gw.conversions.convert function + for the prior distributions Attributes ---------- @@ -116,10 +129,22 @@ class Bilby(GWSingleAnalysisRead): generate_all_posterior_samples: generate all posterior distributions that may be derived from sampled distributions + generate_all_prior_samples: + generate all prior distributions that may be derived from the + stored prior distributions + generate_all_injection_samples: + generate all injection values that may be derived from the stored + injection values + convert: + convert either the posterior distributions, prior distribution and/or + the injection samples. """ def __init__(self, path_to_results_file, **kwargs): super(Bilby, self).__init__(path_to_results_file, **kwargs) self.load(self._grab_data_from_bilby_file, **kwargs) + self.disable_prior_conversion = kwargs.get( + "disable_prior_conversion", False + ) @staticmethod def grab_priors(bilby_object, nsamples=5000): @@ -268,14 +293,18 @@ class Bilby(GWSingleAnalysisRead): return strain_data @staticmethod - def _grab_data_from_bilby_file(path, disable_prior=False, **kwargs): + def _grab_data_from_bilby_file(path, disable_prior_sampling=False, **kwargs): """ Load the results file using the `bilby` library Complex matched filter SNRs are stored in the result file. The amplitude and angle are extracted here. """ - return read_bilby(path, disable_prior=disable_prior, **kwargs) + return CoreBilby._grab_data_from_bilby_file( + path, function=read_bilby, + disable_prior_sampling=disable_prior_sampling, + **kwargs + ) def add_marginalized_parameters_from_config_file(self, config_file): """Search the configuration file and add the marginalized parameters diff --git a/pesummary/gw/file/formats/default.py b/pesummary/gw/file/formats/default.py index 08a1aee730fce4781af0f8027fb61464e414027f..2306ecb0c2e333f786ddec7d55581d2ccb1b12f8 100644 --- a/pesummary/gw/file/formats/default.py +++ b/pesummary/gw/file/formats/default.py @@ -17,6 +17,9 @@ class SingleAnalysisDefault(GWSingleAnalysisRead): ---------- path_to_results_file: str path to the results file you wish to load + add_zero_likelihood: Bool, optional + if True assign 'log_likelihood=0' to every sample if no 'log_likelihood' + samples can be found. Default True Attributes ---------- @@ -50,6 +53,15 @@ class SingleAnalysisDefault(GWSingleAnalysisRead): generate_all_posterior_samples: generate all posterior distributions that may be derived from sampled distributions + generate_all_prior_samples: + generate all prior distributions that may be derived from the + stored prior distributions + generate_all_injection_samples: + generate all injection values that may be derived from the stored + injection values + convert: + convert either the posterior distributions, prior distribution and/or + the injection samples. """ def __init__(self, *args, _data=None, **kwargs): super(SingleAnalysisDefault, self).__init__(*args, **kwargs) @@ -93,6 +105,15 @@ class MultiAnalysisDefault(GWMultiAnalysisRead): generate_all_posterior_samples: generate all posterior distributions that may be derived from sampled distributions + generate_all_prior_samples: + generate all prior distributions that may be derived from the + stored prior distributions + generate_all_injection_samples: + generate all injection values that may be derived from the stored + injection values + convert: + convert either the posterior distributions, prior distribution and/or + the injection samples. """ def __init__(self, *args, _data=None, **kwargs): super(MultiAnalysisDefault, self).__init__(*args, **kwargs) @@ -138,6 +159,12 @@ class Default(CoreDefault): generate_all_posterior_samples: generate all posterior distributions that may be derived from sampled distributions + generate_all_prior_samples: + generate all prior distributions that may be derived from the + stored prior distributions + generate_all_injection_samples: + generate all injection values that may be derived from the stored + injection values """ def load_map(self): _load_map = super(Default, self).load_map(self) diff --git a/pesummary/gw/file/formats/lalinference.py b/pesummary/gw/file/formats/lalinference.py index c1b09696ae72156e94eef226e21fcd3d411948c1..cab45e3fe4acacf428a8ced53979a79207061ef1 100644 --- a/pesummary/gw/file/formats/lalinference.py +++ b/pesummary/gw/file/formats/lalinference.py @@ -90,6 +90,9 @@ class LALInference(GWSingleAnalysisRead): ---------- path_to_results_file: str path to the results file you wish to load in with `LALInference` + add_zero_likelihood: Bool, optional + if True assign 'log_likelihood=0' to every sample if no 'log_likelihood' + samples can be found. Default True Attributes ---------- @@ -123,6 +126,15 @@ class LALInference(GWSingleAnalysisRead): generate_all_posterior_samples: generate all posterior distributions that may be derived from sampled distributions + generate_all_prior_samples: + generate all prior distributions that may be derived from the + stored prior distributions + generate_all_injection_samples: + generate all injection values that may be derived from the stored + injection values + convert: + convert either the posterior distributions, prior distribution and/or + the injection samples. """ def __init__(self, path_to_results_file, injection_file=None, **kwargs): super(LALInference, self).__init__(path_to_results_file, **kwargs) diff --git a/pesummary/gw/file/formats/pesummary.py b/pesummary/gw/file/formats/pesummary.py index 0dfb00fbc5e05bca3a494d506bb91235a1073da4..dea4ac31794d2463774b7e06e9c9233e017c27a3 100644 --- a/pesummary/gw/file/formats/pesummary.py +++ b/pesummary/gw/file/formats/pesummary.py @@ -110,6 +110,15 @@ class PESummary(GWMultiAnalysisRead, CorePESummary): generate_all_posterior_samples: generate all posterior distributions that may be derived from sampled distributions + generate_all_prior_samples: + generate all prior distributions that may be derived from the + stored prior distributions + generate_all_injection_samples: + generate all injection values that may be derived from the stored + injection values + convert: + convert either the posterior distributions, prior distribution and/or + the injection samples. """ def __init__(self, path_to_results_file, **kwargs): super(PESummary, self).__init__( diff --git a/pesummary/gw/inputs.py b/pesummary/gw/inputs.py index 7c40397ef9b53df5e2cde1772befd6b2b29d7029..32df0ac153635bf4f9e5251d9b43c5be71e1337b 100644 --- a/pesummary/gw/inputs.py +++ b/pesummary/gw/inputs.py @@ -391,7 +391,7 @@ class _GWInput(_Input): if i in _opened.keys() and _opened[i] is not None: f = self._open_result_files[i] else: - f = GWRead(i, disable_prior=True) + f = GWRead(i, disable_prior_sampling=True) try: calibration_data = f.interpolate_calibration_spline_posterior() except Exception as e: @@ -1405,7 +1405,8 @@ class IMRCTInput(_Input): def samples(self, samples): from pesummary.utils.samples_dict import MultiAnalysisSamplesDict self._read_samples = { - _label: GWRead(_path, disable_prior=True) for _label, _path in zip( + _label: GWRead(_path, disable_prior_sampling=True) for + _label, _path in zip( self.labels, self.result_files ) } diff --git a/pesummary/io/read.py b/pesummary/io/read.py index fd9b382441f76a7386cd102551a4ccb6b2f983a0..4f7a413e64a78bf55b20b2b5a774062de2a9720e 100644 --- a/pesummary/io/read.py +++ b/pesummary/io/read.py @@ -3,6 +3,7 @@ import os import importlib from pathlib import Path +from pesummary.utils.decorators import docstring_subfunction from pesummary.core.file.formats.ini import read_ini from pesummary.core.file.formats.pickle import read_pickle from pesummary.gw.file.strain import StrainData @@ -60,6 +61,18 @@ def _fetch_from_remote_server(ff): return _fetch(ff, scp_and_read_file) +@docstring_subfunction( + [ + "pesummary.core.file.formats.bilby.Bilby", + "pesummary.core.file.formats.default.Default", + "pesummary.core.file.formats.pesummary.PESummary", + "pesummary.gw.file.formats.bilby.Bilby", + "pesummary.gw.file.formats.GWTC1.GWTC1", + "pesummary.gw.file.formats.lalinference.LALInference", + "pesummary.gw.file.formats.pesummary.PESummary", + "pesummary.gw.file.formats.pesummary.TGRPESummary" + ] +) def read( path, package="gw", file_format=None, skymap=False, strain=False, cls=None, checkpoint=False, **kwargs @@ -87,8 +100,8 @@ def read( checkpoint: Bool, optional if True, treat path as the path to a checkpoint file **kwargs: dict, optional - all additional kwargs are passed to the `pesummary.{}.file.read.read` - function + all additional kwargs are passed to the + `pesummary.{}.file.formats.{}.{}` class. See below for details """ if not os.path.isfile(path) and "https://" in path: path = _fetch_from_url(path) diff --git a/pesummary/tests/read_test.py b/pesummary/tests/read_test.py index 1afdc36cc40afbcb2cc0ae0b53225e2433952f4c..fe028d5ff02ff5e8df831e9237dda7f9355eea65 100644 --- a/pesummary/tests/read_test.py +++ b/pesummary/tests/read_test.py @@ -654,7 +654,7 @@ class BilbyFile(BaseRead): """ for param, prior in self.result.priors["samples"].items(): assert isinstance(prior, np.ndarray) - f = read_function(self.path, disable_prior=True) + f = read_function(self.path, disable_prior_sampling=True) assert not len(f.priors["samples"]) f = read_function(self.path, nsamples_for_prior=200) params = list(f.priors["samples"].keys()) @@ -1555,10 +1555,12 @@ class TestGWJsonBilbyFile(GWBaseRead): assert "final_mass_source_non_evolved" not in self.result.parameters for param, prior in self.result.priors["samples"].items(): assert isinstance(prior, np.ndarray) + self.result.generate_all_prior_samples() assert "final_mass_source_non_evolved" in self.result.priors["samples"].keys() f = read_function(self.path, disable_prior_conversion=True) + self.result.generate_all_prior_samples() assert "final_mass_source_non_evolved" not in f.priors["samples"].keys() - f = read_function(self.path, disable_prior=True) + f = read_function(self.path, disable_prior_sampling=True) assert not len(f.priors["samples"]) f = read_function(self.path, nsamples_for_prior=200) params = list(f.priors["samples"].keys()) diff --git a/pesummary/utils/decorators.py b/pesummary/utils/decorators.py index 0ae864533d8b38a0841a2c02504eafac28f9e620..f7fe89ef8098c4afe27f71da306a31ddbd36abaf 100644 --- a/pesummary/utils/decorators.py +++ b/pesummary/utils/decorators.py @@ -275,3 +275,43 @@ def deprecation(warning): return func(*args, **kwargs) return wrapper_function return decorator + + +def deprecated_kwargs(deprecated): + """Print a warning indicating that a kwarg may be deprecated in future + releases + + Parameters + ---------- + deprecated: dict + dictionary of kwargs to be deprecated. The key should be the deprecated + kwarg and the value should be the new kwarg + + Examples + -------- + >>> @deprecated_kwargs({"n_samples": "nsamples"}) + ... def uniform_samples(nsamples=1000): + ... return np.random.uniform(size=nsamples) + ... + >>> np.random.seed(123456789) + >>> samples = uniform_samples(n_samples=2000) + pesummary/utils/decorators.py:302: UserWarning: n_samples will be deprecated + in future releases. Please use nsamples + "{}".format(_deprecated_kwarg, _new_kwarg) + >>> samples = uniform_samples(nsamples=2000) + """ + def decorator(func): + @functools.wraps(func) + def wrapper_function(*args, **kwargs): + import warnings + for _deprecated_kwarg, _new_kwarg in deprecated.items(): + if _deprecated_kwarg in kwargs.keys(): + warnings.warn( + "{} will be deprecated in future releases. Please use " + "{}".format(_deprecated_kwarg, _new_kwarg) + ) + val = kwargs.pop(_deprecated_kwarg) + kwargs[_new_kwarg] = val + return func(*args, **kwargs) + return wrapper_function + return decorator diff --git a/pesummary/utils/samples_dict.py b/pesummary/utils/samples_dict.py index 84745f405c6d43217b931dc60e00a194a18a44ff..96a99760436de3455a4ee5fe9b069b777db5759d 100644 --- a/pesummary/utils/samples_dict.py +++ b/pesummary/utils/samples_dict.py @@ -72,6 +72,9 @@ class SamplesDict(Dict): Remove the first N samples from each distribution plot: Generate a plot based on the posterior samples stored + convert: + Convert the posterior samples in the SamplesDict object according to + a conversion function generate_all_posterior_samples: Convert the posterior samples in the SamplesDict object according to a conversion function @@ -393,6 +396,21 @@ class SamplesDict(Dict): """ return super(SamplesDict, self).plot(*args, type=type, **kwargs) + def convert(self, function=None, **kwargs): + """Convert samples stored in the SamplesDict according to a conversion + function + + Parameters + ---------- + function: func, optional + function to use when converting posterior samples. Must take a + dictionary as input and return a dictionary of converted posterior + samples. Default `pesummary.gw.conversions.convert + **kwargs: dict, optional + All additional kwargs passed to function + """ + return self.generate_all_posterior_samples(function=function, **kwargs) + def generate_all_posterior_samples(self, function=None, **kwargs): """Convert samples stored in the SamplesDict according to a conversion function