diff --git a/active.md b/active.md index fb25a1653..85e4e11c7 100644 --- a/active.md +++ b/active.md @@ -1,5 +1,20 @@ # Active Tasks +## point-solver-magnification-plane-redshift +- issue: https://github.com/PyAutoLabs/PyAutoLens/issues/480 (filed 2026-04-28, pre-existing) +- issued: 2026-08-27 +- prompt: active/point_solver_magnification_filter_ignores_plane_redshift.md +- status: library-dev +- branch: claude/point-source-json-regime-tmk7la (@PyAutoLens) +- repos: + - PyAutoLens +- worktree-none: remote web session — no task worktree; the container checkout at + /home/user/pyautolens is the working tree and is discarded with the session. +- note: no new issue was opened — PyAutoLens#480 already describes this bug, so /create_issue + was deliberately skipped. Unblocks the two multiple_sources scripts in @autolens_workspace + config/build/no_run.yaml and the parked + draft/bug/pyautolens/point_source_json_datasets_record_no_regime.md. + ## transformed-message-factor-gradient-unpack - issue: https://github.com/PyAutoLabs/PyAutoFit/issues/1501 (issued 2026-08-19) - issued: 2026-08-19 diff --git a/active/point_solver_magnification_filter_ignores_plane_redshift.md b/active/point_solver_magnification_filter_ignores_plane_redshift.md new file mode 100644 index 000000000..5171192eb --- /dev/null +++ b/active/point_solver_magnification_filter_ignores_plane_redshift.md @@ -0,0 +1,175 @@ +# PointSolver magnification filter ignores plane_redshift + +Type: bug +Target: pyautolens +Repos: +- @PyAutoLens +Difficulty: small +Autonomy: supervised +Priority: high +Status: formalised +Filed: 2026-08-27 +Issue: https://github.com/PyAutoLabs/PyAutoLens/issues/480 + +GitHub issue **PyAutoLens#480** already exists (filed 2026-04-28 by Jammy2211, +untouched since). This prompt is the Mind-side record for fixing it; no new issue +should be opened. + +## Why now + +#480 is the gate on two other pieces of work, which is what surfaced it: + +- `point_source/features/multiple_sources/{simulator,modeling}` are excluded from + harness execution in @autolens_workspace `config/build/no_run.yaml:41-42`, + named as blocked by #480. +- `draft/bug/pyautolens/point_source_json_datasets_record_no_regime.md` is parked + behind exactly that exclusion — its re-check log names #480 closing as the + single trigger for doing the work. + +So this is one fix that clears a workspace exclusion, two skipped scripts and a +parked Mind task. + +## The bug + +`PointSolver.solve(..., plane_redshift=z)` returns 0 positions whenever `z` is an +**intermediate** plane of a multi-plane tracer. The triangle search is fine — +`magnification_threshold=0.0` recovers the candidates — only the magnification +post-filter rejects them, because it measures magnification at the wrong plane. + +Two methods on the same class disagree about which plane is being solved for: + +- `autolens/point/solver/shape_solver.py:224` — `_plane_grid` **honours** + `plane_redshift`: `plane_index_via_redshift_from(redshift=...)`, then + `tracer.deflections_between_planes_from(grid=..., plane_i=0, plane_j=plane_index)`. +- `autolens/point/solver/shape_solver.py:322` — `_filter_low_magnification` takes + no `plane_redshift` at all. It builds `ag.LensCalc.from_mass_obj(tracer)`, whose + deflection callable is the whole tracer, i.e. the **last** plane. + +The search therefore solves to plane *j* and the filter judges those candidates +as if they were at plane *-1*. In the issue's repro (z = 0.5 / 1.0 / 2.0, solving +for z=1.0) the candidates come back at magnification ~1e-3–1e-5 — correct for a +z=2.0 source, meaningless for the z=1.0 one — and the 0.1 threshold discards +every one of them. + +`point_solver.py:242` is the call site that drops the argument on the floor: it +has `plane_redshift` in scope (it passed it to `solve_triangles` seven lines +earlier) and does not forward it. + +## The fix already exists as API — do not write new physics + +@PyAutoGalaxy `autogalaxy/operate/lens_calc.py:165` already provides: + +```python +LensCalc.from_tracer(tracer, use_multi_plane=True, plane_i=0, plane_j=-1) +``` + +which binds `tracer.deflections_between_planes_from` with those plane indices via +`functools.partial` — precisely the callable `_filter_low_magnification` should +be measuring against. The solver is simply calling the wrong constructor. + +**It is a provable no-op for the default case**, which is what #480 asks for. +`autolens/lens/tracer.py:879-881`: + +```python +if self.total_planes > 1: + return self.deflections_between_planes_from(grid=grid, xp=xp) +return self.deflections_of_planes_summed_from(grid=grid, xp=xp) +``` + +`deflections_between_planes_from` defaults to `plane_i=0, plane_j=-1`, so for any +multi-plane tracer `from_mass_obj(tracer)` and `from_tracer(tracer, plane_j=-1)` +wrap the *same* callable. Single-plane tracers have one plane, so the summed and +between-planes forms agree there too. Passing `plane_j=-1` when `plane_redshift is +None` changes nothing — this is not an argument that needs a tolerance, it is the +same function. + +## The fit side already does this — the solver is the half that was never wired + +Worth stating because it changes what this task is: not "choose how to measure magnification at a +plane", but "finish a feature the rest of the point-source code already has". + +@PyAutoLens `autolens/point/fit/abstract.py:125-134`, `magnifications_at_positions`: + +```python +use_multi_plane = len(self.tracer.planes) > 2 +plane_j = ( + self.tracer.extract_plane_index_of_profile(profile_name=self.name) + if use_multi_plane else -1 +) +od = ag.LensCalc.from_tracer( + tracer=self.tracer, use_multi_plane=use_multi_plane, plane_j=plane_j +) +``` + +`_lens_calc_for` in `autolens/point/fit/solved.py:65-82` mirrors it for the Jacobian weighting. So +`LensCalc.from_tracer(..., plane_j=...)` is the established pattern for exactly this measurement, +and `from_tracer` exists in @PyAutoGalaxy to serve it. The solver is the single place it never +reached. The two resolve `plane_j` by different keys, each correct for its own API: the fit knows +the profile *name* (`extract_plane_index_of_profile`), the solver is handed a *redshift* +(`plane_index_via_redshift_from`). + +**The two halves of one likelihood evaluation therefore disagreed.** +`autolens/point/fit/positions/image/abstract.py:124-130` already passes the redshift down: + +```python +return self.solver.solve( + tracer=self.tracer, + source_plane_coordinate=self.source_plane_coordinate, + xp=self._xp, + plane_redshift=self.plane_redshift, + remove_infinities=False, +) +``` + +and `plane_redshift`'s own docstring (`abstract.py:187-199`) says it exists "to ensure that if +multi-plane tracing is used when solving the model image-plane positions, the correct source-plane +is used". So for an intermediate-plane source, `model_data` measured magnification at the last +plane and returned nothing, while `magnifications_at_positions` measured at the source's own plane +and was right. That is the mechanism behind #480's "AnalysisPoint log-likelihood evaluation fails" +impact line. + +`autolens/analysis/result.py:115` and `:184` pass `plane_redshift` too. The argument was plumbed +end-to-end through the callers; only the last consumer ignored it. + +## Scope + +1. Give `_filter_low_magnification` a `plane_redshift: Optional[float] = None` + parameter and switch its constructor to `ag.LensCalc.from_tracer(...)` with the + resolved `plane_j`. +2. Forward `plane_redshift` from `point_solver.py:242`. +3. Extract the `plane_redshift -> plane_index` resolution (currently inline in + `_plane_grid`) into one helper both methods call. The bug was two methods + disagreeing about the plane; leaving two copies of the resolution invites the + same drift back. +4. Handle the unmatched-redshift case while in there. `plane_index_via_redshift_from` + returns `None` when no plane is within its 1e-8 tolerance, and `_plane_grid` + then evaluates `traced_grids_list[None]` and raises a bare `TypeError`. A + mistyped redshift should say which redshifts the tracer actually has. This is a + latent bug on the same lines, not scope creep. + +## Testing + +**The reason this shipped is that nothing tests it.** No test under +`test_autolens/point/triangles/` passes `plane_redshift` to the solver at all — +the multi-plane solve path has zero coverage. Adding that coverage is the larger +half of this task. + +- The #480 repro becomes the regression test: a z=0.5/1.0/2.0 tracer, solve for + the z=1.0 source, assert 4 positions rather than 0. +- Assert the no-op directly: for a multi-plane tracer, `plane_redshift=None` and + `plane_redshift=` return identical positions to the current + implementation. +- Cover the unmatched-redshift error from scope item 4. +- The JAX path shares `_filter_low_magnification`, so check the padded/static-shape + path is unaffected (`test_use_jax.py`, `test_implicit_diff.py`). + +## Downstream, out of scope here + +#480's own text notes that @autolens_workspace simplified its multi-source example +to work around this (dropping the intermediate source's mass profile), and wants +that reverted once fixed. That is a separate workspace task — this prompt is the +library fix. Removing the `no_run.yaml` entries should follow the fix, not ride +along with it. + + diff --git a/dashboard.html b/dashboard.html index d4d78270b..1b7ef25ed 100644 --- a/dashboard.html +++ b/dashboard.html @@ -204,7 +204,7 @@

