Overview
Stand up dedicated cluster functionality in autolens_workspace_test to stress-test the cluster likelihood function end-to-end. Today autolens_workspace/scripts/cluster/ has the production csv_api / simulator / modeling scripts (shipped via #187), but there's no test-workspace counterpart that perturbs the truth model and validates likelihood behaviour. This task adds eight deliverables: a paired csv_api.py + simulator.py, the existing visualization_cluster.py moved to its permanent home, two likelihood sanity-check scripts (source-plane + image-plane chi², multi-redshift sensitivity), an imaging-mode counterpart, end-to-end visualization, and two new JAX likelihood functions with numerical assertions.
The goal is explicit: find bugs, edge cases, and anything wrong in the cluster likelihood path. Surprises become follow-up issues; this PR ships the test scaffolding that surfaces them.
Plan
- New
scripts/cluster/csv_api.py paired to autolens_workspace's csv_api guide. Includes the full galaxy population — main + (optional) extra + scaling + source — so workspace_test can exercise every tier the cluster code path supports.
- New
scripts/cluster/simulator.py that loads the model CSVs csv_api.py produces and emits the full dataset (data.fits, point_datasets.csv, source-light CSV, scaling_galaxies.csv, etc.) into dataset/cluster/<name>/.
- Move
scripts/imaging/visualization_cluster.py → scripts/cluster/visualization.py, run against the new simulator outputs.
- New
scripts/cluster/likelihood_sanity.py: take the truth mass model from mass.csv, perturb each numeric mass parameter by 0.1%, 1%, 5%, and assert:
- At 0% perturbation: chi² ≈ 0 for both
FitPositionsSource (source-plane) and FitPositionsImagePair (image-plane) likelihoods.
- Monotonic log-likelihood decrease as perturbation grows for both chi² flavours.
- New
scripts/cluster/likelihood_redshift_sensitivity.py: perturb the source redshifts (small multi-plane displacements) and assert the max log-likelihood lands at the truth redshift; small redshift errors produce small likelihood decreases.
- New
scripts/cluster/likelihood_imaging.py: same perturbation tests but with AnalysisImaging + light profiles (the truth values from light.csv) producing arcs. Assert the likelihood patterns hold for the imaging-mode likelihood too.
- Visualization for the redshift-sensitivity and imaging-mode scripts: image-plane positions overlaid on data, residual maps of the light-profile fit, and any other plots that help a human spot pathology.
- New
scripts/jax_likelihood_functions/cluster/single_plane.py + multi_plane.py: JAX-jit'd cluster likelihood functions with numerical assertions matching the perturbation test results above. Modelled on the existing scripts/jax_likelihood_functions/point_source/{image_plane,source_plane,point}.py.
Detailed implementation plan
Affected Repositories
- autolens_workspace_test (primary, single repo)
Work Classification
Workspace.
Branch Survey
| Repository |
Current Branch |
Dirty? |
| autolens_workspace_test |
main |
clean |
Suggested branch: feature/cluster-test-workspace
Worktree root: ~/Code/PyAutoLabs-wt/cluster-test-workspace/
Implementation Steps
scripts/cluster/csv_api.py — port autolens_workspace/scripts/cluster/csv_api.py's round-trip demonstration into a workspace_test-style guide. Same three family CSVs (mass / light / point) plus scaling_galaxies. The "extras" tier (per-galaxy individually modelled, NOT shared scaling relation) is supported by the same mass.csv schema — include a small example extra_0 row in this guide to demonstrate.
scripts/cluster/simulator.py — mirror autolens_workspace/scripts/cluster/simulator.py under workspace_test naming conventions (deterministic seed, outputs in dataset/cluster/test/). Drop the heavy JAX/PointSolver setup if not needed for the downstream tests; the simulator should always be runnable under PYAUTO_TEST_MODE=2.
- Move
scripts/imaging/visualization_cluster.py → scripts/cluster/visualization.py. Update the dataset_path to point at the new test dataset. Verify the three reference PNGs (overlaid positions, per-source grid, critical curves) still render.
scripts/cluster/likelihood_sanity.py — load the truth mass model from mass.csv. For each (galaxy_name, param_name) in the dPIE / NFW parameter set, perturb by ε ∈ {0, 0.001, 0.01, 0.05}, build a Tracer, evaluate both FitPositionsSource and FitPositionsImagePair against the simulator's point_datasets.csv. Assert chi² ≈ 0 at ε=0 (numerical tolerance ~1e-3); assert log-likelihood is monotone decreasing in |ε|.
scripts/cluster/likelihood_redshift_sensitivity.py — perturb each source's redshift by small multi-plane displacements (δz ∈ {0, 0.01, 0.05, 0.1}). Build the same fit objects, assert the truth redshift maximises log-likelihood and that small δz produces small likelihood decrements.
scripts/cluster/likelihood_imaging.py — same perturbation tests but with al.AnalysisImaging + the light profiles from light.csv. The truth arcs are simulated from data.fits; perturbing the mass profile should produce visibly wrong arcs and a worse figure_of_merit. Assert the imaging-mode log-likelihood pattern matches the point-source pattern.
- Visualization — figures next to each likelihood script: chi²-vs-perturbation curves; image-plane positions overlaid on data for the perturbed runs; residual maps for the imaging fit. Save into
dataset/cluster/test/visualization/.
scripts/jax_likelihood_functions/cluster/single_plane.py + multi_plane.py — JAX-jit'd cluster likelihood functions. Model on the existing scripts/jax_likelihood_functions/point_source/image_plane.py (single-source, single-plane) and the multi-source / multi-plane case respectively. Each carries a numerical assertion block referencing the perturbation results from step 4/5 — e.g. assert chi_squared < tolerance for epsilon == 0. Verify both backends (numpy + JAX) produce matching values.
Key Files
- New:
scripts/cluster/{csv_api.py, simulator.py, visualization.py, likelihood_sanity.py, likelihood_redshift_sensitivity.py, likelihood_imaging.py}
- New:
scripts/jax_likelihood_functions/cluster/{__init__.py, single_plane.py, multi_plane.py}
- Move:
scripts/imaging/visualization_cluster.py → scripts/cluster/visualization.py
- Reference (read-only):
scripts/jax_likelihood_functions/point_source/{image_plane.py, source_plane.py, point.py}
- Reference (read-only):
autolens_workspace/scripts/cluster/{csv_api.py, simulator.py, modeling.py}
Smoke Tests
Add new scripts to smoke_tests.txt once they reliably pass under PYAUTO_TEST_MODE=2. Likelihood scripts are pure-Python (no Nautilus search) so they should smoke quickly.
Open Questions (resolve during implementation)
- Should the "extras" tier (individually modelled middle tier) be exercised explicitly in
csv_api.py, or is the existing main / scaling / source surface sufficient for cluster tests? The user's prompt mentions "mains, extra, scaling, source" so probably include an extra_0 example in the guide and a row in mass.csv.
- How to encode source redshift in a way
likelihood_redshift_sensitivity.py can override at fit time without re-running the simulator. Likely just constructs perturbed PointDataset copies with mutated redshift attrs.
- For the imaging-mode test (step 6), how much of the modeling pipeline does the script actually need? Just
analysis.log_likelihood_function(instance) on perturbed instances, probably — no search.
Stress-Test Mandate
Per the prompt: "be persistent, be nit-picking, look for obvious issues with our implementation." Bugs found during implementation become separate follow-up issues; the test scaffolding ships regardless.
Original Prompt
Click to expand starting prompt
Lets set up autolens_workspace_test to have dediciated cluster functionality, in particular:
- Write a scripts/cluster/csv_api.py, which follows the autolens_workspace/scripts/cluster/csv_api.py and therefore includes a full suite of mains, extra, scaling, source galaxies/
- Write a scripts/cluster/simulator.py, which uses the autolens_workspace/scripts/cluster/simulator.py and uses the csv exa,ple but outputs its point dataset csv, source light profile .csv and all other data.
- move scripts/imaging/visualization_cluster.py to scripts/cluster/visualization.py, run against the simulator.py output.
- Set up a likelihood sanity check, which uses the simulated cluster (will need a scritps/cluster/simulator file) and the does sanity checks on its source plane chi-squared (using Point objects with FitPositionsSource) and image plane chi squared (using FitPositionsImagePair). I think the sensible way to do this is to perturb the mass model parameter inputs to the simulator script by a small amount (e.g. 0.1%) and medium amount (e.g. 1%) and large amount (e.g. 5%) and make sure the chi-squared is near zero for the first and that ther log likelihood decreases. The point is that we havent really done full end to end tests tthat the likelihood function gives sensible results to build up confidence cluster modeling is ok. This should ue the simulator made in the test workspace.
- Build on 3. to do an example where the simulated cluster has sources at different redshifts, and we only get the maximum likelihood soltion when the redshifts are right, and test therefore for that we must get redshifts right. For small changes in redshift (that are still multiplane) make sure we get small likelihood decreases.
- Can you pair the example in 4 with fits which uses Imaging and light profiles (the values used in the simulator to make images with arfcs and again make sure the likelihood patterns hold.
- Produce good visualization of 4 and 5 so we can see where the positions end up compared to the data and what the residuals of the light profiles for the cluster set up look like.
- Now make scripts/jax_likelihood_functions/cluster/single_plane.py and multi_plane.py that has numerical assertions against it using the above test case.
The whole idea behind this task is to stress test cluster likelihood function as much as possible and look for bugs, edge cases and really anything that can (and will go wrong). So be persistent, be nit-picking and look for obvious issues with our implementation.
Overview
Stand up dedicated cluster functionality in
autolens_workspace_testto stress-test the cluster likelihood function end-to-end. Todayautolens_workspace/scripts/cluster/has the production csv_api / simulator / modeling scripts (shipped via #187), but there's no test-workspace counterpart that perturbs the truth model and validates likelihood behaviour. This task adds eight deliverables: a pairedcsv_api.py+simulator.py, the existingvisualization_cluster.pymoved to its permanent home, two likelihood sanity-check scripts (source-plane + image-plane chi², multi-redshift sensitivity), an imaging-mode counterpart, end-to-end visualization, and two new JAX likelihood functions with numerical assertions.The goal is explicit: find bugs, edge cases, and anything wrong in the cluster likelihood path. Surprises become follow-up issues; this PR ships the test scaffolding that surfaces them.
Plan
scripts/cluster/csv_api.pypaired to autolens_workspace's csv_api guide. Includes the full galaxy population — main + (optional) extra + scaling + source — so workspace_test can exercise every tier the cluster code path supports.scripts/cluster/simulator.pythat loads the model CSVscsv_api.pyproduces and emits the full dataset (data.fits, point_datasets.csv, source-light CSV, scaling_galaxies.csv, etc.) intodataset/cluster/<name>/.scripts/imaging/visualization_cluster.py→scripts/cluster/visualization.py, run against the new simulator outputs.scripts/cluster/likelihood_sanity.py: take the truth mass model frommass.csv, perturb each numeric mass parameter by 0.1%, 1%, 5%, and assert:FitPositionsSource(source-plane) andFitPositionsImagePair(image-plane) likelihoods.scripts/cluster/likelihood_redshift_sensitivity.py: perturb the source redshifts (small multi-plane displacements) and assert the max log-likelihood lands at the truth redshift; small redshift errors produce small likelihood decreases.scripts/cluster/likelihood_imaging.py: same perturbation tests but withAnalysisImaging+ light profiles (the truth values fromlight.csv) producing arcs. Assert the likelihood patterns hold for the imaging-mode likelihood too.scripts/jax_likelihood_functions/cluster/single_plane.py+multi_plane.py: JAX-jit'd cluster likelihood functions with numerical assertions matching the perturbation test results above. Modelled on the existingscripts/jax_likelihood_functions/point_source/{image_plane,source_plane,point}.py.Detailed implementation plan
Affected Repositories
Work Classification
Workspace.
Branch Survey
Suggested branch:
feature/cluster-test-workspaceWorktree root:
~/Code/PyAutoLabs-wt/cluster-test-workspace/Implementation Steps
scripts/cluster/csv_api.py— portautolens_workspace/scripts/cluster/csv_api.py's round-trip demonstration into a workspace_test-style guide. Same three family CSVs (mass / light / point) plus scaling_galaxies. The "extras" tier (per-galaxy individually modelled, NOT shared scaling relation) is supported by the same mass.csv schema — include a small exampleextra_0row in this guide to demonstrate.scripts/cluster/simulator.py— mirrorautolens_workspace/scripts/cluster/simulator.pyunder workspace_test naming conventions (deterministic seed, outputs indataset/cluster/test/). Drop the heavy JAX/PointSolver setup if not needed for the downstream tests; the simulator should always be runnable underPYAUTO_TEST_MODE=2.scripts/imaging/visualization_cluster.py→scripts/cluster/visualization.py. Update thedataset_pathto point at the new test dataset. Verify the three reference PNGs (overlaid positions, per-source grid, critical curves) still render.scripts/cluster/likelihood_sanity.py— load the truth mass model frommass.csv. For each(galaxy_name, param_name)in the dPIE / NFW parameter set, perturb by ε ∈ {0, 0.001, 0.01, 0.05}, build aTracer, evaluate bothFitPositionsSourceandFitPositionsImagePairagainst the simulator'spoint_datasets.csv. Assert chi² ≈ 0 at ε=0 (numerical tolerance ~1e-3); assert log-likelihood is monotone decreasing in |ε|.scripts/cluster/likelihood_redshift_sensitivity.py— perturb each source's redshift by small multi-plane displacements (δz ∈ {0, 0.01, 0.05, 0.1}). Build the same fit objects, assert the truth redshift maximises log-likelihood and that small δz produces small likelihood decrements.scripts/cluster/likelihood_imaging.py— same perturbation tests but withal.AnalysisImaging+ the light profiles fromlight.csv. The truth arcs are simulated fromdata.fits; perturbing the mass profile should produce visibly wrong arcs and a worsefigure_of_merit. Assert the imaging-mode log-likelihood pattern matches the point-source pattern.dataset/cluster/test/visualization/.scripts/jax_likelihood_functions/cluster/single_plane.py+multi_plane.py— JAX-jit'd cluster likelihood functions. Model on the existingscripts/jax_likelihood_functions/point_source/image_plane.py(single-source, single-plane) and the multi-source / multi-plane case respectively. Each carries a numerical assertion block referencing the perturbation results from step 4/5 — e.g.assert chi_squared < tolerance for epsilon == 0. Verify both backends (numpy + JAX) produce matching values.Key Files
scripts/cluster/{csv_api.py, simulator.py, visualization.py, likelihood_sanity.py, likelihood_redshift_sensitivity.py, likelihood_imaging.py}scripts/jax_likelihood_functions/cluster/{__init__.py, single_plane.py, multi_plane.py}scripts/imaging/visualization_cluster.py→scripts/cluster/visualization.pyscripts/jax_likelihood_functions/point_source/{image_plane.py, source_plane.py, point.py}autolens_workspace/scripts/cluster/{csv_api.py, simulator.py, modeling.py}Smoke Tests
Add new scripts to
smoke_tests.txtonce they reliably pass underPYAUTO_TEST_MODE=2. Likelihood scripts are pure-Python (no Nautilus search) so they should smoke quickly.Open Questions (resolve during implementation)
csv_api.py, or is the existing main / scaling / source surface sufficient for cluster tests? The user's prompt mentions "mains, extra, scaling, source" so probably include anextra_0example in the guide and a row inmass.csv.likelihood_redshift_sensitivity.pycan override at fit time without re-running the simulator. Likely just constructs perturbedPointDatasetcopies with mutatedredshiftattrs.analysis.log_likelihood_function(instance)on perturbed instances, probably — no search.Stress-Test Mandate
Per the prompt: "be persistent, be nit-picking, look for obvious issues with our implementation." Bugs found during implementation become separate follow-up issues; the test scaffolding ships regardless.
Original Prompt
Click to expand starting prompt
Lets set up autolens_workspace_test to have dediciated cluster functionality, in particular:
The whole idea behind this task is to stress test cluster likelihood function as much as possible and look for bugs, edge cases and really anything that can (and will go wrong). So be persistent, be nit-picking and look for obvious issues with our implementation.