Skip to content

feat: NNLS solver knobs via Settings (nnls_solver_tol / nnls_max_iter), default off - #371

Merged
Jammy2211 merged 2 commits into
mainfrom
feature/nnls-solver-optimization
Jul 9, 2026
Merged

Jammy2211 merged 2 commits into
mainfrom
feature/nnls-solver-optimization

Conversation

@Jammy2211

Copy link
Copy Markdown
Collaborator

Summary

Per-fit knobs for the JAX positive-only (NNLS) interior-point solve, exposed on the Settings class and default off (unset = bit-identical to upstream jaxnnls, validated in solutions and gradients on real production systems):

  • Settings(nnls_solver_tol=1e-6) — ~15-20% of solve time on production HST pixelization+MGE fits, rel Δobjective 3.8e-13 (Δlog-evidence ~1e-8).
  • Settings(nnls_max_iter=...) — caps the solve; under vmap this bounds the worst-case (slowest-lane) batched cost.

Implementation: autoarray/util/jax_nnls.py vendors only jaxnnls's while_loop driver (tolerance/cap are hard-coded upstream); every building block (initialize, pdip_pc_step, solve_relaxed_nnls, diff_nnls) and the relaxed-KKT custom-vjp backward pass are reused from jaxnnls. Knobs are static closure params behind an lru_cache so JIT tracing caches hit. NumPy (fnnls_cholesky) path untouched.

Evidence

Full measurement record: #369 and autolens_profiling/results/notes/nnls_solver_ledger.md (the closed optimization ledger — every deeper lever measured negative; this knob is the one real win).

Test plan

  • New test_autoarray/util/test_jax_nnls.py: module-level JAX-import guard, Settings defaults-off, NumPy-path invariance to the knobs.
  • Full suite: 876 passed.
  • End-to-end JAX validation on the real extracted rect/Delaunay systems: defaults bit-identical to upstream (solutions + gradients); knob reproduces the probe numbers (285→249ms on RTX 2060).
  • Downstream: no API breakage (new optional Settings args); workspace smoke via the normal release gate.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QgtjXWS2iJegXMTDU4GHth

Jammy2211 and others added 2 commits July 9, 2026 22:18
…) — PARKED, not for merge (#369)

Vendored jaxnnls while_loop driver with configurable tolerance and
iteration cap; defaults bit-identical to upstream (solutions and
gradients, validated on real production systems). Parked at user request
2026-07-09 — the measured win (~15-20% of solve) was judged not worth the
new solver code path; this branch preserves the validated implementation
should that change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QgtjXWS2iJegXMTDU4GHth
…), default off (#369)

Rework of the parked d8a1c84: the knobs move from general.yaml config keys
to per-fit Settings attributes (defaults None = jaxnnls's own tolerance
formula and 50-iteration cap — behaviour identical when unset). The vendored
autoarray/util/jax_nnls.py driver is unchanged: reuses all jaxnnls building
blocks + the relaxed-KKT custom-vjp backward; defaults validated bit-identical
to upstream in solutions and gradients on real production systems.

Measured on the real HST pixelization+MGE systems (PyAutoArray#369):
Settings(nnls_solver_tol=1e-6) saves ~15-20% of solve time with rel
delta-objective 3.8e-13 (delta log-evidence ~1e-8); nnls_max_iter also caps
the vmap worst-case lane. Full ledger: autolens_profiling
results/notes/nnls_solver_ledger.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QgtjXWS2iJegXMTDU4GHth
@Jammy2211 Jammy2211 added the pending-release PR queued for the next release build label Jul 9, 2026
@Jammy2211
Jammy2211 merged commit e0cb2fa into main Jul 9, 2026
4 checks passed
@Jammy2211
Jammy2211 deleted the feature/nnls-solver-optimization branch July 9, 2026 21:42
@Jammy2211 Jammy2211 removed the pending-release PR queued for the next release build label Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant