Skip to content

Add a PowerMean off-transversal, target t<1 minimizer - #95

Merged
timholy merged 5 commits into
mainfrom
teh/maxbalance
Oct 24, 2026
Merged

timholy merged 5 commits into
mainfrom
teh/maxbalance

Conversation

@timholy

@timholy timholy commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

This adds a solver for a new approach to asymmetric square matrices: "make them as diagonally-dominant as possible." In the context of an asymmetric matrix, "diagonal" = "transversal." This aims for a target coverage ratio < 1 (default t = 0.01) for the off-transversal entries. It also adds a maxbalance option to compare against Hook, Pestana, Tisseur & Hogg 2019, Algorithm 3.10.

On a benchmark suite, the conditioning numbers are often improved by several fold over the older transversal-tight minimal cover, which itself was already a significant advance on the minimal cover.

`_transversal_system` builds the transversal-tight difference system
(maximum-product transversal, transversal log magnitudes, the edge list
of off-transversal entries, tight components, and the feasible start),
and `_contract_system` reduces it to one unknown per tight component.
`_finish_cover` applies the balance convention, checks that the scales
are representable, exponentiates, and certifies the cover.
`_cover_min_abslog2` now calls these helpers; its results are unchanged.

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

codecov Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.82609% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 98.71%. Comparing base (549fcc5) to head (e251706).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
src/balanced.jl 96.55% 17 Missing ⚠️
src/maxbalance.jl 99.47% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #95      +/-   ##
==========================================
- Coverage   98.84%   98.71%   -0.14%     
==========================================
  Files          23       25       +2     
  Lines        6343     7093     +750     
==========================================
+ Hits         6270     7002     +732     
- Misses         73       91      +18     

☔ 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.

`cover_transversal(PowerMean{p}(), A; target)` returns the hard cover
tight on the maximum-product transversals that minimizes
∑ (r^p - 1 - p*log(r/target)*target^p)/p over the coverage ratios r of
the other entries, pushing them toward `target`.

The solver is a primal active-set Newton method on the contracted
row-scale difference system: rows joined by zero-slack entries move
together as groups, Newton steps solve weighted graph-Laplacian systems
on the groups (dense or sparse Cholesky, or conjugate gradients over the
fill budget), a ratio test and Armijo backtracking keep every iterate
feasible, and at stationary points a maximum-flow test on each group's
active entries either certifies the KKT conditions or yields a set of
rows whose scales decrease, releasing entries. Every iterate is a hard
cover tight on the transversal, so an unconverged solve warns and still
returns one. Statistics are returned as `BalancedCoverStats`.

The objective's terms can span many orders of magnitude, so the solver
works at the resolution of each group's terms. The gradient and the
line-search slope sum the `target^p` part by group with integer counts,
so that the slack changes of a group with as many entries leaving as
entering cancel exactly. A ridge for a Newton matrix that roundoff makes
indefinite is relative to each diagonal entry, so it does not swamp the
curvature of groups whose terms are all small. In each weakly connected
part, the group pinned to remove the common shift is the one whose
gradient has the largest terms: the pinned group's gradient carries the
others' roundoff, which is then smallest relative to its own terms. A
Newton step whose slope is nonnegative but within the roundoff of the
groups' gradients is at the resolution of the arithmetic: the groups are
taken to be stationary and the multiplier test decides whether to
continue, so `converged` can hold with `gradnorm > tol`.

`cover_transversal(AbsLog{2}(), A)` and its mutating form forward to
the existing solver. The Ipopt extension adds the reference
`cover_transversal_jump(::PowerMean, A; target)`, and the Unitful
extension forwards the new methods.

test/balanced.jl checks the maximum-flow multiplier test on small
digraphs, the line search where slack changes cancel, where the slope is
at roundoff, and where an entry at zero slack blocks the step, the
covariance of a solve whose groups' terms differ greatly in size, and
the solver against the Ipopt reference (committed 5×5 matrices, random
sparse patterns, 0/1 matrices), against an extended-precision solve for
p = 4, the KKT identity across a block-triangular cut, a group holding
an inactive entry, the minimax ratio for large p, covariance,
permutation equivariance, independence of the transversal used, sparse
and conjugate-gradient solves, element types, generic axes, the
iteration limit, and the argument errors. The manual describes the new
method.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
`_strong_components(ptr, heads)` labels the strongly connected components
of a digraph given in compressed adjacency form, numbered in reverse
topological order. `_tight_components` calls it on the zero-residual
entries.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
`cover_transversal(ϕ::PowerMean{p}, A; target, maxbalance=true)` and its
mutating form max-balance the scales within each fully indecomposable
block of `A` (Hook, Pestana, Tisseur & Hogg 2019, Algorithm 3.10:
max-plus policy iteration for a cycle of largest mean, a shift by its
bias, contraction of the cycle) and then minimize the PowerMean objective
with a target ratio over the entries joining different blocks, by the
active-set Newton method on one scale per block. The products are
unique and scale-covariant.

A cycle mean accumulates the roundoff of up to `nv` weights, so the
policy iteration treats cycle means within `nv * tol` of each other as
equal; with `tol` as the threshold the comparisons are not transitive
and the iteration can cycle among tied cycles.

`BalancedCoverStats` records `maxbalanced` and the number of fully
indecomposable blocks, `nblocks`, for every solve.

test/maxbalance.jl covers Example 3.11 of Hook et al. (2019), policy
iteration on nearly equal cycle means, the cut balance by brute force
and the minimax ratio on random fully indecomposable matrices,
agreement with an independent implementation of their Algorithm 3.10,
block-triangular matrices (the KKT identity across each cut, an active
entry between blocks, covariance, the products of each block alone),
tied transversals, sparse and wrapped inputs, element types, and the
mutating form.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
cover_transversal gains the PowerMean method and its maxbalance keyword,
new public functionality since 1.1.0.

Assisted-by: Claude Fable 5.1 <noreply@anthropic.com>
@timholy
timholy merged commit d38a2c7 into main Oct 24, 2026
6 of 7 checks passed
@timholy
timholy deleted the teh/maxbalance branch October 24, 2026 11:02
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