diff --git a/bilby/core/prior/base.py b/bilby/core/prior/base.py index db5880da1a3cf77e67aff29fe9a8bdf2f2e69892..802d5f6f12f0ed15e44b76514809832c406f16f3 100644 --- a/bilby/core/prior/base.py +++ b/bilby/core/prior/base.py @@ -68,6 +68,9 @@ class Prior(object): if sorted(self.__dict__.keys()) != sorted(other.__dict__.keys()): return False for key in self.__dict__: + if key == "least_recently_sampled": + # ignore sample drawn from prior in comparison + continue if type(self.__dict__[key]) is np.ndarray: if not np.array_equal(self.__dict__[key], other.__dict__[key]): return False diff --git a/bilby/core/result.py b/bilby/core/result.py index d967138c77ba014af15f5ebc26e6338b6afcaafb..96432e066e99acd63f78ce7b28fb16debe417b33 100644 --- a/bilby/core/result.py +++ b/bilby/core/result.py @@ -23,7 +23,8 @@ from .utils import ( check_directory_exists_and_if_not_mkdir, latex_plot_format, safe_save_figure, BilbyJsonEncoder, load_json, - move_old_file, get_version_information + move_old_file, get_version_information, + decode_bilby_json, ) from .prior import Prior, PriorDict, DeltaFunction @@ -358,10 +359,27 @@ class Result(object): if os.path.isfile(filename): dictionary = deepdish.io.load(filename) - # Some versions of deepdish/pytables return the dictionanary as + # Some versions of deepdish/pytables return the dictionary as # a dictionary with a key 'data' if len(dictionary) == 1 and 'data' in dictionary: dictionary = dictionary['data'] + + if "priors" in dictionary: + # parse priors from JSON string (allowing for backwards + # compatibility) + if not isinstance(dictionary["priors"], PriorDict): + try: + priordict = PriorDict() + for key, value in dictionary["priors"].items(): + if key not in ["__module__", "__name__", "__prior_dict__"]: + priordict[key] = decode_bilby_json(value) + dictionary["priors"] = priordict + except Exception as e: + raise IOError( + "Unable to parse priors from '{}':\n{}".format( + filename, e, + ) + ) try: if isinstance(dictionary.get('posterior', None), dict): dictionary['posterior'] = pd.DataFrame(dictionary['posterior']) @@ -609,8 +627,9 @@ class Result(object): dictionary['sampler_kwargs'][key] = str(dictionary['sampler_kwargs']) try: + # convert priors to JSON dictionary for both JSON and hdf5 files + dictionary["priors"] = dictionary["priors"]._get_json_dict() if extension == 'json': - dictionary["priors"] = dictionary["priors"]._get_json_dict() if gzip: import gzip # encode to a string diff --git a/bilby/core/sampler/base_sampler.py b/bilby/core/sampler/base_sampler.py index 2ae872b33383f03c13756fe89c62ca79a8372ce1..74e880fbb852ac48ecde433bcbc0d54de2115b5d 100644 --- a/bilby/core/sampler/base_sampler.py +++ b/bilby/core/sampler/base_sampler.py @@ -430,6 +430,7 @@ class Sampler(object): likelihood evaluations. """ + logger.info("Generating initial points from the prior") unit_cube = [] parameters = [] likelihood = [] diff --git a/bilby/core/sampler/dynesty.py b/bilby/core/sampler/dynesty.py index a89e8f55a94096d2e0b4769a68d840e67c381f1e..cdf5846409df229728c2f33161ff5823dd325af5 100644 --- a/bilby/core/sampler/dynesty.py +++ b/bilby/core/sampler/dynesty.py @@ -257,11 +257,11 @@ class Dynesty(NestedSampler): # Constructing output. string = [] string.append("bound:{:d}".format(bounditer)) - string.append("nc:{:d}".format(nc)) - string.append("ncall:{:d}".format(ncall)) + string.append("nc:{:3d}".format(nc)) + string.append("ncall:{:.1e}".format(ncall)) string.append("eff:{:0.1f}%".format(eff)) string.append("{}={:0.2f}+/-{:0.2f}".format(key, logz, logzerr)) - string.append("dlogz:{:0.3f}>{:0.2f}".format(delta_logz, dlogz)) + string.append("dlogz:{:0.3f}>{:0.2g}".format(delta_logz, dlogz)) self.pbar.set_postfix_str(" ".join(string), refresh=False) self.pbar.update(niter - self.pbar.n) @@ -793,7 +793,7 @@ def sample_rwalk_bilby(args): v = v_list[idx] logl = logl_list[idx] else: - logger.warning("Unable to find a new point using walk: returning a random point") + logger.debug("Unable to find a new point using walk: returning a random point") u = np.random.uniform(size=n) v = prior_transform(u) logl = loglikelihood(v) diff --git a/bilby/core/sampler/ptemcee.py b/bilby/core/sampler/ptemcee.py index 7baa0a9a65ffce828aab4ef19b7b725130787380..da13250a48b46a2fc45dffa375bfb8594a924c8f 100644 --- a/bilby/core/sampler/ptemcee.py +++ b/bilby/core/sampler/ptemcee.py @@ -193,7 +193,13 @@ class Ptemcee(MCMCSampler): ) self.convergence_inputs = ConvergenceInputs(**convergence_inputs_dict) - # MultiProcessing inputs + # Check if threads was given as an equivalent arg + if threads == 1: + for equiv in self.npool_equiv_kwargs: + if equiv in kwargs: + threads = kwargs.pop(equiv) + + # Store threads self.threads = threads # Misc inputs @@ -221,10 +227,6 @@ class Ptemcee(MCMCSampler): for equiv in self.nwalkers_equiv_kwargs: if equiv in kwargs: kwargs["nwalkers"] = kwargs.pop(equiv) - if "threads" not in kwargs: - for equiv in self.npool_equiv_kwargs: - if equiv in kwargs: - kwargs["threads"] = kwargs.pop(equiv) def get_pos0_from_prior(self): """ Draw the initial positions from the prior diff --git a/bilby/core/sampler/pymultinest.py b/bilby/core/sampler/pymultinest.py index 055d4e14ad969f70da745fa13571db693405784a..9421c8207c09dae938d4b37cf6b61a46c2853a42 100644 --- a/bilby/core/sampler/pymultinest.py +++ b/bilby/core/sampler/pymultinest.py @@ -2,7 +2,10 @@ import importlib import os import tempfile import shutil +import distutils.dir_util import signal +import time +import datetime import numpy as np @@ -115,8 +118,15 @@ class Pymultinest(NestedSampler): # for PyMultiNest >=2.9 the n_params kwarg cannot be None if self.kwargs["n_params"] is None: self.kwargs["n_params"] = self.ndim + if self.kwargs['dump_callback'] is None: + self.kwargs['dump_callback'] = self._dump_callback NestedSampler._verify_kwargs_against_default_kwargs(self) + def _dump_callback(self, *args, **kwargs): + if self.use_temporary_directory: + self._copy_temporary_directory_contents_to_proper_path() + self._calculate_and_save_sampling_time() + def _apply_multinest_boundaries(self): if self.kwargs["wrapped_params"] is None: self.kwargs["wrapped_params"] = [] @@ -154,10 +164,6 @@ class Pymultinest(NestedSampler): shutil.copytree( self.outputfiles_basename, self.temporary_outputfiles_basename ) - if os.path.islink(self.outputfiles_basename): - os.unlink(self.outputfiles_basename) - else: - shutil.rmtree(self.outputfiles_basename) def write_current_state_and_exit(self, signum=None, frame=None): """ Write current state and exit on exit_code """ @@ -166,15 +172,15 @@ class Pymultinest(NestedSampler): signum, self.exit_code ) ) + self._calculate_and_save_sampling_time() if self.use_temporary_directory: self._move_temporary_directory_to_proper_path() os._exit(self.exit_code) - def _move_temporary_directory_to_proper_path(self): + def _copy_temporary_directory_contents_to_proper_path(self): """ - Move the temporary back to the proper path - - Anything in the proper path at this point is removed including links + Copy the temporary back to the proper path. + Do not delete the temporary directory. """ logger.info( "Overwriting {} with {}".format( @@ -185,11 +191,16 @@ class Pymultinest(NestedSampler): outputfiles_basename_stripped = self.outputfiles_basename[:-1] else: outputfiles_basename_stripped = self.outputfiles_basename - if os.path.islink(outputfiles_basename_stripped): - os.unlink(outputfiles_basename_stripped) - elif os.path.isdir(outputfiles_basename_stripped): - shutil.rmtree(outputfiles_basename_stripped) - shutil.move(self.temporary_outputfiles_basename, outputfiles_basename_stripped) + distutils.dir_util.copy_tree(self.temporary_outputfiles_basename, outputfiles_basename_stripped) + + def _move_temporary_directory_to_proper_path(self): + """ + Copy the temporary back to the proper path + + Anything in the temporary directory at this point is removed + """ + self._copy_temporary_directory_contents_to_proper_path() + shutil.rmtree(self.temporary_outputfiles_basename) def run_sampler(self): import pymultinest @@ -197,17 +208,20 @@ class Pymultinest(NestedSampler): self._verify_kwargs_against_default_kwargs() self._setup_run_directory() + self._check_and_load_sampling_time_file() # Overwrite pymultinest's signal handling function pm_run = importlib.import_module("pymultinest.run") pm_run.interrupt_handler = self.write_current_state_and_exit + self.start_time = time.time() out = pymultinest.solve( LogLikelihood=self.log_likelihood, Prior=self.prior_transform, n_dims=self.ndim, **self.kwargs ) + self._calculate_and_save_sampling_time() self._clean_up_run_directory() @@ -222,26 +236,22 @@ class Pymultinest(NestedSampler): self.result.log_evidence_err = out["logZerr"] self.calc_likelihood_count() self.result.outputfiles_basename = self.outputfiles_basename + self.result.sampling_time = datetime.timedelta(seconds=self.total_sampling_time) return self.result def _setup_run_directory(self): """ If using a temporary directory, the output directory is moved to the - temporary directory and symlinked back. + temporary directory. """ if self.use_temporary_directory: temporary_outputfiles_basename = tempfile.TemporaryDirectory().name self.temporary_outputfiles_basename = temporary_outputfiles_basename if os.path.exists(self.outputfiles_basename): - shutil.move(self.outputfiles_basename, self.temporary_outputfiles_basename) + distutils.dir_util.copy_tree(self.outputfiles_basename, self.temporary_outputfiles_basename) check_directory_exists_and_if_not_mkdir(temporary_outputfiles_basename) - os.symlink( - os.path.abspath(self.temporary_outputfiles_basename), - os.path.abspath(self.outputfiles_basename), - target_is_directory=True, - ) self.kwargs["outputfiles_basename"] = self.temporary_outputfiles_basename logger.info("Using temporary file {}".format(temporary_outputfiles_basename)) else: @@ -249,6 +259,21 @@ class Pymultinest(NestedSampler): self.kwargs["outputfiles_basename"] = self.outputfiles_basename logger.info("Using output file {}".format(self.outputfiles_basename)) + def _check_and_load_sampling_time_file(self): + self.time_file_path = self.kwargs["outputfiles_basename"] + '/sampling_time.dat' + if os.path.exists(self.time_file_path): + with open(self.time_file_path, 'r') as time_file: + self.total_sampling_time = float(time_file.readline()) + else: + self.total_sampling_time = 0 + + def _calculate_and_save_sampling_time(self): + current_time = time.time() + new_sampling_time = current_time - self.start_time + self.total_sampling_time += new_sampling_time + with open(self.time_file_path, 'w') as time_file: + time_file.write(str(self.total_sampling_time)) + def _clean_up_run_directory(self): if self.use_temporary_directory: self._move_temporary_directory_to_proper_path() diff --git a/bilby/core/utils.py b/bilby/core/utils.py index 6797fe821a96081db920daba9697c5daebbe83ce..d4704ae5f92e3cfdd34d0adc45501d437260ae5b 100644 --- a/bilby/core/utils.py +++ b/bilby/core/utils.py @@ -1236,6 +1236,13 @@ def kish_log_effective_sample_size(ln_weights): return log_n_eff +def get_function_path(func): + if hasattr(func, "__module__") and hasattr(func, "__name__"): + return "{}.{}".format(func.__module__, func.__name__) + else: + return func + + class IllegalDurationAndSamplingFrequencyException(Exception): pass diff --git a/bilby/gw/likelihood.py b/bilby/gw/likelihood.py index 25d0c37ee3883d364f82c6fc7016ecedfd95cd8a..f32917775fa05c415dd8368ff54fcabc1fd94bce 100644 --- a/bilby/gw/likelihood.py +++ b/bilby/gw/likelihood.py @@ -196,7 +196,7 @@ class GravitationalWaveTransient(Likelihood): "The waveform_generator {} is None. Setting from the " "provided interferometers.".format(attr)) elif wfg_attr != ifo_attr: - logger.warning( + logger.debug( "The waveform_generator {} is not equal to that of the " "provided interferometers. Overwriting the " "waveform_generator.".format(attr)) diff --git a/bilby/gw/waveform_generator.py b/bilby/gw/waveform_generator.py index ace0703537afbe525e9abaee9fbd3cb997b7b0ce..89432314bfddaa588329c9ebf9384b07ad5bae95 100644 --- a/bilby/gw/waveform_generator.py +++ b/bilby/gw/waveform_generator.py @@ -3,6 +3,7 @@ import numpy as np from ..core import utils from ..core.series import CoupledTimeAndFrequencySeries from .utils import PropertyAccessor +from .conversion import convert_to_lal_binary_black_hole_parameters class WaveformGenerator(object): @@ -57,7 +58,7 @@ class WaveformGenerator(object): self.time_domain_source_model = time_domain_source_model self.source_parameter_keys = self.__parameters_from_source_model() if parameter_conversion is None: - self.parameter_conversion = _default_parameter_conversion + self.parameter_conversion = convert_to_lal_binary_black_hole_parameters else: self.parameter_conversion = parameter_conversion if waveform_arguments is not None: @@ -67,6 +68,15 @@ class WaveformGenerator(object): if isinstance(parameters, dict): self.parameters = parameters self._cache = dict(parameters=None, waveform=None, model=None) + utils.logger.info( + "Waveform generator initiated with\n" + " frequency_domain_source_model: {}\n" + " frequency_domain_source_model: {}\n" + " parameter_conversion: {}" + .format(utils.get_function_path(self.frequency_domain_source_model), + utils.get_function_path(self.time_domain_source_model), + utils.get_function_path(self.parameter_conversion)) + ) def __repr__(self): if self.frequency_domain_source_model is not None: @@ -77,7 +87,7 @@ class WaveformGenerator(object): tdsm_name = self.time_domain_source_model.__name__ else: tdsm_name = None - if self.parameter_conversion.__name__ == '_default_parameter_conversion': + if self.parameter_conversion is None: param_conv_name = None else: param_conv_name = self.parameter_conversion.__name__ @@ -237,7 +247,3 @@ class WaveformGenerator(object): raise AttributeError('Either time or frequency domain source ' 'model must be provided.') return set(utils.infer_parameters_from_function(model)) - - -def _default_parameter_conversion(parmeters): - return parmeters, list() diff --git a/cli_bilby/bilby_result.py b/cli_bilby/bilby_result.py index d86a8e98dda4227c644265863bc1b80139dd37bd..6cacfb62db2cafa8ff5ed6797d690c5562875464 100644 --- a/cli_bilby/bilby_result.py +++ b/cli_bilby/bilby_result.py @@ -41,6 +41,10 @@ def setup_command_line_args(): help="Convert all results.", default=False) parser.add_argument("-m", "--merge", action='store_true', help="Merge the set of runs, output saved using the outdir and label") + parser.add_argument("-e", "--extension", type=str, choices=["json", "hdf5"], + default=True, help="Use given extension for the merged output file.") + parser.add_argument("-g", "--gzip", action="store_true", + help="Gzip the merged output results file if using JSON format.") parser.add_argument("-o", "--outdir", type=str, default=None, help="Output directory.") parser.add_argument("-l", "--label", type=str, default=None, @@ -131,4 +135,6 @@ def main(): result.label = args.label if args.outdir is not None: result.outdir = args.outdir - result.save_to_file() + + extension = args.extension + result.save_to_file(gzip=args.gzip, extension=extension) diff --git a/examples/gw_examples/injection_examples/fast_tutorial.py b/examples/gw_examples/injection_examples/fast_tutorial.py index 6bfd0a0ed8bab100ee78d0fd03476a40b83b04fb..89119eb59163292257bec63dce20e71686f4dd41 100644 --- a/examples/gw_examples/injection_examples/fast_tutorial.py +++ b/examples/gw_examples/injection_examples/fast_tutorial.py @@ -42,6 +42,7 @@ waveform_arguments = dict(waveform_approximant='IMRPhenomPv2', waveform_generator = bilby.gw.WaveformGenerator( duration=duration, sampling_frequency=sampling_frequency, frequency_domain_source_model=bilby.gw.source.lal_binary_black_hole, + parameter_conversion=bilby.gw.conversion.convert_to_lal_binary_black_hole_parameters, waveform_arguments=waveform_arguments) # Set up interferometers. In this case we'll use two interferometers diff --git a/setup.py b/setup.py index 97ee428ae0c8eaefb77d6a0a1443c49e04d66b71..d79d44655893308ce4612f2d7591bae58fd33291 100644 --- a/setup.py +++ b/setup.py @@ -39,7 +39,7 @@ def write_version_file(version): except Exception as e: print("Unable to obtain git version information, exception: {}" .format(e)) - git_status = '' + git_status = 'release' version_file = '.version' if os.path.isfile(version_file) is False: diff --git a/test/sampler_test.py b/test/sampler_test.py index 0140b4f7b8a5c5d6d736adc1350e30ae25cf8f41..d9879543cfe8ab5c1413eb85d85b6ef806621f64 100644 --- a/test/sampler_test.py +++ b/test/sampler_test.py @@ -767,7 +767,8 @@ class TestPymultinest(unittest.TestCase): n_iter_before_update=100, null_log_evidence=-1e90, max_modes=100, mode_tolerance=-1e90, seed=-1, context=0, write_output=True, log_zero=-1e100, - max_iter=0, init_MPI=False, dump_callback=None) + max_iter=0, init_MPI=False, dump_callback='dumper') + self.sampler.kwargs['dump_callback'] = 'dumper' # Check like the dynesty print_func self.assertListEqual([1, 0], self.sampler.kwargs['wrapped_params']) # Check this separately self.sampler.kwargs['wrapped_params'] = None # The dict comparison can't handle lists self.assertDictEqual(expected, self.sampler.kwargs) @@ -782,7 +783,7 @@ class TestPymultinest(unittest.TestCase): n_iter_before_update=100, null_log_evidence=-1e90, max_modes=100, mode_tolerance=-1e90, seed=-1, context=0, write_output=True, log_zero=-1e100, - max_iter=0, init_MPI=False, dump_callback=None) + max_iter=0, init_MPI=False, dump_callback='dumper') for equiv in bilby.core.sampler.base_sampler.NestedSampler.npoints_equiv_kwargs: new_kwargs = self.sampler.kwargs.copy() @@ -790,6 +791,7 @@ class TestPymultinest(unittest.TestCase): new_kwargs[ "wrapped_params" ] = None # The dict comparison can't handle lists + new_kwargs['dump_callback'] = 'dumper' # Check this like Dynesty print_func new_kwargs[equiv] = 123 self.sampler.kwargs = new_kwargs self.assertDictEqual(expected, self.sampler.kwargs) diff --git a/test/waveform_generator_test.py b/test/waveform_generator_test.py index 69e3112c07ea995492689fdca5ee40c83a64a0f1..d759c2f9367f7644cdf457b27050aba4a671747f 100644 --- a/test/waveform_generator_test.py +++ b/test/waveform_generator_test.py @@ -73,7 +73,7 @@ class TestWaveformGeneratorInstantiationWithoutOptionalParameters(unittest.TestC self.waveform_generator.start_time, self.waveform_generator.frequency_domain_source_model.__name__, self.waveform_generator.time_domain_source_model, - None, + bilby.gw.conversion.convert_to_lal_binary_black_hole_parameters.__name__, self.waveform_generator.waveform_arguments, ) ) @@ -92,7 +92,7 @@ class TestWaveformGeneratorInstantiationWithoutOptionalParameters(unittest.TestC self.waveform_generator.start_time, self.waveform_generator.frequency_domain_source_model, self.waveform_generator.time_domain_source_model.__name__, - None, + bilby.gw.conversion.convert_to_lal_binary_black_hole_parameters.__name__, self.waveform_generator.waveform_arguments, ) )