The error-aware likelihood

Folding the surrogate's own uncertainty into a parameter-estimation likelihood, so that parameters are measured less confidently wherever the model is less sure. Implemented in nrhjsurrogate/driver/fisher.py.

The short version: the effective noise becomes S_eff = S_n + S_w(theta), the detector noise plus a model-error spectrum built from the surrogate's stored variances. That addition is not a heuristic inflation. It is the exact result of marginalising the likelihood over the surrogate's own posterior, and the derivation below says precisely which premises make it exact and which of them this implementation actually satisfies.

Why variances add

Adding two independent zero-mean Gaussians adds their covariances. A power spectral density is a variance density, so power spectral densities add linearly. Amplitude spectral densities do NOT add linearly; they add in quadrature, which is the same statement written differently. S_n + S_w is the linear addition of variances and is the correct form.

The derivation

Setup. The data is one true signal plus noise,

d = h_true + n ,        n ~ Normal(0, C_n) .

The surrogate predicts h_model(theta). It is a Gaussian process regression, so what it actually holds is not a single set of coefficients but a POSTERIOR over them:

C | training ~ Normal(C_hat(theta), Sigma(theta)) .

The prediction h_model is the posterior MEAN, built from C_hat. Writing a draw from the posterior as C = C_hat + epsilon and letting J be the map from coefficients to waveform samples,

h(theta) = h_model(theta) + J epsilon ,     epsilon ~ Normal(0, Sigma) .

Marginalise. Conditional on a particular draw the likelihood is ordinary:

p(d | theta, epsilon) = Normal( d ; h_model(theta) + J epsilon , C_n ) .

We do not know epsilon, we know its distribution, so integrate it out. The integral is a Gaussian convolution and is analytic:

p(d | theta) = integral Normal(d ; h_model + J epsilon, C_n)
                        Normal(epsilon ; 0, Sigma) d epsilon
             = Normal( d ; h_model(theta) , C_n + J Sigma J^T ) .

So with C_w = J Sigma J^T,

C_eff(theta) = C_n + C_w(theta) ,

ln L(theta) = -1/2 (d - h_model)^T C_eff^-1 (d - h_model)
              -1/2 ln det C_eff  +  const .

This is exact given the premises. There is no approximation in the step from "the surrogate has a Gaussian posterior" to "add the covariances". That is worth being clear about, because an inflated noise budget usually IS a heuristic, and here it is not.

The frequency-domain form, as implemented

InnerProduct (fisher.py:634) is band-limited and frequency-weighted, so C_eff is represented by a spectrum. Under the stationarity assumption discussed in the limits, the two terms become

S_eff(f) = S_n(f) + S_w(f; theta) ,        weight = 4 df / S_eff .

Because C_eff depends on theta, the normalisation is NOT a constant and cannot be dropped. It is carried (fisher.py:891):

logdet = -0.5 * np.sum(np.log(s_eff / self.ip.snf[None, :]), axis=1)
val    = -0.5 * self.ip(r, r, weight=weight) + logdet

What that term does, and why there is an alternative. It penalises regions of parameter space where the model is less sure, so the posterior is pushed away from them. Under the marginalisation above that is formally correct. It is also easy to misread: it is not the same as being uncertain about those regions, and it can present as a preference for parameters where the surrogate happens to be confident. frozen_error_psd (fisher.py:780) evaluates S_w once at a reference point and holds it fixed, which removes the penalty at the cost of no longer varying the error budget with theta. Its own docstring names this penalty as the reason it exists. Which of the two is appropriate is a choice about the claim being made, not a bug in either.

Limits

Ranked by how much they should worry a reader.

1. RETRACTED 2026-09-02, and the correction is the interesting part.

What this section said, and what was wrong with it, is kept here rather than edited away, because the mistake is easy to make again.

IT SAID: the GPR knows only interpolation uncertainty, so the merger-ringdown arm's recorded 100 to 200 percent error is a systematic with no representation in S_w, and marginalising cannot help.

THAT IS BACKWARDS. The merger-ringdown arm's error was established to be parameter-space REGRESSION, essentially in full. Comparing the GPR's predicted element coefficients against each simulation's OWN directly fitted coefficients, with the same pinned breaks, the same p_max, the same remnant map and QNM detrend, so that only the coefficient source changes: pooled e_gpr 3.44e-03 against e_own 2.55e-05, a ratio of 115x, cross term at or below 3e-4 so the split is exact. Waveform level, own fit against oracle, 1.000 median. All ten elements regression dominated at 0.9996 to 1.0000. Not the representation, not the data.

A Gaussian process posterior variance IS an estimate of its own regression uncertainty. So the merger-ringdown error is precisely the KIND of error this construction describes, not a systematic it cannot see.

The 100 to 200 percent number was also being misused. The same log explains it: the absolute regression error is nearly FLAT across the segment, 5.9e-04 rising to 1.4e-03, while local truth power falls 23x, so only the FRACTIONAL error rises. It describes late ringdown where the signal has collapsed, not the merger in general.

SO THE REAL LIMIT IS CALIBRATION, NOT BLINDNESS. The question is whether S_w is correctly SIZED, and one measurement bears on it: the in-sample full-IMR mismatch of 7.90e-07 on (2,2) implies about 1.26e-03 relative error while the envelope reports 4.77e-04, so the envelope is a factor 2.6 OPTIMISTIC. That is in sample. Out of sample it will be worse by an amount nobody has measured.

Cutler and Vallisneri still applies to whatever error the GPR genuinely cannot see, and a marginalisation broadens rather than de-biases. The point of this retraction is only that the merger-ringdown arm is not the example of that.

1b. The original concern, in the form that survives. The posterior variance describes interpolation uncertainty between training simulations. It does not describe a systematic deficiency of the fit itself. The merger-ringdown arm is recorded in this repository as a 100 to 200 percent problem at parameter-estimation masses, regression dominated rather than representation dominated, and the GPR does not know that. That component is a BIAS with no representation in S_w, and marginalising cannot help with a bias.

This is the standard result: a waveform-model error primarily SHIFTS the posterior, by of order one sigma, and only secondarily broadens it (Cutler and Vallisneri 2007, cited at scripts/pe_error_broadening.py:39). The construction on this page captures the secondary effect and is silent on the primary one. It makes parameter estimation honest about interpolation uncertainty. It does not make a biased model unbiased.

2. Dynamics uncertainty is reported but still not propagated, and it is the LARGER term. The surrogate stores variances for 'E', 'J', 'k', 'tau' and 'xpc' alongside the mode content. They used to be dropped: mode_errors kept only tuple-keyed entries and so discarded all five.

REACHABLE since 2026-09-02 through NRHJSurAA.get_dynamics_error_estimate, a separate method so that a caller who does not want them pays nothing (MEASURED: 4.9 ms, against 109 ms for a waveform and 76 ms for the dynamics values themselves, one point, numpy backend, OMP_NUM_THREADS=1). On NRHJSur3dq8_AA_v3 at q = 4.0, chi1z = 0.3, chi2z = -0.2 the medians are tau 2.3796e-01 M, x 1.6956e-05, J 8.5713e-05, E 1.5347e-06, k 7.1095e-03.

STILL EXCLUDED FROM THE BUDGET, and this is the part that matters. tau is the timing content, a timing error is a phase error, and phase is the quantity a gravitational-wave likelihood is most sensitive to. A clock error at fixed orbital phase is an orbital-phase error at fixed time, delta Phi = -omega delta tau to first order, and mode (l, m) carries m times it. MEASURED at the same point, against the per-mode content uncertainty the budget does report:

mode content delta_phi timing-induced ratio
(2,2) 4.7288e-04 rad 1.3492e-02 rad 28.5
(2,1) 3.9928e-03 rad 6.7459e-03 rad 1.7
(3,3) 2.3035e-03 rad 2.0238e-02 rad 8.8
(3,2) 4.5466e-03 rad 1.3492e-02 rad 3.0
(4,4) 4.1196e-03 rad 2.6984e-02 rad 6.5
(4,3) 6.4954e-03 rad 2.0238e-02 rad 3.1
(5,5) 7.1997e-03 rad 3.3729e-02 rad 4.7

The excluded channel is the larger one on every mode, and on the dominant mode it is 28 times larger. So S_w as built today is a small fraction of the model's own phase uncertainty.

Three things stand between that table and folding the term in, and none of them is effort. First it is a LINEARISATION: the time-domain waveform needs Phi(t), which is the INVERSE of tau(Phi), and delta Phi = -omega delta tau is only the first-order term of that inversion; the marginalisation derived above is exact only for a LINEAR map from coefficients to samples. With sigma_tau up to 0.43 M the second-order term has not been measured. Second, the clock and the mode content are fitted from the SAME simulations by the same GPR, so they are not independent and the derivation adds variances only for independent contributions; this package's precedent for correlated fits is the conservative linear sum, not quadrature. Third, folding it would move four published per-mode numbers by more than an order of magnitude. It is a measurement task before it is a coding task.

3. Only the diagonal of Sigma is propagated. The derivation needs the full posterior covariance J Sigma J^T. error_envelope sums T_j(x)^2 Var[C_j] over coefficients, which is J diag(Sigma) J^T, discarding coefficient correlations. Whether that overstates or understates the result depends on the sign of the discarded terms and has not been measured.

4. C_w is not stationary, and the implementation makes it so. The model error is time localised, small early and larger near merger. Representing it as a spectrum forces stationarity. The waveform_error_psd docstring states this plainly: it ignores the true time correlation and non-stationarity of C_w, keeping the leading effect that there is more effective noise where the surrogate is less sure.

