DRAFT: jax ILE -- make --save-samples a fair draw, not the raw sampler cloud - #180
Draft
oshaughnessy-junior wants to merge 1 commit into
Draft
DRAFT: jax ILE -- make --save-samples a fair draw, not the raw sampler cloud#180oshaughnessy-junior wants to merge 1 commit into
oshaughnessy-junior wants to merge 1 commit into
Conversation
The JAX driver's --save-samples export writes whatever cloud the chosen
--mode produced, with no weight column -- so every consumer reads it as an
equal-weight draw from the conditional extrinsic posterior. For several
modes that is wrong, and the correcting weights were computed and then
discarded:
laplace-is (the DEFAULT mode): theta follows the adaptive Gaussian
PROPOSAL; run_laplace_is computes logw = lnL + logp - logq and drops it.
prior-mc: theta are PRIOR draws; w = L.
flowmc*: theta is sampled at inv_T = --adapt-weight-exponent, and
samplers.flowmc_sample* returns post_weight = L^(1-inv_T) as the
correction to the exact posterior. It is uniform only at the default
beta = 1; any other value silently exported a tempered (over-broad)
cloud.
Each estimator now returns its per-sample log importance weight (None when
the sampler already targets the posterior, e.g. the NUTS chain), and
write_samples fair-draws against it before writing -- multinomial resampling
with replacement against w = L p / p_s, the same convention every production
integrator uses (RIFT/integrators/mcsampler.py::integrate and the identical
block in mcsamplerGPU / mcsamplerAdaptiveVolume / mcsamplerEnsemble /
mcsamplerPortfolio). The file format is unchanged: equal-weight rows, no
weight column, same header, so downstream tooling is untouched.
Also, following ILE, the export is capped at 1.5*ESS. Without that a
low-ESS cloud gets resampled up to its original length and the file looks
like N independent draws while holding ~ESS distinct points (laplace-is on a
real BNS: ESS 97 out of 200000).
--fairdraw-extrinsic-output, --fairdraw-extrinsic-output-n-max and
--n-fairdraw-extrinsic-samples move from the accepted-but-ignored list to
real, typed options that bound the count. --adapt-weight-exponent was also
listed as ignored while the flowMC modes were in fact using it as the
tempering exponent; that misreport is fixed too.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
oshaughnessy-junior
deployed
to
private-review-dispatch-rift
August 22, 2026 01:43 — with
GitHub Actions
Active
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
DRAFT — opened for review, not for merge as-is.
What this fixes
bin/integrate_likelihood_extrinsic_jax --save-sampleswrites whatever cloud thechosen
--modeproduced, with no weight column. Every consumer of a*_samples.dattherefore reads it as an equal-weight draw from the conditionalextrinsic posterior — the contract production ILE's
--fairdraw-extrinsic-outputactually provides, by multinomial-resamplingagainst
w = L p / p_sinsideRIFT/integrators/mcsampler.py::integrate(and theidentical block in
mcsamplerGPU/mcsamplerAdaptiveVolume/mcsamplerEnsemble/mcsamplerPortfolio).For several JAX modes that contract was not met, and the correcting weights were
computed and then thrown away:
thetawas at exportlaplace-is(the default)logwcomputed inrun_laplace_is, discardedprior-mcw = Lflowmc*inv_T = --adapt-weight-exponentpost_weight = L^(1-inv_T), returned bysamplers.flowmc_sample*, discardednuts,nuts-phimarg,multistart-nutspost_weightis uniform only at the defaultbeta = 1. At any other valuethe export was a silently tempered — i.e. over-broad — cloud.
The change
Each estimator now returns its per-sample log importance weight (
Nonewhen thesampler already targets the posterior), and
write_samplesfair-draws against itbefore writing. The file format is unchanged — equal-weight rows, no weight
column, same header — so downstream tooling is untouched.
Following ILE, the export is capped at
1.5*ESS. Without that a low-ESS cloud isresampled up to its original length and the file looks like N independent draws
while holding ~ESS distinct points.
--fairdraw-extrinsic-output,--fairdraw-extrinsic-output-n-maxand--n-fairdraw-extrinsic-samplesmove from the accepted-but-ignored list toreal, typed options that bound the count.
--adapt-weight-exponentwas also beingreported as ignored while the flowMC modes were in fact using it as the
tempering exponent (
samplers.flowmc_sample*:inv_T = 1/temper = beta); thatmisreport is fixed.
VERIFIED BY RUNNING CODE
Zero-noise BNS injection (
IMRPhenomD, H1L1V1,m1=1.58 m2=1.26, lnL_max≈266),--mode flowmc-phimarg --distance-marginalization. Ground truth is anindependent defensive importance-sampling reference built on the same
captured JAX likelihood object in the same process: proposal
q = 0.30*prior + 0.70*(400-kernel mixture), exact weightsw = L p / q,multinomial fair draw. Its recovered widths are stable across a 4x proposal
bandwidth range (0.3 / 0.6 / 1.2 → ESS 2700 / 17420 / 16483), so they are not a
proposal artifact.
post_weightis exactly uniform at the defaultbeta = 1.0(min == max == 1/4800,temper = 1.0000) — so the default flowMC path was notexporting a mis-weighted cloud, and this change is a no-op there.
Confirmed end-to-end: patched and unpatched drivers at
beta=1.0producedidentical exports (4800 rows;
sd_ra0.01407,sd_dec0.04501,sd_psi0.81861,
sd_incl0.25316 in both).At
beta = 0.5the discarded weights were real, and the fix corrects a realbias (same event/seed/data, patched vs unpatched, both against the
reference):
beta=1.0beta=0.5beta=0.5i.e. tempering silently inflated the exported sky posterior by ~1.5x relative
to the correctly-drawn arm; the fair draw (ESS 1942 → 2914 rows) removes it.
laplace-is, the default mode, is unusable on this posterior regardless.200000 samples gave ESS = 97; the exported cloud is 3–60x too narrow
(
sd/ref: ra 0.18, dec 0.37, psi 0.016, incl 0.081) both before and after thefix. The fair draw cannot rescue a proposal that missed. This is why the
1.5*ESScap and the loudESS < 200warning are part of the change, and itis a separate bug worth its own issue.
Tests: 6 pass under
/cvmfs/software.igwn.org/conda/envs/igwnpytest, andall 6 were mutation-tested — each was shown to FAIL when the corresponding
behaviour is reverted:
if logw is not None ...→if False(pre-fix export):test_export_is_a_fair_draw_of_the_posteriorandtest_fairdraw_count_options_are_liveFAIL.test_uniform_weights_are_a_no_opandtest_tempered_flowmc_weights_are_not_uniformFAIL.1.5*ESScap:test_ess_clamp_prevents_manufactured_drawsFAILS.test_unreweighted_export_would_fail_the_aboveis a control scored the sameway as the headline test, so the headline test cannot pass vacuously.
INFERRED, NOT VERIFIED HERE
--mode prior-mc,flowmc(5-D),flowmc-dpsimarg,flowmc-phipsimarg,nuts,nuts-phimarg,multistart-nutsand thesmc_puffballpath under this change is inferred from reading their returnvalues. Only
flowmc-phimargandlaplace-iswere exercised end-to-end.smc_puffball_samplereturns a uniformpost_weightafter its own internalmultinomial resample, so it should be a no-op; not run.
SEPARATE PROBLEM THIS DID NOT FIX (please read)
Even at
beta = 1with uniform weights, theflowmc-phimargexport is measurablynarrower than the reference, and flow re-use across an intrinsic batch makes it
far worse. Measured against the defensive-IS reference on the two actual
production files from a 96-draw batch run as twelve 8-event chunks (flow state
threaded across events unless
--no-flow-reuse), reference ESS 22929 and 12788:EXT-0_0(slot 0, fresh flow)EXT-0_7(slot 7, flow reused 7x)The per-slot mean over the 12 intrinsic draws at each slot (the draws are
exchangeable across slots, so this is a pipeline effect, not a physics one) drifts
monotonically:
Sky (ra, dec) is essentially untouched; inclination and psi are not. That is a
flow-reuse pathology, not a weighting one, and it needs its own fix. Until then,
--no-flow-reuselooks mandatory for any run whose extrinsic samples are used.