Skip to content

refactor(mprgp): extract the three moves, and document the solvers by example - #88

Merged
tschm merged 1 commit into
mainfrom
quality/mprgp-decomposition-and-doctests
Aug 15, 2026
Merged

refactor(mprgp): extract the three moves, and document the solvers by example#88
tschm merged 1 commit into
mainfrom
quality/mprgp-decomposition-and-doctests

Conversation

@tschm

@tschm tschm commented Aug 15, 2026

Copy link
Copy Markdown
Member

What

Two changes to readability, no change to behaviour.

MPRGP loop decomposition. _mprgp held the conjugate-gradient, expansion and proportioning moves inline, so the two things the loop actually decides — the projected-gradient stopping test and the proportioning switch — were buried in the numerics of the moves themselves. Each move is now its own function over an Iterate = (x, g, p) state:

  • _cg_step — minimise within the current face; costs no Hessian product of its own.
  • _expansion_step — walk to the bound, then one projected-gradient move; the projection is non-linear, so this is the step that pays a second product.
  • _proportioning_step — release constraints along the chopped gradient.
  • _proportional_step — makes the feasibility choice between the first two and reports how many products it consumed.

_mprgp keeps only the loop, the stopping test and the switch. The per-move counters collapse into a dict keyed by the move name the step returns, so products is accounted for at the one place that knows the cost.

Doctests on ActiveSetSolver, MPRGP and the nncg.inner module header, covering the things a reader gets wrong first: that A enters as a SymmetricOperator and never as a bare array, that kkt_violation scores the answer independently of the solver's own converged flag, that the inner solvers are interchangeable without touching the outer loop, and that MPRGP's step counts sum to iterations while hessian_products exceeds it.

Verification

  • make test — 138 passed, 100% line coverage on every module
  • make typecheck — ty and mypy strict, clean
  • make fmt — all hooks pass
  • pytest --doctest-modules src/nncg — 7 passed

Note for the reviewer

The doctests are not wired into any gate, so nothing runs them in CI and they can rot. Adding --doctest-modules to pytest.ini would fix that, but pytest.ini is Rhiza-owned — it belongs in the template, not in a hand-edit here. Flagging rather than fixing.

🤖 Generated with Claude Code

… example

MPRGP's loop body held the conjugate-gradient, expansion and proportioning
moves inline, so the proportioning switch and the stopping test were buried
in the numerics of the moves themselves. Each move is now its own function
over an `Iterate = (x, g, p)` state, with `_proportional_step` making the
feasibility choice between the CG and expansion moves and reporting the
Hessian products it consumed. `_mprgp` keeps only the loop, the stopping
test and the switch; the per-move counters become a dict keyed by the move
name the step returns.

Add doctests to `ActiveSetSolver`, `MPRGP` and the `nncg.inner` module: the
operator-not-array calling convention, the KKT certificate scoring the
answer independently, the interchangeable inner solvers, and MPRGP's step
counts summing to `iterations`.

Behaviour is unchanged — 138 tests pass at 100% line coverage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 15, 2026 06:32

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

Refactors the MPRGP implementation to make the outer loop’s two key decisions (projected-gradient stopping test and proportioning switch) clearer, and adds doctest-style examples to the public solver surfaces to document expected usage patterns (operator-based A, interpretation of converged vs kkt_violation, and solver interchangeability).

Changes:

  • Decomposes the MPRGP loop into explicit move functions (_cg_step, _expansion_step, _proportioning_step) plus a selector (_proportional_step), while keeping behavior/cost accounting consistent.
  • Adds doctest examples to ActiveSetSolver, MPRGP, and the nncg.inner module header to document typical usage and result interpretation.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/nncg/solver.py Adds an ActiveSetSolver doctest example demonstrating operator inputs, solution properties, and inner-solver swapping.
src/nncg/mprgp.py Extracts MPRGP moves into dedicated functions and adds MPRGP doctest examples + consolidated step/product accounting.
src/nncg/inner.py Adds module-level doctest examples showing how different inner solvers plug into the same outer loop and expected behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@tschm
tschm merged commit a4c279e into main Aug 15, 2026
64 checks passed
@tschm
tschm deleted the quality/mprgp-decomposition-and-doctests branch August 15, 2026 06:53
@tschm
tschm restored the quality/mprgp-decomposition-and-doctests branch August 15, 2026 06:57
@tschm
tschm deleted the quality/mprgp-decomposition-and-doctests branch August 15, 2026 07:01
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.

2 participants