Skip to content

Add cache-aware Nested Slice within Gibbs sampler#128

Open
yallup wants to merge 2 commits into
jim-devfrom
codex/nested-swig-cache
Open

Add cache-aware Nested Slice within Gibbs sampler#128
yallup wants to merge 2 commits into
jim-devfrom
codex/nested-swig-cache

Conversation

@yallup

@yallup yallup commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add BlackJAXSwiGConfig and the blackjax-swig Nested Slice within Gibbs backend
  • validate named blocks against transforms, fixed parameters, and analytically marginalised parameters
  • cache full-frequency or heterodyned waveform polarizations across fast Gibbs blocks
  • factor luminosity distance out of the waveform cache so sampled d_L updates reuse it
  • keep s1_z and s2_z in one joint slow block and use Ripple's built-in IMRPhenomD_NRTidalv2
  • add a GW170817 example that samples d_L and marginalises phase and coalescence time over (-0.03, 0.03)

Motivation

Long BNS waveforms make repeated waveform generation the dominant cost. Nested Slice within Gibbs separates intrinsic waveform updates from detector projection and distance-amplitude updates, allowing the latter to reuse cached polarizations while retaining the existing BlackJAX nested-sampling runner and evidence calculation.

Validation

  • 208 passed across the relevant Jim, likelihood, NSS, SwiG, registry, and config unit suites
  • cached/full likelihood parity tests for full-frequency and heterodyned likelihoods
  • explicit coverage that sampled d_L reuses the cache under time marginalisation
  • Ruff and git diff --check pass

Outstanding validation

The GW170817 example has not yet been fully replicated end-to-end on a GPU. Its block layout, binning, and marginalisation settings may need further tuning before it is treated as a production analysis.

Related to #124. This PR uses the existing time-grid marginalisation but does not add Bilby-style jitter_time.

Summary by CodeRabbit

  • New Features

    • Added the BlackJAX SwiG sampler, supporting block-based nested slice sampling with waveform caching.
    • Added configuration options for sampling blocks, tuning, termination, checkpointing and resuming.
    • Added a gravitational-wave analysis example demonstrating BlackJAX SwiG.
  • Documentation

    • Updated CLI and sampler guides with SwiG configuration, behaviour, requirements and usage details.
  • Bug Fixes

    • Improved waveform-cache reuse for transient and heterodyned likelihood evaluations.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds the BlackJAX SwiG sampler with block-based cached waveform evaluation, configuration validation, Jim integration, documentation, a GW170817 example, and unit tests covering configuration, cache reuse, block validation, registry wiring, and sampler behaviour.

Changes

BlackJAX SwiG

