Skip to content

FEAT: define pytrees for bilby types - #1110

Open
ColmTalbot wants to merge 14 commits into
bilby-dev:mainfrom
ColmTalbot:pytrees
Open

FEAT: define pytrees for bilby types#1110
ColmTalbot wants to merge 14 commits into
bilby-dev:mainfrom
ColmTalbot:pytrees

Conversation

@ColmTalbot

@ColmTalbot ColmTalbot commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

This PR adds JAX pytree definitions for most classes in Bilby including:

  • all (I think) priors
  • all likelihoods
  • the GW interferometer and waveform generator classes

Defining pytrees allows these classes to be used as inputs to JIT-compiled (or other transformations) functions and can dramatically reduce compilation/execution time.

As an example, I've used this in a case where I'm analyzing lots of events in series.
I can then JIT-compile an outer function that takes a likelihood as input and everything works smoothly.

The basic breakdown of pytree definitions relies on breaking the constituent parts of the class into leaves and auxillary data.
Any change in the auxillary data causes a recompilation, whereas leaves are traced through the functions allowing for them to be changed without recompilation.
As an example, the shape of an array would be auxillary data, but the values themselves a leaf/leaves.

TODO:

  • more testing, especially of likelihoods

@ColmTalbot
ColmTalbot marked this pull request as draft July 7, 2026 19:11
@ColmTalbot
ColmTalbot marked this pull request as ready for review July 9, 2026 18:12
@ColmTalbot
ColmTalbot requested a review from a team July 10, 2026 13:42
@ColmTalbot ColmTalbot added this to the 3.0.0 milestone Jul 10, 2026
@mj-will

mj-will commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

On the call, we suggested adding a test to ensure the likelihood doesn't recompile when it shouldn't.

@GregoryAshton

Copy link
Copy Markdown
Collaborator

One top-level thought. Is it possible to add a README or something to the top-level compat? Right now, the functionality of this is quite hidden, while exposing it fits into the broader theme of user-friendliness. I often poke around a project to see what it has an "compat" isn't enough for me right now to understand that.

bilby.core.prior.DeltaFunction(name="test", unit="unit", peak=1),
bilby.core.prior.Gaussian(name="test", unit="unit", mu=0, sigma=1),
bilby.core.prior.Normal(name="test", unit="unit", mu=0, sigma=1),
bilby.core.prior.PowerLaw(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The behaviour of PowerLaw is now different with alpha=-1, and when the array values are small, this could cause differences. Can we add some tests like this to test for that?

def test_powerlaw_alpha_minus_one_rescale(self):
    """Test rescale with alpha=-1 matches LogUniform behavior."""
    prior = PowerLaw(alpha=-1, minimum=1, maximum=10)
    domain = self.xp.linspace(0, 1, 50)
    rescaled = prior.rescale(domain)
    # Verify no NaN/inf in rescaled values
    self.assertTrue(self.xp.all(self.xp.isfinite(rescaled)))
    # Verify inverse CDF property
    cdf_vals = prior.cdf(rescaled)
    self.assertTrue(self.xp.allclose(domain, cdf_vals, atol=1e-9))

def test_powerlaw_alpha_minus_one_prob_and_ln_prob(self):
    """Test prob/ln_prob with alpha=-1 (critical edge case)."""
    prior = PowerLaw(alpha=-1, minimum=1, maximum=10)
    val = self.xp.array([1.0, 5.0, 10.0])
    prob = prior.prob(val)
    ln_prob = prior.ln_prob(val)
    # Verify consistency: ln_prob = log(prob)
    self.assertTrue(self.xp.allclose(ln_prob, self.xp.log(prob), atol=1e-9))
    # Verify no NaN/inf leakage
    self.assertTrue(self.xp.all(self.xp.isfinite(prob)))
    self.assertTrue(self.xp.all(self.xp.isfinite(ln_prob)))

def test_powerlaw_numeric_stability_small_values(self):
    """Test numerical stability near minimum with various alpha values."""
    for alpha in [-1, -0.5, 0, 1, 2]:
        prior = PowerLaw(alpha=alpha, minimum=1e-10, maximum=1)
        val = self.xp.array([1e-10, 1e-5, 1e-1])
        prob = prior.prob(val)
        ln_prob = prior.ln_prob(val)
        # No NaN/inf
        self.assertTrue(self.xp.all(self.xp.isfinite(prob[val >= prior.minimum])))
        self.assertTrue(self.xp.all(self.xp.isfinite(ln_prob[val >= prior.minimum])))

Comment thread test/core/prior/prior_test.py Outdated
bilby.core.prior.PowerLaw(
name="test", unit="unit", alpha=-1, minimum=0.5, maximum=1
),
# bilby.core.prior.PowerLaw(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed on the call adding these back in


cache_size = jit_fn._cache_size()
jitted = jit_fn(likelihood, parameters)
jitted = jit_fn(likelihood, parameters)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ColmTalbot gave a comment on the call as to why this is required. Could that be added as a comment as this otherwise looks like a typo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants