Skip to content

fix: relax the cost bound when CBC cannot hold it - #142

Open
andig wants to merge 1 commit into
fix/reserve-the-tie-break-slicefrom
fix/relax-the-cost-bound-when-cbc-cannot-hold-it
Open

fix: relax the cost bound when CBC cannot hold it#142
andig wants to merge 1 commit into
fix/reserve-the-tie-break-slicefrom
fix/relax-the-cost-bound-when-cbc-cannot-hold-it

Conversation

@andig

@andig andig commented Aug 23, 2026

Copy link
Copy Markdown
Member

What production showed

The new solve logs (#141): ~6% of split requests report LP Infeasible unused, and 315/day report MILP Infeasible unused — the tie break silently lost on requests that reserved 4s for it.

Diagnosis (two captured hits, replayed)

  • The cost stage incumbent is fully integral (worst deviation 0.000000) — not the Refuse a schedule whose binaries came back off the integers #136 fractional case.
  • The pinned pattern reaches the reported cost exactly: a pinned cost-max LP reproduces it to 6e-10. The bound is satisfiable at the point CBC itself just returned.
  • Yet CBC calls the LP Infeasible — with primal simplex, dual simplex, barrier, and perturbation off. The log shows why: Perturbing problem by 0.001% of 16921980 — largest nonzero change 5.9. The model's big-M rows put the solver's own perturbation ~6 orders above the 1e-5 slack.
  • Slack needed for feasibility, measured: 3e-4 (plain case) and 1e-3 (demand-rate case — noise scales with the priciest coefficient, so no single absolute slack fits).

Fix

On Infeasible, walk the slack up ×10 until CBC holds the row, ceiling COST_BOUND_SLACK_CEILING = 1e-2 = default gap_abs, money the cost stage may already have left on the table. keep() follows via budget; the MILP inherits the relaxed row (fixes the MILP Infeasible rows too). The stage label records the landed slack, so the #141 log line measures how often the ladder climbs.

Both captured hits recover their tie break end-to-end (LP (slack 0.0001) Optimal, LP (slack 0.001) Optimal). Deterministic test forces two Infeasible verdicts and asserts the ladder lands at 0.001.

Stacked on #141.

🤖 Generated with Claude Code

Base automatically changed from feat/stage-timing-logs to fix/reserve-the-tie-break-slice August 23, 2026 09:18
…the tie break

Production logs show the pinned tie break LP coming back Infeasible on ~6% of
splits, over a cost bound the incumbent CBC just returned satisfies. It is not
the incumbent: the solution is fully integral, the pinned pattern reaches the
reported cost exactly (pinned cost-max LP reproduces it to 6e-10), and the row
survives rescaling. It is the solve itself: the model's big M rows put CBC's
perturbation (0.001% of a 1.7e7 element) orders above any hundredth-of-a-cent
slack, and primal, dual, barrier and perturbation-off all agree on the wrong
answer.

So the floor walks the slack up by tens until CBC holds the row, ceiling 1e-2:
the default gap_abs, money the cost stage may already have left on the table.
keep() follows through budget, and the MILP inherits the relaxed row. Both
captured production hits recover their tie break at 1e-4 and 1e-3. The stage
label records the slack that landed, so the log line shows how often the
ladder climbs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@andig
andig force-pushed the fix/relax-the-cost-bound-when-cbc-cannot-hold-it branch from 08bd0f9 to 8b250b3 Compare August 23, 2026 09:20
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