Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • john-veitch/bilby
  • duncanmmacleod/bilby
  • colm.talbot/bilby
  • lscsoft/bilby
  • matthew-pitkin/bilby
  • salvatore-vitale/tupak
  • charlie.hoy/bilby
  • bfarr/bilby
  • virginia.demilio/bilby
  • vivien/bilby
  • eric-howell/bilby
  • sebastian-khan/bilby
  • rhys.green/bilby
  • moritz.huebner/bilby
  • joseph.mills/bilby
  • scott.coughlin/bilby
  • matthew.carney/bilby
  • hyungwon.lee/bilby
  • monica.rizzo/bilby
  • christopher-berry/bilby
  • lindsay.demarchi/bilby
  • kaushik.rao/bilby
  • charles.kimball/bilby
  • andrew.matas/bilby
  • juan.calderonbustillo/bilby
  • patrick-meyers/bilby
  • hannah.middleton/bilby
  • eve.chase/bilby
  • grant.meadors/bilby
  • khun.phukon/bilby
  • sumeet.kulkarni/bilby
  • daniel.reardon/bilby
  • cjhaster/bilby
  • sylvia.biscoveanu/bilby
  • james-clark/bilby
  • meg.millhouse/bilby
  • joshua.willis/bilby
  • nikhil.sarin/bilby
  • paul.easter/bilby
  • youngmin/bilby
  • daniel-williams/bilby
  • shanika.galaudage/bilby
  • bruce.edelman/bilby
  • avi.vajpeyi/bilby
  • isobel.romero-shaw/bilby
  • andrew.kim/bilby
  • dominika.zieba/bilby
  • jonathan.davies/bilby
  • marc.arene/bilby
  • srishti.tiwari/bilby-tidal-heating-eccentric
  • aditya.vijaykumar/bilby
  • michael.williams/bilby
  • cecilio.garcia-quiros/bilby
  • rory-smith/bilby
  • maite.mateu-lucena/bilby
  • wushichao/bilby
  • kaylee.desoto/bilby
  • brandon.piotrzkowski/bilby
  • rossella.gamba/bilby
  • hunter.gabbard/bilby
  • deep.chatterjee/bilby
  • tathagata.ghosh/bilby
  • arunava.mukherjee/bilby
  • philip.relton/bilby
  • reed.essick/bilby
  • pawan.gupta/bilby
  • francisco.hernandez/bilby
  • rhiannon.udall/bilby
  • leo.tsukada/bilby
  • will-farr/bilby
  • vijay.varma/bilby
  • jeremy.baier/bilby
  • joshua.brandt/bilby
  • ethan.payne/bilby
  • ka-lok.lo/bilby
  • antoni.ramos-buades/bilby
  • oliviastephany.wilk/bilby
  • jack.heinzel/bilby
  • samson.leong/bilby-psi4
  • viviana.caceres/bilby
  • nadia.qutob/bilby
  • michael-coughlin/bilby
  • hemantakumar.phurailatpam/bilby
  • boris.goncharov/bilby
  • sama.al-shammari/bilby
  • siqi.zhong/bilby
  • jocelyn-read/bilby
  • marc.penuliar/bilby
  • stephanie.letourneau/bilby
  • alexandresebastien.goettel/bilby
  • alec.gunny/bilby
  • serguei.ossokine/bilby
  • pratyusava.baral/bilby
  • sophie.hourihane/bilby
  • eunsub/bilby
  • james.hart/bilby
  • pratyusava.baral/bilby-tg
  • zhaozc/bilby
  • pratyusava.baral/bilby_SoG
  • tomasz.baka/bilby
  • nicogerardo.bers/bilby
  • soumen.roy/bilby
  • isaac.mcmahon/healpix-redundancy
  • asamakai.baker/bilby-frequency-dependent-antenna-pattern-functions
  • anna.puecher/bilby
  • pratyusava.baral/bilby-x-g
  • thibeau.wouters/bilby
  • christian.adamcewicz/bilby
  • raffi.enficiaud/bilby
