Capstone · everything converges

∗

One problem, four traditions

Vehicle routing with time windows, solved by branch-and-cut, branch-and-price, CP-SAT and ALNS, then refereed on Solomon's instances with unit 28's method.

needs 07, 08, 10, 18–19, 26, 28~1¼ h slides + 12–15 h lablab: uv run co lab capstone

capstone · the problem

VRPTW

vehicle routing with time windows

VRPTW: a depot 0 and customers $1..n$. Customer $i$ has demand $q_i$, a window $[a_i, b_i]$ in which service must start, and a service time $s_i$. Identical vehicles of capacity $Q$ leave the depot no earlier than $a_0$ and return by $b_0$. Travel time equals distance $d_{ij}$. Arriving early means waiting.

Find routes that serve every customer once, within capacity and windows, minimising total distance. The fleet is unlimited.

Along a route, service starts satisfy $$t_j = \max\bigl(a_j,\; t_i + s_i + d_{ij}\bigr) \le b_j.$$ Waiting is free, so this start-time recurrence is the entire time-window model.

Solomon (1987) seriescustomerswindows
C1, C2clusteredseries 1: short horizon, tight windows, short routes
series 2: long horizon, wide windows, long routes
R1, R2uniform random
RC1, RC2mixed

56 instances of 100 customers, and the literature's 25- and 50-customer versions: the first $n$ customers of each file. uv run co data downloads them from SINTEF's mirror.

getting around

Words with a dotted underline are glossary terms: click one. G lists them all, N shows the notes. Optional extra: interactive figures, linked from the slides they support by a small ▸ chip; skip them freely.

capstone · the problem · example

The recurrence, on four customers

vertexpositionwindowservice
depot 0(0, 0)[0, 400]0
1(0, 30)[0, 50]10
2(40, 30)[60, 100]10
3(40, 0)[100, 200]10
4(80, 0)[0, 300]10

Demand 1 each, capacity 5, so only time matters. Distances are Euclidean: $d_{01} = 30$, $d_{12} = 40$, $d_{23} = 30$, $d_{30} = 40$, $d_{24} = 50$, $d_{34} = 40$.

route 0 → 1 → 2 → 3 → 0
at$t = \max(a,\ \text{previous} + s + d)$window ok?
1$\max(0,\ 0 + 0 + 30) = 30$$30 \le 50$ ✓
2$\max(60,\ 30 + 10 + 40) = 80$$80 \le 100$ ✓
3$\max(100,\ 80 + 10 + 30) = 120$$120 \le 200$ ✓
depot$120 + 10 + 40 = 170$$170 \le 400$ ✓

Distance $30 + 40 + 30 + 40 = 140$. Customer 4 still needs a place.

The optimum serves all four on one route, $0 \to 1 \to 2 \to 4 \to 3 \to 0$, distance 200: starts 30, 80, 140, 190, back at 240.

capstone · conventions

Two objectives, and why the numbers are in tenths

the heuristic tables

Minimise vehicles first, then distance (a hierarchical objective), with double-precision Euclidean distances. SINTEF's best-known table uses this: R101 at 100 customers is 19 vehicles, 1650.80.

the exact literature, and this lab

Minimise distance only (the distance-only objective), with $d_{ij} = \lfloor 10 \cdot \mathrm{euclid}(i, j) \rfloor / 10$, truncated to one decimal (Kallehauge et al. 2005; Baldacci, Mingozzi & Roberti 2011). Everything is stored as integers in tenths.

  • Integers keep CP-SAT exact, make "equal cost" mean equal, and let a bound be rounded up: a node whose LP is 617.03 can't hold a solution under 617.1.
  • Truncation can break the triangle inequality by up to 0.1. So set partitioning uses $= 1$, not $\ge 1$, where dropping a customer from a route could lengthen it.
  • The two objectives' results aren't comparable. A distance-only optimum may use more vehicles and less distance than the hierarchical best-known.

Check on convention: all four methods give R101.25 = 617.1, C101.25 = 191.3, RC101.25 = 461.1 and R201.25 = 463.3. As far as can be checked from memory, these match the optima reported in the exact literature.

capstone · overview

The four traditions, and the referee

(a) polyhedral · step 2

