Repository navigation
Add flip budgets, priced movement, and regime simulations - #13
Conversation
Queue Shift previously matched NFR interpolation on queue movement only, which silently dropped the negative-flip protection the baseline exists to provide. The solver now also caps (MILP) or penalizes (min-cost flow) case churn or expected negative flips, prices movement per queue so budgets can count cases, hours, or money, and returns per-queue dual prices from the flow form. A regime simulation maps when holding queue totals is costly (updates that move volume) versus nearly free (updates that reorder cases), and compares learned queue prices with exact per-batch assignment. The paper is rewritten around these results; all tables and figures are regenerated. Breaking: solve_assignment takes incumbent labels instead of incumbent loads. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2cf96596d9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| unit_costs = np.asarray(queue_costs, dtype=float) | ||
| if unit_costs.shape != np.shape(queue_loads): | ||
| raise ValueError("queue_costs must contain one cost per queue") | ||
| added = np.maximum(np.asarray(queue_loads) - np.asarray(incumbent_loads), 0) | ||
| return float(unit_costs @ added) |
There was a problem hiding this comment.
Reject invalid costs in workload_shift
When the newly exported workload_shift helper is called directly with negative, NaN, or infinite queue costs, it returns a negative or non-finite workload instead of rejecting the input. These values are explicitly invalid in solve_assignment, so the public helper can otherwise produce a nonsensical budget that callers may persist or reuse; validate that every converted cost is finite and nonnegative here as well.
Useful? React with 👍 / 👎.
Why
The paper compared Queue Shift with NFR interpolation at matched queue movement only. That drops the negative-flip protection interpolation exists to provide: movement-only Queue Shift flips 12–16% of incumbent-correct cases even at zero movement, where interpolation flips none.
What changes
queue_costsmakes cases a currency (hours, money). Added work beyond each queue's incumbent load is priced; freed capacity earns no credit.solve_assignmenttakes incumbent labels instead of loads.Checks
make checkpasses locally (ruff, pyright, pydoclint, 47 tests, build, formal checks, generated-output sync, paper build). The new solver modes match exhaustive search; removing either new constraint fails the tests. Re-running the existing estimated and oracle experiments reproduces the old movement-only numbers exactly.🤖 Generated with Claude Code