109 results
Show changes
Commits on Source (59)
Showing
with 378 additions and 121 deletions
# All notable changes will be documented in this file
## [0.6.8] 2020-05-21
## [1.0.0] 2020-07-06
Version 1.0 release of bilby
### Changes
- Minor bug fixes and typo changes only from 0.6.9, see
git.ligo.org/lscsoft/bilby/-/merge_requests?scope=all&utf8=%E2%9C%93&state=merged&milestone_title=1.0.0
for details
## [0.6.9] 2020-05-21
### Changes
- Improvement to the proposal step in dynesty (!774)
- Fix a bug in checking and making directories (!792)
......
......@@ -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
......@@ -421,7 +424,7 @@ class Prior(object):
val = other_cls.from_repr(vals)
else:
try:
val = eval(val, dict(), dict(np=np))
val = eval(val, dict(), dict(np=np, inf=np.inf, pi=np.pi))
except NameError:
raise TypeError(
"Cannot evaluate prior, "
......
......@@ -659,7 +659,7 @@ class JointPrior(Prior):
raise TypeError("Must supply a JointPriorDist object instance to be shared by all joint params")
if name not in dist.names:
raise ValueError("'{}' is not a parameter in the JointPriorDist")
raise ValueError("'{}' is not a parameter in the JointPriorDist".format(name))
self.dist = dist
super(JointPrior, self).__init__(name=name, latex_label=latex_label, unit=unit, minimum=dist.bounds[name][0],
......
......@@ -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
......@@ -1074,7 +1093,15 @@ class Result(object):
# Create the data array to plot and pass everything to corner
xs = self.posterior[plot_parameter_keys].values
fig = corner.corner(xs, **kwargs)
if len(plot_parameter_keys) > 1:
fig = corner.corner(xs, **kwargs)
else:
ax = kwargs.get("ax", plt.subplot())
ax.hist(xs, bins=kwargs["bins"], color=kwargs["color"],
histtype="step", **kwargs["hist_kwargs"])
ax.set_xlabel(kwargs["labels"][0])
fig = plt.gcf()
axes = fig.get_axes()
# Add the titles
......@@ -1123,15 +1150,16 @@ class Result(object):
idxs = np.arange(nsteps)
fig, axes = plt.subplots(nrows=ndim, figsize=(6, 3 * ndim))
walkers = self.walkers[:, :, :]
parameter_labels = sanity_check_labels(self.parameter_labels)
for i, ax in enumerate(axes):
ax.plot(idxs[:self.nburn + 1], walkers[:, :self.nburn + 1, i].T,
lw=0.1, color='r')
ax.set_ylabel(self.parameter_labels[i])
ax.set_ylabel(parameter_labels[i])
for i, ax in enumerate(axes):
ax.plot(idxs[self.nburn:], walkers[:, self.nburn:, i].T, lw=0.1,
color='k')
ax.set_ylabel(self.parameter_labels[i])
ax.set_ylabel(parameter_labels[i])
fig.tight_layout()
outdir = self._safe_outdir_creation(kwargs.get('outdir'), self.plot_walkers)
......@@ -1622,7 +1650,7 @@ class ResultList(list):
result_weights = np.exp(log_evidences - np.max(log_evidences))
log_errs = [res.log_evidence_err for res in self if np.isfinite(res.log_evidence_err)]
if len(log_errs) > 0:
result.log_evidence_err = logsumexp(2 * np.array(log_errs), b=1. / len(self))
result.log_evidence_err = 0.5 * logsumexp(2 * np.array(log_errs), b=1. / len(self))
else:
result.log_evidence_err = np.nan
posteriors = list()
......@@ -1856,7 +1884,7 @@ def make_pp_plot(results, filename=None, save=True, confidence_interval=[0.68, 0
len(results), pvals.combined_pvalue))
ax.set_xlabel("C.I.")
ax.set_ylabel("Fraction of events in C.I.")
ax.legend(linewidth=1, handlelength=2, labelspacing=0.25, fontsize=legend_fontsize)
ax.legend(handlelength=2, labelspacing=0.25, fontsize=legend_fontsize)
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
fig.tight_layout()
......
......@@ -430,6 +430,7 @@ class Sampler(object):
likelihood evaluations.
"""
logger.info("Generating initial points from the prior")
unit_cube = []
parameters = []
likelihood = []
......
......@@ -114,7 +114,7 @@ class Cpnest(NestedSampler):
out.run()
except SystemExit as e:
import sys
logger.info(f"Caught exit code {e.args[0]}, exiting with signal {self.exit_code}")
logger.info("Caught exit code {}, exiting with signal {}".format(e.args[0], self.exit_code))
sys.exit(self.exit_code)
if self.plot:
......
......@@ -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)
......@@ -631,7 +631,7 @@ class Dynesty(NestedSampler):
filename = "{}/{}_checkpoint_stats.png".format(self.outdir, self.label)
fig, axs = plt.subplots(nrows=3, sharex=True)
for ax, name in zip(axs, ["boundidx", "nc", "scale"]):
ax.plot(getattr(self.sampler, f"saved_{name}"), color="C0")
ax.plot(getattr(self.sampler, "saved_{}".format(name)), color="C0")
ax.set_ylabel(name)
axs[-1].set_xlabel("iteration")
fig.tight_layout()
......@@ -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)
......
......@@ -3,6 +3,7 @@ from __future__ import absolute_import, print_function
from collections import namedtuple
import os
import signal
import shutil
from shutil import copyfile
import sys
......@@ -308,7 +309,7 @@ class Emcee(MCMCSampler):
with open(temp_chain_file, "a") as ff:
for ii, point in enumerate(points):
ff.write(self.checkpoint_info.chain_template.format(ii, *point))
os.rename(temp_chain_file, chain_file)
shutil.move(temp_chain_file, chain_file)
@property
def _previous_iterations(self):
......
......@@ -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
......
from __future__ import absolute_import, print_function
from collections import OrderedDict
from distutils.version import StrictVersion
import numpy as np
......@@ -45,8 +46,6 @@ class Pymc3(MCMCSampler):
'CategoricalGibbsMetropolis'. Note: you cannot provide a PyMC3 step
method function itself here as it is outside of the model context
manager.
nuts_kwargs: dict
Keyword arguments for the NUTS sampler.
step_kwargs: dict
Options for steps methods other than NUTS. The dictionary is keyed on
lowercase step method names with values being dictionaries of keywords
......@@ -56,13 +55,27 @@ class Pymc3(MCMCSampler):
default_kwargs = dict(
draws=500, step=None, init='auto', n_init=200000, start=None, trace=None, chain_idx=0,
chains=2, cores=1, tune=500, nuts_kwargs=None, step_kwargs=None, progressbar=True,
model=None, random_seed=None, discard_tuned_samples=True,
compute_convergence_checks=True)
chains=2, cores=1, tune=500, progressbar=True, model=None, random_seed=None,
discard_tuned_samples=True, compute_convergence_checks=True, nuts_kwargs=None,
step_kwargs=None,
)
default_nuts_kwargs = dict(
target_accept=None, max_treedepth=None, step_scale=None, Emax=None,
gamma=None, k=None, t0=None, adapt_step_size=None, early_max_treedepth=None,
scaling=None, is_cov=None, potential=None,
)
default_kwargs.update(default_nuts_kwargs)
def __init__(self, likelihood, priors, outdir='outdir', label='label',
use_ratio=False, plot=False,
skip_import_verification=False, **kwargs):
# add default step kwargs
_, STEP_METHODS, _ = self._import_external_sampler()
self.default_step_kwargs = {m.__name__.lower(): None for m in STEP_METHODS}
self.default_kwargs.update(self.default_step_kwargs)
super(Pymc3, self).__init__(likelihood=likelihood, priors=priors, outdir=outdir, label=label,
use_ratio=use_ratio, plot=plot,
skip_import_verification=skip_import_verification, **kwargs)
......@@ -454,8 +467,35 @@ class Pymc3(MCMCSampler):
self.set_likelihood()
# get the step method keyword arguments
step_kwargs = self.kwargs.pop('step_kwargs')
nuts_kwargs = self.kwargs.pop('nuts_kwargs')
step_kwargs = self.kwargs.pop("step_kwargs")
if step_kwargs is not None:
# remove all individual default step kwargs if passed together using
# step_kwargs keywords
for key in self.default_step_kwargs:
self.kwargs.pop(key)
else:
# remove any None default step keywords and place others in step_kwargs
step_kwargs = {}
for key in self.default_step_kwargs:
if self.kwargs[key] is None:
self.kwargs.pop(key)
else:
step_kwargs[key] = self.kwargs.pop(key)
nuts_kwargs = self.kwargs.pop("nuts_kwargs")
if nuts_kwargs is not None:
# remove all individual default nuts kwargs if passed together using
# nuts_kwargs keywords
for key in self.default_nuts_kwargs:
self.kwargs.pop(key)
else:
# remove any None default nuts keywords and place others in nut_kwargs
nuts_kwargs = {}
for key in self.default_nuts_kwargs:
if self.kwargs[key] is None:
self.kwargs.pop(key)
else:
nuts_kwargs[key] = self.kwargs.pop(key)
methodslist = []
# set the step method
......@@ -496,13 +536,19 @@ class Pymc3(MCMCSampler):
self.kwargs['step'] = pymc3.__dict__[step_methods[curmethod]](**args)
else:
# re-add step_kwargs if no step methods are set
self.kwargs['step_kwargs'] = step_kwargs
if len(step_kwargs) > 0 and StrictVersion(pymc3.__version__) < StrictVersion("3.7"):
self.kwargs['step_kwargs'] = step_kwargs
# check whether only NUTS step method has been assigned
if np.all([sm.lower() == 'nuts' for sm in methodslist]):
# in this case we can let PyMC3 autoinitialise NUTS, so remove the step methods and re-add nuts_kwargs
self.kwargs['step'] = None
self.kwargs['nuts_kwargs'] = nuts_kwargs
if len(nuts_kwargs) > 0 and StrictVersion(pymc3.__version__) < StrictVersion("3.7"):
self.kwargs['nuts_kwargs'] = nuts_kwargs
elif len(nuts_kwargs) > 0:
# add NUTS kwargs to standard kwargs
self.kwargs.update(nuts_kwargs)
with self.pymc3_model:
# perform the sampling
......@@ -561,6 +607,10 @@ class Pymc3(MCMCSampler):
args = {}
return args, nuts_kwargs
def _pymc3_version(self):
pymc3, _, _ = self._import_external_sampler()
return pymc3.__version__
def set_prior(self):
"""
Set the PyMC3 prior distributions.
......
......@@ -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()
......
from __future__ import absolute_import
import datetime
import distutils.dir_util
import inspect
import os
import shutil
import signal
import tempfile
import time
import numpy as np
from pandas import DataFrame
......@@ -59,7 +63,7 @@ class Ultranest(NestedSampler):
dlogz=None,
max_iters=None,
update_interval_iter_fraction=0.2,
viz_callback="auto",
viz_callback=None,
dKL=0.5,
frac_remain=0.01,
Lepsilon=0.001,
......@@ -81,6 +85,8 @@ class Ultranest(NestedSampler):
plot=False,
exit_code=77,
skip_import_verification=False,
temporary_directory=True,
callback_interval=10,
**kwargs,
):
super(Ultranest, self).__init__(
......@@ -95,6 +101,12 @@ class Ultranest(NestedSampler):
**kwargs,
)
self._apply_ultranest_boundaries()
self.use_temporary_directory = temporary_directory
if self.use_temporary_directory:
# set callback interval, so copying of results does not thrash the
# disk (ultranest will call viz_callback quite a lot)
self.callback_interval = callback_interval
signal.signal(signal.SIGTERM, self.write_current_state_and_exit)
signal.signal(signal.SIGINT, self.write_current_state_and_exit)
......@@ -113,9 +125,18 @@ class Ultranest(NestedSampler):
""" Check the kwargs """
self.outputfiles_basename = self.kwargs.pop("log_dir", None)
if self.kwargs["viz_callback"] is None:
self.kwargs["viz_callback"] = self._viz_callback
NestedSampler._verify_kwargs_against_default_kwargs(self)
def _viz_callback(self, *args, **kwargs):
if self.use_temporary_directory:
if not (self._viz_callback_counter % self.callback_interval):
self._copy_temporary_directory_contents_to_proper_path()
self._calculate_and_save_sampling_time()
self._viz_callback_counter += 1
def _apply_ultranest_boundaries(self):
if (
self.kwargs["wrapped_params"] is None
......@@ -123,10 +144,11 @@ class Ultranest(NestedSampler):
):
self.kwargs["wrapped_params"] = []
for param, value in self.priors.items():
if value.boundary == "periodic":
self.kwargs["wrapped_params"].append(1)
else:
self.kwargs["wrapped_params"].append(0)
if param in self.search_parameter_keys:
if value.boundary == "periodic":
self.kwargs["wrapped_params"].append(1)
else:
self.kwargs["wrapped_params"].append(0)
@property
def outputfiles_basename(self):
......@@ -135,9 +157,11 @@ class Ultranest(NestedSampler):
@outputfiles_basename.setter
def outputfiles_basename(self, outputfiles_basename):
if outputfiles_basename is None:
outputfiles_basename = "{}/ultra_{}".format(self.outdir, self.label)
if outputfiles_basename.endswith("/") is True:
outputfiles_basename = outputfiles_basename.rstrip("/")
outputfiles_basename = os.path.join(
self.outdir, "ultra_{}/".format(self.label)
)
if not outputfiles_basename.endswith("/"):
outputfiles_basename += "/"
check_directory_exists_and_if_not_mkdir(self.outdir)
self._outputfiles_basename = outputfiles_basename
......@@ -147,7 +171,7 @@ class Ultranest(NestedSampler):
@temporary_outputfiles_basename.setter
def temporary_outputfiles_basename(self, temporary_outputfiles_basename):
if temporary_outputfiles_basename.endswith("/") is False:
if not temporary_outputfiles_basename.endswith("/"):
temporary_outputfiles_basename = "{}/".format(
temporary_outputfiles_basename
)
......@@ -156,10 +180,6 @@ class Ultranest(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 """
......@@ -168,24 +188,38 @@ class Ultranest(NestedSampler):
signum, self.exit_code
)
)
# self.copy_temporary_directory_to_proper_path()
self._calculate_and_save_sampling_time()
if self.use_temporary_directory:
self._move_temporary_directory_to_proper_path()
os._exit(self.exit_code)
def copy_temporary_directory_to_proper_path(self):
logger.info(
"Overwriting {} with {}".format(
self.outputfiles_basename, self.temporary_outputfiles_basename
def _copy_temporary_directory_contents_to_proper_path(self):
"""
Copy the temporary back to the proper path.
Do not delete the temporary directory.
"""
if inspect.stack()[1].function != "_viz_callback":
logger.info(
"Overwriting {} with {}".format(
self.outputfiles_basename, self.temporary_outputfiles_basename
)
)
if self.outputfiles_basename.endswith("/"):
outputfiles_basename_stripped = self.outputfiles_basename[:-1]
else:
outputfiles_basename_stripped = self.outputfiles_basename
distutils.dir_util.copy_tree(
self.temporary_outputfiles_basename, outputfiles_basename_stripped
)
# First remove anything in the outputfiles_basename for overwriting
if os.path.exists(self.outputfiles_basename):
if os.path.islink(self.outputfiles_basename):
os.unlink(self.outputfiles_basename)
else:
shutil.rmtree(self.outputfiles_basename, ignore_errors=True)
def _move_temporary_directory_to_proper_path(self):
"""
Move the temporary back to the proper path
shutil.copytree(self.temporary_outputfiles_basename, self.outputfiles_basename)
Anything in the proper path at this point is removed including links
"""
self._copy_temporary_directory_contents_to_proper_path()
shutil.rmtree(self.temporary_outputfiles_basename)
@property
def sampler_function_kwargs(self):
......@@ -252,19 +286,8 @@ class Ultranest(NestedSampler):
stepsampler = self.kwargs.pop("step_sampler", None)
temporary_outputfiles_basename = tempfile.TemporaryDirectory().name
self.temporary_outputfiles_basename = temporary_outputfiles_basename
logger.info("Using temporary file {}".format(temporary_outputfiles_basename))
check_directory_exists_and_if_not_mkdir(temporary_outputfiles_basename)
self.kwargs["log_dir"] = self.temporary_outputfiles_basename
# Symlink the temporary directory with the target directory: ensures data is stored on exit
os.symlink(
os.path.abspath(self.temporary_outputfiles_basename),
os.path.abspath(self.outputfiles_basename),
target_is_directory=True,
)
self._setup_run_directory()
self._check_and_load_sampling_time_file()
# use reactive nested sampler when no live points are given
if self.kwargs.get("num_live_points", None) is not None:
......@@ -288,33 +311,78 @@ class Ultranest(NestedSampler):
"The default step sampling will be used instead."
)
results = sampler.run(**self.sampler_function_kwargs)
if self.use_temporary_directory:
self._viz_callback_counter = 1
self.copy_temporary_directory_to_proper_path()
self.start_time = time.time()
results = sampler.run(**self.sampler_function_kwargs)
self._calculate_and_save_sampling_time()
# Clean up
shutil.rmtree(temporary_outputfiles_basename)
self._clean_up_run_directory()
self._generate_result(results)
self.calc_likelihood_count()
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.
"""
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):
distutils.dir_util.copy_tree(
self.outputfiles_basename, self.temporary_outputfiles_basename
)
check_directory_exists_and_if_not_mkdir(temporary_outputfiles_basename)
self.kwargs["log_dir"] = self.temporary_outputfiles_basename
logger.info(
"Using temporary file {}".format(temporary_outputfiles_basename)
)
else:
check_directory_exists_and_if_not_mkdir(self.outputfiles_basename)
self.kwargs["log_dir"] = self.outputfiles_basename
logger.info("Using output file {}".format(self.outputfiles_basename))
def _clean_up_run_directory(self):
if self.use_temporary_directory:
self._move_temporary_directory_to_proper_path()
self.kwargs["log_dir"] = self.outputfiles_basename
def _check_and_load_sampling_time_file(self):
self.time_file_path = os.path.join(self.kwargs["log_dir"], "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))
self.start_time = current_time
def _generate_result(self, out):
# extract results (samples stored in "v" will change to "points",
# weights stored in "w" will change to "weights")
datakey = "v" if "v" in out["weighted_samples"] else "points"
weightskey = "w" if "w" in out["weighted_samples"] else "weights"
data = np.array(out["weighted_samples"][datakey])
weights = np.array(out["weighted_samples"][weightskey])
# extract results
data = np.array(out["weighted_samples"]["points"])
weights = np.array(out["weighted_samples"]["weights"])
scaledweights = weights / weights.max()
mask = np.random.rand(len(scaledweights)) < scaledweights
nested_samples = DataFrame(data, columns=self.search_parameter_keys)
nested_samples["weights"] = weights
nested_samples["log_likelihood"] = out["weighted_samples"]["L"]
self.result.log_likelihood_evaluations = np.array(out["weighted_samples"]["L"])[
nested_samples["log_likelihood"] = out["weighted_samples"]["logl"]
self.result.log_likelihood_evaluations = np.array(out["weighted_samples"]["logl"])[
mask
]
self.result.sampler_output = out
......@@ -324,3 +392,4 @@ class Ultranest(NestedSampler):
self.result.log_evidence_err = out["logzerr"]
self.result.outputfiles_basename = self.outputfiles_basename
self.result.sampling_time = datetime.timedelta(seconds=self.total_sampling_time)
......@@ -3,6 +3,7 @@ from __future__ import division
from distutils.spawn import find_executable
import logging
import os
import shutil
from math import fmod
import argparse
import traceback
......@@ -1021,6 +1022,8 @@ class BilbyJsonEncoder(json.JSONEncoder):
return {'__complex__': True, 'real': obj.real, 'imag': obj.imag}
if isinstance(obj, pd.DataFrame):
return {'__dataframe__': True, 'content': obj.to_dict(orient='list')}
if isinstance(obj, pd.Series):
return {'__series__': True, 'content': obj.to_dict()}
if inspect.isfunction(obj):
return {"__function__": True, "__module__": obj.__module__, "__name__": obj.__name__}
if inspect.isclass(obj):
......@@ -1063,7 +1066,7 @@ def move_old_file(filename, overwrite=False):
logger.debug(
'Renaming existing file {} to {}.old'.format(filename,
filename))
os.rename(filename, filename + '.old')
shutil.move(filename, filename + '.old')
logger.debug("Saving result to {}".format(filename))
......@@ -1098,6 +1101,8 @@ def decode_bilby_json(dct):
return complex(dct["real"], dct["imag"])
if dct.get("__dataframe__", False):
return pd.DataFrame(dct['content'])
if dct.get("__series__", False):
return pd.Series(dct['content'])
if dct.get("__function__", False) or dct.get("__class__", False):
default = ".".join([dct["__module__"], dct["__name__"]])
return getattr(import_module(dct["__module__"]), dct["__name__"], default)
......@@ -1174,7 +1179,7 @@ def safe_file_dump(data, filename, module):
temp_filename = filename + ".temp"
with open(temp_filename, "wb") as file:
module.dump(data, file)
os.rename(temp_filename, filename)
shutil.move(temp_filename, filename)
def latex_plot_format(func):
......@@ -1236,6 +1241,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
......
......@@ -310,7 +310,7 @@ class Interferometer(object):
time_shift = self.time_delay_from_geocenter(
parameters['ra'], parameters['dec'], parameters['geocent_time'])
# Be careful to first substract the two GPS times which are ~1e9 sec.
# Be careful to first subtract the two GPS times which are ~1e9 sec.
# And then add the time_shift which varies at ~1e-5 sec
dt_geocent = parameters['geocent_time'] - self.strain_data.start_time
dt = dt_geocent + time_shift
......
......@@ -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))
......@@ -399,20 +399,32 @@ class GravitationalWaveTransient(Likelihood):
signal_polarizations = \
self.waveform_generator.frequency_domain_strain(self.parameters)
times = create_time_series(
sampling_frequency=16384,
starting_time=self.parameters['geocent_time'] - self.waveform_generator.start_time,
duration=self.waveform_generator.duration)
times = times % self.waveform_generator.duration
times += self.waveform_generator.start_time
prior = self.priors["geocent_time"]
in_prior = (times >= prior.minimum) & (times < prior.maximum)
times = times[in_prior]
n_time_steps = int(self.waveform_generator.duration * 16384)
d_inner_h = np.zeros(n_time_steps, dtype=np.complex)
d_inner_h = np.zeros(len(times), dtype=np.complex)
psd = np.ones(n_time_steps)
signal_long = np.zeros(n_time_steps, dtype=np.complex)
data = np.zeros(n_time_steps, dtype=np.complex)
h_inner_h = np.zeros(1)
for ifo in self.interferometers:
ifo_length = len(ifo.frequency_domain_strain)
mask = ifo.frequency_mask
signal = ifo.get_detector_response(
signal_polarizations, self.parameters)
signal_long[:ifo_length] = signal
data[:ifo_length] = np.conj(ifo.frequency_domain_strain)
psd[:ifo_length] = ifo.power_spectral_density_array
d_inner_h += np.fft.fft(signal_long * data / psd)
psd[:ifo_length][mask] = ifo.power_spectral_density_array[mask]
d_inner_h += np.fft.fft(signal_long * data / psd)[in_prior]
h_inner_h += ifo.optimal_snr_squared(signal=signal).real
if self.distance_marginalization:
......@@ -424,13 +436,6 @@ class GravitationalWaveTransient(Likelihood):
else:
time_log_like = (d_inner_h.real - h_inner_h.real / 2)
times = create_time_series(
sampling_frequency=16384,
starting_time=self.parameters['geocent_time'] - self.waveform_generator.start_time,
duration=self.waveform_generator.duration)
times = times % self.waveform_generator.duration
times += self.waveform_generator.start_time
time_prior_array = self.priors['geocent_time'].prob(times)
time_post = (
np.exp(time_log_like - max(time_log_like)) * time_prior_array)
......
......@@ -411,10 +411,10 @@ class BBHPriorDict(CBCPriorDict):
"""
if dictionary is None and filename is None:
if aligned_spin:
fname = 'aligned_spins_binary_black_holes.prior'
fname = 'aligned_spins_bbh.prior'
logger.info('Using aligned spin prior')
else:
fname = 'precessing_spins_binary_black_holes.prior'
fname = 'precessing_spins_bbh.prior'
filename = os.path.join(DEFAULT_PRIOR_DIR, fname)
logger.info('No prior given, using default BBH priors in {}.'.format(filename))
elif filename is not None:
......@@ -517,9 +517,9 @@ class BNSPriorDict(CBCPriorDict):
BNSPriorDict.default_conversion_function
"""
if aligned_spin:
default_file = 'aligned_spins_waveform_tides_on.prior'
default_file = 'aligned_spins_bns_tides_on.prior'
else:
default_file = 'precessing_spins_waveform_tides_on.prior'
default_file = 'precessing_spins_bns_tides_on.prior'
if dictionary is None and filename is None:
filename = os.path.join(DEFAULT_PRIOR_DIR, default_file)
logger.info('No prior given, using default BNS priors in {}.'.format(filename))
......
......@@ -6,11 +6,11 @@ mass_1 = Constraint(name='mass_1', minimum=5, maximum=100)
mass_2 = Constraint(name='mass_2', minimum=5, maximum=100)
mass_ratio = Uniform(name='mass_ratio', minimum=0.125, maximum=1)
chirp_mass = Uniform(name='chirp_mass', minimum=25, maximum=100)
chi_1 = bilby.gw.prior.AlignedSpin(name='chi_1', a_prior=Uniform(minimum=0, maximum=0.99))
chi_2 = bilby.gw.prior.AlignedSpin(name='chi_2', a_prior=Uniform(minimum=0, maximum=0.99))
luminosity_distance = bilby.gw.prior.UniformSourceFrame(name='luminosity_distance', minimum=1e2, maximum=5e3)
dec = Cosine(name='dec')
ra = Uniform(name='ra', minimum=0, maximum=2 * np.pi, boundary='periodic')
theta_jn = Sine(name='theta_jn')
psi = Uniform(name='psi', minimum=0, maximum=np.pi, boundary='periodic')
phase = Uniform(name='phase', minimum=0, maximum=2 * np.pi, boundary='periodic')
chi_1 = bilby.gw.prior.AlignedSpin(name='chi_1', a_prior=Uniform(minimum=0, maximum=0.99))
chi_2 = bilby.gw.prior.AlignedSpin(name='chi_2', a_prior=Uniform(minimum=0, maximum=0.99))
chirp_mass = Uniform(name='chirp_mass', minimum=3.56, maximum=3.68)
mass_ratio = Uniform(name='mass_ratio', minimum=0.05, maximum=1)
mass_1 = Constraint(name='mass_1', minimum=1.00, maximum=22.0)
mass_2 = Constraint(name='mass_2', minimum=1.00, maximum=2.95)
chi_1 = bilby.gw.prior.AlignedSpin(name='chi_1', a_prior=Uniform(minimum=0, maximum=0.5))
chi_2 = bilby.gw.prior.AlignedSpin(name='chi_2', a_prior=Uniform(minimum=0, maximum=0.05))
luminosity_distance = bilby.core.prior.PowerLaw(alpha=2, name='luminosity_distance', minimum=1, maximum=750, unit='Mpc')
ra = Uniform(minimum=0, maximum=2 * np.pi, name='ra', boundary='periodic')
# These are the default priors we use for BBH systems.
# Note that you may wish to use more specific mass and distance parameters.
# These commands are all known to bilby.gw.prior.
# Lines beginning "#" are ignored.
mass_1 = Constraint(name='mass_1', minimum=5, maximum=100)
mass_2 = Constraint(name='mass_2', minimum=5, maximum=100)
mass_ratio = Uniform(name='mass_ratio', minimum=0.125, maximum=1)
chirp_mass = Uniform(name='chirp_mass', minimum=25, maximum=100)
luminosity_distance = bilby.gw.prior.UniformSourceFrame(name='luminosity_distance', minimum=1e2, maximum=5e3)
dec = Cosine(name='dec')
ra = Uniform(name='ra', minimum=0, maximum=2 * np.pi, boundary='periodic')
theta_jn = Sine(name='theta_jn')
psi = Uniform(name='psi', minimum=0, maximum=np.pi, boundary='periodic')
phase = Uniform(name='phase', minimum=0, maximum=2 * np.pi, boundary='periodic')
lambda_1 = Uniform(name='lambda_2', minimum=0, maximum=5000)
chi_1 = bilby.gw.prior.AlignedSpin(name='chi_1', a_prior=Uniform(minimum=0, maximum=0.99))
chi_2 = bilby.gw.prior.AlignedSpin(name='chi_2', a_prior=Uniform(minimum=0, maximum=0.99))
lambda_1 = Uniform(name='lambda_1', minimum=0, maximum=5000)
lambda_2 = Uniform(name='lambda_2', minimum=0, maximum=5000)
# These are the default priors we use for BNS systems.
# Note that you may wish to use more specific mass and distance parameters.
# These commands are all known to bilby.gw.prior.
# Lines beginning "#" are ignored.
mass_1 = Constraint(name='mass_1', minimum=0.5, maximum=5)
mass_2 = Constraint(name='mass_2', minimum=0.5, maximum=5)
mass_ratio = Uniform(name='mass_ratio', minimum=0.125, maximum=1)
chirp_mass = Uniform(name='chirp_mass', minimum=0.4, maximum=4.4)
luminosity_distance = bilby.gw.prior.UniformSourceFrame(name='luminosity_distance', minimum=1e2, maximum=5e3)
dec = Cosine(name='dec')
ra = Uniform(name='ra', minimum=0, maximum=2 * np.pi, boundary='periodic')
theta_jn = Sine(name='theta_jn')
psi = Uniform(name='psi', minimum=0, maximum=np.pi, boundary='periodic')
phase = Uniform(name='phase', minimum=0, maximum=2 * np.pi, boundary='periodic')
chi_1 = bilby.gw.prior.AlignedSpin(name='chi_1', a_prior=Uniform(minimum=0, maximum=0.99))
chi_2 = bilby.gw.prior.AlignedSpin(name='chi_2', a_prior=Uniform(minimum=0, maximum=0.99))
# These are the default priors we use for BNS systems.
# Note that you may wish to use more specific mass and distance parameters.
# These commands are all known to bilby.gw.prior.
# Lines beginning "#" are ignored.
mass_1 = Constraint(name='mass_1', minimum=0.5, maximum=5)
mass_2 = Constraint(name='mass_2', minimum=0.5, maximum=5)
mass_ratio = Uniform(name='mass_ratio', minimum=0.125, maximum=1)
chirp_mass = Uniform(name='chirp_mass', minimum=0.4, maximum=4.4)
luminosity_distance = bilby.gw.prior.UniformSourceFrame(name='luminosity_distance', minimum=1e2, maximum=5e3)
dec = Cosine(name='dec')
ra = Uniform(name='ra', minimum=0, maximum=2 * np.pi, boundary='periodic')
theta_jn = Sine(name='theta_jn')
psi = Uniform(name='psi', minimum=0, maximum=np.pi, boundary='periodic')
phase = Uniform(name='phase', minimum=0, maximum=2 * np.pi, boundary='periodic')
chi_1 = bilby.gw.prior.AlignedSpin(name='chi_1', a_prior=Uniform(minimum=0, maximum=0.99))
chi_2 = bilby.gw.prior.AlignedSpin(name='chi_2', a_prior=Uniform(minimum=0, maximum=0.99))
lambda_1 = Uniform(name='lambda_1', minimum=0, maximum=5000)
lambda_2 = Uniform(name='lambda_2', minimum=0, maximum=5000)