Skip to content

aa: opt-in trust region + adaptive regularization#54

Open
bodono wants to merge 6 commits into
masterfrom
aa-trust-factor
Open

aa: opt-in trust region + adaptive regularization#54
bodono wants to merge 6 commits into
masterfrom
aa-trust-factor

Conversation

@bodono
Copy link
Copy Markdown
Member

@bodono bodono commented May 15, 2026

Summary

Adds a single new aa_init parameter, trust_factor, that turns on two coupled mechanisms for slow-contraction maps (ADMM/DRS). trust_factor = INFINITY (the new "no cap" sentinel) is the default and keeps the existing behavior — no test changes for callers who don't opt in.

When trust_factor is finite and positive (recommended value: ~10):

  1. Trust region — each solve rejects the step if ||D γ||_2 > trust_factor · ||g||_2. This catches the failure mode where γ passes the LS solve and weight-norm cap, but the resulting iterate displacement is much larger than the current residual — i.e. the LS basis is pointing somewhere unhelpful. Each step marginally satisfies the safeguard's monotone test, but the iterate drifts laterally rather than approaching the fixed point.

  2. Adaptive regularization — replaces ε·||S||_F·||Y||_F with a self-tuning r:

    • Initial value: 1.0 (so initial γ ≈ 0, i.e. AA ≈ DRS).
    • On each safeguard accept: r *= 0.9 (earn trust, let AA do more).
    • On each rejection (safeguard, trust, or in-solve): r *= 10 (back off toward DRS).
    • Clamped to [1e-12, 1e30].

The two mechanisms feed each other: the trust region detects bad steps, adaptive r damps γ in response, the next solve produces smaller γ and the trust check usually passes. The equilibrium r settles at the value appropriate for the problem.

Same INFINITY-as-sentinel convention now applies to max_weight_norm as well — previously an isfinite() check rejected it; the new validation uses isnan() || ≤ 0, so callers can write INFINITY for both fields to mean "no cap." The 34 existing tests pass through identically because they don't opt in.

Why this exists

Diagnosed on the SCS Maros-Meszaros / Netlib QP/LP benchmark. With the best AA settings at default aa_init parameters, ~30 problems hit max_iters for type-II while type-I solved them. The mechanism:

  • Near convergence on slow-contraction maps, ||Y||_F and ||S||_F shrink and r = ε·||S||·||Y|| underflows to ~1e-15.
  • γ blows up from the resulting LS solve (~||γ||₂ = 700 observed on PRIMALC1).
  • The safeguard's monotone test ||g_new|| ≤ ||g_old|| is too weak to catch this — a wild step can satisfy it by a hair while the iterate drifts laterally.
  • The result is "creep": many barely-improving steps that don't approach the fixed point.

The trust region directly catches the "step too big" condition, and adaptive r provides the feedback so AA self-tunes per-problem rather than picking a magic regularization at init time.

Empirical results (SCS benchmark)

On the type-II failure set (33 problems where type-II hits max_iters at default settings, while type-I solves):

