ILE: label the exported time grid with the spacing the likelihood steps by (minimal, export path only) - #181
Open
oshaughnessy-junior wants to merge 1 commit into
Conversation
…ps by
resample_samples() built the time-marginalization grid as
tvals = linspace(-t_ref_wind, t_ref_wind, int(t_ref_wind*2/P.deltaT))
whose spacing is 2W/(N-1). DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop
does not step by that: it uses only tvals[0] (tfirst = t_det + tvals[0]) and
reads lnLt[k] from the precomputed Q_lm buffer at index ifirst + k, i.e. at
detector time tfirst + k*deltaT. Labelling those values with a closed-interval
linspace stretches the exported time about the start of the window,
t_reported = t_true + (t_true - (event_time - W))*delta,
delta = (1 + f)/(N - 1), with 2*W*srate = N + f
which at the production settings (--srate 4096,
--data-integration-window-half 0.075) is N = 614, delta = 0.0022838: the
exported geocentre time is +171 us late at the window centre and any exported
time posterior is 0.23% too wide. --interpolate-time does not fix it (the
stencil still steps one sample) and --srate-resample-time-marginalization does
not either (the dense grid is tvals[0] + arange(n)/srate_resample and inherits
the labels).
Minimal by construction. This is the only site whose tvals values leave the
process as data; tvals[0] and len(tvals) are unchanged, so the likelihood reads
exactly the same Q_lm samples and lnLt is bit-identical. Verified against the
shipped likelihood on this branch: lnLt bitwise equal and the time-marginalized
lnL equal to the last digit under the two grids, and a synthetic Q_lm feature
recovered 97-280 us late (window edge to edge) under the old grid versus a
constant +17 us -- the separate nearest-sample snap -- under the new one, the
difference matching the closed form above to <0.05 us.
The other six grid sites are deliberately left alone: four feed vectorized
likelihoods that already integrate with dx=deltaT and export no times, so
relabelling them changes nothing, and two feed
FactoredLogLikelihoodTimeMarginalized, which integrates with
dx=tvals[1]-tvals[0] and would therefore shift lnL by a constant
-log(1+delta) = -0.00228 nats.
oshaughnessy-junior
deployed
to
private-review-dispatch-rift-upstream
August 22, 2026 10:38 — with
GitHub Actions
Active
4 tasks
oshaughnessy-junior
marked this pull request as ready for review
August 22, 2026 14:04
oshaughnessy-junior
deployed
to
private-review-dispatch-rift-upstream
August 22, 2026 14:04 — 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. Minimal, export-path-only fix for a time-labelling defect that is present in
every GWTC-6 production run. Opened as a draft against
oshaughn:rift_O4cbecause this isthe branch
Reviewed-RIFT-20260807.sif(tag 0.0.17.13) is built from and it needs aminimal path forward. No review requested.
The defect
resample_samples()builds the time-marginalization grid aswhose spacing is
2W/(N-1). The likelihood does not step by that.DiscreteFactoredLogLikelihoodViaArrayVectorNoLoopuses onlytvals[0]— it setstfirst = t_det + tvals[0]— and then readslnLt[k]from the precomputed Q_lm buffer atindex
ifirst + k, i.e. at detector timetfirst + k*deltaT. Labelling those values witha closed-interval linspace stretches the reported time about the start of the window:
At the production settings (
--srate 4096,--data-integration-window-half 0.075):N = 614,f = 0.4,delta = 0.0022838, sorising to +263 µs at +40 ms), and
deltais not1/srate— it depends on the fractional part of2*W*srate:--srateNeither existing option works around it.
--interpolate-timedoes not (the cubic stencilstill steps exactly one sample), and
--srate-resample-time-marginalizationdoes not (thedense export grid is built as
tvals[0] + arange(n)/srate_resampleand inherits themislabelling).
The likely origin is someone making the integer grid-size rounding come out right:
int(2W/deltaT)andtvals[0]are both already correct, only the spacing of the labelsis wrong.
The fix
One line: tie the label spacing to
deltaT, the step the likelihood actually takes.tvals[0]andlen(tvals)are unchanged, so the likelihood reads exactly the sameQ_lm samples. This relabels the times; it does not move the likelihood.
Deliberately not the
rift_O4dconvention.rift_O4d(junior PR #162, issue #146) nowuses a centred grid,
(arange(npts) - npts//2)*deltaT, so thatt = 0sits on the gridand the JAX driver agrees sample-for-sample. That shifts
tvals[0]by 0.2 samples, whichcan round
ifirstto a different integer and therefore change which data samples areintegrated. Keeping
tvals[0] = -t_ref_windhere is what makes this patch provablynumerically inert, which is the right trade for the release branch.
Scope: why only this one site
There are seven grid sites in this file. This is the only one whose
tvalsvalues leavethe process as data (they become
t_out, hence the exportedt_ref). Checked on theO4c file, not assumed — the branch lines have diverged:
DiscreteFactoredLogLikelihoodViaArrayVector/
...NoLoop, which consume onlytvals[0]andlen(tvals)and integrate withdx=deltaT. Relabelling them changes nothing at all, so a minimal patch leaves them.FactoredLogLikelihoodTimeMarginalized, which integrateswith
dx=tvals[1]-tvals[0](factored_likelihood.py:694). Fixing those would shiftlnLby a constant-log(1+delta) = -0.00228nats on the non-vectorized and ROM paths.Correct, but not numerically inert, so out of scope here.
Verification
Against the shipped likelihood on this branch (synthetic Q_lm carrying a feature at a
known detector arrival time,
<h|h>cross terms zeroed solnL(t)tracks the feature):Recovered peak position of the injected feature:
legacy − fixedmatches the closed form above to better than 0.05 µs at every offset.The residual +17 µs that survives is constant — it is the separate nearest-sample
detector-time snap for that sky position, not this defect.
--srate-resample-time-marginalizationstill lands on exactly1/srate_resample:The dense grid loses a few points at the far edge because
tvals[-1]moves from+W(alabel the likelihood never actually evaluated) to the last real sample. No coverage is
lost: the likelihood always evaluated
nptssamples steppingdeltaTfrom-W, and theold code merely mislabelled the last of them as
+W.On real data (recorded separately, not rerun here): re-running the extrinsic stage of
GWTC-6 S250331o over identical blocks of the final intrinsic grid with this correction
moves the exported posterior by −146.4 ± 22.6 µs against a −149.4 µs prediction fixed
before the run, with a null replicate (same arguments, different RNG) at +14.4 ± 23.8 µs.
How much does it matter in practice
JS divergence attributable to this defect alone, per event, over the 21 GWTC-6 events with
a bilby SEOBNRv5PHM counterpart (the bias is a deterministic map, so it is evaluated by
applying the inverse map to the production samples — no rerun needed):
So it is negligible for poorly-localised events — median 6.7e-4 bits, below the
~2e-3 bit scale at which these comparisons are normally called indistinguishable, on the
broad (≈40 ms wide) events — and dominant for sharply-localised ones: 2.4e-2 bits on
S250331o (90 % width 1.65 ms), which is larger than the entire current RIFT-vs-bilby JS
for that event (1.2e-2 bits), and 1.1e-2 bits on S250213dg.
Tests
MonteCarloMarginalizeCode/Code/test/test_time_marginalization_grid_labels.py(13 tests,no GPU, no data): the corrected grid's spacing, that
tvals[0]andlen(tvals)arepreserved, the closed-form bias at five offsets, the srate ladder, the width stretch, and
a drift guard that reads the shipped source line, evaluates it at five sample rates, and
fails if the export grid becomes a closed-interval linspace again.
Verified to fail for the right reason. Against the unpatched tree the two source-reading
guards fail and the other eleven pass. Mutation battery, with the unmutated control run
through the same scoring path:
(An earlier revision of this patch introduced
_npts_ton its own line; review caught thatthe guard then never evaluated it and the "npts halved" mutant survived.
nptsis nowinlined into the guarded expression.)
Run with:
Not addressed here
A second, independent defect in the same machinery — the nearest-sample detector-time snap
broadens the time posterior where the inter-detector delay is sharp (the constant +17 µs
above, and a +12.5 % width excess on S250331o).
rift_O4dhas--interpolate-timefor it;rift_O4cdoes not. Out of scope for a minimal release-branch patch.