Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 19 additions & 14 deletions campaigns.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,18 @@ campaigns:
title: Linear-solver accuracy and cost
status: active
updated: '2026-10-08'
next: 'Phase 3b MERGED 2026-10-08: autolens_profiling#396 (86cb1460), issue #395 closed, Mind record
complete/2026/10/linear-solver-p3b-gpu-timing.md. A100 job 398249: pdip_raw per-eval min ms CPU 1.35/0.88/0.68 vs
A100 3.95/0.58/0.19 at B=1/16/50 (SLaM distinct lanes), compile 0.3-1.2 s separate; jacobi 1.94x at A100 B=50 because
one diverging lane pins the batch at the 50 cap. Timing is not admissibility; no pin moves. Phase 4a ISSUED
2026-10-08 (autolens_profiling#397, research): why jacobi batched vs unbatched trajectories differ on the A100 only
(19/50 lanes; CPU identical) - Jacobi is the Mapper default, so this feeds a future GPU-default decision; wiki
research page + decision table, one authorized A100 job. Next: /prm on the phase-4a PR when open.'
evidence: https://github.com/PyAutoLabs/autolens_profiling/blob/86cb1460/results/notes/linear_solver_accuracy_2026_09.md
next: 'Phase 4a MERGED 2026-10-08 (autolens_profiling#398, fe9ca472; issue #397 closed; A100 job 399050): the A100-only
jacobi batched/unbatched difference is deterministic batch-shape-dependent Cholesky rounding (cho_factor/cho_solve differ
on 50/50 lanes from initialize at ~1e-15), amplified to order one within 1-2 iterations on the 24 Jacobi-unstable lanes
(all 20 of the 3a divergence set); not nondeterminism (deterministic-ops flag bit-identical), not a vmap defect; controls
pdip_raw <= 1.3e-13, certified <= 3.4e-12. Wiki wiki/research/jacobi_a100_batched_divergence.md with the decision table
(B ruled out, C unlikely, A or D need a Mapper corpus). Human chose option 2 (bounded evidence step): phase 5 Mapper
corpus ISSUED as #399 (delaunay_hst, rectangular_hst, slam_mixed_hst, optional interferometer_delaunay; accuracy,
timing and divergence cells; one authorized A100 job). Phase 5 delivered (PR #400, A100 job 399225): Jacobi converged 24/24 Mapper/mixed systems, 0 batch-sensitive lanes;
raw+polish 1.05-1.62x Jacobi cost. DECISION 2026-10-08 (decisions/linear-solver-mapper-default-jacobi.md): keep Jacobi
as the Mapper default, raw+polish for MGE-only, no library change. Next: /prm on the re-landed phase-5 PR (corpus out
of git, human choice); then close the programme in the task.'
evidence: https://github.com/PyAutoLabs/autolens_profiling/blob/fe9ca472/wiki/research/jacobi_a100_batched_divergence.md
- id: imaging
title: Production imaging and likelihood breakdown
status: active
Expand Down Expand Up @@ -207,12 +211,13 @@ tasks:
priority: medium
path: tasks/mge_nnls_fix_pyautoarray_571_slam_60.md
updated: '2026-10-08'
issue: https://github.com/PyAutoLabs/autolens_profiling/issues/397
next: 'Phase 3 COMPLETE: 3a #394 (parity) and 3b #396 (vmap timing) merged; Mind records complete/2026/10/linear-solver-p3a-a100-parity.md
and linear-solver-p3b-gpu-timing.md. Phase 4a research issued as #397 (Mind active/linear_solver_phase4_jacobi_a100_batched_divergence.md,
worktree linear-solver-p4a-jacobi-a100-divergence, Opus executing). Task stays active until the 4a PR is merged and the
human reads the decision table.'
evidence: https://github.com/PyAutoLabs/autolens_profiling/pull/396
issue: https://github.com/PyAutoLabs/autolens_profiling/issues/399
next: 'Phases 3a/3b/4a merged (#394, #396, #398; Mind records complete/2026/10/linear-solver-p3a-a100-parity.md,
linear-solver-p3b-gpu-timing.md, linear-solver-p4a-jacobi-a100-divergence.md). Phase 5 Mapper corpus issued as #399
(Mind active/linear_solver_phase5_mapper_corpus.md, worktree linear-solver-p5-mapper-corpus, Opus executing). Task
stays active until the re-landed phase-5 PR is merged. Decision taken 2026-10-08: keep Jacobi for Mapper, raw+polish
for MGE-only (decisions/linear-solver-mapper-default-jacobi.md).'
evidence: https://github.com/PyAutoLabs/autolens_profiling/pull/398
- id: autofit_profiling_bootstrap
campaign: autofit
title: 'autofit_profiling: bootstrap the repo + general PyAutoFit profiling epic'
Expand Down
2 changes: 1 addition & 1 deletion dashboard.html

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions dashboard.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PyAutoPulse β€” profiling dashboard

<!-- pulse-campaigns:38baea2335f2ea205320d7ae48f2a51e5c48a9d632a5525a17082c23829f86dd -->
<!-- pulse-campaigns:5561dbba1d4ce4ed1c8800f397a77dd99346300db80e0872771e47492295f923 -->
## Profiling Check In

<details><summary>Full check-in prompt</summary>
Expand Down Expand Up @@ -108,13 +108,13 @@ Reviewed 2026-10-07. Decision matrix complete: last cell merged (autolens_profil

<details><summary>Linear-solver accuracy and cost β€” active</summary>

Reviewed 2026-10-08. Phase 3b MERGED 2026-10-08: autolens_profiling#396 (86cb1460), issue #395 closed, Mind record complete/2026/10/linear-solver-p3b-gpu-timing.md. A100 job 398249: pdip_raw per-eval min ms CPU 1.35/0.88/0.68 vs A100 3.95/0.58/0.19 at B=1/16/50 (SLaM distinct lanes), compile 0.3-1.2 s separate; jacobi 1.94x at A100 B=50 because one diverging lane pins the batch at the 50 cap. Timing is not admissibility; no pin moves. Phase 4a ISSUED 2026-10-08 (autolens_profiling#397, research): why jacobi batched vs unbatched trajectories differ on the A100 only (19/50 lanes; CPU identical) - Jacobi is the Mapper default, so this feeds a future GPU-default decision; wiki research page + decision table, one authorized A100 job. Next: /prm on the phase-4a PR when open.
Reviewed 2026-10-08. Phase 4a MERGED 2026-10-08 (autolens_profiling#398, fe9ca472; issue #397 closed; A100 job 399050): the A100-only jacobi batched/unbatched difference is deterministic batch-shape-dependent Cholesky rounding (cho_factor/cho_solve differ on 50/50 lanes from initialize at ~1e-15), amplified to order one within 1-2 iterations on the 24 Jacobi-unstable lanes (all 20 of the 3a divergence set); not nondeterminism (deterministic-ops flag bit-identical), not a vmap defect; controls pdip_raw &lt;= 1.3e-13, certified &lt;= 3.4e-12. Wiki wiki/research/jacobi_a100_batched_divergence.md with the decision table (B ruled out, C unlikely, A or D need a Mapper corpus). Human chose option 2 (bounded evidence step): phase 5 Mapper corpus ISSUED as #399 (delaunay_hst, rectangular_hst, slam_mixed_hst, optional interferometer_delaunay; accuracy, timing and divergence cells; one authorized A100 job). Phase 5 delivered (PR #400, A100 job 399225): Jacobi converged 24/24 Mapper/mixed systems, 0 batch-sensitive lanes; raw+polish 1.05-1.62x Jacobi cost. DECISION 2026-10-08 (decisions/linear-solver-mapper-default-jacobi.md): keep Jacobi as the Mapper default, raw+polish for MGE-only, no library change. Next: /prm on the re-landed phase-5 PR (corpus out of git, human choice); then close the programme in the task.

[Campaign evidence](https://github.com/PyAutoLabs/autolens_profiling/blob/86cb1460/results/notes/linear_solver_accuracy_2026_09.md)
[Campaign evidence](https://github.com/PyAutoLabs/autolens_profiling/blob/fe9ca472/wiki/research/jacobi_a100_batched_divergence.md)

### Active tasks

- [Linear-solver programme phase 3: GPU/vmap/A100 timing and parity rows for the solver corpus](https://github.com/PyAutoLabs/PyAutoPulse/blob/main/tasks/mge_nnls_fix_pyautoarray_571_slam_60.md) β€” active: Phase 3 COMPLETE: 3a #394 (parity) and 3b #396 (vmap timing) merged; Mind records complete/2026/10/linear-solver-p3a-a100-parity.md and linear-solver-p3b-gpu-timing.md. Phase 4a research issued as #397 (Mind active/linear_solver_phase4_jacobi_a100_batched_divergence.md, worktree linear-solver-p4a-jacobi-a100-divergence, Opus executing). Task stays active until the 4a PR is merged and the human reads the decision table.
- [Linear-solver programme phase 3: GPU/vmap/A100 timing and parity rows for the solver corpus](https://github.com/PyAutoLabs/PyAutoPulse/blob/main/tasks/mge_nnls_fix_pyautoarray_571_slam_60.md) β€” active: Phases 3a/3b/4a merged (#394, #396, #398; Mind records complete/2026/10/linear-solver-p3a-a100-parity.md, linear-solver-p3b-gpu-timing.md, linear-solver-p4a-jacobi-a100-divergence.md). Phase 5 Mapper corpus issued as #399 (Mind active/linear_solver_phase5_mapper_corpus.md, worktree linear-solver-p5-mapper-corpus, Opus executing). Task stays active until the re-landed phase-5 PR is merged. Decision taken 2026-10-08: keep Jacobi for Mapper, raw+polish for MGE-only (decisions/linear-solver-mapper-default-jacobi.md).

</details>

Expand Down Expand Up @@ -282,7 +282,7 @@ Project `autolens_profiling`, scope `setup-catalogue`, read from [PyAutoLabs/aut
</details>


<!-- decision-history:4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945 -->
<!-- decision-history:8f1840916151d6a62b1cb1cd473edede0a3647c198fe8ef2371ce31a66c873a5 -->
## Decision History

No decisions recorded yet.
- <a href="https://github.com/PyAutoLabs/PyAutoPulse/blob/main/decisions/linear-solver-mapper-default-jacobi.md">Keep Jacobi PDIP as the Mapper default and raw+polish as the MGE-only default</a>
6 changes: 5 additions & 1 deletion decisions.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
# Human decisions only; see decisions/README.md for capture and record format.
decisions: []
decisions:
- id: linear-solver-mapper-default-jacobi
title: Keep Jacobi PDIP as the Mapper default and raw+polish as the MGE-only default
date: '2026-10-08'
url: https://github.com/PyAutoLabs/PyAutoPulse/blob/main/decisions/linear-solver-mapper-default-jacobi.md
48 changes: 48 additions & 0 deletions decisions/linear-solver-mapper-default-jacobi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Keep Jacobi PDIP as the Mapper default and raw+polish as the MGE-only default

Date: 2026-10-08
Decision maker: James Nightingale
Decision source: chat, 2026-10-08, in the PyAutoPulse profiling check-in. The human stated the goal ("I want a fast solution but it also needs to be stable for all likelihood functions"), chose the bounded evidence step over wrapping up or switching the default on MGE-only evidence ("Lets do option 2"), and after the Mapper-corpus result accepted the recommendation to keep Jacobi for Mapper inversions ("I agree with your decision"; recorded on the human's instruction).

## Campaign

Linear-solver programme of the `linear-solver` campaign (Pulse task `mge_nnls_fix_pyautoarray_571_slam_60`; project autolens_profiling, `scripts/lens/solver/`). Every PyAutoLens likelihood with linear light profiles or a pixelization ends in a non-negative least-squares solve of `(curvature_reg_matrix, data_vector)`. The library runs two JAX primal-dual interior-point (PDIP) modes: the raw system with a data-scaled tolerance and a Jacobi-space polish (`pdip_raw`, the MGE-only default since PyAutoArray#595) and Jacobi-preconditioned PDIP (`pdip_jacobi`, the default whenever a Mapper is present). The question was whether one solver could be both fast and stable for every likelihood, and in particular whether the Mapper default should move off Jacobi on GPU.

## Evidence

All measurements at library tag 2026.10.7.1 (PyAutoNerves c5ade605, PyAutoFit 710f4b34, PyAutoArray ccddfba6, PyAutoGalaxy b4946b8a, PyAutoLens b6bf543c), fp64, JAX 0.10.2; CPU rows on the laptop (WSL2, i9-10885H) from tag worktrees, GPU rows on RAL A100 80GB nodes from a private checkout. Ledger: `results/notes/linear_solver_accuracy_2026_09.md`; research page: `wiki/research/jacobi_a100_batched_divergence.md`.

- **MGE corpus (81 SLaM systems, n = 60).** Phase 3a (autolens_profiling#394, merge a55dcacb; A100 job 397475): `pdip_raw` reproduces its CPU rows on the A100 (max |Ξ” flux_inactive_rel| 5.4e-14, iterations identical 81/81) but stays inadmissible under the pre-registered rule on two systems; `pdip_jacobi` diverges on 29 (CPU) / 19 (A100) systems. Phase 3b (#396, merge 86cb1460; A100 job 398249): per-evaluation cost inside `jit(vmap)`, compile excluded, `pdip_raw` 0.19 ms vs `pdip_jacobi` 0.37 ms at batch 50 on the A100, because one diverging Jacobi lane pins the whole batch at the 50-iteration cap.
- **Cause of the A100-only batched/unbatched difference.** Phase 4a (#398, merge fe9ca472; A100 job 399050): deterministic, batch-shape-dependent rounding in the A100 Cholesky (`cho_factor`/`cho_solve` differ between batched and single solves on 50/50 lanes from the initialisation step at ~1e-15 relative), amplified to order one within 1–2 iterations only on Jacobi-unstable systems; not nondeterminism (the deterministic-ops flag changed no bit), not a `vmap` defect; `pdip_raw` and `certified` stay at ≀ 3e-12 with identical flags.
- **Mapper corpus (phase 5, issue #399; A100 job 399225, 6:20).** Three new groups of 8 near-truth systems: `delaunay_hst` (n = 1500), `rectangular_hst` (n = 1369), `slam_mixed_hst` (lens 2Γ—20 MGE + Delaunay source, the production SLaM source_pix configuration, n = 1540). `pdip_jacobi` converged on 24/24 systems with 0/24 A100 batch-sensitive lanes (max |Ξ”| 1.5e-12). `pdip_raw` is admissible under the phase-1 rule, stated as extended to this corpus, on Delaunay and mixed; both PDIP modes fail criterion 2 on rectangular. `certified` fails on mixed and rectangular. Per-evaluation cost at batch 8 on the A100, compile separate: `pdip_raw` 1.05Γ— (Delaunay), 1.62Γ— (rectangular), 1.15Γ— (mixed) the Jacobi cost. The phase-5 rows were delivered in PR #400 (commit 70e8a9f) and re-landed with the corpus files kept out of git; the measurements are unchanged.
- **Limitations.** All Mapper vectors are near-truth (four near-truth vectors plus two noise rescalings); wide-prior systems as sampled early in a search are unmeasured. No interferometer group (no importable capture harness). On the laptop CPU at n ~ 1500, batched and unbatched Jacobi differ by up to 4.2e-13 without changing iterations or flags (bit-identical at n = 60); not localised. Why the mixed systems stay Jacobi-stable despite carrying signal-free MGE columns is not established.

## Options considered

- **A. Keep Jacobi as the Mapper default and document it** (chosen). No library change; the known weakness is confined to Jacobi-unstable systems, of which the Mapper corpus contained none.
- **B. XLA deterministic-ops flag.** Ruled out by phase 4a: the effect is not nondeterminism.
- **C. Tolerance or iteration-cap change for Jacobi.** Not expected to help: sensitive lanes go order-one within two iterations, before any stopping test.
- **D. Move the Mapper default to raw+polish or certified.** Would give one solver for every likelihood, but adds no stability the measured Mapper systems need, costs 5–62 % more per evaluation, and raw+polish fails on rectangular while certified fails on mixed and rectangular.
- **E. One lowering for batched and single solves.** Bit-parity only, not stability.

## Decision

Keep the released configuration: `pdip_jacobi` wherever a Mapper is present (pixelized and mixed inversions, CPU and GPU) and `pdip_raw` with polish for MGE-only inversions. Document the batch-shape rounding behaviour rather than change a default. No tolerance, cap or pin changes. Phase 5 is the programme's last evidence phase for this question.

## Implications

- Nothing ships from this decision; it confirms the defaults already released in 2026.10.7.1 and the SLaM pipeline configuration that depends on them.
- GPU `vmap` batches and single re-fits of a Mapper system agree to rounding level and fully in iterations and flags on every measured system; on a Jacobi-unstable system (none seen among Mapper systems) they could differ, and the research page says so.
- Known accuracy gaps are recorded, not resolved: `pdip_raw` on two MGE corpus systems and both PDIP modes on the rectangular group fail criterion 2 of the phase-1 rule.
- The research page and the campaign wiki hold the decision table and the Mapper evidence for the next revisit.

## Revisit conditions

- A Mapper or mixed system, in a wide-prior or production search, found Jacobi-unconverged or batch-sensitive.
- An interferometer corpus (W-tilde systems) once a capture harness exists.
- A library change to the PDIP primitives, the Jacobi scaling or the polish rule, or a JAX/cuSOLVER change that alters the batched Cholesky path.
- A rectangular-mesh accuracy study, if criterion-2 failures there matter in practice.

## History

First decision on this question. No supersession.
14 changes: 14 additions & 0 deletions tasks/mge_nnls_fix_pyautoarray_571_slam_60.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ The original task below is retained verbatim. Current scheduling state lives in
- autolens_profiling#396 merged 2026-10-08T08:26Z (`86cb1460`), every CI leg green; issue #395 closed; Mind record `complete/2026/10/linear-solver-p3b-gpu-timing.md` (Mind d43d3864); laptop worktree removed; Pulse ledger PR #35 merged.
- Human decision (chat 2026-10-08): run phase 4 as research and keep the write-up in the wiki for a future decision. Filed and issued as [autolens_profiling#397](https://github.com/PyAutoLabs/autolens_profiling/issues/397) (phase 4a: localise the A100-only jacobi batched/unbatched divergence β€” determinism, vmap lowering vs lane count, tiled lanes, first-differing iteration via `max_iter=k`, cond(Q) join; wiki/research page with a decision table; one A100 job authorized). Mind `active/linear_solver_phase4_jacobi_a100_batched_divergence.md` (cff6e8e6), worktree `linear-solver-p4a-jacobi-a100-divergence`. 4b (corpus widening, kernel-selection probes, flag experiments) deferred unless 4a leaves it open.


### Close-out 2026-10-08 β€” phase 4a merged, phase 5 issued

- autolens_profiling#398 merged 2026-10-08T09:37Z (`fe9ca472`), lint green; issue #397 closed; Mind record `complete/2026/10/linear-solver-p4a-jacobi-a100-divergence.md` (bab8b2d4); worktree removed.
- Finding (A100 job 399050, default + deterministic-ops; CPU control from tag worktrees): the batched/unbatched jacobi difference is deterministic, batch-shape-dependent Cholesky rounding (batched `cho_factor`/`cho_solve` differ from the single solve on 50/50 lanes from `initialize`, 1.1–2.1e-15 relative), amplified to order one within 1–2 iterations on 24 Jacobi-unstable lanes (βŠ‡ all 20 of the 3a divergence set); 26 quiet lanes ≀ 8.2e-13 with identical flags; controls pdip_raw ≀ 1.3e-13, certified ≀ 3.4e-12; cond(Q) uninformative (AUC ~0.6). Decision table in `wiki/research/jacobi_a100_batched_divergence.md`: B ruled out, C unlikely to help, A/D need a Mapper corpus, E bit-parity only. Caveat: committed A100 ulp fields rounded (probe bug fixed post-run); relative norms exact.
- Human decision (chat 2026-10-08, "option 2"): one bounded evidence step before any default decision β€” a Mapper corpus. Issued as [autolens_profiling#399](https://github.com/PyAutoLabs/autolens_profiling/issues/399): capture `delaunay_hst`, `rectangular_hst`, `slam_mixed_hst` (lens MGE + Delaunay source, the production case), optional `interferometer_delaunay`; run accuracy, timing and divergence cells over them (CPU + one authorized A100 job); judge raw+polish admissibility and Jacobi stability under the phase-1 rule stated as extended; recommendation table in the wiki. Mind `active/linear_solver_phase5_mapper_corpus.md`, worktree `linear-solver-p5-mapper-corpus`.


### Decision 2026-10-08 β€” Mapper default stays Jacobi

- Phase 5 delivered (autolens_profiling PR #400, A100 job 399225, 6:20; three Mapper groups of 8 near-truth systems, n 1369–1540): `pdip_jacobi` converged 24/24 with 0/24 A100 batch-sensitive lanes; `pdip_raw` admissible (rule stated as extended) on Delaunay and mixed, both PDIP modes fail criterion 2 on rectangular; raw costs 1.05Γ— / 1.62Γ— / 1.15Γ— Jacobi at A100 batch 8, compile separate. Interferometer group skipped (no capture harness); vectors are near-truth only.
- Human decision: keep Jacobi as the Mapper default and raw+polish for MGE-only; no library change. Canonical record `decisions/linear-solver-mapper-default-jacobi.md` (indexed in `decisions.yaml`, Decision History on the board).
- Human decision (engineering, not a Pulse decision record): the three 50–56 MB corpus files stay out of git β€” external copies on RAL and the laptop, sha256 + regenerate command in the manifest; PR #400 to be re-landed from a fresh branch, never by rewriting the pushed one.

---

# Linear-solver programme phase 3: GPU/vmap/A100 timing and parity rows for the solver corpus
Expand Down
Loading