Skip to content

Stateless Dispatching Rules

This gallery compares the stateless dispatching rules on a single seeded, multi-stage push shop. Each rule decides only the order in which a server processes the jobs already waiting in its queue — it needs no shop-wide state, just attributes of the candidate job (and, for WINQ, the next queue). Because the shop has variable multi-operation routings and due dates, every rule produces a distinct ordering, so their flow-time and tardiness trade-offs are directly comparable.

The seven rules:

  • SPT — shortest processing time first; picks the job with the smallest imminent operation time.
  • EDD — earliest due date; orders by the job's global due date.
  • ODD — operational due date; orders by the per-operation due date derived from the global one.
  • MODD — modified operational due date; falls back to processing time once a job is no longer at risk of tardiness.
  • CR — critical ratio (slack ÷ remaining work); smaller ratios are more urgent.
  • FCFS — first come first served; the neutral arrival-order baseline.
  • WINQ — work in next queue; favours jobs heading to the least-loaded downstream server.

See also: Dispatching Rules API

Comparison

"""Stateless dispatching rules compared on one seeded multi-stage shop.

Runs SPT, EDD, ODD, MODD, CR, FCFS, and WINQ as the queue-ordering rule of an
immediate-release (push) shop and prints a comparison table. The shop has
variable multi-operation routings and due dates, so every rule produces a
distinct ordering.

Run: uv run python examples/gallery_dispatching_stateless.py
"""

from __future__ import annotations

from simulatte.builders import build_immediate_release_system
from simulatte.dispatching_rules import (
    critical_ratio,
    earliest_due_date,
    first_come_first_served,
    modified_operational_due_date,
    operational_due_date,
    shortest_processing_time,
    work_in_next_queue,
)
from simulatte.distributions import Uniform
from simulatte.environment import Environment
from simulatte.scenario import Scenario

SEED = 42
HORIZON = 800.0

RULES = {
    "SPT": shortest_processing_time,
    "EDD": earliest_due_date,
    "ODD": operational_due_date,
    "MODD": modified_operational_due_date,
    "CR": critical_ratio,
    "FCFS": first_come_first_served,
    "WINQ": work_in_next_queue,
}


def run_rule(rule) -> tuple[int, float, float, float]:
    with Environment(seed=SEED) as env:  # identical seeded streams for every rule -> fair comparison
        _, _servers, shop_floor, _, _ = build_immediate_release_system(
            env=env, scenario=Scenario(due_date_offset=Uniform(10.0, 18.0)), priority_policies=rule
        )
        env.run(until=HORIZON)
        done = shop_floor.jobs_done
        n = len(done)
        avg_tis = shop_floor.average_time_in_system
        tardiness = [max(0.0, j.lateness) for j in done]
        mean_tard = sum(tardiness) / n if n else 0.0
        pct_tardy = 100.0 * sum(1 for t in tardiness if t > 0) / n if n else 0.0
        return n, avg_tis, mean_tard, pct_tardy


def main() -> None:
    print("Stateless dispatching rules (immediate release, seed=42)")
    print(f"{'Rule':<6}{'Done':>6}{'AvgTIS':>9}{'MeanTard':>10}{'%Tardy':>8}")
    for name, rule in RULES.items():
        n, tis, mt, pt = run_rule(rule)
        print(f"{name:<6}{n:>6}{tis:>9.2f}{mt:>10.2f}{pt:>7.1f}%")


if __name__ == "__main__":
    main()

Run it:

uv run python examples/gallery_dispatching_stateless.py

Output

Stateless dispatching rules (immediate release, seed=42)
Rule    Done   AvgTIS  MeanTard  %Tardy
SPT     1164    10.38      2.66   20.8%
EDD     1159    16.54      4.87   61.9%
ODD     1164    16.51      4.53   67.7%
MODD    1164    13.02      1.98   32.0%
CR      1161    16.54      4.43   72.4%
FCFS    1162    18.69      7.45   59.6%
WINQ    1164    14.29      4.89   41.2%

Interpretation

SPT minimises average time in system (10.38 vs FCFS's 18.69) by always clearing the shortest job first; the due-date-blind FCFS baseline runs more than half its jobs late (59.6% tardy, mean tardiness 7.45) and posts the highest AvgTIS of all the rules here. The shop's due dates are deliberately set to bind — the uniform offset is tightened to (10.0, 18.0), close to the achievable flow times — so the due-date family is genuinely exercised rather than collapsing to zero tardiness. With binding due dates the rules separate clearly: MODD is the standout of the due-date family, cutting mean tardiness to 1.98 (32.0% tardy) while keeping AvgTIS at 13.02, because it falls back to shortest-processing-time once a job is no longer at risk of lateness. The pure due-date rules trade differently: ODD (16.51) and EDD (16.54) both lower flow time versus FCFS and cut mean tardiness below it (4.53 and 4.87 vs 7.45), but leave more jobs marginally late (67.7% and 61.9% tardy) as they chase the now-binding due dates. CR is the most conservative on tardiness (the most jobs late, 72.4% tardy), its slack ratio repeatedly deferring short jobs. SPT itself, despite reading no due date, still posts the fewest tardy jobs (20.8%): in this loaded shop its flow-time dominance clears queues fast enough to suppress lateness. Note that none of these rules controls work-in-process — they only reorder what is already on the floor. To cap WIP and shape release, combine a dispatching rule with a release policy (see the workload-control release gallery).