Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
BILBY_ALLOW_PARAMETERS_AS_STATE: FALSE

jobs:
unittests:
name: Unit tests - Python ${{ matrix.python-version }} (${{ matrix.os }})
Expand All @@ -36,11 +39,19 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .[test]
pip install .[test] "numpy<2.4" # numpy 2.4 causes issues with bilby
- name: Install bilby>=2.7
if: matrix.python-version != '3.9'
run: |
pip install "bilby>=2.7"
- name: Set MPL backend on Windows
if: runner.os == 'Windows'
run: |
echo "MPLBACKEND=agg" >> $env:GITHUB_ENV
- name: Test with pytest
run: |
python -m pytest
- name: Test with bilby<2.7
run: |
pip install "bilby<2.7"
python -m pytest
11 changes: 11 additions & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Contributors

This file preserves contributor attribution from bilby history, including commits from git.ligo.org that are not present in this plugin repository.

It is a legacy attribution snapshot from before the sampler was moved to this plugin repository; new plugin-era contributions are tracked in this repository's git history.


- Colm Talbot
- Gregory Ashton
- Michael J. Williams
- Moritz Huebner
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ build-backend = "setuptools.build_meta"
[project]
name = "nessai-bilby"
authors = [
{name = "Michael J. Williams", email = "michaeljw1@googlemail.com"},
{name = "Bilby Developers"},
]
maintainers = [
{name = "Colm Talbot", email = "colm.talbot@ligo.org"},
{name = "Michael J. Williams", email = "michael.williams@ligo.org"},
]
description = "Interface and plugin for using nessai in bilby"
readme = "README.md"
Expand Down
42 changes: 30 additions & 12 deletions src/nessai_bilby/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def __init__(
self.use_ratio = use_ratio
self.names = self.bilby_priors.non_fixed_keys
self._update_bounds()
self.fixed_parameters = self._fixed_parameters_dict(self.bilby_priors)

if self.use_ratio:
self.bilby_log_likelihood_fn = (
Expand All @@ -52,6 +53,14 @@ def __init__(

self.validate_bilby_likelihood()

@staticmethod
def _fixed_parameters_dict(bilby_priors: "PriorDict") -> dict:
"""Dictionary of fixed parameters."""
theta = {}
for key in bilby_priors.fixed_keys:
theta[key] = bilby_priors[key].value
return theta

def _update_bounds(self):
self.bounds = {
key: [
Expand All @@ -61,27 +70,35 @@ def _update_bounds(self):
for key in self.names
}

def _try_bilby_likelihood(self, theta):
"""Try to evaluate the bilby likelihood with the given parameters."""
try:
return self.bilby_log_likelihood_fn(theta)
except TypeError:
self.bilby_likelihood.parameters.update(theta)
return self.bilby_log_likelihood_fn()

def validate_bilby_likelihood(self) -> None:
"""Validate the bilby likelihood object"""
theta = self.bilby_priors.sample()
self.bilby_likelihood.parameters.update(theta)
self.bilby_log_likelihood_fn()
theta = self.fixed_parameters.copy()
theta_new = self.bilby_priors.sample()
theta.update(theta_new)
return self._try_bilby_likelihood(theta)

def log_likelihood(self, x):
"""Compute the log likelihood"""
theta = {n: x[n].item() for n in self.names}
self.bilby_likelihood.parameters.update(theta)
return self.bilby_log_likelihood_fn()
theta = self.fixed_parameters.copy()
for n in self.names:
theta[n] = x[n].item()
return self._try_bilby_likelihood(theta)

def log_prior(self, x):
"""Compute the log prior.

Also evaluates the likelihood constraints.
"""
theta = {n: x[n] for n in self.names}
return self.bilby_priors.ln_prob(theta, axis=0) + np.log(
self.bilby_priors.evaluate_constraints(theta)
)
return self.bilby_priors.ln_prob(theta, axis=0)

def new_point(self, N=1):
"""Draw a point from the prior"""
Expand Down Expand Up @@ -116,11 +133,12 @@ def log_likelihood(self, x):

Also evaluates the likelihood constraints.
"""
theta = {n: x[n].item() for n in self.names}
theta = self.fixed_parameters.copy()
for n in self.names:
theta[n] = x[n].item()
if not self.bilby_priors.evaluate_constraints(theta):
return -np.inf
self.bilby_likelihood.parameters.update(theta)
return self.bilby_log_likelihood_fn()
return self._try_bilby_likelihood(theta)

def log_prior(self, x):
"""Compute the log prior."""
Expand Down
27 changes: 25 additions & 2 deletions src/nessai_bilby/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,18 +214,36 @@ def run_sampler(self):
"n_pool=1, overriding n_pool to None to disable multiprocessing"
)
n_pool = None
self._npool = None

if n_pool is not None:
logger.info(
f"Using bilby pool for multiprocessing with n_pool={n_pool}"
)
self._setup_pool(model)
else:
# bilby doesn't define pool by default
self.pool = None

# Remove pool if it exists since it will have been used in `_setup_pool`
# we instead pass the pool to the FlowSampler directly.
kwargs.pop("pool", None)

# Configure the sampler
self.fs = FlowSampler(
model,
signal_handling=False, # Disable signal handling so it can be handled by bilby
importance_nested_sampler=self._importance_nested_sampler,
n_pool=n_pool,
pool=self.pool,
close_pool=False, # Don't close the pool since it is managed by bilby
**kwargs,
)
# Run the sampler
self.fs.run(**run_kwargs)

self._close_pool()

# Update the result
self.update_result()

Expand Down Expand Up @@ -306,8 +324,13 @@ def get_expected_outputs(cls, outdir=None, label=None):
filenames = []
return filenames, dirs

def _setup_pool(self):
pass
def _setup_pool(self, model):
from nessai.utils.multiprocessing import (
initialise_pool_variables,
)

initialise_pool_variables(model)
super()._setup_pool()


class ImportanceNessai(Nessai):
Expand Down
47 changes: 27 additions & 20 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,33 @@
from nessai.livepoint import reset_extra_live_points_parameters


class GaussianLikelihood(bilby.Likelihood):
def __init__(self):
"""A very simple Gaussian likelihood"""
super().__init__(parameters={"x": None, "y": None})

def log_likelihood(self):
"""Log-likelihood."""
return -0.5 * (
self.parameters["x"] ** 2.0 + self.parameters["y"] ** 2.0
) - np.log(2.0 * np.pi)


@pytest.fixture
def bilby_gaussian_likelihood_and_priors():
likelihood = GaussianLikelihood()
priors = dict(
x=bilby.core.prior.Uniform(-10, 10, "x"),
y=bilby.core.prior.Uniform(-10, 10, "y"),
)
return likelihood, priors
def model(x, m, c):
return m * x + c


def conversion_func(parameters):
# d = |m| + |c|
parameters["d"] = abs(parameters["m"]) + abs(parameters["c"])
return parameters


@pytest.fixture()
def bilby_likelihood(rng):
x = np.linspace(0, 10, 100)
injection_parameters = dict(m=0.5, c=0.2)
sigma = 1.0
y = model(x, **injection_parameters) + rng.normal(0.0, sigma, len(x))
likelihood = bilby.likelihood.GaussianLikelihood(x, y, model, sigma)
return likelihood


@pytest.fixture()
def bilby_priors():
priors = bilby.core.prior.PriorDict(conversion_function=conversion_func)
priors["m"] = bilby.core.prior.Uniform(0, 5, boundary="periodic")
priors["c"] = bilby.core.prior.Uniform(-2, 2, boundary="reflective")
priors["d"] = bilby.core.prior.Constraint(name="d", minimum=0, maximum=5)
return priors


@pytest.fixture(autouse=True)
Expand Down
Loading
Loading