Skip to content

fix: honor warm start across all inner solvers (#24) - #25

Merged
tschm merged 1 commit into
mainfrom
fix/eq-warm-start-all-inner-solvers
Jul 5, 2026
Merged

fix: honor warm start across all inner solvers (#24)#25
tschm merged 1 commit into
mainfrom
fix/eq-warm-start-all-inner-solvers

Conversation

@tschm

@tschm tschm commented Jul 5, 2026

Copy link
Copy Markdown
Member

Closes #24.

Problem

solve_nnqp / solve_nnqp_eq accept a warm=(free_mask, x_prev) tuple, but it was only partially wired. pcg had no x0 parameter, so the inner-solve warm seed never reached it, and the docstrings overstated the gap — claiming only inner="cg" consumed the warm start.

In fact the outer warm start (starting the active-set loop from the prior free set) already applied to every inner solver — that is what yields the single-outer-step property on a support-stable step. Only the inner-solve seeding was cg-only.

Change

  • krylov.py — add an x0 warm start to pcg, mirroring cg (initial residual b - A x0); return zeros for a zero rhs regardless of x0.
  • solver.py — thread x0 into the pcg branch of _make_free_solve; rewrite the warm docstrings so they are precise: cg and pcg both consume the v0 inner seed, exact is a direct solve with nothing to seed, and all three start from the warm free set (single outer step on a support-stable step). Also document why the v1 columns stay cold — their right-hand sides are the rows of B_F, unrelated to x_prev.
  • tests (+4, 63 → 67)
    • test_krylov.py::test_pcg_warm_start_reduces_iterations — direct proof pcg honors x0.
    • test_eq.py — parametrized the support-stable single-step test over cg/pcg; added test_eq_warm_start_exact_single_outer_step pinning the intentional "exact has no inner seed" behaviour (outer == 1 from the warm free set alone).
    • test_solver.py::test_warm_start_pcg_reduces_inner_iterations — covers the new pcg x0 path in the bound-only solver.

Acceptance criteria (from #24)

  • ✅ A support-stable warm solve_nnqp_eq step terminates in a single outer step — now asserted for cg and pcg.
  • pcg now honors the warm start; exact's ignore is documented-as-intentional with a dedicated test.

Verification

make test → 67 passed, 100% coverage. make fmt and make typecheck (ty + mypy --strict) both clean.

🤖 Generated with Claude Code

The warm= start was only partially wired: pcg had no x0 parameter, so
the inner-solve seed never reached it, and the docstrings claimed only
inner="cg" consumed the warm start at all. The outer warm start (starting
from the prior free set) in fact already applied to every inner solver —
only the inner-solve seeding was cg-only.

- krylov.py: add an x0 warm start to pcg, mirroring cg (initial residual
  b - A x0); return zeros for a zero rhs regardless of x0.
- solver.py: thread x0 into the pcg branch of _make_free_solve; rewrite
  the warm docstrings — cg and pcg both consume the v0 inner seed, exact
  is direct with nothing to seed, and all three start from the warm free
  set (single outer step on a support-stable step). Document why the v1
  columns stay cold (their rhs is B_F, unrelated to x_prev).
- tests: prove pcg honors x0 (test_krylov, test_solver); parametrize the
  eq support-stable single-step test over cg/pcg; pin the intentional
  "exact has no inner seed" behaviour with a dedicated eq test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 5, 2026 07:17
@tschm
tschm merged commit 77f3481 into main Jul 5, 2026
56 checks passed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR completes warm-start support across inner solvers by adding x0 seeding to pcg and threading it through the NNQP solver paths, while updating docstrings and tests to reflect the intended warm-start semantics for cg, pcg, and exact.

Changes:

  • Add an x0 warm-start parameter to pcg (analogous to cg) and propagate it through _make_free_solve.
  • Clarify warm-start behavior in solve_nnqp / solve_nnqp_eq docstrings (outer free-set warm start applies to all inners; inner seeding applies to cg/pcg, not exact).
  • Expand tests to assert pcg iteration reductions from warm-starting and to cover equality-solver warm-start behavior across cg/pcg and the intentional exact semantics.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/nncg/krylov.py Adds x0 warm start handling to pcg and adjusts zero-RHS behavior.
src/nncg/solver.py Threads x0 into the pcg free-block solve path and updates warm-start docstrings.
tests/test_krylov.py Adds a test asserting warm-start reduces PCG iterations.
tests/test_eq.py Parametrizes warm-start support-stable test over cg/pcg; adds exact warm-start outer-step test.
tests/test_solver.py Adds bound-only solver test that warm-start reduces PCG inner iterations and yields one outer step.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/nncg/krylov.py
Comment on lines +105 to +116
if x0 is None:
x = np.zeros_like(rhs)
r = rhs.copy()
else:
x = x0.astype(np.float64, copy=True)
r = rhs - matvec(x)
z = dinv * r
p = z.copy()
rz = float(r @ z)
bnorm = float(np.linalg.norm(rhs))
if bnorm == 0.0:
return x, 0
return np.zeros_like(rhs), 0
@tschm
tschm deleted the fix/eq-warm-start-all-inner-solvers branch July 5, 2026 14:24
@tschm
tschm restored the fix/eq-warm-start-all-inner-solvers branch July 13, 2026 20:34
@tschm
tschm deleted the fix/eq-warm-start-all-inner-solvers branch July 14, 2026 09:45
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.

Complete solve_nnqp_eq warm-start across all inner solvers

2 participants