Repository navigation
Description
Overview
Design phase (1 of 5) for implementing the point-source likelihood variants of Lombardi 2024 (arXiv:2406.15280, Gravity.jl) in PyAutoLens: source-plane and image-plane chi-squareds with no free source-centre (y,x) parameters (analytic, Jacobian-weighted solve), tensor noise weighting for better errors, analytic flux/reference-time alternatives (magnification-first), the principled missing-image penalty, and (final phase) JAX gradients incl. solver implicit differentiation. This issue carries the design decisions; no source edits happen under it.
Plan
- Read Lombardi 2024 §§2, 5, 6.1 in full; pin exact equations for the linearized source-plane likelihood, pairing-marginalization normalization, flux/time-delay marginalization, and gradients.
- Decide the variant set: tensor-weighted solved-centre source-plane; centre-free
PairRepeat+PairAll; verdict on the numpy-only HungarianPair; fluxF*/ reference-timeT*alternatives; missing-image penalty disposition (PairAllalready carries-log(n_permutations)). - Decide the API: parameter-free
al.psmodel class + fit-side centre resolution (priors derive from__init__, sofit_positions_clsalone cannot remove the (y,x) priors); addfit_flux_cls/fit_time_delays_clshooks (currently hard-wired);source_plane_coordinatesemantics. - Decide the JAX plan:
xpthreading, the two knownFitPositionsSourcejit blockers, and the phase-5custom_jvpboundary forPointSolver. - Design the phase-2 test matrix (numpy-only): analytic optima vs brute force, solved ≈ profiled likelihood parity, tensor-vs-scalar weighting case.
- Post the design here for approval; update the phase 2–5 Mind prompts to match.
Detailed implementation plan
Work Classification
Library (design phase; the 5-phase series is Both — library first, workspaces in phases 3–5).
Affected Repositories
- PyAutoLens (primary)
- PyAutoGalaxy + PyAutoArray (expected from phase 2 —
al.psmodel class;Grid2DIrregularxp fix) - autolens_workspace, autolens_workspace_test, autolens_profiling, autolens_workspace_developer (phases 3–5)
Branch Survey (2026-07-27)
| Repository | Current Branch | Dirty? |
|---|---|---|
| ./PyAutoLens | main | clean |
| ./PyAutoGalaxy | main | clean |
| ./PyAutoArray | main | clean |
Warnings: PyAutoGalaxy is claimed by in-flight task validation-searches-env-optax (hard conflict if still active at phase 2 start); PyAutoArray has unregistered feature/delaunay-nan-callback checked out in a worktree (warning only).
Suggested branch: feature/point-source-chi-squared-variants (phases 2+; phase 1 makes no source edits)
Worktree root: ~/Code/PyAutoLabs-wt/point-source-chi-squared-variants/ (created by /start_library at phase 2)
Implementation Steps
The 9 design decisions are specified in the Mind prompt (reproduced verbatim below): variant set; parameter-free al.ps class + fit-side API; name-pairing preservation; JAX plan; error-fidelity test design; analytic flux (magnification-first flux space vs paper-exact magnitude space); analytic reference time vs min-subtraction; missing-image penalty vs existing -log(n_permutations) and unmatched_model_policy; phase-5 gradient groundwork.
Key Files
PyAutoGalaxy/autogalaxy/profiles/point_sources.py—Point/PointFlux(only classes; centre always free)PyAutoLens/autolens/point/fit/abstract.py:144-153—source_plane_coordinatesingle funnelPyAutoLens/autolens/point/fit/positions/—FitPositionsImagePair{,All,Repeat},FitPositionsSourcePyAutoLens/autolens/point/fit/dataset.py:34,114,130—fit_positions_clshook; hard-wiredFitFluxes/FitTimeDelaysPyAutoLens/autolens/point/model/analysis.py:42,181-218—AnalysisPointdefault + pytree registrationPyAutoArray/autoarray/structures/grids/irregular_2d.py:170— jit blocker (noxp)- Five false "barycenter" docstrings +
autolens_workspace/scripts/cluster/likelihood_function.py:312-318
Original Prompt
Click to expand starting prompt (phase 1 of 5; umbrella prompt in PyAutoMind)
Point-source chi-squared variants (arXiv:2406.15280) — Phase 1: design
Parent: point_source_chi_squared_paper_variants.md (verbatim request there).
Phase 1 of 5. Judgment-heavy — stays with the lead (Opus) session per
PyAutoBrain/skills/WORKFLOW.md; no source edits in this phase.
Goal
Produce the approved design for centre-free point-source likelihoods:
which variants from Lombardi 2024 (arXiv:2406.15280, Gravity.jl) we
implement, and the exact API. Output = design writeup on the GitHub issue
- any corrections to the phase 2–5 prompts.
Grounding (verified 2026-07-27)
- Model classes:
PyAutoGalaxy/autogalaxy/profiles/point_sources.pyhas only
Point(centre)andPointFlux(centre, flux)— centre is always a free
(y,x). No centre-free class exists anywhere. - Centre plumbing is a single funnel:
PyAutoLens/autolens/point/fit/abstract.py:144-153
source_plane_coordinate → self.profile.centre. Every fit uses it. - Fit classes (
autolens/point/fit/positions/):FitPositionsImagePair
(Hungarian, numpy-only, not jittable),FitPositionsImagePairAll
(all-to-all LogSumExp mixture, from this paper, JAX-blessed),
FitPositionsImagePairRepeat(nearest-with-repeats + unmatched-model
policies, JAX-shaped,AnalysisPointdefault),FitPositionsSource
(source-plane, scalar µ² noise weighting). fit_positions_clsis the only pluggable hook (fit/dataset.py:34default
FitPositionsImagePair;model/analysis.py:42default
FitPositionsImagePairRepeat— note the defaults DIFFER).- FALSE DOCSTRINGS: a "barycenter of ray-traced positions" option is claimed
but not implemented infit/positions/abstract.py:48,
image/abstract.py:49,image/pair.py:30,image/pair_all.py:26,
image/pair_repeat.py:18, and the workspace guide
autolens_workspace/scripts/cluster/likelihood_function.py:312-318
(claimsFitPositionsSource(profile=None)uses a centroid — it actually
raisesPointExtractionException). Phase 2 must make the docstrings true
or delete them; phase 4 fixes the guide. - JAX blockers on the source-plane path (relevant to any new source-plane
variant):Grid2DIrregular.grid_2d_via_deflection_grid_from
(PyAutoArray/autoarray/structures/grids/irregular_2d.py:170) takes no
xp;FitPositionsSourcemissing from pytree registration
(model/analysis.py:181-218).distances_to_coordinate_from(:204) is
already_xp-threaded.
Paper taxonomy to design against
- Image-plane (Eqs. 29–38): direct association / best-match / marginalize
over all pairings (LogSumExp — ourPairAll). Baseline forms keep the
source position β as a free non-linear parameter. - Source-plane linearized (Eqs. ~41–48): linearize the lens equation around
each observed image; β enters linearly and is solved analytically as a
precision-weighted (full local Jacobian / inverse-magnification-tensor
weighted) mean of back-traced positions. This is BOTH the centre-free
source-plane chi-squared AND the better-errors variant (tensor weighting
vs our scalar µ²). - Centre-free image-plane: profile the analytic β* (from weighted
back-projection) into the image-plane chi-squareds — forward-solve model
images from β* instead of a sampledprofile.centre, for the pair-repeat
and all-to-all schemes.
Decisions to make (the actual design work)
- Variant set: (a) source-plane analytic-centre with tensor weighting;
(b) source-plane analytic-centre with current scalar µ² weighting (for
continuity/comparison)? (c) centre-freePairRepeat; (d) centre-free
PairAll; (e) is a centre-free HungarianPairworth it given it is
numpy-only? Recommend which becomes the documented default for cluster
fits (Lenstool parity usesFitPositionsSource). - API: removing the (y,x) priors REQUIRES a parameter-free
al.psmodel
class (priors derive from__init__) —fit_positions_clsalone cannot
do it. Decide: one new class (e.g.al.ps.PointSolved, name TBD) +
orthogonal centre-resolution on the fit side, vs new fit subclasses per
scheme (the guide's existing class-attribute pattern, cf.
unmatched_model_policy), vs both. Decide the semantics of
source_plane_coordinatewhen the centre is solved (keep the single
funnel; no silent None-guards — absence of a centre must be explicit). - Name-pairing: dataset
name→ profile pairing must keep working for the
new class (fit/abstract.py:84-90). - JAX plan: the analytic β* (weighted mean) is fixed-shape and xp-friendly;
decide whether phase 2 also fixes the twoFitPositionsSourcejit
blockers above (recommended — new source-plane variants sit on the same
primitives). AnyPyAutoArraychange makes this a 3-repo task
(autoarray + autogalaxy + autolens); the FeatureDecision's
"repos: autolens" is understated — record the override. - Error-fidelity check design: how phase 2 unit tests demonstrate (numpy
only, no JAX in unit tests) that solved-centre likelihoods match the
free-centre likelihood profiled over centre, and that tensor weighting
reproduces image-plane errors better than scalar µ² on a known asymmetric
configuration. - Fluxes — IN SCOPE, alternative to the current implementation. Paper
(Eq. 39 + §6.1): magnitude-space Gaussian with lensing modulus
LMᵢ = 2.5 log10|Aᵢ⁻¹|; intrinsic magnitude M enters linearly →
analytically marginalized/profiled (conjugate prior), removing M as a
free parameter. PyAutoLens is magnification-first flux-space:
FitFluxes.model_data = |µᵢ| × profile.flux(fit/fluxes.py:110-124)
withfluxa free param onPointFlux. Decide the port: flux-space
analytic profiling (linear least squares,
F* = Σ µᵢ f̂ᵢ/σᵢ² / Σ µᵢ²/σᵢ²— natural fit to our flux-space noise
maps and outputs) vs paper-exact magnitude space; and what the model
class looks like (flux-freePointFluxsibling composing with the
solved-centre class). NOTE: there is nofit_flux_clshook —
FitFluxes/FitTimeDelaysare hard-wired infit/dataset.py:114,130;
the design must add pluggable hooks (mirroringfit_positions_cls) or
an equivalent mechanism. - Time delays — IN SCOPE, alternative to current. Paper (Eqs. 24-25):
tᵢ = T + Tᵢwith reference time T entering linearly → analytic
marginalization, same structure as M. CurrentFitTimeDelaystakes
residuals relative to the MINIMUM delay (fit/times_delays.py:109-110)
— an ad-hoc T elimination. Design the precision-weighted analytic T
alternative and compare error behaviour vs min-subtraction. - Missing-image penalty — scope against what exists. The paper's
mechanism is purely combinatorial: marginalizing over pairings with
normalization1/|Σ|(with repetition|Σ| = P^I; without repetition
|Σ| = P!/(P−I)!) penalizes many-predicted/few-observed configurations;
no explicit detection-probability term.FitPositionsImagePairAll
ALREADY implements-log(n_permutations)(pair_all.py:151-154, cited
to this paper). Verify it matchesP^Iexactly; decide whether to add
the without-repetition variant; decide whether the paper-principled
penalty should be offered as anunmatched_model_policyalternative to
PairRepeat's ad-hocno_image_residual = 1e4floor and
noise-normalized "penalize" policy (pair_repeat.py:93-95, 215-219). - JAX-gradient groundwork for phase 5 (design only, no implementation):
confirm which variants get analytic/custom-JVP treatment vs plain
autodiff — see the phase 5 prompt for the paper's gradient formulas.
Consults
autolens_assistant/wiki/core/concepts/point_source.md(read; background
current as of 2026-07-09) andskills/al_point_source.md.- PyAutoMemory
methodswiki (likelihood/jax) per FeatureDecision. - Paper full text: https://arxiv.org/html/2406.15280 §§2, 5 (and 6.1 for the
deferred marginalization option).
Exit criteria
Design posted to the issue; user approves variant set + API; phase 2–4
prompts updated to match. No code changes.
Activity
Jammy2211 commentedon Jul 27, 2026
Phase 1 design — point-source likelihood variants (Lombardi 2024, arXiv:2406.15280)
All code facts below verified against
mainon 2026-07-27.D0. Paper results this design builds on
θ̂ᵢ, givingβ ≈ β̂ᵢ + Aᵢ(θ̂ᵢ − θᵢ)withAᵢthe lens Jacobian at the observed position. β then enters the chi-squared linearly and is solved in closed form:β* = (Σᵢ AᵢᵀΘᵢAᵢ)⁻¹ Σᵢ AᵢᵀΘᵢ β̂ᵢ(Θᵢ = image-plane position precision).Marginalizing β (flat prior) adds a
−½ log|Σᵢ AᵢᵀΘᵢAᵢ|-type determinant term. The per-image precision transformAᵢᵀΘᵢAᵢis the tensor weighting — the principled version of our scalarµ²weighting, and the "better errors" variant.1/|Σ|,|Σ| = P^Iwith repetition — a purely combinatorial Occam penalty. Verified:FitPositionsImagePairAll.chi_squaredimplements exactly this (n_permutations = n_finite_model^len(data),pair_all.py:145-154). Nothing new to implement; the without-repetition form (P!/(P−I)!, Eq. 37) requires factorial permutation enumeration — JAX-hostile, and rejected below.M* = Σλᵢ(m̂ᵢ+LMᵢ)/Σλᵢ(magnitude space);T* = Στᵢ(t̂ᵢ−Tᵢ)/Στᵢ.D1. Variant set (decision)
weighting = "jacobian" | "magnification") for Lenstool-style comparisons; cheap, no new classFitPositionsSource(free centre, scalar µ²)FitPositionsImagePair, solved centrePairAll;PairRepeat'sunmatched_model_policyheuristics stay as-is (best-match is not a normalized mixture; no principled combinatorial term applies)F*F* = Σᵢ µᵢ f̂ᵢ/σᵢ² / Σᵢ µᵢ²/σᵢ²+−½log(Σµᵢ²/σᵢ²)marginalization term. Magnitude space (paper-exact) REJECTED: our noise maps are flux-space Gaussians; converting changes the error model, not just the parametrization. Magnification-first, matching `FitFluxes.model_data =T*T*+ norm term, as alternative to the current double min-subtraction (times_delays.py:109-110)D2. API (decision)
Model class — one new, parameter-free class in
autogalaxy/profiles/point_sources.py:__init__, so nofit_positions_clssetting can do it.profile.flux(F* comes from data + magnifications), soPointSolvedcovers positions+fluxes+delays datasets alone.FitFluxes(free flux) keeps requiringPointFlux.fit/abstract.py:84-90) unchanged — pairing is by dataset/profile name, not class.Fit side — a
SolvedCentremixin overriding the single funnel:AbstractFitPoint.source_plane_coordinate → profile.centre(abstract.py:144-153). The mixin overrides that property to return β* (tensor-weighted back-projection); everything downstream (incl. thePointSolverforward solve for image-plane fits,image/abstract.py:121-127) is untouched.FitPositionsSourceSolved,FitPositionsImagePairAllSolved,FitPositionsImagePairRepeatSolved,FitFluxesSolved,FitTimeDelaysSolved(names open to bikeshed).PointSolvedraises with a message naming the Solved variant; a Solved fit givenPoint/PointFluxraises (otherwise the centre priors would be sampled but ignored — silent 2-parameter waste).fit_flux_cls/fit_time_delays_clsonFitPointDatasetandAnalysisPoint, mirroringfit_positions_cls; defaults = currentFitFluxes/FitTimeDelays(behaviour unchanged). Currently hard-wired atfit/dataset.py:114,130.positions/abstract.py:48,image/abstract.py:49,pair.py:30,pair_all.py:26,pair_repeat.py:18) become true by pointing at the mixin; the workspace guide's falseprofile=Noneclaim (cluster/likelihood_function.py:312-318) is fixed in phase 4.Jacobian source:
A = I − ∂α/∂θassembles fromag.LensCalc.hessian_from(lens_calc.py:382, xp-threaded, JAX path exists) — same machinery as today's Hessian magnifications; no new differential-geometry code.D3. JAX plan (decision)
xp-threaded from day one; new fit classes registered inAnalysisPoint._register_fit_point_pytrees(model/analysis.py:181-218).FitPositionsSourcejit blockers —Grid2DIrregular.grid_2d_via_deflection_grid_fromgetsxp(PyAutoArray/autoarray/structures/grids/irregular_2d.py:170), andFitPositionsSourcejoins the pytree registration. This makes the series a 3-repo task: PyAutoArray + PyAutoGalaxy + PyAutoLens (recorded override of the FeatureDecision's single-repo estimate).custom_jvpvia the fixed-point rule∂θᵢ/∂p = Aᵢ⁻¹ ∂α/∂p) remain phase 5; phase 3's gradient scripts cover only the solver-free solved variants.D4. Phase-2 test matrix (numpy-only; JAX via workspace_test scripts only)
F*/T*equal brute-force scans; solved ≈ profiled parity for both.n_permutations == P_finite^Ion a counted mock; predicted-image surplus lowers likelihood monotonically.{Point,PointFlux,PointSolved} × {centre-requiring, solved} fits— every invalid cell raises with an actionable message.__Env__conventions mirrortest_autolens/point/.D5. Consequences for phases 3–5
fit_flux_cls/fit_time_delays_clshooks,FitFluxesSolved,FitTimeDelaysSolved, the PyAutoArray xp fix. Phase prompts updated accordingly.FitPositionsSource?) is answered by phase 3's clusterlikelihood_breakdownnumbers + phase 2's error-fidelity tests; phase 4 writes the recommendation.Jammy2211 commentedon Jul 27, 2026
Convention correction to D0/D2 above (before phase 2 reads it): with the standard lensing convention
A ≡ ∂β/∂θ(soA⁻¹is the magnification tensor,µ = 1/det A), the per-image precision on a back-traced position isWᵢ = Aᵢ⁻ᵀ Θᵢ Aᵢ⁻¹— inverse Jacobians, i.e. magnification-tensor weighting — andβ* = (Σᵢ Wᵢ)⁻¹ Σᵢ Wᵢ β̂ᵢ. Sanity check: the isotropic limit givesWᵢ → µᵢ²/σᵢ², matching the existing scalar weighting inFitPositionsSource.chi_squared_map. TheAᵀΘAform written above is correct only if A is read as∂θ/∂β; taken in the standard convention it would deweight high-magnification images — backwards. Phase 2 implementsWᵢ = Aᵢ⁻ᵀΘᵢAᵢ⁻¹with A assembled fromLensCalc.hessian_from, and the parity/brute-force tests in D4 guard the sign of this choice.Jammy2211 commentedon Jul 27, 2026
Resolution of the isotropic-limit dispute (raised by the phase-2 implementer — they were right): with
A = aI,µ = 1/a², so the tensor weighting's isotropic limit isW → (µ/σ²)I, notµ²/σ²as my correction comment claimed. Full picture: the eigenvalues ofW = σ⁻²A⁻ᵀA⁻¹areλ²/σ²with λ the linear stretch per eigendirection — isotropically each stretch is√µ(→µ/σ²), while near a critical curve the tangential stretch is ≈ µ (→µ²/σ²along the arc). So the existing scalarµ²/σ²convention (FitPositionsSource, Lenstool-style) is the near-critical tangential limit, not the isotropic one, and the tensor is exact in both regimes. Implementation keeps both:weighting=\"jacobian\"(exact, default) andweighting=\"magnification\"(traditional scalar, for comparisons). Also fixed in review: the 2-D marginalization constant is now the exact(2π)²Gaussian-integral normalization. Bonus catch by the implementer:ag.LensCalc.jacobian_fromreturns (x,y)-ordered components against the codebase's (y,x) convention — invisible to its determinant-only existing callers, re-indexed at the new consumption site and flagged in its docstring.Jammy2211 commentedon Jul 27, 2026
Phase 2 library PRs created (all suites green: autoarray 906, autogalaxy 1009, autolens 473; all labeled pending-release):
grid_2d_via_deflection_grid_from(merge first)ag.ps.PointSolved(+jacobian_fromordering docstring warning)*Solvedfit classes,SolvedCentremixin,fit_flux_cls/fit_time_delays_clshooks, pytree registrations, mismatch errors, docstring truth sweepAPI changes are additions-only — no existing script is affected. Heart was YELLOW at ship (pre-existing, unrelated reasons; human-acknowledged: workspace validation 13-failed of 2026-07-21 [predates today's searches-shard fix], 33 stale parked scripts, tenant-firewall manifest drift ×5).
Next: phases 3 (workspace_test jax_likelihood + profiling likelihood_runtime/breakdown examples) and 4 (guides) via /start_workspace after these merge; phase 5 (JAX solver gradients) last.
Jammy2211 commentedon Jul 27, 2026
Independent Codex review (codex-cli 0.145.0) of the three PRs: FINDINGS — 9 items. Disposition after adversarial verification:
noise_normalizationuseddet Wᵢ(model-dependent) instead of observed-planedet Θᵢ— spuriously favours high magnificationΣ log((2π)²σᵢ⁴);FitPositionsSource's magnified-noise convention intentionally unchanged, difference documentedjacobian_fromcrashes under jit for all solved fitsxp.asarray(deflection_grid)hits__array__on traced wrappers.arraybefore subtraction; harness now passes where it crashedFitPointDataset's component-skipexceptblocksexc.PointProfileMismatchException, deliberately not aPointExtractionExceptionsubclass; regression test throughFitPointDatasetxp=npdefault replaced the pre-existingself._xppropagationxp=None → self._xpMockPointSolverignoressource_plane_coordinate→ forwarding untestedRecordingMockSolvertestBonus from the fix cycle: the pre-existing source-plane jit blocker is now genuinely cleared —
scripts/point_source/jax_likelihood/source_plane.py's vmap+jit path passes (wasTracerArrayConversionError); only the separately-trackedfit_from-returns-PointSolverpytree gap remains (pre-existing, out of scope).All suites green after fixes: autolens 477, autoarray 906, autogalaxy 1009. Fix commits pushed to PyAutoLabs/PyAutoArray#414 and #659 (PyAutoGalaxy#531 unchanged by the review).
Merge pull request #659 from PyAutoLabs/feature/point-source-chi-squa…
Jammy2211 commentedon Jul 27, 2026
Phases 1+2 complete — all three library PRs MERGED (2026-07-27, post codex-review fixes; CI green on 3.12+3.13 everywhere): PyAutoLabs/PyAutoArray#414 → PyAutoLabs/PyAutoGalaxy#531 → #659. Feature branches and the task worktree are cleaned up;
active.mdreleased the library-repo claims.Resuming here (phases 3–5): run
/start_workspaceonPyAutoMind/draft/feature/autolens/point_source_chi_squared_paper_variants_phase_3_workspace_examples.md(workspace_test jax_likelihood + profiling likelihood_runtime/breakdown examples — note the solved source-plane path already passes vmap+jit with exact numpy parity, so phase 3 mostly extends harnesses + records baselines), then phase 4 (guides incl. thelikelihood_function.pyfalse-claim fix), then phase 5 (PointSolvercustom_jvpgradients). The phase prompts carry the binding design deltas + this thread has the weighting-convention resolution.Jammy2211 commentedon Jul 30, 2026
Phase 3 shipped — 2026-07-30
PRs (all merged)
Summary
Every phase-2 solved fit class now has JAX regression coverage: vmap literals with exact numpy parity for
FitPositionsSourceSolved(−94.70750993),FitPositionsImagePairAllSolved(−82.33883111),FitPositionsImagePairRepeatSolved(−89.71129442), and the fluxes+time-delays combo (−137.67455417);jit(fit_from)passes exactly for all image-plane solved variants; finite-difference gradient checks pass for the solved source-plane fit. Profiling: the analytic β* step costs ~timing-noise-level overhead per likelihood (cluster image-plane net +3% across systems, source-plane +9–30% on sub-0.1 s eager totals) while removing 2 free parameters per point source from the non-linear space.Session Notes
jit(fit_from)gap is the fit-return pytree issue (PointSolverleaf, output component [1][1]) — tracked in PyAutoPrompt/autolens/fit_point_pytree.md, affects old and solved fits alike, vmap/log_likelihood_functionunaffected.al.output_to_jsonhas nondeterministic JSON key order run-to-run (value-identical output; PYTHONHASHSEED-driven) — filed in Mind ideas for a serializer fix.imaging/subhalo_recovery.pyexceeds the 300 s smoke cap on the WSL dev laptop (solo, quiet machine) while green on main CI — margin worth watching after the parked-sweep re-measure.profile=None-centroid claim incluster/likelihood_function.py), 5 (PointSolvercustom_jvpgradients).Jammy2211 commentedon Jul 31, 2026
Phase 4 (guides) PR open — 2026-07-31
PR
pending-release, awaiting human merge)Upstream
Library API already on main from phase 2 (PyAutoArray#414 / PyAutoGalaxy#531 / PyAutoLens#659) — no library-first merge gate applies.
Summary
The workspace guides now document the full likelihood-option matrix (pairing scheme × free-vs-solved centre): the pairing guide carries the deep prose (solved-variant table, tensor
weighting = "jacobian"|"magnification"conventions, solved fluxes/time delays, missing-image-penalty status, runnable solved-centre demo),point_source/fit.pygains a runnable__Solved Source Centre__section plus the honest defaults story (FitPointDataset→FitPositionsImagePairvsAnalysisPoint→FitPositionsImagePairRepeat), and the falseFitPositionsSource(profile=None)back-traced-barycenter claim incluster/likelihood_function.pyis replaced with the real centre-free option. The deferred cluster recommendation (phase-1 design item) is now written from the phase-3 numbers: search withFitPositionsSourceSolved, validate with the image-plane chi-squared; demonstrated defaults in the modeling scripts are unchanged.Session Notes
interferometer/start_here.pyOOM (~86 GB JAX allocation) in the release-wheels leg — unrelated to this docs-only change; filed asPyAutoMind/draft/bug/autolens/interferometer_release_leg_oom.md.custom_jvpgradients) — note PyAutoLens is currently claimed by potential-correction-validation (fix: potential-correction end-to-end validation (parity + evidence sampling) #672); hand-check file overlap before starting.Jammy2211 commentedon Jul 31, 2026
Phase 5 (solver gradients) PRs open — 2026-07-31
PRs (library-first; all
pending-release, awaiting human merge)custom_jvp(merge FIRST)multi_start_prodigyimage_plane_solvedcellSummary
PointSolverpositions now carry gradients via an implicit fixed-pointcustom_jvp(autolens/point/solver/implicit_diff.py):A dθ = dα|_θ + dβat the solved positions — the same mechanism gravity.jl itself uses (the paper never differentiates through its solver; its autodiff only verifies the analytic rule). FD-certified across the variant matrix: solved and sampled centres through the solver (≤2% vs the stair-noise floor, spike best-steps <0.4%), PairRepeat subgradients live, and nested autodiff supplies the flux/magnification gradients the paper declares analytically out of reach.af.MultiStartProdigycan now run on image-plane point-source likelihoods (first search-integration cell registered in the profiling sweep). All values are unchanged: numpy path byte-identical,test_autolens/492 passed, everyjax_likelihoodregression literal re-verified.Session Notes
pixel_scale_precision, so naive FD reads exactly zero against a correct nonzero AD gradient (the exact-solve envelope derivative — what a gradient search wants). Certification uses a fine-precision solver + per-parameter FD step sweep; methodology encoded in the cert script prose.af.Model/Fitnessregistered path); hand-built tracers keep the old zero-gradient forward — nothing existing changes behaviour.custom_jvpboundary (Tracerkeepscosmologyasno_flattenaux → stale tracer). Harmless for 2-plane positions; multi-plane free-cosmology gradient fits need cosmology flattened into the pytree.FitPositionsImagePairAllmixture underflows to-infat ≳38σ mismatch (not a stable logsumexp) — shrinks the gradient-search basin; stabilization follow-up in Mind ideas.imaging/subhalo_recovery.pyWSL 300s-cap margin, green on CI.Merge pull request #677 from PyAutoLabs/feature/point-solver-implicit…
Jammy2211 commentedon Jul 31, 2026
Gradient-search benchmark — the gradients work in a real search (2026-07-31)
PR: PyAutoLabs/autolens_profiling#99 (truth-anchored Prodigy-vs-Nautilus on the galaxy-scale point-source dataset; full table + six result JSONs inside).
Headline:
MultiStartProdigyonPointSolved+FitPositionsImagePairAllSolvedconverges to the truth through the lens-equation solver — er 1.574, centre (0.00, 0.01) vs truth (1.6, (0,0)), logL +2.37 vs truth anchor +7.74, in ~16 min with the default 64×300 budget. This is the implicit-diffcustom_jvpdoing real work end-to-end.Supporting findings:
PairAll-inf-underflow plateau issue and the solved variant's dimensionality edge.Follow-ups filed in Mind per human decisions: cluster examples move demonstrated default to
PointSolved(galaxy-scale stays free-centre); cluster-scale gradient-search benchmark next after point-source wraps.Jammy2211 commentedon Aug 6, 2026
Closing as shipped: design fully implemented across all 5 phases — PyAutoArray#414, PyAutoGalaxy#531, PyAutoLens#659/#677, autolens_workspace#425 all merged (2026-08-06 tracker reconciliation audit).