Card 07/ 08

ExerciseDifficulty: Advanced4 min

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.

The eight refit jobs and the jobs each must wait for
JobWaits for
StripNothing
ElectricsStrip
PlumbingStrip
PlasterElectrics and Plumbing
UnitsPlaster
WorktopUnits
TilesWorktop
PaintPlaster
  1. Run Kahn's algorithm, taking ready jobs in the order they are listed above. What schedule does it give?
  2. Give a second valid schedule that differs from the first.
  3. 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.
Unmet dependencies of each refit job as Kahn's algorithm schedules it

InputEight jobs and eight dependencies, ready jobs taken in the order they are listed

  1. Start

    1. Strip:0
    2. Electrics:1
    3. Plumbing:1
    4. Plaster:2
    5. Units:1
    6. Worktop:1
    7. Tiles:1
    8. 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.

  2. Strip

    1. Strip:outnext
    2. Electrics:0
    3. Plumbing:0
    4. Plaster:2
    5. Units:1
    6. Worktop:1
    7. Tiles:1
    8. Paint:1

    Strip is done, and Electrics and Plumbing both drop to 0.

  3. Electrics

    1. Strip:out
    2. Electrics:outnext
    3. Plumbing:0
    4. Plaster:1
    5. Units:1
    6. Worktop:1
    7. Tiles:1
    8. Paint:1

    Electrics is done, and Plaster drops to 1: it still needs Plumbing.

  4. Plumbing

    1. Strip:out
    2. Electrics:out
    3. Plumbing:outnext
    4. Plaster:0
    5. Units:1
    6. Worktop:1
    7. Tiles:1
    8. Paint:1

    Plumbing is done, and Plaster reaches 0.

  5. Plaster

    1. Strip:out
    2. Electrics:out
    3. Plumbing:out
    4. Plaster:outnext
    5. Units:0
    6. Worktop:1
    7. Tiles:1
    8. Paint:0

    Plaster is done. Units and Paint both reach 0 and join the queue, Units first because it was listed first.

  6. Units

    1. Strip:out
    2. Electrics:out
    3. Plumbing:out
    4. Plaster:out
    5. Units:outnext
    6. Worktop:0
    7. Tiles:1
    8. Paint:0

    Units is done, and Worktop reaches 0, behind Paint in the queue.

  7. Paint

    1. Strip:out
    2. Electrics:out
    3. Plumbing:out
    4. Plaster:out
    5. Units:out
    6. Worktop:0
    7. Tiles:1
    8. Paint:outnext

    Paint is done. Nothing waited on it.

  8. Worktop

    1. Strip:out
    2. Electrics:out
    3. Plumbing:out
    4. Plaster:out
    5. Units:out
    6. Worktop:outnext
    7. Tiles:0
    8. Paint:out

    Worktop is done, and Tiles reaches 0.

  9. Tiles

    1. Strip:out
    2. Electrics:out
    3. Plumbing:out
    4. Plaster:out
    5. Units:out
    6. Worktop:out
    7. Tiles:outnext
    8. Paint:out

    Tiles is done, and all eight jobs are scheduled.

ResultStrip Electrics Plumbing Plaster Units Paint Worktop Tiles

  1. 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.
  2. 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.
  3. 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.
refit.pypy
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)}")
OutputRan with python:3.14.8-slim-trixie (Python 3.14.8)
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