Algorithm Engineering — T04
The textbook problems were the warm-up. This is what customers actually bring you.
Three families today: shop scheduling, employee rostering, vehicle routing.
| Domain | Optimization challenge |
|---|---|
| Milk & dairy processing | Blend raw milk of varying fat/protein into consistent products |
| Power grid dispatch | Commit generators across horizons, keep the grid stable |
| School redistricting | Assign students under capacity, distance, and fairness |
| Grocery store pricing | Jointly price products accounting for substitution |
| Forestry & harvesting | Plan decades of cutting and planting for yield and carbon |
| Food truck location | Pick daily stops for a fleet to maximize expected profit |
| Emissions compliance | Allocate production across markets to meet emissions limits at minimal cost |
Watch the full tour: Gurobi’s Opti 202 “Optimization 360 (Optimization Everywhere)” walks through many more.
A “real-world solver” is not one algorithm. It is a stack of modeling choices, decompositions, and heuristics, picked to match a specific mix of these complications.

Shop scheduling
Tasks → machines, over time.

Employee rostering
People → shifts, over a horizon.

Vehicle routing
Stops → vehicles, in space and time.
Each of the three families has open-source packages with the variant catalog baked in. Worth knowing before encoding everything from scratch in a generic MIP.
| Family | Open-source packages |
|---|---|
| Shop scheduling | PyJobShop (on OR-Tools CP-SAT) |
| Rostering | Timefold, SolverForge |
| Vehicle routing | PyVRP, OR-Tools VRP, Timefold, SolverForge |
PyJobShop (PyJobShop 2025), Timefold (Timefold 2025), SolverForge (SolverForge 2025), PyVRP (PyVRP 2025), OR-Tools (Google OR-Tools 2025).
Read the feature list of the specialized solver first. If most of the constraint mix is native there, that is usually the right starting point.
Allocate tasks to machines over time.
Manufacturing, projects, cloud dispatch: same math, different vocabulary.
Variant taxonomy and figures follow the PyJobShop documentation (PyJobShop 2025).
| Concept | What it is | Example |
|---|---|---|
| Job | What the customer ordered; usually a chain of tasks | A car body to assemble; a customer order to fulfill |
| Task | The smallest unit of work, runs on a resource for a duration | “Drill the four holes”; “render the audio file” |
| Resource | What does the work; handles one task at a time | A CNC mill, an oven, a delivery van, a human operator |
A solution is a Gantt chart: for every task, which resource, when it starts, when it ends.


Each task can run on one of several eligible machines, with possibly different durations.
We now decide which machine and when. The search space multiplies; balancing load gets easier; the model gets harder.
Every job visits the machines in the same order, but jobs may overtake each other on later stages.
The classic assembly-line layout: stamp, then weld, then paint, then assemble. Routings collapse to a single shared sequence, so the only decision left is the order in which jobs enter the line.
Flow shop plus no overtaking: every machine sees the jobs in the same order.
What a literal conveyor belt enforces: once you are ahead at station one, you stay ahead everywhere. Far smaller search space (pick one permutation), but still NP-hard from three stages on.
Flow shop, but each stage has a pool of parallel machines.
A common topology in volume manufacturing: stamping, welding, painting, final assembly each run on several near-identical stations. Within a stage, the solver assigns jobs across machines; between stages, the flow constraint still applies.
Each job visits each machine once, but the order is free.
Maintenance checklists, independent lab analyses on one sample, parallel test suites on shared hardware: anywhere the steps can run in any order. The extra freedom packs tighter schedules at the cost of a much larger decision space.
Change: tasks no longer block a whole machine; they consume a quantity of a resource.
The schedule above violates capacity: at \(t \in [2,4)\) demand reaches 5 but only 4 is available (red block).
A valid schedule keeps total demand under capacity at every moment, e.g. by delaying an activity until the resource frees up.
Each task has several modes with different duration / resource trade-offs.
Rent more workers and finish faster, or work with the small crew and take longer. Picking the mode is now part of the optimization.
Resources deplete until something tops them up; stock must stay non-negative at every moment.
Renewable resources come back (workers go home, fuel up, return). Consumables don’t: raw material, screws, money, fuel. This couples producing tasks to consuming tasks in a way the renewable model cannot capture.
Between two tasks on the same machine, a setup is needed whose length depends on both the previous and the next task.
Order on each machine now affects cost; the problem inherits TSP-like structure per machine.
Two flavors of “no buffer between stages”:
No-wait
Once a job starts, no idle time between tasks. Steel rolling, food, chemistry.
Blocking
No buffer between stages: a finished job occupies its machine until the next stage is free.
Resources are not available 24/7: lunch breaks, weekends, maintenance windows.
Shift patterns are a frequent production-floor request, in both the “must avoid” and “interruptible” flavors.
Not every task has to be executed. We pick which tasks to schedule.
Use cases: alternative process plans, scheduling with rejections, “all-or-none” job structures.
Each job has time-window information.
| Objective | Formula | When it matters |
|---|---|---|
| Makespan \(C_{\max}\) | finish time of last job | Throughput, batch production |
| Total tardiness \(\sum T_j\) | \(\sum \max(0, C_j - d_j)\) | Service-level penalties |
| Tardy jobs \(\sum U_j\) | how many miss their due date | SLA breach count |
| Max tardiness \(T_{\max}\) | worst late job | Fairness, contractual caps |
| Total flow time \(\sum C_j\) | sum of completion times | Average customer wait |
| Setup time | sum of setup gaps | Energy, wear |
Allocate people to shifts over a horizon.
The people-side mirror of shop scheduling, where the math meets labor law.
Background survey (Burke et al. 2004); variant catalog cross-checked against Timefold’s documentation (Timefold 2025).
Every roster is personal: it decides whose nights, weekends, and holidays get sacrificed. Built by hand it is quickly perceived as unfair and becomes a source of internal conflict, which makes it a natural thing to outsource to a neutral system.
Three things make rostering its own problem:
Inputs: planning horizon split into shifts; coverage demand per shift; pool of employees with contracts, skills, availability, preferences.
Output: a roster matrix: for every (employee, day) cell, the assigned shift (or off).
Hard: law, safety, contract. Violation means infeasible. Soft: preferences, fairness, nice-to-haves. Violation is allowed but penalized.
Modern solvers use a multi-level score \((\text{hard}, \text{medium}, \text{soft})\) compared lexicographically: hard must be zero before soft is even looked at.
Skills Who can do what.

Availability Who is available when.

“When Beth has next Friday off for a wedding party, she should not be assigned to a shift on that Friday.”
A “technically feasible” roster that always lands the night shifts on the same person is not feasible in any meaningful sense. They quit.
A contract typically specifies:
Some staff prefer night shifts; others would rather keep specific evenings free for childcare, training, or a second job.
Granting non-conflicting preferences costs the employer nothing and improves retention.
Many organizations use rotating schedules instead of re-solving from scratch each cycle.
Rotations trade flexibility for predictability. Most real systems use a rotating base pattern with ad-hoc overrides.
Some assignments come in groups, not individually.
| Type | Example | Hard? |
|---|---|---|
| Required pair | New hire must always be paired with mentor | ✓ |
| Preferred pair | Two employees who carpool together | ✗ |
| Prohibited pair | Couple whose HR policy forbids same-shift work | ✓ |
| Unpreferred pair | Siblings alternating eldercare | ✗ |
Monday morning, four hours before opening. The cook calls in sick for the rest of the week. The published roster is now wrong, and somebody has to fix it before the shift starts.
Allocate stops to vehicles in space and time.
Last-mile delivery, parcel networks, field service, waste collection, ride-sharing.
Variant taxonomy and figures follow PyVRP (PyVRP 2025).
One depot, a set of clients, a fleet of vehicles.
Visit every client exactly once, every route starts and ends at the depot, minimize total cost.
Without it, one vehicle would always do everything; capacity is what determines the fleet size.
Clients are not always available all day.
Time windows couple the sequence of visits to the clock. Two clients close in space may be impossible to serve on one route because their windows conflict.
Earliest start, latest end, overtime band: usually a soft limit.
EU/US law: 45-min break after 4.5h of driving.
The break is not at a fixed time. Where it lands in the schedule, and at which location, is itself a decision, and it shifts every later arrival downstream.
Which depot serves which client is part of the problem, not an input.
Distance and duration matrices depend on the vehicle type.
Not every job has to be done today. Each client carries a prize \(\pi_i\):
\[\min \;\text{routing cost} - \sum_{i \text{ visited}} \pi_i\]

“Exactly one of”:
The tour visits exactly one member of each group; the solver picks which.
Three families, each a deep catalog of variants and constraints.

Shop scheduling
PyJobShop · CP-SAT

Rostering
Timefold · SolverForge

Vehicle routing
PyVRP · OR-Tools
Algorithm Engineering SS 2026 — T04 Real-World Problems