Config Type-I solved Type-II solved Regressions on working sets
Master (pre-PR #53) 33/33 0/33
PR #53 (sym S
This PR (trust_factor=10) 30/33 30/33 none on type-I or type-II

Both types now solve essentially all the problems the other can solve, at parity. The 3 still-failing problems (stair, capri, share2b) also hit max_iters with plain DRS — they're DRS-level failures, not AA failures.

Test plan

  • make test passes all 34 existing tests
  • New validation tests added for trust_factor (NaN/0/negative rejected; INFINITY and positive finite values accepted)
  • max_weight_norm=INFINITY now accepted (previously rejected — see flipped assertion in test_nonfinite_scalar_options_rejected)
  • SCS benchmark on Maros-Meszaros + Netlib: 30/33 recovery on both types, 0 regressions

Breaking changes

  • aa_init signature now takes trust_factor between max_weight_norm and ir_max_steps. Callers in this repo (tests, gd, bench, Python binding) all updated to pass INFINITY (= old behavior). Downstream callers (e.g. SCS) need a one-line update to pass INFINITY for the new parameter, or a finite positive value to opt in.

🤖 Generated with Claude Code

bodono and others added 6 commits May 14, 2026 21:53
The type-II regularizer was ε · ||Y||_F², while type-I used ε · ||S||_F ·
||Y||_F. On slow-contraction maps (typical of ADMM/DRS applications), the
type-II scaling decays quadratically as ‖Y‖ → 0 and underflows to noise
(~1e-15) near the optimum. The augmented LS solve then produces huge γ
values, and the safeguard's monotone check (‖g_new‖ ≤ ‖g_old‖) is too
weak to catch the resulting "creep" — each step marginally reduces the
residual without approaching the fixed point.

Switching type-II to the same ||S||_F · ||Y||_F formula keeps r in a
useful range without changing behavior on cases where ||S|| ≈ ||Y||
(gradient descent on convex quadratics, etc.). Diagnosed on the SCS
Maros-Meszaros / netlib benchmark where ~30 problems previously hit
max_iters with type-II at default settings; this change is a meaningful
component of the recovery on those.

All 34 existing tests pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ust_factor accept INFINITY)

Adds a single new aa_init parameter, `trust_factor`, that turns on two
coupled mechanisms for slow-contraction maps (ADMM/DRS):

  1. Trust region: each solve rejects the step if ||D γ||_2 exceeds
     trust_factor * ||g||_2. Catches the failure mode where γ passes the
     LS solve and the weight-norm cap but the resulting iterate
     displacement is much larger than the current residual — the LS
     basis is pointing away from the descent direction. Each step
     marginally satisfies the safeguard's monotone test while the
     iterate drifts laterally, accumulating to no real progress.

  2. Adaptive regularization: replaces the ε·||S||_F·||Y||_F baseline
     with a self-tuning r. Starts at 1.0 (so γ ≈ 0 and AA ≈ DRS),
     shrinks by 0.9× on each safeguard accept (let AA do more), grows
     by 10× on any rejection (trust, safeguard, or in-solve). This is
     what keeps LASER-style problems converging even when the trust
     region trips occasionally — each trip damps subsequent γ.

The two mechanisms feed each other: trust region detects bad steps,
the adaptive r bumps the regularization in response, the next solve
produces smaller γ and the trust check usually passes. Equilibrium
settles at the r value appropriate for the problem.

trust_factor = INFINITY (the new "no cap" sentinel) disables both
mechanisms and keeps the original ε·||S||·||Y|| path, so existing
callers see no behavior change. Same convention now applies to
max_weight_norm: INFINITY is accepted (was previously rejected by an
isfinite() check).

Diagnosed and validated on the SCS Maros-Meszaros / Netlib QP/LP
benchmark, where ~30 problems previously hit max_iters with type-II
at default settings. With trust_factor=10, both types now solve 30/33
of those problems cleanly, with no regression on either working set
(no problem that previously solved becomes inaccurate).

All 34 existing aa tests pass — they implicitly opt out by passing
INFINITY for the new parameter.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
max_weight_norm = inf is now accepted (was rejected by the old
isfinite() check), and trust_factor follows the same convention. NaN
and non-positive values remain rejected with an updated error message.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a parenthetical to the `regularization` row in the parameters
table calling out that Type-I and Type-II now share the same scaling
formula (the original Type-II `‖Y‖²` underflowed on slow-contraction
maps).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add trust_factor to the parameters table.
- Add a new "Trust-region mode (opt-in)" section explaining the
  mechanism, when it helps (ADMM/DRS slow-contraction maps), and
  when to leave it off (the default).
- Update C / Python signatures to show the new parameter.
- Update max_weight_norm row to note INFINITY is now an accepted
  "no cap" sentinel.
- Expand the stats / diagnostics tip to point at trust_factor as a
  remedy for the high-n_safeguard_reject + low-last_regularization
  + high-last_aa_norm pattern.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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