Arc variables on SCIP, big-M time propagation, and your rounded capacity cuts through a constraint handler. Units 05, 08, 09, 27.

(b) decomposition · step 3

Set partitioning over routes, priced by an elementary shortest path with capacity and time windows, branching on arcs. Units 07, 10.

(c) constraint programming · step 4

CP-SAT's multiple-circuit constraint with enforced time and load, plus a hint and a chosen strategy. Units 17–21.

(d) local search · step 5

ALNS with constant-time feasible insertion, Shaw removal and regret repair. Units 19, 26.

the referee · steps 1 and 6

A checker that trusts no method, primal gaps against the best any method found, paired tests with Holm's correction, and the verdict "indistinguishable" when the data say so. Units 00, 28.

section 1

Four models of one problem

What each tradition writes down, and what it leaves to the solver.

capstone · (a) branch-and-cut

A compact model whose capacity lives in your cuts

The arc MIP: $x_{ij} \in \{0,1\}$ for arcs that some route could use, and start times $t_i \in [a_i, b_i]$:

$$\begin{aligned} \min\ & \textstyle\sum d_{ij} x_{ij} \\ & x(\delta^+(i)) = x(\delta^-(i)) = 1 \quad i \ne 0 \\ & t_j \ge t_i + s_i + d_{ij} - M_{ij}(1 - x_{ij}) \end{aligned}$$

with $M_{ij} = \max(0,\ b_i + s_i + d_{ij} - a_j)$, the smallest $M$ that's valid.

The time constraints already rule out subtours: around a cycle, each start would exceed the previous one by $s_i + d_{ij} > 0$. They don't limit load.

rounded capacity inequalities

For every customer set $S$: $\;x(\delta^-(S)) \ge \lceil q(S) / Q \rceil$.

Separation by connected components of the support graph. On an integer point the components are the routes, so an overloaded route always yields a violated cut. That makes the handler exact as a lazy constraint. On a fractional point it's a heuristic: exact separation is NP-hard (CVRPSEP is the standard code).

colib.vrptw.lazy_cut_handler supplies SCIP's plumbing; you supply separate(values).

capstone · (b) branch-and-price

Routes as columns, found by labelling

$$\min \sum_{r \in \Omega} c_r \lambda_r \quad \text{s.t.} \sum_{r \ni i} \lambda_r = 1\ \ \forall i,\quad \lambda \ge 0$$

Set partitioning over routes: $\Omega$ is every feasible route, too many to write down. Unit 10's loop: solve over a few, read duals $\pi$, and find a route with $c_r - \sum_{i \in r} \pi_i < 0$.

a label at a vertex

(reduced cost, load, start time, unreachable set, route). Extend along allowed arcs. $L$ dominates $L'$ when it's no worse in cost, load and time, and its unreachable set is a subset of $L'$'s.

  • Feillet et al. (2004): count customers you can't reach any more as visited. Dominance then fires between labels whose routes differ.
  • Branch on arc flows $\sum_r [ij \in r] \lambda_r$. Forbidding an arc keeps pricing the same problem on a smaller graph; forcing $ij$ forbids its alternatives.
  • Integral arc flows mean integral $\lambda$: each customer has one arc in and one out with flow 1, so every column in the support follows them.
  • Bound $\lceil LP \rceil$ because distances are integers. A restricted master IP at the root gives the first incumbent.

capstone · (c) CP-SAT

A circuit constraint, enforced implications, and a strategy

m.add_multiple_circuit([(i, j, x[i, j]) for i, j in arcs])
for (i, j), b in x.items():
    if i and j:
        m.add(t[j] >= t[i] + s[i] + d[i][j]).only_enforce_if(b)
        m.add(u[j] >= u[i] + q[j]).only_enforce_if(b)
m.minimize(sum(d[i][j] * b for (i, j), b in x.items()))

The multiple-circuit constraint: vertex 0 may carry many circuits; every other vertex is on exactly one. That replaces the degree constraints and subtour elimination at once. No big-M: the enforced implication is propagated directly, and lazy clause generation (unit 21) learns from its failures.

search choices you make
  • A hint: a complete solution (from ALNS) that CP-SAT tries first.
  • A decision strategy: branch on short arcs first, value 1. With one worker it's only followed under FIXED_SEARCH.
  • Workers: CP-SAT is a portfolio. One worker is a fair comparison with the single-threaded methods; eight is how it's meant to be run.

The brief asks for a strategy chosen deliberately. Deliberate includes measuring whether it helps and dropping it if it doesn't.

capstone · (d) ALNS

Feasible insertion in constant time

Keep, per route, the forward starts $t_k$ and the latest starts $\ell_k$:

$$\begin{aligned} \ell_k &= \min\bigl(b_k,\; \ell_{k+1} - s_k - d_{k,k+1}\bigr), \\ \ell_{\text{depot}} &= b_0. \end{aligned}$$

Inserting $c$ between $p$ and $q$ is feasible iff $$\begin{aligned} t_c &= \max(a_c,\ t_p + s_p + d_{pc}) \le b_c, \\ &\max(a_q,\ t_c + s_c + d_{cq}) \le \ell_q, \end{aligned}$$ plus the load. Waiting absorbs any push up to $\ell_q$, so nothing after $q$ needs rechecking: a constant-time feasible insertion.

  • Shaw removal: take out customers related in space, time and demand, so repair has room to rearrange them.
  • Regret-k repair: insert first the customer with most to lose, $\sum_{h=2}^{k} (\text{cost}_h - \text{cost}_1)$, over its best position per route.
  • A new route is always an option. Without it, repair can never increase the number of routes, and minimising distance sometimes needs more of them. The first prototype lacked it and got stuck 13 % above optimal on R201.25.
  • Annealing acceptance, and three destroy × three repair operators with adaptive weights (unit 26).

capstone · (d) ALNS · example

Constant-time insertion, with numbers

Route $0 \to 1 \to 2 \to 3 \to 0$ from the four-customer example. Insert customer 4 (window $[0, 300]$).

latest starts, computed backwards
position$\ell = \min(b,\ \ell_{\text{next}} - s - d)$start $t$
3$\min(200,\ 400 - 10 - 40) = 200$120
2$\min(100,\ 200 - 10 - 30) = 100$80
1$\min(50,\ 100 - 10 - 40) = 50$30

Customer 2 has only 20 of slack for anything inserted before it: it starts at 80, and its latest start is 100, its window's end.

every position for customer 4
between4 starts atnext start vs its latestextra distance
0 and 180at 1: $175 > 50$ ✗—
1 and 2125at 2: $185 > 100$ ✗—
2 and 3140at 3: $190 \le 200$ ✓60
3 and depot170back at $260 \le 400$ ✓80

Two numbers per position, no rescheduling of the rest. The cheapest feasible insertion, 60, gives the optimal route of distance $140 + 60 = 200$.

section 2

Refereeing

A result is a claim. The referee checks it.

capstone · referee

What the harness holds fixed, and what it measures

held fixedhow
budget60 s wall clock per run; the method gets the limit, the harness 5 s of grace
threadsone each: SCIP, HiGHS, CP-SAT with num_workers=1, Python
instancesall 56 Solomon instances at 25, 50 and 100 customers
trustevery solution re-checked, every cost recomputed
runawayskilled 30 s past the limit or beyond 2.5 GB, and recorded

The referee also runs a violation checker on every answer. Six runs side by side on eight cores, so absolute seconds include some contention. Every method pays it.

primal gap (Berthold 2013)

$\gamma(v, v^*) = |v - v^*| / \max(|v|, |v^*|)$, 1 with no solution. $v^*$ is the best value any method found.

On the example ($v^* = 200$): inserting 4 at the end of $0 \to 1 \to 2 \to 3 \to 0$ gives $v = 220$, $\gamma = 20/220 = 0.091$. Serving 4 on its own route gives $140 + 160 = 300$, $\gamma = 100/300 = 0.333$. No solution: 1.

two questions, two metrics

Who finds good solutions? Primal gap at the limit, all four methods. Who proves optimality fastest? PAR1 seconds, the three exact methods. ALNS never proves anything, so it has no place in the second.

Paired over instances, Wilcoxon signed-rank, Holm over all pairs at once.

section 3

What the referee found

672 runs, and no claim refuted.

capstone · measured

Proofs and gaps, by size

Proven optimal of 56 (mean primal gap at 60 s against the best found):

n(a) B&C(b) B&P(c) CP-SAT(d) ALNSany
2533 (1.6%)49 (9.2%)36 (0.08%)– (0.43%)51
5016 (11.2%)16 (53.9%)14 (6.2%)– (0.20%)21
10011 (28.9%)0 (96.5%)6 (18.9%)– (0.15%)11

Integrity: no invalid solution, no crash, no "optimal" refuted (no refuted claim), no two different proven optima.

  • B&P is all or nothing. Its gaps are runs with no solution: 3, 26 and 49 of 56. Ten runs overran the limit inside one uninterruptible pricing call.
  • ALNS's gaps flatter it, because the "best found" is often its own value. Against proven optima: 47 of 51 at n = 25, 20 of 21 at 50, 9 of 11 at 100. The worst miss is 7.7%, on C205.100.
  • Proven at n = 100: C101 827.3, C201 589.1, R101 1637.7. As far as can be checked from memory, these match the exact literature.

capstone · measured

Which characteristics favour which method

serieswindow / horizonn = 25n = 50n = 100
C10.25B&C 8, B&P 1B&C 6, ALNS 2, B&P 1ALNS 5, B&C 4
C20.27B&C 4, CP-SAT 4B&C 5, ALNS 2, CP-SAT 1B&C 6, ALNS 2
R10.38B&P 11, B&C 1B&P 5, ALNS 5, B&C 1, CP-SAT 1ALNS 11, B&C 1
R20.45B&P 5, CP-SAT 4, B&C 1, ALNS 1ALNS 9, B&C 1, CP-SAT 1ALNS 10, CP-SAT 1
RC10.36B&P 5, B&C 2, CP-SAT 1ALNS 7, CP-SAT 1ALNS 8
RC20.38B&P 4, CP-SAT 3, B&C 1ALNS 6, B&C 1, CP-SAT 1ALNS 8

Per instance: smallest primal gap, then fastest proof (the head-to-head verdict).

supported by the paired tests
  • Tight windows, random, 25 customers → B&P: proves all 20 R1/RC1, fastest proofs overall (Holm p ≤ 0.02).
  • Clustered → the compact MIP makes most proofs. At n = 100, SCIP made all 11: ten clustered, plus R101.
  • Past 25 customers → ALNS for solution quality on every series; every pair decisive (Holm p ≤ 6·10⁻⁶).
indistinguishable
  • n = 25, solution quality: CP-SAT vs ALNS, and B&P vs each of the other three.
  • n = 50, time to proof: B&P vs B&C, and B&P vs CP-SAT.

capstone · honest

What this run can't tell you

limitationwhy it mattersthe extra experiment
one seed per runSCIP and CP-SAT times move a lot with the seed (unit 27)five seeds, averaged per instance
six jobs on eight coresC104.25: B&P proved it in 39.5 s in a lighter prototype run, and found nothing in 60 s hereone job at a time
Python against C++B&P's and ALNS's results are partly implementation findingsstate it; or ng-routes and bidirectional labelling
one thread for CP-SATa portfolio solver, run against its design8 workers, reported beside the 1-worker result
gap at one time limitignores when each method got therelog incumbents; primal integral
the report's rule

Every sentence of NOTES.md ties a method to an instance characteristic, points at a table, and names the test behind it. Where the test says indistinguishable, the sentence says so.

capstone · checkpoint

Done when

checkpoint
  • All six lab steps are ✓, and then reports no invalid solution and no refuted claim.
  • NOTES.md says, for each series and size, which method wins on quality and on proof time, with the paired test behind each claim and "indistinguishable" where it applies.
  • It states its limitations, and the experiment that would remove each.

Reading

Solomon, Oper. Res. 35 (1987).
Kallehauge et al., in Column Generation (2005).
Feillet et al., Networks 44 (2004).
Baldacci, Mingozzi & Roberti, Oper. Res. 59 (2011).
Savelsbergh, ORSA J. Comput. 4 (1992).
Ropke & Pisinger, Transp. Sci. 40 (2006).
Berthold, Oper. Res. Lett. 41 (2013).

After the course

VRPSolver (Pessoa et al. 2020) for exact routing, HGS (Vidal 2022) for heuristics, and Gehring–Homberger's 200–1000 customers to see where each one stops.