Repository navigation
fix(plan-selection): run the group selection MILP to proven optimality and cost exactly (#1185, part 1) - #1188
Merged
Merged
Conversation
…y and cost exactly (#1185) The group selection MILP stopped at wall-clock deadlines and costed some candidates inexactly past hardcoded caps, so the selected plan depended on machine speed and could differ from the optimum of the model. Deadlines removed: - select_plan no longer takes a deadline; the facade stops passing one (its own round budgets and the Pass 2 deadline are unchanged). - The warm start has no time budget: it tries workload order, its reverse and MORE_ORDERS pseudo-random orders, every run. - No per-part time split and no HiGHS time_limit: every part is solved to proven optimality. Any HiGHS status other than Optimal or Infeasible is a solver error. - Deleted WARM_START_MOST_TIME, MOMENT_TO_READ_THE_START_S, LP_TIME_LIMIT_S (the LP diagnosis's LPs and one-node root solve end on their own), SelectionMilpStatus, SelectionMilpError::{DeadlineReached, NoSolution}, SearchStatus::DeadlineReached, ErrorSelectingPlan::NoPlanByDeadline and the solution's relative_gap (always 0 now). Caps removed (every combination is costed and encoded exactly): - MAX_CONTEXTS, MAX_PRODUCER_CHOICES and UsageContext::TooManyContexts. - MAX_WINDOW_KINDS: every union of a scan run's window kinds is costed and encoded with exact indicators; the widest-window fallback is gone. - MAX_COMPOSITIONS: every composition is built and checked, so no set of approximate candidates is marked NotChecked for its size. HiGHS threads are now SelectionMilpOptions::highs_threads, default 1 (was 4), so ties between plans of equal cost break the same way. Snapshot changes: only the search status. o11y_bench q05, q19, q25 and the sketch workloads shared-quantiles-1h-mvp and quantile-single-mvp used to stop at the time limit with the optimal plan; they are now proven optimal (proven_optimal true, search_status optimal). Plans and costs are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rflow; update deadline docs (review) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… selection MILP is infeasible The dag-viewer test test_a_plan_without_a_rate_fact_names_the_missing_fact failed on main since the group MILP became the default: candidates whose usage asap-cost could not price (e.g. a source without an arrival rate) were left out silently and the error said only 'infeasible'. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zzylol
force-pushed
the
feat/1185-milp-no-deadlines-caps
branch
from
November 3, 2026 02:25
c696599 to
e85dddc
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Part 1 of #1185. The group selection MILP (#1095), Stage 3's only search since #1187, stopped on wall-clock deadlines and costed some candidates inexactly behind hardcoded caps. On a loaded machine HiGHS returned an unproven incumbent, and the warm start tried a different number of query orders on each run, so the selected plan depended on machine speed (the flakes behind #1067/#1175/#1180). The caps silently approximated cost or dropped options, so the returned plan was not the optimum of the model we meant to solve.
What
select_planhas nodeadlineparameter; the MILP always runs to proven optimality.MORE_ORDERS), not by time.MAX_CONTEXTS,MAX_PRODUCER_CHOICES,MAX_WINDOW_KINDS,MAX_COMPOSITIONS.SelectionMilpOptions::highs_threads, default 1 (was a constant 4), so tie-breaking is deterministic. No public API or CLI sets it yet; every caller uses the default.MOST_PASSES,MAX_SHIFTS,MOST_STEPS_PER_QUERY.planner/src/run.rs, and Pass 2's generation deadline. The facade simply stops passing its deadline into Stage 3 here.How
solve_selection_milp/solve_partlose their deadline, the per-part time split and the HiGHStime_limit;warm_startloses its budget.Solvererror.groups.rsevery context combination and producer choice is costed, andadd_unionsencodes every union of a scan's window kinds; the "widest charged" fallback incost_terms.rsis deleted.accuracy_positions.rs,combinations/rootsreturn every composition (noOption).lp-diagnosis) losesLP_TIME_LIMIT_S: its LP solves and one-node root solve finish on their own.Key code interfaces added / modified
select.rsreport.rs(Every other
ErrorSelectingPlanvariant is unchanged; see the file.)milp/model.rs,milp/warm_start.rs,groups.rs,groups/accuracy_positions.rs(crate-private)ASAP_STAGE3_REPORTJSON lines loserelative_gapand the per-partstatus.Usage:
stage_pipeline --workload tools/dag-viewer/memo-examples/promql/rangeheavy-t8-t5-1h.json --cost-model aws-fargate-us-east-1 --enforce-accuracy-guarantees --out doc.json. The facade runs Pass 1, Pass 2 and Stage 2, then callsselect_plan(&physical, &entries, &data, &checks, &cost_inputs, &realizer). That builds the MILP withSelectionMilpOptions { highs_threads: 1, .. }, warm-starts it with up to2 + MORE_ORDERSquery orders, and solves each part with HiGHS to optimality.doc.json'ssearch_report.statusis always"optimal", andplan_costequals the objective.Major algorithm changes
MILP stopping rule
shared-quantiles-1h-mvpandquantile-single-mvp, the snapshots recordeddeadline_reached. The time-limited HiGHS run returned an incumbent without an optimality proof, and the result varied with load.time_limit; at the limit its incumbent is returned asDeadlineReached.MORE_ORDERSpseudo-random orders.NoValidCandidate; any other status →Solvererror.plan_costequals the objective (relative 10⁻⁶) now always runs in its Optimal branch.Exact costing instead of caps
TooManyContexts, the first usage standing in for the rest). Scans with more than 4 window kinds were charged only the union of all kinds. Past 64 compositions, a set was marked unusable, which dropped it from the search.groups::costed_usage*andadd_unionstruncated at the caps; now they enumerate every combination or union.accuracy_positions::combinationsreturnedNonepast 64; now it returns them all.TooManyContextsand the union-only charge were the inexact terms behindInexactObjectivein these cases; those paths are gone.2^kin the scan's window kindsk. Compositions scale with the product of choices per input. No workload in the tests or corpus hit a cap, so no measured change.2^ksubsets although at mostk² + k + 1distinct hulls exist; a scan with 64 or more kinds is an explicit panic rather than a silent zero charge (shift overflow).Infeasible selections name uncostable candidates
sum(rate(m[1m]))with no series count) returned only "the selection MILP is infeasible"; the dag-viewer testtest_a_plan_without_a_rate_fact_names_the_missing_factfailed on main.select::with_uncostable_reasonsappends each distinctasap-costerror of the candidates left out asUsageNotCostableto the infeasible rejection's reason (e.g. "pricing source m needs an arrival rate").Before this PR
Stage 3's plan could depend on wall time and machine load; some costs were approximated past caps without an error.
After this PR
Given the same Stage 2 catalog, Stage 3 selects the same plan with a proven-optimal search status, costed exactly. Full run-to-run determinism also needs part 2 (the facade and Pass 2 deadlines).
Verification
GitHub CI (test, format-and-lint) passed on e85dddc.
python3 -m unittest test_server(dag-viewer): 28 passed.Also fixes main's build: fix(plan-selection): reject an unpriceable candidate in the reference pricer (#1067) #1164 imported
crate::AnalyticalCostErrorinselector.rs, which refactor(plan-selection): delete the old Stage 3 engine; select_plan runs only the group selection MILP #1187 had moved; now imported fromasap_cost::analytical.cargo test -p asap-plan-selection --lib: 141 passed. Deleteda_deadline_yields_the_warm_starts_plan(zero time limit).cargo test -p asap-planner: lib 11 passed; tests 9 passed, 77 ignored (Re-enable tests ignored for wall-clock planning nondeterminism after Stage 3 design is final #1175 facade-budget markers, part 2).cargo test -p asap-devtools --test devtools stage_pipeline: 5 passed, 46 ignored (unchanged from main).cargo test -p asap-devtools --release --test corpus_snapshots -- --include-ignored: 10 passed; the sketch snapshot suite (including the two formerly deadline-limited workloads) finishes in 53 s.cargo check --workspace --tests: clean.--features lp-diagnosisbuilds.cargo fmt --all --check: clean.Snapshot changes, plans and costs identical, search status only:
o11y_bench/q05,q19,q25proven_optimalfalse → trueshared-quantiles-1h-mvp,quantile-single-mvpsearch_statusdeadline_reached→optimal; theirdeadline_reached_reasonmanifest entries are removedScreenshots: not applicable.
Limitations and follow-ups
run.rs) and the Pass 2 generation deadline, then re-enable or delete the Planner tests depend on wall-clock search budgets and flake under load #1067/Re-enable tests ignored for wall-clock planning nondeterminism after Stage 3 design is final #1175/test: ignore every test whose result depends on a wall-clock budget or deadline (#1067) #1180 ignored tests.inexactis empty and objective =plan_cost.ErrorSelectingPlan::EvaluationDeadlineExceededis now produced only by the reference pricer; revisit in part 2.Part of #1185.
🤖 Generated with Claude Code