Skip to content

CBC-drawn performance stack: factorized basis, presolve, GMI cuts, heuristics#11

Merged
andig merged 6 commits into
mainfrom
mip-cbc-heuristics
Jul 5, 2026
Merged

CBC-drawn performance stack: factorized basis, presolve, GMI cuts, heuristics#11
andig merged 6 commits into
mainfrom
mip-cbc-heuristics

Conversation

@andig

@andig andig commented Jul 4, 2026

Copy link
Copy Markdown
Member

MIP performance: CBC/Clp-drawn optimizations for the optimizer golden cases

Goal: all 13 optimizer golden cases (optimizer/test_cases/*.json) passing TestGoldenCases within a 30s solver budget. Techniques drawn from COIN-OR CBC/Clp, validated against real CBC (bundled PuLP binary, msg=1 logs, incl. a preprocess-off control run).

Scoreboard (30s budget)

12/13 pass (009–019 in 0.3–5.2s, 021 in 11s).

What's in this PR

Simplex / LP

  • EXPAND anti-degeneracy (Gill et al.): expanding working bounds + guaranteed minimum step, exact snap-back at refactorization, cleanup re-price before concluding
  • Sparse dual pivot row via a row-wise mirror of A: the dual ratio test and reduced-cost update touch only the BTRAN row's support (was a full-matrix pass per pivot); dual re-solve cap 512 → 1024, so heavy warm starts finish in the dual instead of truncating into primal Phase 1
  • Full Bland's rule (entering + leaving); Clp-style bound perturbation

Presolve / cuts

  • Sound CglProbing presolve + probing implication cuts (slackened against propagation drift), TwoMIR-lite pairwise tableau aggregations — both big-instance-gated; GMI with hygiene, poison retraction with retries, slack-cut dropping

Branch & bound

  • Strong-branch fixing: a probe side that is infeasible or cannot beat the incumbent fixes the variable at the node — no branch spent (CBC's mechanism; its preprocess-off run closes 020 in 50 nodes this way). Probes 80ms×6 candidates: at 20ms three quarters were unresolved, leaving branching blind
  • Node-level propagation, rc-fixing per incumbent, same-model restart (3/5 budget split measured best)

Primal heuristics

  • Face walk (least-degradation dive on the LP-optimal face; tolerance fixed at LP-noise, deliberately not the proof gap): proves 021 outright and lands 020's incumbent at node 1
  • 1-opt polish, RENS+rounding, batch dive, pump, RINS with exponential failure backoff; bursts time-boxed, skipped when the incumbent already sits near the node bound

Case 020 — final ledger

Models verified identical to the Python reference; real CBC needs 1–2s. cbcgo now finds an incumbent inside the tests' economics tolerance at node 1 (0.36158 vs optimum 0.36146) and proves a bound of 0.347–0.357 vs the 0.361 needed — 94% of the original bound distance closed. A 60s budget does not close the rest: the residual is structural. Scoped follow-ups: multi-row aggregation cuts (pairwise TwoMIR is in; full c-MIR needs more), CglPreProcess-style row/column elimination (CBC works on a 4× reduced model), dual-EXPAND (cap 2048 helps 020 but perturbs 021's face-walk trajectory).

🤖 Generated with Claude Code

@andig andig force-pushed the mip-cbc-heuristics branch from bc79236 to 174b583 Compare July 5, 2026 14:45
@andig andig marked this pull request as ready for review July 5, 2026 16:00
andig and others added 6 commits July 5, 2026 18:03
Replaces the dense-inverse simplex core:
- two-phase singleton triangularization with a sparse-LU kernel and
  product-form eta updates (O(nnz) pivots, periodic refactorization),
  property-tested against a dense reference on random bases
- dual simplex for warm re-solves after bound changes: incremental
  duals (y absorbs each pivot row), lazily materialized reduced costs,
  long-step bound flips, a sparse pivot row via a row-wise mirror of A,
  and a pivot cap that bails degenerate runs to the primal
- EXPAND anti-degeneracy (Gill et al.): working bounds widen per pivot
  with a guaranteed minimum step, exact snap-back at refactorization,
  and a cleanup re-price before concluding Optimal/Infeasible
- full Bland's rule (entering and leaving) after degenerate streaks,
  Clp-style bound perturbation in cold solves with clean-bound restore

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Iterated activity-based bound tightening and big-M coefficient
tightening, plus CglProbing-style binary probing: infeasible sides fix
the binary, merged implied bounds apply to integer columns only
(continuous merges compound outward-rounding drift along equality
chains into bound collapse), and derived bounds round outward by the
row's error scale. Probing is time-boxed and skipped on restart passes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Gomory mixed-integer cuts from fractional basic integers with
support/dynamism hygiene and normalization; probing implication cuts
(CglProbing as a cut generator) whose implied bounds are slackened
against propagation drift, gated to large instances; TwoMIR-lite
pairwise tableau aggregations through the shared MIR derivation;
poison batches retract with retries and slack cuts drop before the
tree so only root-active rows ride into node re-solves.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Best-first search with depth-first plunging, warm-started child bases,
wall-clock deadlines (Limits.MaxTime becomes a time.Duration; the CLI
flag converts), node-level bound propagation at branching, monotone
bounds, a bound-based optimality proof at exit, reduced-cost fixing
re-run on every improving incumbent, SOS1/SOS2 via Beale-Tomlin
splitting, and a same-model restart that inherits cuts, fixings and
probe facts (bound continuity across passes).

Branching: strong branching to depth 6 on large instances (16
otherwise) with 80ms probes — shorter probes leave most unresolved and
branching blind; a probe side that is infeasible or cannot beat the
incumbent fixes the variable at the node without spending a branch
(CBC strong-branch fixing); pseudocosts select deeper.

Heuristics, all time-boxed (root burst MaxTime/3, deeper MaxTime/8):
completed MIP start, face walk (least-degradation dive holding the LP
on its optimal face at a fixed LP-noise tolerance — proves degenerate
alternate-optima instances outright at the root), 1-opt incumbent
polish via single-bound warm dual re-solves (CbcHeuristicLocal), RENS
with single-shot rounding, batch rounding dive, feasibility pump, and
RINS with exponential failure backoff.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Marchand-Wolsey c-MIR over aggregated equality chains: maximal-path
walk of the EQ multigraph, exact variable-bound substitution
(x = M*z - slack, the slack carried through MIR as the continuous
negative part and re-substituted afterwards), divisor/complement
search, violation-based separation. The sound derivation separates
zero violated cuts at the golden-case roots — GMI plus probing cuts
dominate this cut space — so the generator stays unwired, with a
property test that brute-forces feasible points of random chain MIPs
against every generated cut (it caught an unsound earlier draft that
substituted the VUB as an inequality and cut off optima).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@andig andig force-pushed the mip-cbc-heuristics branch from 174b583 to edd58cf Compare July 5, 2026 16:03
@andig andig merged commit 164420a into main Jul 5, 2026
2 checks passed
@andig andig deleted the mip-cbc-heuristics branch July 5, 2026 16:10
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