Overview
Phase 2a of oversampled PSF convolution: the PyAutoArray core API. Implements the design approved in #353 (PyAutoMind/feature/autoarray/oversampling_design.md) verbatim — Convolver.convolve_over_sample_size, the fine-mask machinery, Imaging plumbing and the adaptive guards, with unit tests pinned to the phase-1 numerical ground truth. Behaviour at convolve_over_sample_size=1 is byte-identical to today.
Run mode: --auto at effective level supervised (plan-to-issue, ship sign-off parks with a batched question). Phase split: 2a (this issue) → 2b inversion wiring → 2c PyAutoGalaxy consumer (queued, blocked by an unrelated PyAutoGalaxy worktree claim) → phases 3–4 (workspace, docs).
Plan
- Add
mask_2d_upscaled_from(mask, s) and the cached permutation between autoarray's per-pixel sub-block ordering and the fine mask's row-major slim ordering.
- Extend
Convolver with convolve_over_sample_size: int = 1: fine-resolution kernel semantics, fine ConvolverState from state_from, oversampled scatter → existing convolve → s×s mean bin → slim in all four convolution methods, loud shape/pixel-scale validation.
- Extend
Imaging/GridsDataset with convolve_over_sample_size_lp / convolve_over_sample_size_pixelization (int, default 1), the equality rule vs over_sample_size_*, blurring grid at over_sample_size=s, psf_setup_state fine state, ctor/from_fits/apply_* pass-through, sparse_operator + adaptive guards.
- Add
binned: bool = True pass-through to the over_sample decorator (autoarray side only).
- Unit tests (numpy-only): s=1 strict parity, s=2 ground-truth reference values, guard raises; full suite green.
Detailed implementation plan
Affected Repositories
- PyAutoArray (primary, only repo edited)
Branch Survey
| Repository |
Current Branch |
Dirty? |
| ./PyAutoArray |
main |
clean |
Suggested branch: feature/psf-oversample-core
Worktree root: ~/Code/PyAutoLabs-wt/psf-oversample-core/
Implementation Steps
- Fine-mask utils —
autoarray/mask/mask_2d_util.py: mask_2d_upscaled_from(mask_2d, over_sample_size) (each unmasked pixel → s×s unmasked block, pixel scale ps/s, same origin); permutation builder sub_slim_to_fine_slim_from(mask, s) mapping per-pixel sub-block slim indices to fine-mask row-major slim indices (and its inverse). Unit tests: round-trip permutation, upscaled-mask geometry, s=1 identity.
- ConvolverState fine geometry —
autoarray/operators/convolver.py: when built for s>1, construct from (kernel_fine, mask_fine); cache sub_slim_to_fine_slim, the blurring-region permutation, and bin/reshape indices alongside the existing FFT precomputes.
- Convolver API —
convolve_over_sample_size: int = 1 ctor param (validate plain int ≥ 1, TypeError otherwise); state_from(mask) derives the fine mask when s>1 and checks kernel.pixel_scales ≈ mask.pixel_scales / s (KernelException on mismatch); convolved_image_from, convolved_mapping_matrix_from and both _via_real_space_* variants accept over-sampled slim inputs when s>1 (length n_unmasked·s², sub-block order), raise on binned-length input, return image-resolution slim via mean bin-down. s=1 paths untouched.
- Imaging / GridsDataset —
autoarray/dataset/imaging/dataset.py + dataset/grids.py: new ctor ints (default 1) stored + passed through from_fits / apply_mask / apply_over_sampling; equality rule (s>1 requires matching over_sample_size_* uniform int equal to it, DatasetException otherwise, checked via OverSampler.sub_is_uniform); blurring grid gets over_sample_size=s when s>1; psf_setup_state builds the fine state; raise if sparse_operator is not None and convolve_over_sample_size_pixelization > 1.
- Decorator —
autoarray/operators/over_sampling/decorator.py: binned: bool = True kwarg; binned=False returns sub-gridded values in sub-block order (the s>1 Convolver input format). No callers change in this PR.
- Tests —
test_autoarray/: mirror the ground-truth scene (11×11, ps=1, r=3.5 circular mask, Gaussian source σ=1.2 @ (0.3,−0.4), Gaussian PSF σ=0.8, kernel radius 2.0"): s=1 parity vs existing path; s=2 == design §7 reference values (sum 2.796562184524787, slim0 3.726289901353439e-02, slim17 2.025075336159483e-01, slim36 1.090767109119494e-02); guard raises; decorator binned=False ordering.
Key Files
autoarray/operators/convolver.py — ConvolverState + Convolver (design §§1–2)
autoarray/mask/mask_2d_util.py — upscale + permutation utils
autoarray/dataset/imaging/dataset.py, autoarray/dataset/grids.py — plumbing + guards (design §3, §6)
autoarray/operators/over_sampling/decorator.py — binned kwarg (design §5)
PyAutoMind/feature/autoarray/oversampling_design.md — the approved design (do not re-design)
PyAutoMind/feature/autoarray/oversampling_ground_truth.py — reference numbers
Acceptance
- Full PyAutoArray pytest suite green; every pre-existing test unmodified.
- s=2 unit tests reproduce the ground-truth numbers to ≤1e-12.
- PR body carries
## API Changes for phases 2b/2c/3.
Original Prompt
Click to expand starting prompt
See PyAutoMind/issued/oversampling_phase_2a_convolver_dataset.md (phase 2a split of oversampling_phase_2_core_api.md, itself phase 2 of oversampling.md; design approved in #353).
Overview
Phase 2a of oversampled PSF convolution: the PyAutoArray core API. Implements the design approved in #353 (
PyAutoMind/feature/autoarray/oversampling_design.md) verbatim —Convolver.convolve_over_sample_size, the fine-mask machinery,Imagingplumbing and the adaptive guards, with unit tests pinned to the phase-1 numerical ground truth. Behaviour atconvolve_over_sample_size=1is byte-identical to today.Run mode:
--autoat effective levelsupervised(plan-to-issue, ship sign-off parks with a batched question). Phase split: 2a (this issue) → 2b inversion wiring → 2c PyAutoGalaxy consumer (queued, blocked by an unrelated PyAutoGalaxy worktree claim) → phases 3–4 (workspace, docs).Plan
mask_2d_upscaled_from(mask, s)and the cached permutation between autoarray's per-pixel sub-block ordering and the fine mask's row-major slim ordering.Convolverwithconvolve_over_sample_size: int = 1: fine-resolution kernel semantics, fineConvolverStatefromstate_from, oversampled scatter → existing convolve → s×s mean bin → slim in all four convolution methods, loud shape/pixel-scale validation.Imaging/GridsDatasetwithconvolve_over_sample_size_lp/convolve_over_sample_size_pixelization(int, default 1), the equality rule vsover_sample_size_*, blurring grid atover_sample_size=s,psf_setup_statefine state, ctor/from_fits/apply_*pass-through,sparse_operator+ adaptive guards.binned: bool = Truepass-through to theover_sampledecorator (autoarray side only).Detailed implementation plan
Affected Repositories
Branch Survey
Suggested branch:
feature/psf-oversample-coreWorktree root:
~/Code/PyAutoLabs-wt/psf-oversample-core/Implementation Steps
autoarray/mask/mask_2d_util.py:mask_2d_upscaled_from(mask_2d, over_sample_size)(each unmasked pixel → s×s unmasked block, pixel scale ps/s, same origin); permutation buildersub_slim_to_fine_slim_from(mask, s)mapping per-pixel sub-block slim indices to fine-mask row-major slim indices (and its inverse). Unit tests: round-trip permutation, upscaled-mask geometry, s=1 identity.autoarray/operators/convolver.py: when built for s>1, construct from(kernel_fine, mask_fine); cachesub_slim_to_fine_slim, the blurring-region permutation, and bin/reshape indices alongside the existing FFT precomputes.convolve_over_sample_size: int = 1ctor param (validate plain int ≥ 1, TypeError otherwise);state_from(mask)derives the fine mask when s>1 and checkskernel.pixel_scales ≈ mask.pixel_scales / s(KernelException on mismatch);convolved_image_from,convolved_mapping_matrix_fromand both_via_real_space_*variants accept over-sampled slim inputs when s>1 (lengthn_unmasked·s², sub-block order), raise on binned-length input, return image-resolution slim via mean bin-down. s=1 paths untouched.autoarray/dataset/imaging/dataset.py+dataset/grids.py: new ctor ints (default 1) stored + passed throughfrom_fits/apply_mask/apply_over_sampling; equality rule (s>1 requires matchingover_sample_size_*uniform int equal to it, DatasetException otherwise, checked viaOverSampler.sub_is_uniform); blurring grid getsover_sample_size=swhen s>1;psf_setup_statebuilds the fine state; raise ifsparse_operator is not Noneandconvolve_over_sample_size_pixelization > 1.autoarray/operators/over_sampling/decorator.py:binned: bool = Truekwarg;binned=Falsereturns sub-gridded values in sub-block order (the s>1 Convolver input format). No callers change in this PR.test_autoarray/: mirror the ground-truth scene (11×11, ps=1, r=3.5 circular mask, Gaussian source σ=1.2 @ (0.3,−0.4), Gaussian PSF σ=0.8, kernel radius 2.0"): s=1 parity vs existing path; s=2 == design §7 reference values (sum 2.796562184524787, slim0 3.726289901353439e-02, slim17 2.025075336159483e-01, slim36 1.090767109119494e-02); guard raises; decorator binned=False ordering.Key Files
autoarray/operators/convolver.py— ConvolverState + Convolver (design §§1–2)autoarray/mask/mask_2d_util.py— upscale + permutation utilsautoarray/dataset/imaging/dataset.py,autoarray/dataset/grids.py— plumbing + guards (design §3, §6)autoarray/operators/over_sampling/decorator.py—binnedkwarg (design §5)PyAutoMind/feature/autoarray/oversampling_design.md— the approved design (do not re-design)PyAutoMind/feature/autoarray/oversampling_ground_truth.py— reference numbersAcceptance
## API Changesfor phases 2b/2c/3.Original Prompt
Click to expand starting prompt
See
PyAutoMind/issued/oversampling_phase_2a_convolver_dataset.md(phase 2a split ofoversampling_phase_2_core_api.md, itself phase 2 ofoversampling.md; design approved in #353).