Definition · A job shopA set of jobs, each an ordered list of operations. Operation i of job j needs a named machine for a given duration and cannot start until operation i − 1 of the same job has finished. A machine runs one operation at a time. The route is fixed by the data; the order in which each machine serves the operations assigned to it is the decision.
A flow shop is the special case where every job has the same route. A job shop allows the routes to differ, and that is the whole of the difficulty.
Definition · Disjunctive pairs and orientationsTwo operations needing the same machine form a disjunctive pair, and an orientation chooses, for every such pair, which of the two goes first. With k pairs there are 2^k orientations. An orientation together with the route arcs is a directed graph; when it is acyclic the earliest start times are a forward pass over it and the makespan is its longest path.
That last sentence is the whole method and it is borrowed rather than invented. Once every pair is oriented there is nothing left to decide, and what remains is the project network of “Networks: Flows, Paths and Assignments”, with the same forward pass and the same longest path. The scheduling content is entirely in the choice of orientation.
Two jobs, two machines, opposite routes
operations, in each job’s own order
J1 M1 for 3, then M2 for 2
J2 M2 for 4, then M1 for 1
machine loads M1 = 3 + 1 = 4 M2 = 2 + 4 = 6
job lengths J1 = 5 J2 = 5
lower bound max(4, 6, 5, 5) = 6
two shared pairs — one on M1, one on M2 — so 2 to the 2 = 4 orientations
J2 first on M1, J2 first on M2 makespan 10
J1 first on M1, J2 first on M2 makespan 6 the best
J2 first on M1, J1 first on M2 DEADLOCK
J1 first on M1, J1 first on M2 makespan 10
The best orientation is the one that lets the two jobs start at once on the machines they each need first. J1 takes M1 from 0 to 3; J2 takes M2 from 0 to 4; then J1 moves to M2 at 4 and finishes at 6, and J2 moves to M1 at 4 and finishes at 5. The makespan is 6, which equals the load on M2 — that machine never stopped, so nothing could have been faster.
Example · The orientation that cannot be runChoose J2 before J1 on M1 and J1 before J2 on M2. Now follow the requirements round: J1's M1 operation waits for J2's M1 operation, which waits — by J2's own route — for J2's M2 operation, which waits for J1's M2 operation, which waits — by J1's own route — for J1's M1 operation.
Four requirements in a circle, and not one of the four is unreasonable on its own. Nothing can start. The lab reports this orientation as deadlocked rather than assigning it a makespan or quietly dropping it, and it counts how many of the orientations are in that state: one of the four here, seven of the sixteen on its three-job preset.
Every orientation, scored
At this size the lab does the only completely honest thing: it builds all 2^k orientations, computes start times for each acyclic one, and reads the schedule back off the drawing to check that no machine runs two operations at once and no job starts a step before the one before it finished. Then it reports the best, the count and how many deadlocked.
preset operations pairs orientations deadlocked best
two jobs 4 2 4 1 6
three jobs 5 4 16 7 9
one busy machine 5 4 16 4 10
2^k is the number to look at. Four pairs is sixteen orientations and is comfortable; ten pairs is a thousand and is still fine; thirty pairs is a billion and is not. The count of pairs grows roughly as the square of the operations on the busiest machine, so a shop with one machine everything passes through gets expensive fastest — which is what the lab's third preset is for, with three of its four pairs on a single machine.
Why this is where the course stops
There is no exchange argument here and there is not going to be. The single-machine rules worked because a schedule was an order, two jobs could be adjacent, and swapping them moved the objective by one subtraction. In a job shop a schedule is a set of binary choices whose feasibility is a property of the whole set — one flipped bit can turn a working schedule into a deadlock — and no local argument reaches that.
What remains is search, and the search is the one “Integer Programming” builds: branch on a disjunction, bound each node, and report a gap when the tree has not closed. The lower bound is free and worth taking — every machine's total load and every job's total length are both lower bounds on the makespan, since a machine cannot compress its own work and a job cannot overlap its own operations. On the two-job shop that gives 6, which the best orientation attains, so the enumeration was confirming something a bound had already proved.