Overview
The trapped-lane counter (PyAutoFit#1475 004f798, PyAutoGalaxy#572 695b27c) ran on the real imaging/mge cell and cleared the ell_comps plateau as a suspect — and surfaced a much larger effect nobody has characterised. On a production dataset/model/analysis via build_for_cell (16 starts x 150 steps, cloud CPU, 352s):
| counter |
lane-steps |
share |
n_value_nan_lane_steps |
1498 |
62.42% |
n_grad_nan_lane_steps |
9 |
0.38% |
n_constrained_lane_steps |
0 |
0.00% |
n_resurrections |
0 |
— |
The population fell from alive 16/16 to alive 2/16. Roughly seven of every eight starts die, and the best fit is being found by two survivors.
This contradicts a documented assumption: AbstractMultiStartGradient's resurrect docstring justifies the resurrect=False default by asserting the parametric MGE-class cell "has only the measure-zero singularity, so the apply_if_finite guard suffices". The measurement says otherwise, and the dominant mode is value-NaN, which apply_if_finite does not rescue — it only zeroes the step, so the lane stays dead and resurrect=False never redraws it. n_resurrections: 0 confirms that mechanism.
This task finds the cause. It does not change any search behaviour — see Boundary.
Plan
- Find where the non-finite likelihood comes from — which parameters/regions and which operation. This is the open question and it does not need a GPU.
- Measure whether
resurrect=True recovers the budget on this cell, and what it costs.
- Confirm the effect at production budget, on GPU, across seeds — graded on the alive-versus-step curve, not on reproducing the scalar "62%".
- Record the finding in this repo's hazard index.
Detailed implementation plan
Affected repositories
autolens_profiling (primary) — investigation, measurement, hazard-index entry
PyAutoFit / PyAutoGalaxy — read only here; any fix is a separate task
Branch survey
| Repository |
Current branch |
Dirty? |
| autolens_profiling |
main (a34d6191) |
clean |
| PyAutoMind |
claude/mge-lane-death-ih8q92 |
clean |
Suggested branch: research/mge-lane-death
Implementation steps, ordered by information per unit time
This ordering is deliberate and differs from the order the questions were first written down. The cause is the unanswered question and it is the cheap one; the GPU reproduction confirms an effect already measured once. Do not queue for a GPU before step 1 has been attempted.
- Where the NaNs come from. Candidates: the mask edge, a Gaussian
sigma collapsing, the linear inversion / NNLS solve, the sqrt at r=0. The existing ~6-minute 16x150 CPU run already produces all 1498 deaths — instrument it to dump each lane's parameter vector at the step its value first goes non-finite, and the distribution of those vectors is the answer. Minutes-long iteration, not a queued job.
- Whether
resurrect=True recovers the budget on this cell, and what it costs. The docstring says resurrection is for pixelized sources; this evidence suggests the parametric cell may need it too. Measurement of the candidate remedy, not adoption of it.
- Whether it holds at production budget, on GPU, across seeds. One seed at reduced budget on CPU so far, and the float64 GPU path is different numerics. Log
alive N/16 per step so curves can be overlaid across budgets.
Grade step 3 on the curve, not the scalar
62% is a survival integral, not a hazard rate: a value-NaN lane is frozen by apply_if_finite and never redrawn, so it keeps counting a value-NaN lane-step on every subsequent step. Hold the death curve fixed and run 300 steps instead of 150 and the same physics reports ~75%. A production run coming back above 62% is therefore not automatically a worse result. Compare alive-versus-step curves; use the scalar only within a fixed budget.
"When they happen" is already partly answered
Inverting the 1498 over 14 dead lanes with no resurrections gives sum(k_i) = 14*150 - 1498 = 602, i.e. a mean death step of ~43 of 150. Deaths concentrate in the first third — trajectories walking into a wall mid-descent, not bad initial draws that _broad_starts failed to filter. Step 1's per-lane death steps should confirm or overturn this; a disagreement is itself a finding, since it would imply lanes recovering and re-dying, which resurrect=False says they cannot.
Key files
scripts/misc/searches/_setup.py — build_for_cell, the production dataset/model/analysis. Note it writes into dataset/ (rewrites the HST FITS, adds positions.json, emits results/simulators/*); it is not read-only.
results/searches/multi_start_nan_accounting/ — where the existing NaN-accounting artefact lives; the new results belong alongside it.
- The hazard index in this repo — where the cause gets recorded.
Environment
- Python 3.12+ (autonerves);
pip install jaxnnls is required for the JAX NNLS solver path and is not pulled in by default.
- Install
autolens with --no-deps if using editable local autofit/autogalaxy, or the released wheels clobber them.
- Cell scripts honour
SEARCHES_N_STARTS / SEARCHES_N_STEPS / SEARCHES_BATCH_SIZE / SEARCHES_DISABLE_VIZ.
imaging/mge runs on CPU in ~6 min at 16x150. The pixelized mesh cells do not — that is JIT compile, not memory (batch_size=1 clears the OOM), and they need the GPU. Shrinking the source mesh is the wrong cost lever; the image-plane grid at mask_radius 3.5 dominates.
- On A100 set
jax_enable_x64 explicitly — it is not inherited under sbatch, and float32 would understate the quantity under test.
A trap worth knowing before re-running
validate_zero.py (in the prompt) is the positive control that makes the 0 meaningful — without it, a zero cannot be distinguished from "nothing was watching". Two notes:
- It must probe with
jnp arrays. Concrete Python floats trip validate_ell_comps and raise before the constraint is ever reached.
- Its four checks call discovery/evaluation/the predicate directly, so none of them exercises the search loop that accumulates the counter. What closes that gap is in the result JSON: the counters come back as integer
0/1498, not null, and the driver reads them via si.get(k) — which would have given null had _fit never written the key. A null in the counters block on a re-run means broken plumbing, not a clean cell. The two look identical in a summary table.
Original prompt
The full prompt — including the verbatim result JSON and both self-contained driver scripts (rerun_cell.py, validate_zero.py) — lives in PyAutoMind and travels with the task:
draft/research/autolens_profiling/mge_lane_death.md (permalink, 0b8895ae)
The scripts are deliberately not committed to this repo: they are throwaway measurement drivers, not part of the script tiers, and they hardcode a workspace root and a scratchpad output path. Copy them out to run.
Background on the counter itself: complete/2026/08/frozen-lane-counter.md — the full run, the detector-design finding, and the positive control.
Overview
The trapped-lane counter (PyAutoFit#1475
004f798, PyAutoGalaxy#572695b27c) ran on the realimaging/mgecell and cleared theell_compsplateau as a suspect — and surfaced a much larger effect nobody has characterised. On a production dataset/model/analysis viabuild_for_cell(16 starts x 150 steps, cloud CPU, 352s):n_value_nan_lane_stepsn_grad_nan_lane_stepsn_constrained_lane_stepsn_resurrectionsThe population fell from
alive 16/16toalive 2/16. Roughly seven of every eight starts die, and the best fit is being found by two survivors.This contradicts a documented assumption:
AbstractMultiStartGradient'sresurrectdocstring justifies theresurrect=Falsedefault by asserting the parametric MGE-class cell "has only the measure-zero singularity, so theapply_if_finiteguard suffices". The measurement says otherwise, and the dominant mode is value-NaN, whichapply_if_finitedoes not rescue — it only zeroes the step, so the lane stays dead andresurrect=Falsenever redraws it.n_resurrections: 0confirms that mechanism.This task finds the cause. It does not change any search behaviour — see Boundary.
Plan
resurrect=Truerecovers the budget on this cell, and what it costs.Detailed implementation plan
Affected repositories
autolens_profiling(primary) — investigation, measurement, hazard-index entryPyAutoFit/PyAutoGalaxy— read only here; any fix is a separate taskBranch survey
a34d6191)claude/mge-lane-death-ih8q92Suggested branch:
research/mge-lane-deathImplementation steps, ordered by information per unit time
This ordering is deliberate and differs from the order the questions were first written down. The cause is the unanswered question and it is the cheap one; the GPU reproduction confirms an effect already measured once. Do not queue for a GPU before step 1 has been attempted.
sigmacollapsing, the linear inversion / NNLS solve, thesqrtat r=0. The existing ~6-minute 16x150 CPU run already produces all 1498 deaths — instrument it to dump each lane's parameter vector at the step its value first goes non-finite, and the distribution of those vectors is the answer. Minutes-long iteration, not a queued job.resurrect=Truerecovers the budget on this cell, and what it costs. The docstring says resurrection is for pixelized sources; this evidence suggests the parametric cell may need it too. Measurement of the candidate remedy, not adoption of it.alive N/16per step so curves can be overlaid across budgets.Grade step 3 on the curve, not the scalar
62% is a survival integral, not a hazard rate: a value-NaN lane is frozen by
apply_if_finiteand never redrawn, so it keeps counting a value-NaN lane-step on every subsequent step. Hold the death curve fixed and run 300 steps instead of 150 and the same physics reports ~75%. A production run coming back above 62% is therefore not automatically a worse result. Compare alive-versus-step curves; use the scalar only within a fixed budget."When they happen" is already partly answered
Inverting the 1498 over 14 dead lanes with no resurrections gives
sum(k_i) = 14*150 - 1498 = 602, i.e. a mean death step of ~43 of 150. Deaths concentrate in the first third — trajectories walking into a wall mid-descent, not bad initial draws that_broad_startsfailed to filter. Step 1's per-lane death steps should confirm or overturn this; a disagreement is itself a finding, since it would imply lanes recovering and re-dying, whichresurrect=Falsesays they cannot.Key files
scripts/misc/searches/_setup.py—build_for_cell, the production dataset/model/analysis. Note it writes intodataset/(rewrites the HST FITS, addspositions.json, emitsresults/simulators/*); it is not read-only.results/searches/multi_start_nan_accounting/— where the existing NaN-accounting artefact lives; the new results belong alongside it.Environment
pip install jaxnnlsis required for the JAX NNLS solver path and is not pulled in by default.autolenswith--no-depsif using editable localautofit/autogalaxy, or the released wheels clobber them.SEARCHES_N_STARTS/SEARCHES_N_STEPS/SEARCHES_BATCH_SIZE/SEARCHES_DISABLE_VIZ.imaging/mgeruns on CPU in ~6 min at 16x150. The pixelized mesh cells do not — that is JIT compile, not memory (batch_size=1clears the OOM), and they need the GPU. Shrinking the source mesh is the wrong cost lever; the image-plane grid atmask_radius 3.5dominates.jax_enable_x64explicitly — it is not inherited undersbatch, and float32 would understate the quantity under test.A trap worth knowing before re-running
validate_zero.py(in the prompt) is the positive control that makes the0meaningful — without it, a zero cannot be distinguished from "nothing was watching". Two notes:jnparrays. Concrete Python floats tripvalidate_ell_compsand raise before the constraint is ever reached.0/1498, notnull, and the driver reads them viasi.get(k)— which would have givennullhad_fitnever written the key. Anullin the counters block on a re-run means broken plumbing, not a clean cell. The two look identical in a summary table.Original prompt
The full prompt — including the verbatim result JSON and both self-contained driver scripts (
rerun_cell.py,validate_zero.py) — lives in PyAutoMind and travels with the task:draft/research/autolens_profiling/mge_lane_death.md(permalink,0b8895ae)The scripts are deliberately not committed to this repo: they are throwaway measurement drivers, not part of the script tiers, and they hardcode a workspace root and a scratchpad output path. Copy them out to run.
Background on the counter itself:
complete/2026/08/frozen-lane-counter.md— the full run, the detector-design finding, and the positive control.