Overview
Step 3 of the weak gravitational lensing series. Steps 1–2 added the WeakDataset data class, SimulatorShearYX simulator and the aplt plotters for shear catalogues (PR #523 / autolens_workspace #186, shipped 2026-05-18). This task adds the fit layer — a FitWeak class that mirrors FitImaging, computes residuals/chi-squared/log-likelihood for a model shear field against an observed WeakDataset, and a FitWeak plotter set that overlays the model on the data via quiver.
Plan
- Add
autolens/weak/fit.py with a FitWeak class mirroring autolens/imaging/fit_imaging.py — same six-step pipeline pattern adapted for shear vectors (no PSF blur, no inversion; model shear computed directly from the tracer).
- Compute
model_shear via LensCalc.from_tracer(tracer).shear_yx_2d_via_hessian_from(grid=dataset.positions) (the exact primitive SimulatorShearYX uses), then derive residuals, chi-squared and log-likelihood in the standard -0.5 * (chi_squared + noise_normalization) form.
- Add a
FitWeak plotter set under autolens/weak/plot/fit_weak_plots.py — plot_data_vs_model (two quivers overlaid), plot_residuals, plot_chi_squared_map, subplot_fit_weak (2×2 mosaic). Re-export into the aplt namespace.
- Add unit tests under
test_autolens/weak/test_fit.py and test_autolens/weak/plot/test_fit_weak_plots.py. ~5× fewer tests than test_fit_imaging.py (no linear-light / pixelization variants).
- Eyeball checkpoint after the first 3
test_fit.py tests, per the prompt.
Detailed implementation plan
Affected Repositories
- PyAutoLens (primary —
autolens/weak/fit.py + plotters + tests)
- autolens_workspace (workspace follow-up —
scripts/weak/fit.py example, after library ships)
Work Classification
Library, with workspace follow-up.
Branch Survey
| Repository |
Current Branch |
Dirty? |
| PyAutoLens |
main |
clean |
| autolens_workspace |
main |
dirty (unrelated dataset/interferometer/simple/* regenerated artifacts; scripts/weak/ clean) |
Suggested branch: feature/weak-fit
Worktree root: ~/Code/PyAutoLabs-wt/weak-fit/ (created later by /start_library)
Implementation Steps
-
PyAutoLens/autolens/weak/fit.py mirroring autolens/imaging/fit_imaging.py:
class FitWeak taking (dataset, tracer).
@property model_shear → LensCalc.from_tracer(tracer).shear_yx_2d_via_hessian_from(grid=dataset.positions).
@property residual_map → dataset.shear_yx - model_shear (operates on the underlying (N, 2) storage; the [:, 0]=γ₂, [:, 1]=γ₁ convention is component-symmetric for subtraction).
@property chi_squared_map → per-galaxy ((residual / noise_map) ** 2).sum(axis=-1) (sum across the γ₁, γ₂ components).
@property log_likelihood → standard -0.5 * (chi_squared + noise_normalization). No log_evidence (no inversion).
figure_of_merit returns log_likelihood.
-
PyAutoLens/autolens/weak/plot/fit_weak_plots.py (lands on top of the new weak/plot/ package shipped in step 2):
plot_data_vs_model(fit, ax=None, ...) — two quivers overlaid: data (color="black") and model (color="red", alpha=0.6), both with the headless pivot="middle", headwidth=0, headlength=0, headaxislength=0 style established by plot_shear_yx_2d.
plot_residuals(fit, ax=None, ...) — quiver of residual_map colour-coded by |residual|.
plot_chi_squared_map(fit, ax=None, ...) — scalar-coloured scatter via autoarray.plot.grid.plot_grid (same pattern as plot_ellipticities).
subplot_fit_weak(fit, ...) — 2×2: data | model | overlay | chi-squared.
- Wire all 4 into
aplt via autolens/plot/__init__.py.
-
Unit tests — PyAutoLens/test_autolens/weak/test_fit.py (~6 tests):
test__model_shear_matches_simulator_for_no_noise — SimulatorShearYX(noise_sigma=0.0) round-trip through FitWeak should give residual_map ≈ 0.
test__chi_squared_zero_for_perfect_fit.
test__log_likelihood_against_hand_computed (4-galaxy deterministic dataset, hand-computed expected value).
test__residual_map_shape ((n_galaxies, 2)).
test__log_likelihood_drops_for_wrong_model (perturb einstein_radius by 0.1, assert LL drops by ≥ 1).
test__pickle_round_trip (matches the viz-subprocess-feasibility precedent — FitWeak must round-trip cleanly for future JIT/subprocess viz).
-
Plotter tests — PyAutoLens/test_autolens/weak/plot/test_fit_weak_plots.py (~4 tests, one per fn, using the plot_patch fixture).
-
Eyeball checkpoint — after the first 3 test_fit.py tests, pause and present them for user review before writing the rest. Per the prompt: "Have me eyeball a few unit tests so I can see they make sense."
Key Files
- New:
PyAutoLens/autolens/weak/fit.py
- New:
PyAutoLens/autolens/weak/plot/fit_weak_plots.py
- New:
PyAutoLens/test_autolens/weak/test_fit.py
- New:
PyAutoLens/test_autolens/weak/plot/test_fit_weak_plots.py
- Edit:
PyAutoLens/autolens/weak/__init__.py — export FitWeak.
- Edit:
PyAutoLens/autolens/plot/__init__.py — add the 4 new plotter imports.
Reused utilities (no need to re-implement)
LensCalc.from_tracer(...).shear_yx_2d_via_hessian_from(grid=...) — PyAutoGalaxy/autogalaxy/operate/lens_calc.py (the simulator's exact primitive).
WeakDataset.{positions, shear_yx, noise_map} — PyAutoLens/autolens/weak/dataset.py:76.
aplt.plot_shear_yx_2d styling + subplot_save / tight_layout / conf_subplot_figsize from autoarray.plot.utils (shipped in step 2).
plot_patch fixture — test_autolens/conftest.py:27.
Verification
pytest test_autolens/weak/ -v — all new tests pass alongside the 16 existing weak tests (5 plotter + 11 dataset/simulator).
- JAX cleanliness:
grep "^import jax\|^from jax" PyAutoLens/autolens/weak/fit.py PyAutoLens/autolens/weak/plot/fit_weak_plots.py → empty.
- Workspace follow-up smoke (post-merge):
python autolens_workspace/scripts/weak/fit.py runs end-to-end and writes the fit subplot PNG.
- Library-first merge gate:
/ship_library first, then /ship_workspace.
Original Prompt
Click to expand starting prompt
We are now going to add weak lensing Fit class
First, we need to create the fit.py module, so inspect @autolens_workspace/scripts/weak and
@PyAutoLens/autolens/imaging/fit modules . We are basically going to make everything weak does from here a "mirror" of
the imaging model API (and also interferoter.)
So, set up a FitWeak module which compute the same key quantities as other Fit objects, such as residuals, chi
squared and log likelihood. Put unit tests in following the imaging unit test stucture, baring in mind there should
be far fewer as there are no variants like linear light profiles of pixelizations. Have me eyeball a few unit tests
so I can see they make sense.
In a second phase, inspect @PyAutoLens/autolens/plot and set up the FitWeak weak_plots.py, you may need to do
some research on how best to plot these quantities, it may be we want to plot the dataset values via quiver on top
of the model ones.
Overview
Step 3 of the weak gravitational lensing series. Steps 1–2 added the
WeakDatasetdata class,SimulatorShearYXsimulator and theapltplotters for shear catalogues (PR #523 / autolens_workspace #186, shipped 2026-05-18). This task adds the fit layer — aFitWeakclass that mirrorsFitImaging, computes residuals/chi-squared/log-likelihood for a model shear field against an observedWeakDataset, and aFitWeakplotter set that overlays the model on the data via quiver.Plan
autolens/weak/fit.pywith aFitWeakclass mirroringautolens/imaging/fit_imaging.py— same six-step pipeline pattern adapted for shear vectors (no PSF blur, no inversion; model shear computed directly from the tracer).model_shearviaLensCalc.from_tracer(tracer).shear_yx_2d_via_hessian_from(grid=dataset.positions)(the exact primitiveSimulatorShearYXuses), then derive residuals, chi-squared and log-likelihood in the standard-0.5 * (chi_squared + noise_normalization)form.FitWeakplotter set underautolens/weak/plot/fit_weak_plots.py—plot_data_vs_model(two quivers overlaid),plot_residuals,plot_chi_squared_map,subplot_fit_weak(2×2 mosaic). Re-export into theapltnamespace.test_autolens/weak/test_fit.pyandtest_autolens/weak/plot/test_fit_weak_plots.py. ~5× fewer tests thantest_fit_imaging.py(no linear-light / pixelization variants).test_fit.pytests, per the prompt.Detailed implementation plan
Affected Repositories
autolens/weak/fit.py+ plotters + tests)scripts/weak/fit.pyexample, after library ships)Work Classification
Library, with workspace follow-up.
Branch Survey
dataset/interferometer/simple/*regenerated artifacts;scripts/weak/clean)Suggested branch:
feature/weak-fitWorktree root:
~/Code/PyAutoLabs-wt/weak-fit/(created later by/start_library)Implementation Steps
PyAutoLens/autolens/weak/fit.pymirroringautolens/imaging/fit_imaging.py:class FitWeaktaking(dataset, tracer).@property model_shear→LensCalc.from_tracer(tracer).shear_yx_2d_via_hessian_from(grid=dataset.positions).@property residual_map→dataset.shear_yx - model_shear(operates on the underlying(N, 2)storage; the[:, 0]=γ₂, [:, 1]=γ₁convention is component-symmetric for subtraction).@property chi_squared_map→ per-galaxy((residual / noise_map) ** 2).sum(axis=-1)(sum across the γ₁, γ₂ components).@property log_likelihood→ standard-0.5 * (chi_squared + noise_normalization). Nolog_evidence(no inversion).figure_of_meritreturnslog_likelihood.PyAutoLens/autolens/weak/plot/fit_weak_plots.py(lands on top of the newweak/plot/package shipped in step 2):plot_data_vs_model(fit, ax=None, ...)— two quivers overlaid: data (color="black") and model (color="red", alpha=0.6), both with the headlesspivot="middle", headwidth=0, headlength=0, headaxislength=0style established byplot_shear_yx_2d.plot_residuals(fit, ax=None, ...)— quiver ofresidual_mapcolour-coded by|residual|.plot_chi_squared_map(fit, ax=None, ...)— scalar-coloured scatter viaautoarray.plot.grid.plot_grid(same pattern asplot_ellipticities).subplot_fit_weak(fit, ...)— 2×2: data | model | overlay | chi-squared.apltviaautolens/plot/__init__.py.Unit tests —
PyAutoLens/test_autolens/weak/test_fit.py(~6 tests):test__model_shear_matches_simulator_for_no_noise—SimulatorShearYX(noise_sigma=0.0)round-trip throughFitWeakshould giveresidual_map ≈ 0.test__chi_squared_zero_for_perfect_fit.test__log_likelihood_against_hand_computed(4-galaxy deterministic dataset, hand-computed expected value).test__residual_map_shape((n_galaxies, 2)).test__log_likelihood_drops_for_wrong_model(perturb einstein_radius by 0.1, assert LL drops by ≥ 1).test__pickle_round_trip(matches theviz-subprocess-feasibilityprecedent —FitWeakmust round-trip cleanly for future JIT/subprocess viz).Plotter tests —
PyAutoLens/test_autolens/weak/plot/test_fit_weak_plots.py(~4 tests, one per fn, using theplot_patchfixture).Eyeball checkpoint — after the first 3
test_fit.pytests, pause and present them for user review before writing the rest. Per the prompt: "Have me eyeball a few unit tests so I can see they make sense."Key Files
PyAutoLens/autolens/weak/fit.pyPyAutoLens/autolens/weak/plot/fit_weak_plots.pyPyAutoLens/test_autolens/weak/test_fit.pyPyAutoLens/test_autolens/weak/plot/test_fit_weak_plots.pyPyAutoLens/autolens/weak/__init__.py— exportFitWeak.PyAutoLens/autolens/plot/__init__.py— add the 4 new plotter imports.Reused utilities (no need to re-implement)
LensCalc.from_tracer(...).shear_yx_2d_via_hessian_from(grid=...)—PyAutoGalaxy/autogalaxy/operate/lens_calc.py(the simulator's exact primitive).WeakDataset.{positions, shear_yx, noise_map}—PyAutoLens/autolens/weak/dataset.py:76.aplt.plot_shear_yx_2dstyling +subplot_save/tight_layout/conf_subplot_figsizefromautoarray.plot.utils(shipped in step 2).plot_patchfixture —test_autolens/conftest.py:27.Verification
pytest test_autolens/weak/ -v— all new tests pass alongside the 16 existing weak tests (5 plotter + 11 dataset/simulator).grep "^import jax\|^from jax" PyAutoLens/autolens/weak/fit.py PyAutoLens/autolens/weak/plot/fit_weak_plots.py→ empty.python autolens_workspace/scripts/weak/fit.pyruns end-to-end and writes the fit subplot PNG./ship_libraryfirst, then/ship_workspace.Original Prompt
Click to expand starting prompt
We are now going to add weak lensing Fit class
First, we need to create the fit.py module, so inspect @autolens_workspace/scripts/weak and
@PyAutoLens/autolens/imaging/fit modules . We are basically going to make everything weak does from here a "mirror" of
the imaging model API (and also interferoter.)
So, set up a FitWeak module which compute the same key quantities as other Fit objects, such as residuals, chi
squared and log likelihood. Put unit tests in following the imaging unit test stucture, baring in mind there should
be far fewer as there are no variants like linear light profiles of pixelizations. Have me eyeball a few unit tests
so I can see they make sense.
In a second phase, inspect @PyAutoLens/autolens/plot and set up the FitWeak weak_plots.py, you may need to do
some research on how best to plot these quantities, it may be we want to plot the dataset values via quiver on top
of the model ones.