Skip to content

Interferometer + Delaunay sparse-operator curvature math is wrong (silent ~34% error) #314

@Jammy2211

Description

@Jammy2211

Summary

InversionInterferometerSparse computes a curvature matrix that disagrees with the mapping path by ~34% Frobenius norm when paired with a Delaunay-mesh mapper. The regularized matrix then loses positive-definiteness, raising numpy.linalg.LinAlgError: Matrix is not positive definite deep inside Inversion.log_det_curvature_reg_matrix_term's Cholesky call.

PR https://github.com/PyAutoLabs/PyAutoArray/pull/_TBD_ adds a defensive NotImplementedError at the entry point. This issue tracks the actual math fix.

Reproducer

Realistic ALMA setup (Hannah Stacey's profiler): Interferometer.from_fits(transformer_class=TransformerDFT) at 16984 visibilities × (40, 40) real-space mask × Delaunay(pixels=578, zeroed_pixels=30) + ConstantSplit regularization. Then dataset.apply_sparse_operator(use_jax=True) and try fit.figure_of_merit — fails.

Minimal in-Python compare against the mapping path (one-off script, since deleted; reproducible from the public profiler):

=== curvature_matrix comparison ===
  mapping shape: (578, 578)
  sparse  shape: (578, 578)
  ||sparse - mapping||_F        = 20.7711
  ||mapping||_F                  = 60.3154
  on-diagonal disagreement  |diag(sparse - mapping)|.max = 4.33995
  off-diagonal disagreement max                            = 1.80408

=== curvature_reg_matrix_reduced eigenvalues ===
  mapping eigvals: min = 0.664671   max = 11.447
  sparse  eigvals: min = -4.82475   max = 12.6188

=== Cholesky attempts ===
  mapping Cholesky: PASS
  sparse  Cholesky: FAIL (Matrix is not positive definite)

What is/isn't the trigger

  • Not zeroed_pixels: same exact failure (Frobenius diff 20.77, smallest eigvalue -4.82) with zeroed_pixels=0.
  • Not use_jax: apply_sparse_operator(use_jax=False) matches use_jax=True to 5.5e-14 — both produce the same wrong matrix.
  • Not Rectangular mapping: rectangular_sparse.py in autolens_workspace_test works; the existing unit tests at test_abstract.py::test__curvature_matrix__via_sparse_operator__identical_to_mapping pass.
  • Triggered by Delaunay (Pmax = 3 source pixels per image pixel via barycentric interpolation, weights summing to 1). Top-10 most-disagreeing rows are all on active vertices — not in the zeroed-edge block — and the disagreement is mostly off-diagonal, so source-pixel cross-coupling is structurally wrong.

Root cause hypothesis (unverified)

The implementation in autoarray/inversion/inversion/interferometer/inversion_interferometer_util.py:677–839 (InterferometerSparseOperator.curvature_matrix_via_sparse_operator_from) was written assuming a single source pixel per image pixel (Pmax=1, weight=1, the Rectangular case). For Delaunay each image pixel has three source pixels and weights summing to 1; the COO accumulation + FFT-operator-application + segment_sum cols aggregation likely doesn't compose correctly when multiple source pixels contribute to the same image pixel.

The Imaging sparse path (inversion_imaging_util.curvature_matrix_diag_from) handles Delaunay correctly — see test_abstract.py::test__curvature_matrix_via_sparse_operator__includes_source_interpolation__identical_to_mapping. It uses mapper.sparse_triplets_curvature (flat COO via rows, cols, vals) instead of the (M_masked, Pmax) shape the interferometer path uses. The interferometer path may need rewriting to mirror the imaging path's structure.

Test coverage gap

Before this fix, no test combined Interferometer + Delaunay + sparse:

  • test_autoarray/inversion/inversion/test_abstract.py:101 and :160 use aa.Imaging (imaging sparse path) — different code.
  • autolens_workspace_test/scripts/jax_likelihood_functions/interferometer/rectangular_sparse.py is the only end-to-end interferometer sparse test, Rectangular-only.
  • autolens_workspace_test/scripts/jax_likelihood_functions/imaging/delaunay_mge.py:123 has # dataset = dataset.apply_sparse_operator(batch_size=128) commented out — never activated.

Recommended fix scope

Rewrite InterferometerSparseOperator.curvature_matrix_via_sparse_operator_from to mirror the structure of the imaging sparse path. Add a sibling regression test test__curvature_matrix__interferometer_sparse_operator__delaunay__identical_to_mapping, modeled on the existing imaging interpolation test, that verifies sparse-vs-mapping agreement at rtol=1e-4 for a Delaunay interferometer dataset. Once that's in, the guard added by the linked PR can be removed.

Workaround

Use the plain DFT or NUFFT path (i.e. don't call apply_sparse_operator) for Delaunay interferometer fits. The just-shipped JAX-traceable DFT path at autolens_workspace_developer/jax_profiling/jit/{interferometer,datacube}/delaunay.py is the reference setup.

🤖 Filed via Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions