Overview
Add the numpy-baseline reference scripts under scripts/jax_likelihood_functions/ellipse/ for ellipse likelihood fitting. Step 2 of 7 in the ellipse_fitting_jax feature decomposition (see PyAutoPrompt/z_features/ellipse_fitting_jax.md). The scripts print every component of AnalysisEllipse.fit_list_from(...).figure_of_merit on the numpy path so the JAX round-trip added in prompt 7 has a stable target to assert against with np.testing.assert_allclose(rtol=1e-4).
Plan
- Add
scripts/jax_likelihood_functions/ellipse/__init__.py (empty package marker).
- Add a small dedicated simulator (50x50 pixels, single Sersic galaxy at
0.2"/px) writing into dataset/ellipse/jax_test/. Independent of the imaging jax_test simulator so the ellipse reference numbers don't drift if imaging/simulator.py is later retuned.
- Add
fit.py: load (or auto-simulate) the dataset, build a single fixed Ellipse model, instantiate AnalysisEllipse(dataset=...), print log_likelihood, chi_squared, noise_normalization, figure_of_merit per FitEllipse. Leave a clearly-marked TODO(7_analysis_ellipse_jax.md) block at the end where the JIT round-trip will go.
- Add
multipoles.py: same shape as fit.py but adds an EllipseMultipole(m=4) per ellipse. Locks the multipole code path which has its own JAX-incompatible while loops that prompt 5 will rewrite.
- All four scripts numpy-only —
import jax must not appear anywhere.
- The scripts run cleanly under
bash run_all_scripts.sh and the printed numbers are reproducible to ~1e-6.
Detailed implementation plan
Affected Repositories
- autogalaxy_workspace_test (primary)
- PyAutoGalaxy (read-only, for understanding
AnalysisEllipse)
- autogalaxy_workspace (read-only, for
scripts/ellipse/modeling.py composition shape)
Work Classification
Workspace
Branch Survey
| Repository |
Current Branch |
Dirty? |
| ./autogalaxy_workspace_test |
main |
clean |
Suggested branch: feature/ellipse-jax-likelihood-tests
Worktree root: ~/Code/PyAutoLabs-wt/ellipse-jax-likelihood-tests/ (created later by /start_workspace)
Implementation Steps
- Create
scripts/jax_likelihood_functions/ellipse/__init__.py (empty).
- Create
scripts/jax_likelihood_functions/ellipse/simulator.py. Patterned on scripts/jax_likelihood_functions/imaging/simulator.py but trimmed: Grid2D.uniform(shape_native=(50, 50), pixel_scales=0.2), single Galaxy with a Sersic bulge centered at (0, 0), noise_seed=1 for reproducibility, writes data.fits, psf.fits (ignored by ellipse but kept for Imaging.from_fits symmetry), noise_map.fits to dataset/ellipse/jax_test/.
- Create
scripts/jax_likelihood_functions/ellipse/fit.py:
- Auto-simulate the dataset via
subprocess.run([sys.executable, "scripts/jax_likelihood_functions/ellipse/simulator.py"]) when data.fits is missing.
ag.Imaging.from_fits(...) then apply a generous circular mask (radius=3.0).
- Compose:
ellipse = af.Model(ag.Ellipse) with centre.{0,1} uniform priors, ell_comps.{0,1} uniform priors, major_axis=0.5 fixed. Wrap as model = af.Collection(ellipses=af.Collection(ellipse_0=ellipse)) to match the prior used in the visualization test.
analysis = ag.AnalysisEllipse(dataset=dataset) (today defaults to use_jax=False).
instance = model.instance_from_prior_medians(), then fit_list = analysis.fit_list_from(instance=instance). Print log_likelihood, chi_squared, noise_normalization, figure_of_merit per fit and the aggregate.
- Final placeholder block:
# TODO(7_analysis_ellipse_jax.md): jax.jit(analysis.fit_from) round-trip + np.testing.assert_allclose(rtol=1e-4) against numpy reference above.
- Create
scripts/jax_likelihood_functions/ellipse/multipoles.py: identical to fit.py but the model has multipoles=af.Collection(ellipse_0=af.Collection(m4=af.Model(ag.EllipseMultipole, m=4, multipole_comps=(0.05, 0.0)))). Same TODO placeholder.
- Use the workspace docstring style (
"""...""" blocks, __Section Name__ headers). No # comments for prose.
- Numpy-only — no
import jax, no xp=jnp. The script imports must match fit.py from the visualization PR: import autofit as af, import autogalaxy as ag, plus numpy as np only if needed for printing reference numbers.
Testing Approach
- Local:
python scripts/jax_likelihood_functions/ellipse/simulator.py then python scripts/jax_likelihood_functions/ellipse/fit.py and python scripts/jax_likelihood_functions/ellipse/multipoles.py. All three exit 0 and print stable numbers.
- Reproducibility: run
fit.py twice, confirm chi_squared matches byte-for-byte (numpy simulator uses noise_seed=1).
- Smoke:
bash run_all_scripts.sh picks the new scripts up automatically (no smoke_tests.txt registration needed yet; mirroring the imaging precedent which is also in smoke_tests.txt — flag in PR if user wants it added).
Key Files
scripts/jax_likelihood_functions/ellipse/__init__.py — new.
scripts/jax_likelihood_functions/ellipse/simulator.py — new, dedicated small-grid Sersic dataset.
scripts/jax_likelihood_functions/ellipse/fit.py — new, numpy reference + TODO placeholder.
scripts/jax_likelihood_functions/ellipse/multipoles.py — new, numpy reference for the multipole path + TODO placeholder.
scripts/jax_likelihood_functions/imaging/lp.py — read-only reference for the imaging pattern this mirrors.
Original Prompt
Click to expand starting prompt
Step 2 of the ellipse-JAX series. Step 1 (1_workspace_visualization.md) added the visualization integration test. This prompt adds the likelihood-function integration tests, still on the numpy path. They lock in the reference numbers we'll later assert against once the JAX path lands in 7_analysis_ellipse_jax.md.
The pattern to mirror is @autogalaxy_workspace_test/scripts/jax_likelihood_functions/imaging/lp.py, which (a) auto-simulates a small dataset on first run, (b) builds a model + analysis, (c) computes a baseline log_likelihood on the numpy path, and (d) compares the JIT path with np.testing.assert_allclose(rtol=1e-4). For this prompt, only build steps (a)-(c) — leave a # TODO(7_analysis_ellipse_jax.md) placeholder where the JIT comparison will go.
Please:
-
Add @autogalaxy_workspace_test/scripts/jax_likelihood_functions/ellipse/__init__.py.
-
Add @autogalaxy_workspace_test/scripts/jax_likelihood_functions/ellipse/simulator.py modelled on @autogalaxy_workspace_test/scripts/jax_likelihood_functions/imaging/simulator.py — small grid (e.g. shape_native=(50, 50), pixel_scales=0.2), single Sersic galaxy, written into dataset/ellipse/jax_test/.
-
Add @autogalaxy_workspace_test/scripts/jax_likelihood_functions/ellipse/fit.py:
- Load (or auto-simulate via
simulator.py) the dataset.
- Build an
af.Collection(ellipses=af.Collection(ellipse_0=af.Model(ag.Ellipse, major_axis=...)))-style model (consult @autogalaxy_workspace/scripts/ellipse/modeling.py for the exact composition shape).
- Build
analysis = ag.AnalysisEllipse(dataset=dataset) (today this defaults to use_jax=False).
- Compute
fit_np = analysis.fit_list_from(instance=model.instance_from_prior_medians()) and print every component — log_likelihood, chi_squared, noise_normalization, figure_of_merit — to capture the reference numbers.
- Leave a
# TODO(7_analysis_ellipse_jax.md): jax.jit(analysis.fit_from) round-trip placeholder block at the end.
-
Add @autogalaxy_workspace_test/scripts/jax_likelihood_functions/ellipse/multipoles.py: same as fit.py but with multipole_list=[ag.EllipseMultipole(m=4, multipole_comps=(0.05, 0.0))] per ellipse. This locks in the multipole code path which has its own JAX-incompatible while loops in EllipseMultipole.get_shape_angle (handled in prompt 5).
-
Use the workspace docstring style throughout (see prompt 1 for reference).
-
Test bar: both scripts run cleanly under bash run_all_scripts.sh and the printed reference numbers are stable to ~1e-6 across runs. No JAX imports yet — import jax should not appear in any of these files.
Overview
Add the numpy-baseline reference scripts under
scripts/jax_likelihood_functions/ellipse/for ellipse likelihood fitting. Step 2 of 7 in theellipse_fitting_jaxfeature decomposition (seePyAutoPrompt/z_features/ellipse_fitting_jax.md). The scripts print every component ofAnalysisEllipse.fit_list_from(...).figure_of_meriton the numpy path so the JAX round-trip added in prompt 7 has a stable target to assert against withnp.testing.assert_allclose(rtol=1e-4).Plan
scripts/jax_likelihood_functions/ellipse/__init__.py(empty package marker).0.2"/px) writing intodataset/ellipse/jax_test/. Independent of the imagingjax_testsimulator so the ellipse reference numbers don't drift ifimaging/simulator.pyis later retuned.fit.py: load (or auto-simulate) the dataset, build a single fixedEllipsemodel, instantiateAnalysisEllipse(dataset=...), printlog_likelihood,chi_squared,noise_normalization,figure_of_meritperFitEllipse. Leave a clearly-markedTODO(7_analysis_ellipse_jax.md)block at the end where the JIT round-trip will go.multipoles.py: same shape asfit.pybut adds anEllipseMultipole(m=4)per ellipse. Locks the multipole code path which has its own JAX-incompatiblewhileloops that prompt 5 will rewrite.import jaxmust not appear anywhere.bash run_all_scripts.shand the printed numbers are reproducible to ~1e-6.Detailed implementation plan
Affected Repositories
AnalysisEllipse)scripts/ellipse/modeling.pycomposition shape)Work Classification
Workspace
Branch Survey
Suggested branch:
feature/ellipse-jax-likelihood-testsWorktree root:
~/Code/PyAutoLabs-wt/ellipse-jax-likelihood-tests/(created later by/start_workspace)Implementation Steps
scripts/jax_likelihood_functions/ellipse/__init__.py(empty).scripts/jax_likelihood_functions/ellipse/simulator.py. Patterned onscripts/jax_likelihood_functions/imaging/simulator.pybut trimmed:Grid2D.uniform(shape_native=(50, 50), pixel_scales=0.2), singleGalaxywith aSersicbulge centered at(0, 0),noise_seed=1for reproducibility, writesdata.fits,psf.fits(ignored by ellipse but kept forImaging.from_fitssymmetry),noise_map.fitstodataset/ellipse/jax_test/.scripts/jax_likelihood_functions/ellipse/fit.py:subprocess.run([sys.executable, "scripts/jax_likelihood_functions/ellipse/simulator.py"])whendata.fitsis missing.ag.Imaging.from_fits(...)then apply a generous circular mask (radius=3.0).ellipse = af.Model(ag.Ellipse)withcentre.{0,1}uniform priors,ell_comps.{0,1}uniform priors,major_axis=0.5fixed. Wrap asmodel = af.Collection(ellipses=af.Collection(ellipse_0=ellipse))to match the prior used in the visualization test.analysis = ag.AnalysisEllipse(dataset=dataset)(today defaults touse_jax=False).instance = model.instance_from_prior_medians(), thenfit_list = analysis.fit_list_from(instance=instance). Printlog_likelihood,chi_squared,noise_normalization,figure_of_meritper fit and the aggregate.# TODO(7_analysis_ellipse_jax.md): jax.jit(analysis.fit_from) round-trip + np.testing.assert_allclose(rtol=1e-4) against numpy reference above.scripts/jax_likelihood_functions/ellipse/multipoles.py: identical tofit.pybut the model hasmultipoles=af.Collection(ellipse_0=af.Collection(m4=af.Model(ag.EllipseMultipole, m=4, multipole_comps=(0.05, 0.0)))). Same TODO placeholder."""..."""blocks,__Section Name__headers). No#comments for prose.import jax, noxp=jnp. The script imports must matchfit.pyfrom the visualization PR:import autofit as af,import autogalaxy as ag, plusnumpy as nponly if needed for printing reference numbers.Testing Approach
python scripts/jax_likelihood_functions/ellipse/simulator.pythenpython scripts/jax_likelihood_functions/ellipse/fit.pyandpython scripts/jax_likelihood_functions/ellipse/multipoles.py. All three exit 0 and print stable numbers.fit.pytwice, confirmchi_squaredmatches byte-for-byte (numpy simulator usesnoise_seed=1).bash run_all_scripts.shpicks the new scripts up automatically (nosmoke_tests.txtregistration needed yet; mirroring the imaging precedent which is also in smoke_tests.txt — flag in PR if user wants it added).Key Files
scripts/jax_likelihood_functions/ellipse/__init__.py— new.scripts/jax_likelihood_functions/ellipse/simulator.py— new, dedicated small-grid Sersic dataset.scripts/jax_likelihood_functions/ellipse/fit.py— new, numpy reference + TODO placeholder.scripts/jax_likelihood_functions/ellipse/multipoles.py— new, numpy reference for the multipole path + TODO placeholder.scripts/jax_likelihood_functions/imaging/lp.py— read-only reference for the imaging pattern this mirrors.Original Prompt
Click to expand starting prompt
Step 2 of the ellipse-JAX series. Step 1 (
1_workspace_visualization.md) added the visualization integration test. This prompt adds the likelihood-function integration tests, still on the numpy path. They lock in the reference numbers we'll later assert against once the JAX path lands in7_analysis_ellipse_jax.md.The pattern to mirror is
@autogalaxy_workspace_test/scripts/jax_likelihood_functions/imaging/lp.py, which (a) auto-simulates a small dataset on first run, (b) builds a model + analysis, (c) computes a baselinelog_likelihoodon the numpy path, and (d) compares the JIT path withnp.testing.assert_allclose(rtol=1e-4). For this prompt, only build steps (a)-(c) — leave a# TODO(7_analysis_ellipse_jax.md)placeholder where the JIT comparison will go.Please:
Add
@autogalaxy_workspace_test/scripts/jax_likelihood_functions/ellipse/__init__.py.Add
@autogalaxy_workspace_test/scripts/jax_likelihood_functions/ellipse/simulator.pymodelled on@autogalaxy_workspace_test/scripts/jax_likelihood_functions/imaging/simulator.py— small grid (e.g. shape_native=(50, 50), pixel_scales=0.2), single Sersic galaxy, written intodataset/ellipse/jax_test/.Add
@autogalaxy_workspace_test/scripts/jax_likelihood_functions/ellipse/fit.py:simulator.py) the dataset.af.Collection(ellipses=af.Collection(ellipse_0=af.Model(ag.Ellipse, major_axis=...)))-style model (consult@autogalaxy_workspace/scripts/ellipse/modeling.pyfor the exact composition shape).analysis = ag.AnalysisEllipse(dataset=dataset)(today this defaults touse_jax=False).fit_np = analysis.fit_list_from(instance=model.instance_from_prior_medians())and print every component —log_likelihood,chi_squared,noise_normalization,figure_of_merit— to capture the reference numbers.# TODO(7_analysis_ellipse_jax.md): jax.jit(analysis.fit_from) round-tripplaceholder block at the end.Add
@autogalaxy_workspace_test/scripts/jax_likelihood_functions/ellipse/multipoles.py: same asfit.pybut withmultipole_list=[ag.EllipseMultipole(m=4, multipole_comps=(0.05, 0.0))]per ellipse. This locks in the multipole code path which has its own JAX-incompatiblewhileloops inEllipseMultipole.get_shape_angle(handled in prompt 5).Use the workspace docstring style throughout (see prompt 1 for reference).
Test bar: both scripts run cleanly under
bash run_all_scripts.shand the printed reference numbers are stable to ~1e-6 across runs. No JAX imports yet —import jaxshould not appear in any of these files.