Overview
Two earlier attempts to put zero_contour-based critical curves on the default visualization path were silently broken (2026-04-19 yaml flip revert, 2026-05-16 Euclid pipeline regression). A 2026-05-21 perf benchmark identified a third, independent issue: LensCalc._critical_curve_list_via_zero_contour rebuilds its closure + ZeroSolver on every call, busting the JIT cache and making every call cost ~10 s. With the closure cached, warm calls drop to ~66 ms — fast enough to be a default for any JIT'd likelihood function.
This task lands all three coupled fixes that the future Phase A config dispatch depends on: the perf bug, the broad-except that silently swallowed both prior failures, and the first __Visualization Sanity__ regression-net block.
Plan
- Cache
(f, solver) inside LensCalc keyed on (kind, pixel_scales, tol, max_newton) so subsequent _via_zero_contour_from() calls reuse JAX's compile cache.
- Tighten the broad
except Exception: in PyAutoLens/autolens/imaging/plot/fit_imaging_plots.py:52 so future regressions of this class fail loudly with a logged warning, not silently.
- Add the first
__Visualization Sanity__ block on autolens_workspace_test/scripts/imaging/modeling_visualization_jit.py — asserts non-empty critical curves, finite/positive Einstein radius, AND warm-call latency under 100 ms (regression net for the closure-cache-busting bug).
- Verify via
/smoke_test against euclid_strong_lens_modeling_pipeline that the pipeline still runs cleanly.
- NO config flip — that's the next sub-prompt (revised Phase A: context-aware dispatch).
- NO
z_projects/euclid edits — live science work, separate Phase B prompt targets the pipeline workspace.
Detailed implementation plan
Affected Repositories
- PyAutoGalaxy (primary — perf fix in
LensCalc)
- PyAutoLens (broad-except tighten)
- autolens_workspace_test (Visualization Sanity pilot block)
Work Classification
Library (then workspace follow-up bundled into the same PR pair)
Branch Survey
| Repository |
Current Branch |
Dirty? |
| ./PyAutoGalaxy |
main |
clean |
| ./PyAutoLens |
main |
clean |
| ./autolens_workspace_test |
main |
dirty — pre-existing drift (README + dataset/build/*), unrelated to this task |
Suggested branch: feature/fast-viz-zero-contour-perf
Worktree root: ~/Code/PyAutoLabs-wt/fast-viz-zero-contour-perf/ (created later by /start_library)
Implementation Steps
- PyAutoGalaxy
autogalaxy/operate/lens_calc.py — in _critical_curve_list_via_zero_contour (around line 1121), introduce self._zero_contour_cache: dict (lazy init) keyed on (kind, pixel_scales, tol, max_newton). On cache hit, reuse the stored (f, solver). Unit test: two calls with same params reuse same (f, solver) identity; different params produce distinct entries.
- PyAutoLens
autolens/imaging/plot/fit_imaging_plots.py:52 — replace bare except Exception: with except (ModuleNotFoundError, ValueError): (silent — known recoverable) + except Exception as exc: logger.warning(..., exc_info=True) (loud — anything else). Unit test re-broadens to confirm the WARNING log fires.
- autolens_workspace_test
scripts/imaging/modeling_visualization_jit.py — append a __Visualization Sanity__ prose block following the same style as the existing __Likelihood Sanity__ at line ~170. Three assertions: len(tangential_critical_curve_list) > 0, np.isfinite(er) and er > 0, and warm-call latency < 100 ms on the second call.
Key Files
PyAutoGalaxy/autogalaxy/operate/lens_calc.py:1121-1178 — perf cache fix
PyAutoLens/autolens/imaging/plot/fit_imaging_plots.py:19-53 — broad-except tighten
autolens_workspace_test/scripts/imaging/modeling_visualization_jit.py — Sanity block
Out of scope (deferred to follow-up sub-prompts)
- Config flip / context-aware dispatch (revised Phase A)
- Latent migration in
euclid_strong_lens_modeling_pipeline/util.py:491 (Phase B)
- IPython
update_display(fig, display_id=...) wiring (Phase C)
- Rollout of
__Visualization Sanity__ across other dataset types (Phase D)
Original Prompt
Click to expand starting prompt
See PyAutoPrompt/issued/fast_viz_zero_contour_perf_fix.md for the verbatim authored prompt — the implementation steps above already mirror it. Key reference points kept inline for issue-time visibility:
-
April 2026 revert (PyAutoGalaxy commit abd7b717): ZeroSolver raised inside model-fits and the exception was swallowed by the broad except Exception: at fit_imaging_plots.py:52 — critical curves silently vanished on HPC runs.
-
May 2026 revert (PyAutoFit PR #1280): same failure shape on Euclid pipeline; source-plane FITS files wrote all-zero, Einstein-radius posteriors collapsed to the full prior, nothing raised.
-
Perf benchmark (2026-05-21) on SIE + circular source:
| Method |
First call |
Warm call |
marching_squares |
32 ms |
32 ms |
zero_contour (current code) |
10300 ms |
10300 ms |
zero_contour (reused f / solver) |
10679 ms |
66 ms |
The closure cache-busting bug means every call rebuilds JAX's compile cache. Fix: cache (f, solver) on LensCalc keyed on (kind, pixel_scales, tol, max_newton). Warm calls drop to ~66 ms.
-
Out of scope (deferred sub-prompts): config flip, Euclid latent migration, IPython display wiring, Phase D rollout. The full out-of-scope list and reference memory entries live in the authored prompt file.
Overview
Two earlier attempts to put zero_contour-based critical curves on the default visualization path were silently broken (2026-04-19 yaml flip revert, 2026-05-16 Euclid pipeline regression). A 2026-05-21 perf benchmark identified a third, independent issue:
LensCalc._critical_curve_list_via_zero_contourrebuilds its closure + ZeroSolver on every call, busting the JIT cache and making every call cost ~10 s. With the closure cached, warm calls drop to ~66 ms — fast enough to be a default for any JIT'd likelihood function.This task lands all three coupled fixes that the future Phase A config dispatch depends on: the perf bug, the broad-except that silently swallowed both prior failures, and the first
__Visualization Sanity__regression-net block.Plan
(f, solver)insideLensCalckeyed on(kind, pixel_scales, tol, max_newton)so subsequent_via_zero_contour_from()calls reuse JAX's compile cache.except Exception:inPyAutoLens/autolens/imaging/plot/fit_imaging_plots.py:52so future regressions of this class fail loudly with a logged warning, not silently.__Visualization Sanity__block onautolens_workspace_test/scripts/imaging/modeling_visualization_jit.py— asserts non-empty critical curves, finite/positive Einstein radius, AND warm-call latency under 100 ms (regression net for the closure-cache-busting bug)./smoke_testagainsteuclid_strong_lens_modeling_pipelinethat the pipeline still runs cleanly.z_projects/euclidedits — live science work, separate Phase B prompt targets the pipeline workspace.Detailed implementation plan
Affected Repositories
LensCalc)Work Classification
Library (then workspace follow-up bundled into the same PR pair)
Branch Survey
Suggested branch:
feature/fast-viz-zero-contour-perfWorktree root:
~/Code/PyAutoLabs-wt/fast-viz-zero-contour-perf/(created later by/start_library)Implementation Steps
autogalaxy/operate/lens_calc.py— in_critical_curve_list_via_zero_contour(around line 1121), introduceself._zero_contour_cache: dict(lazy init) keyed on(kind, pixel_scales, tol, max_newton). On cache hit, reuse the stored(f, solver). Unit test: two calls with same params reuse same(f, solver)identity; different params produce distinct entries.autolens/imaging/plot/fit_imaging_plots.py:52— replace bareexcept Exception:withexcept (ModuleNotFoundError, ValueError):(silent — known recoverable) +except Exception as exc: logger.warning(..., exc_info=True)(loud — anything else). Unit test re-broadens to confirm the WARNING log fires.scripts/imaging/modeling_visualization_jit.py— append a__Visualization Sanity__prose block following the same style as the existing__Likelihood Sanity__at line ~170. Three assertions:len(tangential_critical_curve_list) > 0,np.isfinite(er) and er > 0, and warm-call latency< 100 mson the second call.Key Files
PyAutoGalaxy/autogalaxy/operate/lens_calc.py:1121-1178— perf cache fixPyAutoLens/autolens/imaging/plot/fit_imaging_plots.py:19-53— broad-except tightenautolens_workspace_test/scripts/imaging/modeling_visualization_jit.py— Sanity blockOut of scope (deferred to follow-up sub-prompts)
euclid_strong_lens_modeling_pipeline/util.py:491(Phase B)update_display(fig, display_id=...)wiring (Phase C)__Visualization Sanity__across other dataset types (Phase D)Original Prompt
Click to expand starting prompt
See
PyAutoPrompt/issued/fast_viz_zero_contour_perf_fix.mdfor the verbatim authored prompt — the implementation steps above already mirror it. Key reference points kept inline for issue-time visibility:April 2026 revert (PyAutoGalaxy commit
abd7b717):ZeroSolverraised inside model-fits and the exception was swallowed by the broadexcept Exception:atfit_imaging_plots.py:52— critical curves silently vanished on HPC runs.May 2026 revert (PyAutoFit PR #1280): same failure shape on Euclid pipeline; source-plane FITS files wrote all-zero, Einstein-radius posteriors collapsed to the full prior, nothing raised.
Perf benchmark (2026-05-21) on SIE + circular source:
marching_squareszero_contour(current code)zero_contour(reusedf/ solver)The closure cache-busting bug means every call rebuilds JAX's compile cache. Fix: cache
(f, solver)onLensCalckeyed on(kind, pixel_scales, tol, max_newton). Warm calls drop to ~66 ms.Out of scope (deferred sub-prompts): config flip, Euclid latent migration, IPython display wiring, Phase D rollout. The full out-of-scope list and reference memory entries live in the authored prompt file.