PyAutoMindDashboard

Intent. Priority. Flow.

Every task the Mind is holding. Tap a task's 📋 and its /start_dev command is on your clipboard — paste it into a Claude Code chat to route Claude straight to that task. Recent is the same work by date — what has been happening rather than what to do next.

- +

Last updated 2026-08-27. This page is generated from active/, draft/ and the registry files, so it is only as current as they are. dashboard_refresh.yml re-renders it on every push to main — that heals a stale page, but not a stale prompt: a task that shipped without its prompt advancing to complete/ keeps rendering here as pickable backlog. Reconciling those is the refresh below.

TRIAGE: needs manual review before routing❓ triagemediumsafehigh

+

Deep research: Can we speed up Delaunay in PyAutoArray?🔬 researchautoarraytoo-largesupervisedhigh

Split lensing regimes: multi_galaxy / group / cluster (epic plan)📖 docsautolenstoo-largesupervisedhigh

Re-baseline the MGE imaging JIT profiling regression value🧪 testautolens_workspace_developertoo-largesupervisedhigh

Fix release JAX runtime compatibility and likelihood parity🐛 bughealth_fixestoo-largesupervisedhigh

-

Fix JIT quick-update visualization output regressions🐛 bughealth_fixestoo-largesupervisedhigh

Quick wins (small enough, and safe enough to run unattended)

