NVIDIA / NVIDIA/cuopt

[BUG] Routing solver: break-dimension bugs

Open
#1,195 7 comments 0 reactions 1 assignee View on GitHub

@rg20 is already working on this.

Since May 11, 2026.

awaiting response bug
Dominant language
Cuda
Stars
1k
Forks
233
Avg merge
4d 4h
Merged PRs (30d)
95

Description

Describe the bug

For the entire problem setup and reproduce, see repro_break_dim_bugs.py.

Setup Observed Expected
A unreachable break TW [5, 5], break loc 200 s away status 0, route 0→1→0 (break silently missing) status 1 INFEASIBLE
B break TW [100, 200], customer at t=10, route can finish at t=20 status 0, route 0→2→1→0, cost 410.25 status 0, route 0→1→0, cost 20

Both stem from a single design assumption: every route has exactly N breaks. The cost formula max(0, breaks − N) only penalizes excess; squeeze unconditionally inserts every dim; eject_until_feasible protects breaks; find_break_insertions only moves existing breaks.

Observed

expect status 1 since the problem is infeasible
  A: cost=20.00 vehicles=1
      vehicle 0: 0(Depot) -> 1(Delivery) -> 0(Depot)
expect status 0 and no break in solution since it is not needed
  B: cost=410.25 vehicles=1
      vehicle 0: 0(Depot) -> 2(Break) -> 1(Delivery) -> 0(Depot)

Expected

expect status 1 since the problem is infeasible
  A: INFEASIBLE (status=1)
expect status 0 and no break in solution since it is not needed
  B: cost=20.00 vehicles=1
      vehicle 0: 0(Depot) -> 1(Delivery) -> 0(Depot)

Why it matters

This issue negatively affects UX and is a blocker for feature development.
Case A is a bug with straightforward fix.
Case B appears to be a solver/design limitation rather than a simple feasibility bug.

Environment

  • cuOpt: b311a990 (main), built from source (Release).
  • Single A100 80 GB PCIe
  • Driver Version: 580.65.06, CUDA Version: 13.0, nvcc V13.1.115
  • Linux 5.15.179, Python 3.14, miniconda.

Notes

  • Adding distance breaks semantics forces EVs travel around chargers to comply with the required breaks due to issue B.
  • To fix case A override inf_cost[BREAK] in route_t::compute_cost with max(0, required - breaks) using a dynamic required-count derived from the route's actual arrival_at_depot. Squeeze still inserts every dim so no construction path changes; all 83 routing tests pass.
  • an attempt to add in-LS eject-only candidate to fix case B perturbed LS convergence (heterogenous_breaks became flaky).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.