Skip to content

fix: apply_over_sampling keeps the sparse operator; cache operated_mapping_matrix_list - #586

Open
Jammy2211 wants to merge 1 commit into
mainfrom
feature/sparse-operator-oversampling-cache
Open

Jammy2211 wants to merge 1 commit into
mainfrom
feature/sparse-operator-oversampling-cache

Conversation

@Jammy2211

Copy link
Copy Markdown
Collaborator

Summary

Fixes #585. Two performance defects found during the Euclid DR1 campaign:

  • Imaging.apply_over_sampling rebuilt the dataset without the sparse_operator (and without the
    noise_covariance_matrix). Calling apply_sparse_operator[_cpu]() before apply_over_sampling() therefore
    silently fell back to the dense InversionImagingMapping route. Every Euclid DR1 vis_pix fit ran dense
    because of this. Both attributes are now carried through; neither depends on over-sampling.
  • operated_mapping_matrix_list was an uncached @property, so the dense route PSF-convolved (imaging) or
    NUFFT-transformed (interferometer) the mapping matrix twice per likelihood evaluation. It is now a
    cached_property on the inversion instance. Inversions are built per evaluation / per JAX trace, so the
    cache never outlives its inputs.

Adjacent hardening from a sweep of every dataset rebuild:

  • apply_mask and apply_noise_scaling still discard an attached operator (it is stale there), but now log a
    warning instead of dropping it silently.
  • apply_sparse_operator_cpu gains the convolve_over_sample_size > 1 guard that apply_sparse_operator
    already had.
  • apply_sparse_operator[_cpu] and apply_noise_scaling carry convolve_over_sample_size_* through.

Measured on a real DR1 tile (median of 16 evals, 1 thread, logL bit-identical in every comparison):

Route main this PR
Operator applied before over-sampling 1011 ms (dense, operator lost) 169.5 ms (InversionImagingSparseNumba)
Dense (no operator) 1166.5 ms (2 convolutions per eval) 592.8 ms (1 convolution per eval)

API Changes

No signatures change. Behaviour changes:

  • Imaging.apply_over_sampling now keeps sparse_operator and noise_covariance_matrix. Scripts that applied
    the operator first now take the sparse route (same likelihood, faster).
  • Imaging.apply_mask / apply_noise_scaling warn when they discard a sparse operator.
  • Imaging.apply_sparse_operator_cpu raises DatasetException for a PSF with convolve_over_sample_size > 1.
  • operated_mapping_matrix_list (imaging and interferometer inversions) is cached per inversion.
    See full details below.

Test Plan

  • New witnesses, each run red on unfixed main first and green after the fix:
    • sparse operator kept through apply_over_sampling;
    • discard warnings logged;
    • CPU guard raises;
    • one PSF convolution per eval (imaging);
    • one NUFFT per eval (interferometer).
  • Full pytest test_autoarray: 1743 passed, 0 failed.
  • Euclid DR1 A/B on a real tile (table above): inversion class selected, per-eval timing, logL parity.
Full API Changes (for automation & release notes)

Changed Behaviour

  • Imaging.apply_over_sampling(...): carries sparse_operator and noise_covariance_matrix into the returned
    dataset. Previously both were dropped.
  • Imaging.apply_mask(...), Imaging.apply_noise_scaling(...): log a warning when an attached
    sparse_operator is discarded.
  • Imaging.apply_noise_scaling(...): carries convolve_over_sample_size_lp / convolve_over_sample_size_pixelization.
  • Imaging.apply_sparse_operator(...), Imaging.apply_sparse_operator_cpu(): carry
    convolve_over_sample_size_lp / convolve_over_sample_size_pixelization.
  • Imaging.apply_sparse_operator_cpu(): raises DatasetException when psf.convolve_over_sample_size > 1,
    matching apply_sparse_operator.
  • AbstractInversionImaging.operated_mapping_matrix_list,
    AbstractInversionInterferometer.operated_mapping_matrix_list: property → cached_property.
  • AbstractInversion.operated_mapping_matrix: with a single linear object, returns that object's cached
    operated mapping matrix directly rather than an hstack copy.

Migration

  • None required. Operator-first scripts get faster; operator-last scripts are unchanged.

Known follow-ups (not in this PR):

  • The PyAutoGalaxy aggregator reloaders (aggregator/imaging/imaging.py, interferometer.py) do not restore
    a sparse operator, so reloaded fits run dense.
  • AbstractDataset.trimmed_after_convolution_from (copy.copy) keeps a stale operator. Its only callers are
    the Galaxy/Lens simulators, where no operator is attached.

Generated by the PyAutoLabs agent workflow.

🤖 Generated with Claude Code

…pping_matrix_list

apply_over_sampling rebuilt Imaging without sparse_operator (and without
noise_covariance_matrix), so applying the sparse operator before over-sampling
silently fell back to the dense InversionImagingMapping route. Both are now
carried through; neither depends on over-sampling.

operated_mapping_matrix_list is now a cached_property (imaging and
interferometer), so the dense route convolves / NUFFT-transforms the mapping
matrix once per likelihood evaluation instead of twice.

apply_mask / apply_noise_scaling warn when they discard a (stale) operator;
apply_sparse_operator_cpu gains the convolve_over_sample_size > 1 guard; the
sparse methods and apply_noise_scaling carry convolve_over_sample_size_*.

DR1 tile: operator-first 1011 -> 169.5 ms (sparse), dense 1166.5 -> 592.8 ms,
logL bit-identical. test_autoarray: 1743 passed.

Fixes #585

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Jammy2211 Jammy2211 added the pending-release PR queued for the next release build label Sep 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pending-release PR queued for the next release build

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: apply_over_sampling drops sparse operator; cache operated_mapping_matrix_list

1 participant