5. Two spectral conventions that are measured, not cosmetic. Both exist because a spectrum is being built from a slowly varying amplitude.

6. The merger-ringdown envelope is a different quantity and must not be taken from error_envelope. Under the SWSpH representation the regressed coefficients are the detrended spheroidal residual, so error_envelope returns the uncertainty of that residual, before re-winding, mixing and the remnant-map term. That is wrong rather than merely uncalibrated. spherical_plunge_spheroidal_ringdown_error_envelope is the correct source. The released arm IS SWSpH.

Since 2026-09-02 the dispatch is made in one place, waveform_error_estimate.merger_ringdown_error_envelope, which both waveform_error_psd and NRHJSurAA.get_waveform_error_estimate call, and it GUARDS rather than guesses: an arm that carries the spheroidal ringdown and plunge groups but is not the recognised class raises instead of falling through to the plain coefficient envelope.

Its remnant_sigma defaults, (1.2e-4, 5.3e-5), are SOURCED: they are loo_Mf_rms and loo_chif_rms in paper_data/swsph_remnant_map.npz, the exact-leave-one-out errors of the shipped remnant map written by scripts/swsph_remnant_map.py. The final-mass number excludes SXS:BBH:1124 as a catalogue metadata outlier; keeping it gives 2.7e-4 instead. Both are RMS, and the maxima are about 5x and 8x larger.

7. inflate is a stress-test knob only. inflate = 1 is the calibrated envelope. Any other value no longer represents the model's actual uncertainty and must not be used to produce a quoted result.

In the time domain

Nothing in the derivation is specific to a Fourier basis. Marginalising a Gaussian coefficient posterior gives C_n + C_w in any basis, and the normalisation becomes ln det C_eff.

What changes is that limits 4 and 5 stop being necessary. The modulation convention exists only because a periodogram of a slowly varying amplitude misplaces power in frequency; with the error series in hand there is no periodogram. The incoherent-sum rule was calibrated as a rule for combining periodograms and would have to be revisited rather than carried across.

The obstacle is that a time-domain whitening built on a Toeplitz solver assumes stationary noise. tdanalysis builds its Gohberg-Semencul form from an autocorrelation (tdanalysis/likelihoods/single/pe.py, via compute_invLij_from_acorr), so a non-stationary C_w breaks the fast path.

NOT YET TESTED, and recorded here as a proposal rather than a result: C_w is naturally low rank. It is J Sigma J^T with Sigma over a modest number of Chebyshev coefficients, 14 elements by 25 coefficients per mode on the released artifact, which is small against the number of samples in a segment. That is the low-rank-update case, so the Woodbury identity gives the inverse and the matrix determinant lemma gives the determinant, both cheaply, while Gohberg-Semencul continues to handle the stationary detector noise. If that holds, the exact non-stationary correlated model-error covariance becomes affordable and limits 4 and 5 go away together. The cost has not been measured, and it should be measured on a batch, since parameter estimation is batched.

Where the code is

what where
band-limited inner product nrhjsurrogate/driver/fisher.py, class InnerProduct
the model-error spectrum fisher.py, waveform_error_psd
the frozen variant fisher.py, frozen_error_psd
the theta-dependent normalisation fisher.py:891
the per-mode envelope it is built from nrhjsurrogate/waveform_error_estimate.py
the merger-ringdown envelope nrhjsurrogate/physics/spin_weighted_angular_harmonics.py, spherical_plunge_spheroidal_ringdown_error_envelope
the SWSpH dispatch and its guard waveform_error_estimate.py, merger_ringdown_error_envelope
the seam ramp, shared with the value join waveform_error_estimate.py, inspiral_merger_ringdown_blend_weight
the per-mode estimate a user calls driver/aa_api.py, NRHJSurAA.get_waveform_error_estimate
the dynamics uncertainties driver/aa_api.py, NRHJSurAA.get_dynamics_error_estimate
worked example examples/usage/08_model_error.py

What the per-mode envelope covers

Restated here because limit 1 and limit 2 are both about what S_w does NOT contain, and a reader needs the positive statement too. Since 2026-09-02 the envelope that feeds S_w covers ALL SEVEN served modes over the WHOLE waveform span, not four modes over the inspiral. The four-mode reach was an artifact of the AA arm spreading one mode over three content groups while the documented route pointed at the one group holding only the odd-m modes; the AdA arm, which is what waveform_error_psd actually reads, keeps all seven in one group and was never limited. The two arms' envelopes over the join disagree by factors 0.43 to 6.94 depending on mode and parameter point, worst and most systematically on (2,2); the ramp spreads that over 15 M rather than concentrating it, and the joined envelope is continuous (verified by grid refinement, the estimator converging as dt^3).