Skip to content

Report solver stats - #84

Merged
timholy merged 7 commits into
mainfrom
teh/stats-reporting
Oct 9, 2026
Merged

timholy merged 7 commits into
mainfrom
teh/stats-reporting

Conversation

@timholy

@timholy timholy commented Oct 9, 2026

Copy link
Copy Markdown
Member

This adds systematic solver stats for the optimizing methods in the package.

When the active-set finish certifies a minimizer, `stats.converged` was
set from the finish but the only residual reported was the multiplier
iteration's `kkt` trace, which can sit far above `vtol`. The finish now
reports its own KKT check (`primal`, `ptol`, `dual`, `dtol`) in
`stats.polish`.

A finish that fails to certify returns the multiplier iterate, and now
always warns; previously it warned only when that iterate's residual
exceeded `vwarn`.

With `maxouter = 0` (the soft AbsLog{2} covers) nothing is iterated, so
`converged` is now `false` rather than `true`; no warning is issued.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
`symcover_min`, `cover_min`, `cover_transversal`, their mutating forms,
and the soft `AbsLog{2}` covers now return a statistics object as their
last output: `a, stats = symcover_min(A)`, `a, b, stats = cover_min(A)`.

The new public (unexported) `AbstractCoverStats` has one subtype per
solver family; this commit adds `AugmentedLagrangianStats` (with
`ActiveSetStats` for the finish) and `LeastSquaresStats`. The accessors
`converged`, `iterations`, `residual`, and `tolerance` describe the
returned cover; `LeastSquaresStats` implements none of them, since the
soft cover has no stopping criterion.

The augmented Lagrangian gains a `tol` keyword (default `1000eps`), and
any result that does not meet it warns; the separate warning threshold
is gone. The public methods declare their keywords instead of
forwarding them to the workers, so internal options are no longer
reachable, and the sparse methods delegate to the generic ones.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
`soft_symcover`, `soft_cover`, and their mutating forms with a
`PowerMean` penalty, including the no-penalty forms that default to
`PowerMean{2}()`, now return a `PowerMeanStats` object as their last
output: `a, stats = soft_symcover(A)`, `a, b, stats = soft_cover(A)`.

`PowerMeanStats{T}` is a public (unexported) subtype of
`AbstractCoverStats`. `converged`, `residual`, and `tolerance` report
whether the largest row (and column) imbalance of the returned cover
meets `tol`; `iterations` counts power-mean updates plus Newton steps.
The element type is the working type of the solve, so the result is
inferrable. The warning on an unconverged solve is unchanged.

The public methods declare their keywords (`tol`, `maxiter`, `newton`,
`maxnewton`, `linsolve`) instead of forwarding them, with the same
defaults. The private solvers and the statistics tuples they return are
unchanged.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
The covers computed through JuMP now return a statistics object as
their last output, like the native solvers: `symcover_min`,
`cover_min`, `soft_symcover`, `soft_cover`, and their mutating forms
with `AbsLog{1}` (HiGHS) or `AbsLinear` (Ipopt).

`ExternalSolverStats` records one entry per optimization stage: the
solver, the termination status (as a `Symbol`), the objective value of
the model, the iteration count the solver reports, and its run time.
The `AbsLog{1}` covers run two stages (the LP, then the QP over its
optimal face); Ipopt runs one. Failures still throw `SolverFailure`,
so `converged` is always `true`; `iterations`, `residual`, and
`tolerance` are not implemented.

The `AbsLinear` multistarts return `MultistartStats`, which holds the
label, statistics, and objective of each start whose refinement
succeeded, the index of the selected one, and the labels of the
random starts that failed. Its accessors delegate to the selected
start. The test-only `labels`/`objs` keywords of the soft multistart
are removed, since the statistics carry the same information.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
Reading HiGHS's QP iteration counter required its C interface, whose
info keys are not guaranteed across the HiGHS versions allowed by
`[compat]`. The counts are now the simplex and interior-point
iterations that MathOptInterface reports, which do not include the QP
stage.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
Update the docstrings of the optimizing solvers for the trailing stats
output, the `tol` keyword of the AbsLog{2} hard covers, and the policy
of warning (native solvers) versus throwing SolverFailure (JuMP-backed
solvers). Port the doctests to the new return values and add a short
manual section on convergence and solver statistics.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.08676% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 99.07%. Comparing base (7389e12) to head (133689e).

Files with missing lines Patch % Lines
src/stats.jl 94.11% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #84      +/-   ##
==========================================
- Coverage   99.09%   99.07%   -0.03%     
==========================================
  Files          21       22       +1     
  Lines        6085     6132      +47     
==========================================
+ Hits         6030     6075      +45     
- Misses         55       57       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

`symcover_min(::AbsLinear, A)` built its starts in a comprehension that
captured the element type, which Julia 1.10 cannot infer; it now copies
a typed template vector.

Two tests assumed that a solve without the active-set finish stalls
above the tolerance, which depends on rounding and fails on Julia 1.10.
They now check that the solve warns exactly when it does not converge.
The multistart test's closure no longer captures a type through a local
variable.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
@timholy
timholy merged commit e177c5e into main Oct 9, 2026
4 of 5 checks passed
@timholy
timholy deleted the teh/stats-reporting branch October 9, 2026 12:29
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