(none right now)

In flight markdown version

Issued — each has an open GitHub issue and usually a branch.

@PyAutoFit TransformedMessage.factor_gradient crashes on first call — issue #1501 — issued 2026-08-19 — HOLD — do not start dev. Fix-or-delete hangs off the PyAutoFit#1498 logpdf-contract

+

PointSolver magnification filter ignores plane_redshift — issue #480 — issued 2026-08-27 — library-dev

Parked markdown version

3 task(s) @@ -269,9 +270,9 @@

Planned

latent-nan-guard-honest-run — planned 2026-07-22

Backlog markdown version

-

132 filed prompts, not started — sorted most-pickable first (priority, then size). 25 of them belong to an epic and are listed only under Epics below.

+

135 filed prompts, not started — sorted most-pickable first (priority, then size). 25 of them belong to an epic and are listed only under Epics below.

-feature — 27 +feature — 28 @@ -286,6 +287,7 @@

Backlog

Claude Development Prompt: Arcsecond Tick Label Decimal Placement✨ featureautoarraylargesupervisednormal

EP analytic updates — implement the four planned work packages✨ featureautofitlargesupervisednormal

Remote-MCP deployment tiers (2 + 3) for the results-inspector server✨ featureautofit_assistantlargehuman-requirednormal

+

Multi-plane time delays✨ featureautolenslargesupervisednormal

Adopt oversampled PSFs in the start-here dataset chain (option a)✨ featureautolens_workspacelargesupervisednormal

The Brain board should work in a session that has no gh✨ featurepyautobrainlargesupervisednormal

@@ -343,7 +345,8 @@

Backlog

Regenerate setup_notebook-drifted notebooks in autogalaxy/autofit/HowToFit workspaces🧹 maintenanceworkspacessmallsupervisedlow

-bug — 16 +bug — 17 +

Fix release JAX runtime compatibility and likelihood parity🐛 bughealth_fixestoo-largesupervisedhigh

Fix JIT quick-update visualization output regressions🐛 bughealth_fixestoo-largesupervisedhigh

Fix release result/sample parameter-path regressions🐛 bughealth_fixestoo-largesupervisedhigh

@@ -377,10 +380,11 @@

Backlog

plot coverage — follow-ups deferred from plot-coverage-gaps📖 docsworkspaces

-refactor — 5 +refactor — 6

Split Fitness.batch_size into lh_batch_size and latent_batch_size♻️ refactorautofitsmallsupervisednormal

+

One construction path for plane-bound lensing quantities♻️ refactorautolensmediumsupervisednormal

Remove the dead EDEN packaging tooling from PyAutoFit♻️ refactorpyautofitmediumsupervisednormal

Deduplicate repos_sync.py's check/write pairs♻️ refactorpyautomindmediumsafelow

@@ -408,6 +412,30 @@

Backlog The 50 newest things to happen to the work in hand, newest first — issued, parked, filed. Every other section on this page is laid out by state, which is exactly why none of them can answer “what has been happening?”. Shipped work is not here: it is read from complete/index.md, and a thousand records deep it would crowd out everything anyone can still act on. Showing the newest 10; … opens the next 10.

+ + + + + + + + + + + + + + + + + + + + + + + + @@ -443,25 +471,25 @@

Backlog Un-park multi_galaxy/features/scaling_relation/slam once a capped run…

- + - + - + - + @@ -683,30 +711,6 @@

Backlog Un-park imaging/features/scaling_relation/slam — the PyAutoArray#431…

- - - - - - - - - - - - - - - - - - - - - - - -
2026-08-27issuedPointSolver magnification filter ignores plane_redshift
2026-08-27filedOne construction path for plane-bound lensing quantities
2026-08-27filedMulti-plane time delays
2026-08-27filedLensCalc NumPy Hessian step is too coarse for multi-plane tracers
2026-08-26 filed jax_profiling/gradient/imaging/pixelization.py: 3.2% of its pin move…

Epics markdown version

diff --git a/dashboard.md b/dashboard.md index 6cb67ff8b..9414f9cf2 100644 --- a/dashboard.md +++ b/dashboard.md @@ -42,14 +42,14 @@ anything you could not verify. | Where | Count | |-------|------:| -| [In flight](#in-flight) (`active/`) | 1 | +| [In flight](#in-flight) (`active/`) | 2 | | [Parked](#parked) (`parked.md`) | 3 | | [Planned](#planned) (`planned.md`) | 5 | -| [Backlog](#backlog) (`draft/`) | 132 | +| [Backlog](#backlog) (`draft/`) | 135 | ## Start here -**Highest priority** (filed as `high`) — showing 12 of 15 +**Highest priority** (filed as `high`) — showing 12 of 16
📋 TRIAGE: needs manual review before routing — medium · safe · high @@ -91,6 +91,14 @@ anything you could not verify.
+
📋 LensCalc NumPy Hessian step is too coarse for multi-plane tracers — autogalaxy · large · supervised · high + +``` +/start_dev draft/bug/autogalaxy/lenscalc_numpy_hessian_step_is_too_coarse.md +``` + +
+
📋 multi_galaxy package: new regime package in autolens_workspace — autolens · large · supervised · high ``` @@ -139,14 +147,6 @@ anything you could not verify.
-
📋 Fix JIT quick-update visualization output regressions — health_fixes · too-large · supervised · high - -``` -/start_dev draft/bug/health_fixes/jit_visualization_outputs.md -``` - -
- **Quick wins** (small enough, and safe enough to run unattended) - _(none right now)_ @@ -163,6 +163,14 @@ Issued — each has an open GitHub issue and usually a branch. The full record f +
📋 PointSolver magnification filter ignores plane_redshift — issue #480 — issued 2026-08-27 — library-dev + +``` +/start_dev active/point_solver_magnification_filter_ignores_plane_redshift.md +``` + +
+ ## Parked Started or scoped, not currently in flight — resume by moving the row back to `active.md`. Full detail in [`parked.md`](parked.md). @@ -247,10 +255,10 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned. ## Backlog -**132** filed prompts, not started. Each section is sorted most-pickable first (priority, then size). **25** of them belong to an epic and are listed only under [Epics](#epics) below. +**135** filed prompts, not started. Each section is sorted most-pickable first (priority, then size). **25** of them belong to an epic and are listed only under [Epics](#epics) below.
-feature — 27 +feature — 28
📋 Numba CPU likelihood phase 1: batched MGE convolution + operated-matrix caching — autoarray · medium · supervised · high @@ -364,6 +372,14 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
+
📋 Multi-plane time delays — autolens · large · supervised · normal + +``` +/start_dev draft/feature/autolens/multi_plane_time_delays.md +``` + +
+
📋 Search settings-estimation + profiling infrastructure (n_starts / batch_size / n_batch) — autolens_profiling · large · supervised · normal ``` @@ -769,7 +785,15 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
-bug — 16 +bug — 17 + +
📋 LensCalc NumPy Hessian step is too coarse for multi-plane tracers — autogalaxy · large · supervised · high + +``` +/start_dev draft/bug/autogalaxy/lenscalc_numpy_hessian_step_is_too_coarse.md +``` + +
📋 Fix release JAX runtime compatibility and likelihood parity — health_fixes · too-large · supervised · high @@ -1003,7 +1027,7 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
-refactor — 5 +refactor — 6
📋 einstein_radius_jit_from: replace static init_guess with a JAX-native seed finder — autogalaxy · too-large · supervised · high @@ -1029,6 +1053,14 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
+
📋 One construction path for plane-bound lensing quantities — autolens · medium · supervised · normal + +``` +/start_dev draft/refactor/autolens/one_construction_path_for_plane_bound_lensing.md +``` + +
+
📋 Remove the dead EDEN packaging tooling from PyAutoFit — pyautofit · medium · supervised · normal ``` @@ -1156,76 +1188,76 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | Date | Event | Task | |------|-------|------| +| 2026-08-27 | issued | PointSolver magnification filter ignores plane_redshift | +| 2026-08-27 | filed | One construction path for plane-bound lensing quantities | +| 2026-08-27 | filed | Multi-plane time delays | +| 2026-08-27 | filed | LensCalc NumPy Hessian step is too coarse for multi-plane tracers | | 2026-08-26 | filed | jax_profiling/gradient/imaging/pixelization.py: 3.2% of its pin move… | | 2026-08-26 | filed | The Brain board should work in a session that has no gh | | 2026-08-24 | filed | wiki-currency's --check-version gate rots on every library main merge | | 2026-08-24 | filed | interferometer/jax_grad/gradient.py: eager and jitted likelihoods… | | 2026-08-24 | filed | autocti_workspace has no Navigator Check, so its CI can never roll… | | 2026-08-24 | filed | Un-park multi_galaxy/features/scaling_relation/slam once a capped run… | -| 2026-08-24 | filed | Induct PyAutoReduce into the PyAutoHands release machinery (date… | -| 2026-08-24 | filed | Heart's local smoke runner cannot run any CTI workspace — no autocti… | -| 2026-08-23 | filed | pynufft removal: unswept downstream residue (1 hard break + stale… | -| 2026-08-23 | filed | Phase 3: stop installing pynufft in Hands/Heart CI and PyAutoCTI… |
… 10 more (40 left) | Date | Event | Task | |------|-------|------| +| 2026-08-24 | filed | Induct PyAutoReduce into the PyAutoHands release machinery (date… | +| 2026-08-24 | filed | Heart's local smoke runner cannot run any CTI workspace — no autocti… | +| 2026-08-23 | filed | pynufft removal: unswept downstream residue (1 hard break + stale… | +| 2026-08-23 | filed | Phase 3: stop installing pynufft in Hands/Heart CI and PyAutoCTI… | | 2026-08-23 | filed | Brain board follow-ups: what real mornings surface | | 2026-08-22 | filed | smoke_install.sh's stale jax<0.7 pin — CI is on the right jax… | | 2026-08-22 | filed | Untrack the generated FITS test artifacts in autoarray | | 2026-08-22 | filed | The reconstruction noise map describes a different estimator than the… | | 2026-08-22 | filed | Point-source JSON datasets record no resolution regime | | 2026-08-22 | filed | Is Intel macOS a supported platform, and what is the numpy-only… | -| 2026-08-21 | filed | Rectangular mesh split: Bilinear (fast CPU default) vs RTU… | -| 2026-08-20 | filed | Numba CPU likelihood phase 2: kernel-CDF numba fast path (the 49-88%… | -| 2026-08-20 | filed | Numba CPU likelihood phase 1: batched MGE convolution +… | -| 2026-08-19 | filed | autofit_profiling: bootstrap the repo + general PyAutoFit profiling… |
… 10 more (30 left) | Date | Event | Task | |------|-------|------| +| 2026-08-21 | filed | Rectangular mesh split: Bilinear (fast CPU default) vs RTU… | +| 2026-08-20 | filed | Numba CPU likelihood phase 2: kernel-CDF numba fast path (the 49-88%… | +| 2026-08-20 | filed | Numba CPU likelihood phase 1: batched MGE convolution +… | +| 2026-08-19 | filed | autofit_profiling: bootstrap the repo + general PyAutoFit profiling… | | 2026-08-19 | issued | @PyAutoFit TransformedMessage.factor_gradient crashes on first… | | 2026-08-19 | filed | Release board: local run_logs enrichment | | 2026-08-19 | filed | RTD organism docs currency: Nerves page, organ-count drift, hands.md… | | 2026-08-19 | filed | Deduplicate repos_sync.py's check/write pairs | | 2026-08-19 | filed | Bug in autocti_workspace: the dataset_1d results/database example… | | 2026-08-18 | parked | single-source-density-design | -| 2026-08-18 | parked | prior-message-collapse-design | -| 2026-08-18 | filed | @PyAutoFit TransformedMessage.logpdf/pdf omit the transform… | -| 2026-08-17 | filed | Which other searches need prior-support handling — coverage audit… | -| 2026-08-17 | filed | Teach repos_sync --write to stamp organ config surfaces |
… 10 more (20 left) | Date | Event | Task | |------|-------|------| +| 2026-08-18 | parked | prior-message-collapse-design | +| 2026-08-18 | filed | @PyAutoFit TransformedMessage.logpdf/pdf omit the transform… | +| 2026-08-17 | filed | Which other searches need prior-support handling — coverage audit… | +| 2026-08-17 | filed | Teach repos_sync --write to stamp organ config surfaces | | 2026-08-16 | filed | LogGaussianPrior misreports its own support as (-inf, inf) | | 2026-08-14 | filed | Three jax_likelihood pins are stale by ~1.24e-4 and fail the smoke… | | 2026-08-09 | found | isothermal-ell-sph-oversampling-at-the-cusp | | 2026-08-08 | parked | pyautoreduce-slacs1430-acs-comparison | | 2026-08-08 | filed | Regenerate autolens_workspace markdown/ so the MGE pages show… | | 2026-08-07 | filed | Regenerate setup_notebook-drifted notebooks in… | -| 2026-08-06 | filed | Triage: Convolver "No blurring_image provided" warning in canonical… | -| 2026-08-06 | filed | Rewrite PyAutoCTI docs/api — 55 of 89 autosummary entries are dead | -| 2026-08-06 | filed | Dependency-cap refresh 2026-08: safe bumps, astropy 8 decision, two… | -| 2026-08-05 | filed | Give PyAutoFit searches a seed — today no search can be made… |
… 10 more (10 left) | Date | Event | Task | |------|-------|------| +| 2026-08-06 | filed | Triage: Convolver "No blurring_image provided" warning in canonical… | +| 2026-08-06 | filed | Rewrite PyAutoCTI docs/api — 55 of 89 autosummary entries are dead | +| 2026-08-06 | filed | Dependency-cap refresh 2026-08: safe bumps, astropy 8 decision, two… | +| 2026-08-05 | filed | Give PyAutoFit searches a seed — today no search can be made… | | 2026-08-04 | filed | dataset/imaging/jwst_lw is untracked because the gitignore was never… | | 2026-08-04 | filed | cosmos_web_ring stores boolean masks as float64, wasting ~3.4 MB of… | | 2026-08-04 | filed | autolens_workspace_developer: broad stale-API rot (56 symbols, no CI) | | 2026-08-04 | filed | Nightly release has been blocked 8 nights running — triage the streak | | 2026-08-04 | filed | HowToLens ch4 tutorial 3: mask overlay is never actually drawn | | 2026-08-03 | filed | Un-park imaging/features/scaling_relation/slam — the PyAutoArray#431… | -| 2026-07-31 | filed | interferometer/start_here.py OOM in nightly release-validation… | -| 2026-07-31 | filed | Quick-update plotting cost — minutes per update, and it is not JAX… | -| 2026-07-31 | filed | Cluster-scale gradient-search benchmark (Prodigy vs Nautilus… | -| 2026-07-30 | filed | plot coverage — follow-ups deferred from plot-coverage-gaps |
diff --git a/draft/bug/autogalaxy/lenscalc_numpy_hessian_step_is_too_coarse.md b/draft/bug/autogalaxy/lenscalc_numpy_hessian_step_is_too_coarse.md new file mode 100644 index 000000000..1bbdc52cf --- /dev/null +++ b/draft/bug/autogalaxy/lenscalc_numpy_hessian_step_is_too_coarse.md @@ -0,0 +1,55 @@ +# LensCalc NumPy Hessian step is too coarse for multi-plane tracers + +Type: bug +Target: PyAutoGalaxy +Repos: +- PyAutoGalaxy +- PyAutoLens +Difficulty: large +Autonomy: supervised +Priority: high +Status: formalised + +Filed: 2026-08-27 + +LensCalc's NumPy Hessian uses a hardcoded finite-difference step that is too coarse for +multi-plane configurations, returning magnifications that are wrong by >100% with flipped signs. + +Found on 2026-08-27 while cross-checking the fix for PyAutoLens#480. It is a separate, pre-existing +bug: #480's fix is accurate, and this was found by the control arm of that check. + +`LensCalc._hessian_via_richardson` (autogalaxy/operate/lens_calc.py) evaluates the Hessian by +central finite differences at a hardcoded `buffer=0.01` arcsec, Richardson-extrapolated at h and +h/2. That step is fixed regardless of the scale the deflection field actually varies on. + +Measured, on the tracer from PyAutoLens#480 (lens z=0.5 Isothermal R_E=1.6; source z=1.0 with its +own Isothermal R_E=0.2; source z=2.0), magnification at the four image positions of the z=1.0 +source, computed three ways: + + last plane (z=2.0) + numpy Richardson FD -0.00694 -0.00221 0.00139 0.00246 + jax exact autodiff 0.04508 0.01099 -0.08602 -0.01118 + ray-traced Jacobian 0.04508 0.01099 -0.08602 -0.01101 + +JAX autodiff (float64) and a Jacobian derived independently from `traced_grid_2d_list_from` agree +with each other; the NumPy path disagrees by 122% and has the WRONG SIGN on three of four points. +The ray-traced values are stable across step sizes h=1e-4 to 1e-7, so this is not noise in the +cross-check. + +The same three-way comparison at the intermediate plane (z=1.0) agrees to 1.7e-08. So the failure +is configuration-dependent, not general: the map to z=1.0 involves only the smooth main lens, while +the map to z=2.0 additionally passes the compact z=1.0 deflector (R_E=0.2), whose deflection field +varies on scales where a 0.01 arcsec step is far too coarse. + +Why it matters: the NumPy path is the default. `AbstractFitPoint.magnifications_at_positions` uses +it, so point-source flux fits and source-plane chi-squareds on multi-plane models with a compact +intermediate deflector are exposed, as is `PointSolver`'s magnification threshold. A sign flip on a +magnification is not a small error. + +Scope to consider: scale the step to the local deflection scale rather than hardcoding it; or +error-estimate from the Richardson pair (the h vs h/2 difference already bounds the truncation +error and is currently discarded) and warn or refine when it is large; or make the JAX path +reachable from NumPy callers. A regression test should pin the multi-plane configuration above +against the ray-traced Jacobian, which is the independent oracle used to find this. + + diff --git a/draft/bug/pyautolens/point_source_json_datasets_record_no_regime.md b/draft/bug/pyautolens/point_source_json_datasets_record_no_regime.md index 35031b57d..f6e1067ac 100644 --- a/draft/bug/pyautolens/point_source_json_datasets_record_no_regime.md +++ b/draft/bug/pyautolens/point_source_json_datasets_record_no_regime.md @@ -110,6 +110,35 @@ so they are fixed here rather than left to bite again: Likewise the weak simulator is `scripts/weak/simulator.py` in @autolens_workspace. +**2026-08-27 — both facts re-verified, STILL BLOCKED. Do not build.** +Second gate check in four days, same verdict: no issue was opened, no branch was +cut, no worktree claimed. + +1. **PyAutoLens#480 is still open.** `updated_at` is still byte-identical to + `created_at` (2026-04-28T21:01:57Z) — not one edit, comment, label or + assignment since it was filed; still no assignees, still + `closed_by_pull_requests: 0`. Four months untouched. +2. **`weak/simple` is still regime-invariant.** `scripts/weak/simulator.py:122` + still calls `simulator.via_tracer_from(tracer=tracer, grid=positions, + name=dataset_name)`, and the file still contains no `os.environ` read of any + kind. Its last commit is 2026-07-27, i.e. it has not moved since the previous + re-check either. + +The `no_run.yaml` entries happen to sit at `config/build/no_run.yaml:41-42` in +@autolens_workspace again, matching the original citation the 2026-08-23 note +found drifted. That is coincidence, not a reason to trust line numbers — keep +matching on the entry text. + +One standing risk the previous note did not record (it predates that note; the +file has not changed since 2026-07-27, so this is an omission, not a change): +`weak/simulator.py` *advertises* the helper that would break fact 2. Its prose +blocks at `:94-95` and `:116-117` name +`via_tracer_random_positions_from(tracer=..., n_galaxies=..., grid_extent=...)` +as the "quick uniform-square catalogue" alternative to the explicit annulus this +script passes. So the invariant is one plausible simplification away from being +reversed, with nothing in the suite to catch it. Not a reason to build now — but +if fact 2 ever expires, that is the likely route. + Next re-check: when PyAutoLens#480 closes. That is the only trigger — fact 2 is a standing invariant, not a countdown, and only breaks if someone switches `weak/simulator.py` to the random-positions helper. diff --git a/draft/feature/autolens/multi_plane_time_delays.md b/draft/feature/autolens/multi_plane_time_delays.md new file mode 100644 index 000000000..206406bb9 --- /dev/null +++ b/draft/feature/autolens/multi_plane_time_delays.md @@ -0,0 +1,60 @@ +# Multi-plane time delays + +Type: feature +Target: PyAutoLens +Repos: +- PyAutoLens +Difficulty: large +Autonomy: supervised +Priority: normal +Status: formalised + +Filed: 2026-08-27 + +Multi-plane time delays: the time-delay equivalent of the PyAutoLens#480 magnification fix. + +#480 fixed magnification being measured at the tracer's last plane rather than the plane being +solved for. Time delays have the same shape of gap one level up, and it is now the blocking one: +with #480 fixed, multi-plane point-source modeling works, so a PointDataset can carry +`time_delays` for a system whose time delays cannot be computed. + +Two distinct pieces. + +1. `Tracer.time_delays_from` refuses more than two planes. `tracer_util.time_delays_from` raises + `RayTracingException` when `len(plane_redshifts) != 2`, and its docstring says so plainly: "It + requires a two-plane system (lens and source), and does not currently support multi-plane time + delay calculations involving more than two planes, but it could be extended to do so in the + future." This is an honest, enforced limitation rather than a silent wrong answer, which is why + it is a feature and not a bug. Extending it needs the multi-plane time-delay formalism: the + time-delay distance `D_dt = (1+z_l) D_d D_s / D_ds` is a two-plane construction, and the + multi-plane generalisation is a sum of per-plane terms, not a substitution into the same + expression. + +2. `LensCalc.fermat_potential_from` silently mixes planes, and this one IS a latent bug of exactly + the #480 kind. `LensCalc.from_tracer(tracer, use_multi_plane=True, plane_j=j)` binds the + deflection callable to plane j via `functools.partial`, but takes + `potential_2d_from = getattr(tracer, "potential_2d_from", None)` unchanged and passes it in both + branches. `Tracer.potential_2d_from` is the sum over ALL galaxies with no plane awareness + ("the summed 2D potential of all galaxies in the tracer"). So a plane-bound LensCalc computes + `fermat_potential_from` as a plane-j geometric term minus an all-planes potential -- including + the potential of deflectors at and beyond plane j, which should not contribute to a source at + plane j. `from_tracer` binds half of what the plane affects. + + Not currently reached by `Tracer.time_delays_from`, which builds via `from_mass_obj(galaxies)` + on a guarded two-plane system, so this is latent rather than live. It becomes live the moment + anything constructs a plane-bound LensCalc and asks for a Fermat potential -- which is what + extending time delays to multi-plane will do. Fix it as part of this, or guard it: a plane-bound + LensCalc whose potential is not plane-bound should refuse `fermat_potential_from` rather than + answer wrongly. + +Also worth checking while in here: `time_delay_geometry_term_from` computes +`src = grid - deflections` then `0.5 * ((grid - src)**2)`, which reduces algebraically to +`0.5 * |alpha|^2`. Correct for two planes; confirm what it should be for multi-plane before reusing +it there. + +Prior art to read first: PyAutoLens#480 and the branch that fixed it, which established the +`from_tracer(..., plane_j=...)` pattern and the ray-traced-Jacobian cross-check used to validate +plane-dependent quantities. Use the same cross-check here -- an independent oracle matters more for +time delays, where there is no equivalent of "the solver returns 0 images" to make an error obvious. + + diff --git a/draft/refactor/autolens/one_construction_path_for_plane_bound_lensing.md b/draft/refactor/autolens/one_construction_path_for_plane_bound_lensing.md new file mode 100644 index 000000000..7bb32a4a1 --- /dev/null +++ b/draft/refactor/autolens/one_construction_path_for_plane_bound_lensing.md @@ -0,0 +1,57 @@ +# One construction path for plane-bound lensing quantities + +Type: refactor +Target: PyAutoLens +Repos: +- PyAutoLens +Difficulty: medium +Autonomy: supervised +Priority: normal +Status: formalised + +Filed: 2026-08-27 + +Give plane-bound lensing quantities one construction path instead of three hand-rolled ones. + +Motivated by PyAutoLens#480, where the solver measured magnification at the wrong plane for four +months. The fix was small, but the reason the bug existed is structural: every caller that needs a +lensing quantity "at plane j" builds its own LensCalc, resolves the plane index by its own key, and +nothing checks the answers agree. + +The three sites today: + +- `AbstractFitPoint.magnifications_at_positions` (autolens/point/fit/abstract.py) -- + `use_multi_plane = len(self.tracer.planes) > 2`, then + `plane_j = tracer.extract_plane_index_of_profile(profile_name=self.name)`, then + `ag.LensCalc.from_tracer(...)`. +- `_lens_calc_for` (autolens/point/fit/solved.py) -- the same six lines again, duplicated + deliberately with a comment saying it "Mirrors AbstractFitPoint.magnifications_at_positions". +- `ShapeSolver._filter_low_magnification` / `_plane_grid` (autolens/point/solver/shape_solver.py) -- + resolves by redshift instead, via `plane_index_via_redshift_from`. This is the one that was + wrong; it now shares a `_plane_index` helper between the search and the filter, which is a local + fix of the same duplication one level down. + +So the plane is resolved by profile name in two places and by redshift in a third, and the fit +carries its answer to the solver as `plane_redshift = planes[plane_index].redshift` -- a +name -> index -> redshift -> index round trip across two modules. It happens to be consistent (there +is now a test pinning it), but nothing structural makes it so. + +Two further smells worth folding in: + +- `use_multi_plane = len(planes) > 2` is a no-op distinction. `Tracer.deflections_yx_2d_from` + dispatches to `deflections_between_planes_from(0, -1)` whenever `total_planes > 1`, so the + `use_multi_plane=False` branch of `from_tracer` wraps the same callable as the True branch with + `plane_j=-1`. Two code paths, one behaviour. Verified numerically during #480: magnifications + compare bit-identical, max|diff| = 0.0, for multi-plane and single-plane tracers alike. +- `from_tracer` binds the deflection callable to the plane but leaves `potential_2d_from` as the + tracer's all-planes sum, so a plane-bound LensCalc is only half plane-bound. Filed separately as + the multi-plane time-delay task; a refactor here should make that shape impossible to express + rather than leave it to be remembered. + +Likely direction, to be decided in the task: a single accessor on Tracer -- something like +`tracer.lens_calc_at(plane_index=... | redshift=... | profile_name=...)` -- that resolves the plane +once, binds every plane-dependent callable consistently, and is the only way the point-source code +obtains one. Behaviour-preserving: the #480 tests, the fit/solver agreement test and the +ray-traced-Jacobian cross-check already pin the numbers this must not change. + +