Skip to content

Core GNPE extensions for BNS support#354

Open
stephengreen wants to merge 1 commit into
bnsfrom
bns-gnpe-extensions
Open

Core GNPE extensions for BNS support#354
stephengreen wants to merge 1 commit into
bnsfrom
bns-gnpe-extensions

Conversation

@stephengreen

Copy link
Copy Markdown
Member

Summary

  • Add _preprocess_data() hook to GNPESampler for subclass data preprocessing (e.g., heterodyning, decimation) before the Gibbs loop
  • Add fixed_context_parameters to GNPESampler to pin proxy parameters (e.g., chirp_mass_proxy) that should not be iterated during GNPE sampling
  • Add FixedInitSampler class for providing known-ahead-of-time proxy values without a trained init network
  • Add return_rho2opt parameter to d_inner_h_complex_multi() for returning optimal SNR squared alongside complex inner products (needed for synthetic phase normalization)

These are the foundational changes needed for BNS single-step GNPE, where chirp mass heterodyning is implemented via the existing GNPE framework with num_iterations=1 and a fixed chirp mass proxy.

Test plan

  • New tests/core/test_samplers.py with 7 tests for FixedInitSampler
  • New tests/gw/test_likelihood.py with 9 tests for inner product methods and likelihood decomposition
  • All existing tests pass (27 core + 2 likelihood frequency masking)

🤖 Generated with Claude Code

Extend GNPESampler with capabilities needed for BNS single-step GNPE:
- _preprocess_data() hook for subclass data preprocessing (e.g., heterodyning)
- fixed_context_parameters to pin proxy params during GNPE (no iteration)
- FixedInitSampler for known-ahead-of-time proxy values
- return_rho2opt on d_inner_h_complex_multi for synthetic phase normalization

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@stephengreen stephengreen requested a review from max-dax February 24, 2026 18:14
@stephengreen

Copy link
Copy Markdown
Member Author

Note on _preprocess_data() design

On main, GNPESampler._run_sampler uses transform_pre in two distinct roles:

(1) One-time data prep before the Gibbs loop: self.init_sampler.transform_pre(context) -- borrows the init sampler's chain (whiten, decimate, repackage, to_torch).

(2) Per-iteration inside the Gibbs loop: self.transform_pre(x) -- the GWSamplerGNPE's own chain (proxy sampling, time shifts, context standardization).

Max's branch refactors this by pulling the one-time data prep into GWSamplerMixin.initialize_transforms_pre(), so GNPESampler owns its own transform_pre rather than borrowing the init sampler's. He renames the in-loop transforms to transform_gnpe_loop_pre/post. His motivation: for BNS with MFD and fixed chirp_mass_proxy, he needs to insert HeterodynePhase before decimation in the one-time chain, which requires mutating self.transform_pre.transforms.insert(0, ...) -- awkward to do on a borrowed object.

_preprocess_data() achieves the same goal more simply: the GW subclass overrides it to heterodyne raw context before calling super()._preprocess_data() (which delegates to init_sampler). This avoids the mixin refactor and the insert() mutation, and works because heterodyning always precedes everything in the one-time chain.

@stephengreen

Copy link
Copy Markdown
Member Author

Follow-up on the above: considered also renaming the per-iteration transforms to transform_gnpe_loop_pre/post (as Max does), but decided against it. Since _preprocess_data already disambiguates the one-time step, the dual meaning of transform_pre on main is resolved. Two concepts is simpler than three. A broader sampler class refactoring can be considered later.

stephengreen added a commit that referenced this pull request Jul 7, 2026
From PR #355: factor_fiducial_waveform and HeterodynePhase (leading-order
chirp heterodyning with an optional 1PN correction; a fixed-parameters
mode for inference-time use) and the GNPEChirp proxy transform, with
their tests -- including a slow integration test on real NRTidal
waveforms (roundtrip mismatch < 1e-14, an order of magnitude fewer zero
crossings after heterodyning).

From PR #354: d_inner_h_complex_multi gains return_rho2opt, returning
(h|h) alongside (d|h) so the full likelihood decomposition
log L = log_Zn + Re[(d|h) e^(2i phase)] - (h|h)/2 is available from a
single waveform generation (the basis for the chirp-mass sweep), with
its tests. The synthetic-phase profile path is unchanged (re-verified
bit-exact).

PR #354's sampler extensions (fixed_context_parameters,
_preprocess_data) are deliberately not ported: the composed spine
already expresses them as a DeltaFactor pin and the context's one-time
prepared_data. The remaining #355 changes (the sample-dict interface for
the dataset-compression transforms) are training-side and come later as
a package.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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