Guard apply_sparse_operator against unequal real/imag noise sigma - #503
Merged
Jammy2211 merged 1 commit intoAug 28, 2026
Merged
Conversation
The interferometer sparse operator builds W~ = Re(F^H W F) from a single precision operator computed with the real-part sigma only, which is exact only when every visibility has sigma_real == sigma_imag. With unequal sigmas the sparse curvature matrix silently disagreed with the dense path (5e-16 -> 5e-10..3e-2 relative). apply_sparse_operator now raises DatasetException naming the cause and the dense-path workaround. Closes #502 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012JM45sA4YGEUw6KYW8Pm96
Jammy2211
deleted the
feature/sparse-interferometer-unequal-sigma-guard
branch
August 28, 2026 16:10
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.
Summary
Interferometer.apply_sparse_operatorbuilt itsW~ = Re(FᴴWF)precision operator from the real-part noise sigma alone, a reduction that is exact only when every visibility hassigma_real == sigma_imag. With unequal sigmas the sparseInversionInterferometerSparsecurvature matrix silently disagreed with the denseInversionInterferometerMappingpath (5e-16 relative with equal sigmas, 5e-10 to 3e-2 with unequal, geometry dependent) — latent becauseSimulatorInterferometerand real datasets satisfy the equality, but unguarded. Found while shipping #499 / #500. The method now raises aDatasetExceptionnaming the cause and the dense-path workaround, and the precondition is documented onapply_sparse_operatorandInterferometerSparseOperator. Closes #502.API Changes
Interferometer.apply_sparse_operatornow raisesDatasetExceptionwhen any visibility has unequal real/imag noise sigma (previously produced a silently wrong sparse curvature matrix). No signature changes.See full details below.
Test Plan
pytest test_autoarray— 1290 passed, 81 warnings in 55s (no existing test used unequal real/imag sigma withapply_sparse_operator)Assessment: general two-operator extension
Writing
F_ki = cos(φ_ki) − i sin(φ_ki), the exact curvature for unequal sigmas isC_ij = Σ_k [ Re(F_ki)Re(F_kj)/σr_k² + Im(F_ki)Im(F_kj)/σi_k² ], which expands toΣ_k { ½(wr_k+wi_k)·cos(φ_ki − φ_kj) + ½(wr_k−wi_k)·cos(φ_ki + φ_kj) }.Only the first term is translation-invariant, i.e. a function of the pixel offset — that is exactly today's kernel, reweighted by
(wr+wi)/2(and whenwr == withe second term vanishes and it collapses to the current preload, which is the consistency check). The second term depends on the pixel sum(y_i+y_j, x_i+x_j), so it needs a genuinely second kernel on a sum-index grid weighted by(wr−wi)/2. It is still FFT-expressible —Σ_j K[i+j] f_jis a correlation, i.e. a convolution against a reversed image — but it is not the same kernel and not the same apply:from_nufft_precision_operator's singleKhatplus offset-indexedcol_offsetsmachinery would need a parallel sum-indexed path, and the densetranslation_invariant_kernel[y_diff, x_diff]assembly a[y_sum, x_sum]twin.Cost: 2x the (already hours-long) precision-operator setup, 2x
Khatmemory, ~2 FFT convolutions per apply, and a second index-mapping code path in both the JAX and numba assemblers.Need: none known.
SimulatorInterferometerwrites a constant equal-sigma noise map, and measurement-set weights are per-visibility scalars applied to both real and imaginary parts, so unequal sigmas only arise from hand-constructed noise maps.Recommendation: don't do it. The guard is the right cost/benefit — roughly doubling the most expensive precomputation in the library plus a second assembler path, for a case no real or simulated dataset currently produces. Revisit only if a dataset with genuinely per-part weights appears; the algebra above is the recipe.
Full API Changes (for automation & release notes)
Changed Behaviour
Interferometer.apply_sparse_operator— raisesDatasetExceptionfor unequal real/imag noise sigma.Generated by the PyAutoLabs agent workflow.
🤖 Generated with Claude Code
https://claude.ai/code/session_012JM45sA4YGEUw6KYW8Pm96