Layer / File(s) Summary
SwiG configuration and documentation
src/jimgw/samplers/config.py, docs/guides/*.md, tests/unit/samplers/test_config.py
Adds BlackJAXSwiGConfig, registers the blackjax-swig type, validates blocks and tuning values, and documents the backend and checkpoint fields.
Transient waveform caching
src/jimgw/core/single_event/likelihood.py, src/jimgw/core/jim.py, tests/unit/core/single_event/test_likelihood.py
Adds cached waveform generation and cached likelihood evaluation for transient likelihoods, including distance restoration and cache reuse tests.
Jim SwiG wiring and block resolution
src/jimgw/core/jim.py, src/jimgw/samplers/__init__.py, tests/unit/core/single_event/test_likelihood.py
Resolves parameter blocks and cache-refresh dependencies, validates compatible likelihoods and priors, and passes cache-aware hooks into the registered backend.
Cache-aware nested sampler
src/jimgw/samplers/blackjax/swig.py, src/jimgw/samplers/blackjax/nss.py, tests/unit/samplers/blackjax/test_swig.py, tests/unit/samplers/test_registry.py
Implements block-covariance Gibbs slice updates with optional cache refreshes and generalises NSS construction and checkpoint metadata for SwiG.
GW170817 SwiG example
examples/GW170817_SwiG.py
Adds a detector-data analysis example configuring and running BlackJAX SwiG with transient waveform marginalisation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested labels: enhancement

Suggested reviewers: thomasckng

Sequence Diagram(s)

sequenceDiagram
  participant Jim
  participant BlackJAXSwiGSampler
  participant TransientLikelihoodFD
  participant NestedSampler
  Jim->>BlackJAXSwiGSampler: build sampler with blocks and cache hooks
  BlackJAXSwiGSampler->>NestedSampler: construct nested sampling algorithm
  NestedSampler->>BlackJAXSwiGSampler: execute constrained Gibbs slice step
  BlackJAXSwiGSampler->>TransientLikelihoodFD: refresh or reuse waveform cache
  TransientLikelihoodFD-->>BlackJAXSwiGSampler: return cached log-likelihood
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.95% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding a cache-aware Nested Slice within Gibbs sampler.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/nested-swig-cache

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@yallup
yallup marked this pull request as ready for review July 20, 2026 10:09

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/jimgw/samplers/blackjax/swig.py (1)

99-133: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Bind loop variables into the slice closures to avoid late-binding fragility.

proposal_generator/slice_fn close over block_array, covariance, and must_refresh from the enclosing for block, must_refresh, covariance in zip(...) loop (Ruff B023). It is correct today only because jax.lax.scan traces one_slice eagerly within the same iteration; any future change that defers execution would silently reuse the last block's values. Binding them as default arguments removes the hazard and clears the linter.

♻️ Bind loop variables explicitly
-                def one_slice(carry, key):
+                def one_slice(
+                    carry,
+                    key,
+                    block_array=block_array,
+                    covariance=covariance,
+                    must_refresh=must_refresh,
+                ):
                     current, all_accepted, expansions, shrink = carry
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/jimgw/samplers/blackjax/swig.py` around lines 99 - 133, Bind the
enclosing loop variables block_array, covariance, and must_refresh explicitly in
the proposal_generator and slice_fn closures within one_slice, using
closure/default-argument binding so each iteration retains its own values.
Preserve the existing proposal and cache-refresh behavior while eliminating late
binding and Ruff B023 warnings.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/GW170817_SwiG.py`:
- Around line 12-29: Move the jax.config.update call enabling x64 immediately
after the jax and jax.numpy imports, before all jimgw imports in the example.
Keep the existing configuration unchanged and preserve the current import
structure otherwise.

---

Nitpick comments:
In `@src/jimgw/samplers/blackjax/swig.py`:
- Around line 99-133: Bind the enclosing loop variables block_array, covariance,
and must_refresh explicitly in the proposal_generator and slice_fn closures
within one_slice, using closure/default-argument binding so each iteration
retains its own values. Preserve the existing proposal and cache-refresh
behavior while eliminating late binding and Ruff B023 warnings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d89b4ef6-b935-4d32-8557-7cd526f6c398

📥 Commits

Reviewing files that changed from the base of the PR and between acaa34b and d1b5cd9.

📒 Files selected for processing (13)
  • docs/guides/cli.md
  • docs/guides/samplers.md
  • examples/GW170817_SwiG.py
  • src/jimgw/core/jim.py
  • src/jimgw/core/single_event/likelihood.py
  • src/jimgw/samplers/__init__.py
  • src/jimgw/samplers/blackjax/nss.py
  • src/jimgw/samplers/blackjax/swig.py
  • src/jimgw/samplers/config.py
  • tests/unit/core/single_event/test_likelihood.py
  • tests/unit/samplers/blackjax/test_swig.py
  • tests/unit/samplers/test_config.py
  • tests/unit/samplers/test_registry.py

Comment thread examples/GW170817_SwiG.py
Comment on lines +12 to +29
import jax
import jax.numpy as jnp
from jimgw.core.jim import Jim
from jimgw.core.prior import (
CombinePrior,
CosinePrior,
PowerLawPrior,
SinePrior,
UniformPrior,
)
from jimgw.core.single_event.data import Data
from jimgw.core.single_event.detector import get_H1, get_L1, get_V1
from jimgw.core.single_event.likelihood import TransientLikelihoodFD
from jimgw.core.single_event.transforms import MassRatioToSymmetricMassRatioTransform
from jimgw.core.single_event.waveform import RippleIMRPhenomD_NRTidalv2
from jimgw.samplers.config import BlackJAXSwiGConfig

jax.config.update("jax_enable_x64", True)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== File: examples/GW170817_SwiG.py ==\n'
cat -n examples/GW170817_SwiG.py | sed -n '1,80p'

printf '\n== Search for jax_enable_x64 in repo ==\n'
rg -n "jax_enable_x64|enable_x64" .

printf '\n== Locate imported modules ==\n'
python3 - <<'PY'
from pathlib import Path
paths = [
    "jimgw/core/jim.py",
    "jimgw/core/prior.py",
    "jimgw/core/single_event/data.py",
    "jimgw/core/single_event/detector.py",
    "jimgw/core/single_event/likelihood.py",
    "jimgw/core/single_event/transforms.py",
    "jimgw/core/single_event/waveform.py",
    "jimgw/samplers/config.py",
]
for p in paths:
    fp = Path(p)
    print(f"\n== {p} ==")
    if fp.exists():
        print(fp.resolve())
        print("lines:", sum(1 for _ in fp.open()))
    else:
        print("MISSING")
PY

Repository: GW-JAX-Team/Jim

Length of output: 5196


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Relevant source files ==\n'
git ls-files 'src/jimgw/**/*.py' 'src/jimgw/*.py' | sed -n '1,200p'

printf '\n== Top of imported modules ==\n'
for f in \
  src/jimgw/core/jim.py \
  src/jimgw/core/prior.py \
  src/jimgw/core/single_event/data.py \
  src/jimgw/core/single_event/detector.py \
  src/jimgw/core/single_event/likelihood.py \
  src/jimgw/core/single_event/transforms.py \
  src/jimgw/core/single_event/waveform.py \
  src/jimgw/samplers/config.py
do
  if [ -f "$f" ]; then
    echo
    echo "== $f =="
    wc -l "$f"
    sed -n '1,220p' "$f" | cat -n | sed -n '1,120p'
  else
    echo
    echo "== $f =="
    echo "MISSING"
  fi
done

Repository: GW-JAX-Team/Jim

Length of output: 38525


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== time_utils top ==\n'
cat -n src/jimgw/core/single_event/time_utils.py | sed -n '1,120p'

printf '\n== detector x64 checks ==\n'
sed -n '180,220p' src/jimgw/core/single_event/detector.py | cat -n

printf '\n== example import order samples ==\n'
for f in \
  examples/GW170817_multiband.py \
  examples/GW170817_heterodyne.py \
  examples/GW150914_NS_AW.py \
  examples/injection.py \
  examples/GW150914_flowMC.py \
  examples/GW150914_SMC.py \
  examples/GW150914_NSS.py
do
  echo
  echo "== $f =="
  cat -n "$f" | sed -n '1,25p'
done

Repository: GW-JAX-Team/Jim

Length of output: 12633


Move jax_enable_x64 above the jimgw imports.

src/jimgw/core/single_event/time_utils.py requires 64-bit mode at import time, so this example can fail before it reaches line 29. Put the config update immediately after the JAX imports, before any jimgw imports.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/GW170817_SwiG.py` around lines 12 - 29, Move the jax.config.update
call enabling x64 immediately after the jax and jax.numpy imports, before all
jimgw imports in the example. Keep the existing configuration unchanged and
preserve the current import structure otherwise.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant