Card 07/ 08
Practice: Scheduling the Jobs of a Kitchen Refit
Eight jobs with dependencies need an order; then one late change makes a dependency loop.
Scheduling jobs that depend on each other is a topological sort of the graph whose edges are the dependencies. This exercise schedules a kitchen refit, asks for a second valid schedule, and then adds one change that makes scheduling impossible.
A kitchen refit has eight jobs: Strip, Electrics, Plumbing, Plaster, Units, Worktop, Tiles and Paint. The table gives what each job must wait for.
| Job | Waits for |
|---|---|
| Strip | Nothing |
| Electrics | Strip |
| Plumbing | Strip |
| Plaster | Electrics and Plumbing |
| Units | Plaster |
| Worktop | Units |
| Tiles | Worktop |
| Paint | Plaster |
- Run Kahn's algorithm, taking ready jobs in the order they are listed above. What schedule does it give?
- Give a second valid schedule that differs from the first.
- The tiler now insists on tiling before the units go in, so Units must also wait for Tiles. What does Kahn's algorithm report, and which jobs can be scheduled?
Answer all three parts, then compare with the trace and the run.
InputEight jobs and eight dependencies, ready jobs taken in the order they are listed
Start
- Strip:0
- Electrics:1
- Plumbing:1
- Plaster:2
- Units:1
- Worktop:1
- Tiles:1
- Paint:1
Each cell is a job and how many of the jobs it needs are not yet done. Only Strip needs nothing, so it is the one ready job.
Strip
- Strip:outnext
- Electrics:0
- Plumbing:0
- Plaster:2
- Units:1
- Worktop:1
- Tiles:1
- Paint:1
Strip is done, and Electrics and Plumbing both drop to 0.
Electrics
- Strip:out
- Electrics:outnext
- Plumbing:0
- Plaster:1
- Units:1
- Worktop:1
- Tiles:1
- Paint:1
Electrics is done, and Plaster drops to 1: it still needs Plumbing.
Plumbing
- Strip:out
- Electrics:out
- Plumbing:outnext
- Plaster:0
- Units:1
- Worktop:1
- Tiles:1
- Paint:1
Plumbing is done, and Plaster reaches 0.
Plaster
- Strip:out
- Electrics:out
- Plumbing:out
- Plaster:outnext
- Units:0
- Worktop:1
- Tiles:1
- Paint:0
Plaster is done. Units and Paint both reach 0 and join the queue, Units first because it was listed first.
Units
- Strip:out
- Electrics:out
- Plumbing:out
- Plaster:out
- Units:outnext
- Worktop:0
- Tiles:1
- Paint:0
Units is done, and Worktop reaches 0, behind Paint in the queue.
Paint
- Strip:out
- Electrics:out
- Plumbing:out
- Plaster:out
- Units:out
- Worktop:0
- Tiles:1
- Paint:outnext
Paint is done. Nothing waited on it.
Worktop
- Strip:out
- Electrics:out
- Plumbing:out
- Plaster:out
- Units:out
- Worktop:outnext
- Tiles:0
- Paint:out
Worktop is done, and Tiles reaches 0.
Tiles
- Strip:out
- Electrics:out
- Plumbing:out
- Plaster:out
- Units:out
- Worktop:out
- Tiles:outnext
- Paint:out
Tiles is done, and all eight jobs are scheduled.
ResultStrip Electrics Plumbing Plaster Units Paint Worktop Tiles
- Strip, Electrics, Plumbing, Plaster, Units, Paint, Worktop, Tiles. Plaster waits until both Electrics and Plumbing are out, and Paint joins the queue with Units, behind it.
- Keeping the ready jobs on a stack instead gives Strip, Plumbing, Electrics, Plaster, Paint, Units, Worktop, Tiles, which the run printed. Any order that keeps each job after the jobs it waits for is right; swapping Electrics and Plumbing in the first answer is another.
- Units now waits for Tiles, Tiles for Worktop, and Worktop for Units: a cycle. Kahn's algorithm outputs Strip, Electrics, Plumbing, Plaster and Paint, 5 of the 8 jobs, and then has nothing ready. The three jobs left are exactly the cycle, and the refit cannot be scheduled until the tiler's demand or the worktop's goes.
from collections import deque
JOBS = ["Strip", "Electrics", "Plumbing", "Plaster", "Units", "Worktop", "Tiles", "Paint"]
NEEDS = [
("Strip", "Electrics"),
("Strip", "Plumbing"),
("Electrics", "Plaster"),
("Plumbing", "Plaster"),
("Plaster", "Units"),
("Plaster", "Paint"),
("Units", "Worktop"),
("Worktop", "Tiles"),
]
def schedule(jobs, needs, take_from_front=True, trace=False):
after = {job: [] for job in jobs}
waiting = {job: 0 for job in jobs}
for first, then in needs:
after[first].append(then)
waiting[then] += 1
ready = deque(job for job in jobs if waiting[job] == 0)
order = []
def cells():
return " ".join(f"{j}:out" if j in order else f"{j}:{waiting[j]}" for j in jobs)
if trace:
print(f"TRACE | Start | {cells()} |")
while ready:
job = ready.popleft() if take_from_front else ready.pop()
order.append(job)
for then in after[job]:
waiting[then] -= 1
if waiting[then] == 0:
ready.append(then)
if trace:
print(f"TRACE | {job} | {cells()} | next={jobs.index(job)}")
left = [job for job in jobs if job not in order]
return order, left
order, _ = schedule(JOBS, NEEDS, trace=True)
print("queue:", " ".join(order))
order, _ = schedule(JOBS, NEEDS, take_from_front=False)
print("stack:", " ".join(order))
order, left = schedule(JOBS, NEEDS + [("Tiles", "Units")])
print(f"with Tiles before Units: output {len(order)} of {len(JOBS)}: {' '.join(order)}; left {' '.join(left)}")TRACE | Start | Strip:0 Electrics:1 Plumbing:1 Plaster:2 Units:1 Worktop:1 Tiles:1 Paint:1 |
TRACE | Strip | Strip:out Electrics:0 Plumbing:0 Plaster:2 Units:1 Worktop:1 Tiles:1 Paint:1 | next=0
TRACE | Electrics | Strip:out Electrics:out Plumbing:0 Plaster:1 Units:1 Worktop:1 Tiles:1 Paint:1 | next=1
TRACE | Plumbing | Strip:out Electrics:out Plumbing:out Plaster:0 Units:1 Worktop:1 Tiles:1 Paint:1 | next=2
TRACE | Plaster | Strip:out Electrics:out Plumbing:out Plaster:out Units:0 Worktop:1 Tiles:1 Paint:0 | next=3
TRACE | Units | Strip:out Electrics:out Plumbing:out Plaster:out Units:out Worktop:0 Tiles:1 Paint:0 | next=4
TRACE | Paint | Strip:out Electrics:out Plumbing:out Plaster:out Units:out Worktop:0 Tiles:1 Paint:out | next=7
TRACE | Worktop | Strip:out Electrics:out Plumbing:out Plaster:out Units:out Worktop:out Tiles:0 Paint:out | next=5
TRACE | Tiles | Strip:out Electrics:out Plumbing:out Plaster:out Units:out Worktop:out Tiles:out Paint:out | next=6
queue: Strip Electrics Plumbing Plaster Units Paint Worktop Tiles
stack: Strip Plumbing Electrics Plaster Paint Units Worktop Tiles
with Tiles before Units: output 5 of 8: Strip Electrics Plumbing Plaster Paint; left Units Worktop Tiles← → move through sections and cards