Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions docs/paper/reductions.typ
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
"SubgraphIsomorphism": [Subgraph Isomorphism],
"PartitionIntoTriangles": [Partition Into Triangles],
"FlowShopScheduling": [Flow Shop Scheduling],
"MinimumTardinessSequencing": [Minimum Tardiness Sequencing],
)

// Definition label: "def:<ProblemName>" — each definition block must have a matching label
Expand Down Expand Up @@ -1781,6 +1782,82 @@ NP-completeness was established by Garey, Johnson, and Stockmeyer @gareyJohnsonS
) <fig:flowshop>
]

#{
let x = load-model-example("MinimumTardinessSequencing")
let ntasks = x.instance.num_tasks
let deadlines = x.instance.deadlines
let precs = x.instance.precedences
let sol = x.optimal.at(0)
let tardy-count = sol.metric.Valid
// Decode Lehmer code to permutation (schedule order)
let lehmer = sol.config
let schedule = {
let avail = range(ntasks)
let result = ()
for c in lehmer {
result.push(avail.at(c))
avail = avail.enumerate().filter(((i, v)) => i != c).map(((i, v)) => v)
}
result
}
// Compute inverse: task-pos[task] = position
let task-pos = range(ntasks).map(task => {
schedule.enumerate().filter(((p, t)) => t == task).at(0).at(0)
})
// Identify tardy tasks
let tardy-tasks = range(ntasks).filter(t => task-pos.at(t) + 1 > deadlines.at(t))
[
#problem-def("MinimumTardinessSequencing")[
Given a set $T$ of $n$ unit-length tasks, a deadline function $d: T -> ZZ^+$, and a partial order $prec.eq$ on $T$, find a one-machine schedule $sigma: T -> {1, 2, dots, n}$ that respects the precedence constraints (if $t_i prec.eq t_j$ then $sigma(t_i) < sigma(t_j)$) and minimizes the number of _tardy_ tasks, i.e., tasks $t$ with $sigma(t) > d(t)$.
][
Minimum Tardiness Sequencing is a classical NP-complete scheduling problem catalogued as SS2 in Garey & Johnson @garey1979. In standard scheduling notation it is written $1 | "prec", p_j = 1 | sum U_j$, where $U_j = 1$ if job $j$ finishes after its deadline and $U_j = 0$ otherwise.

The problem is NP-complete by reduction from Clique (Theorem 3.10 in @garey1979). When the precedence constraints are empty, the problem becomes solvable in $O(n log n)$ time by Moore's algorithm @moore1968: sort tasks by deadline and greedily schedule each task on time, removing the task with the largest processing time whenever a deadline violation occurs. With arbitrary precedence constraints and unit processing times, the problem remains strongly NP-hard.

*Example.* Consider $n = #ntasks$ tasks with deadlines $d = (#deadlines.map(v => str(v)).join(", "))$ and precedence constraint #{precs.map(p => [$t_#(p.at(0)) prec.eq t_#(p.at(1))$]).join(", ")}. An optimal schedule places tasks in order $(#schedule.map(t => $t_#t$).join(", "))$, giving #tardy-count tardy #if tardy-count == 1 [task] else [tasks]#{if tardy-tasks.len() > 0 [ ($#{tardy-tasks.map(t => $t_#t$).join(", ")}$ #if tardy-tasks.len() == 1 [finishes] else [finish] after #if tardy-tasks.len() == 1 [its deadline] else [their deadlines])]}.

#figure(
canvas(length: 1cm, {
import draw: *
let colors = (rgb("#4e79a7"), rgb("#e15759"), rgb("#76b7b2"), rgb("#f28e2b"), rgb("#59a14f"))
let scale = 1.2
let row-h = 0.6

// Draw schedule blocks (single machine, unit-length tasks)
for (pos, task) in schedule.enumerate() {
let x0 = pos * scale
let x1 = (pos + 1) * scale
let is-tardy = tardy-tasks.contains(task)
let fill = colors.at(calc.rem(task, colors.len())).transparentize(if is-tardy { 70% } else { 30% })
let stroke-color = colors.at(calc.rem(task, colors.len()))
rect((x0, -row-h / 2), (x1, row-h / 2),
fill: fill, stroke: 0.4pt + stroke-color)
content(((x0 + x1) / 2, 0), text(7pt, $t_#task$))
// Deadline marker for this task
let dl = deadlines.at(task)
if dl <= ntasks {
let dl-x = dl * scale
line((dl-x, row-h / 2 + 0.05 + task * 0.12), (dl-x, row-h / 2 + 0.15 + task * 0.12),
stroke: (paint: if is-tardy { red } else { green.darken(20%) }, thickness: 0.6pt))
}
}

// Time axis
let y-axis = -row-h / 2 - 0.2
line((0, y-axis), (ntasks * scale, y-axis), stroke: 0.4pt)
for t in range(ntasks + 1) {
let x = t * scale
line((x, y-axis), (x, y-axis - 0.1), stroke: 0.4pt)
content((x, y-axis - 0.25), text(6pt, str(t + 1)))
}
content((ntasks * scale / 2, y-axis - 0.45), text(7pt)[finish time])
}),
caption: [Optimal schedule for #ntasks tasks. #if tardy-tasks.len() > 0 [Faded #if tardy-tasks.len() == 1 [block indicates the] else [blocks indicate] tardy #if tardy-tasks.len() == 1 [task] else [tasks] (finish time exceeds deadline).] else [All tasks meet their deadlines.]],
) <fig:mts>
]
]
}

// Completeness check: warn about problem types in JSON but missing from paper
#{
let json-models = {
Expand Down
11 changes: 11 additions & 0 deletions docs/paper/references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ @article{juttner2018
doi = {10.1016/j.dam.2018.02.018}
}

@article{moore1968,
author = {J. Michael Moore},
title = {An $n$ Job, One Machine Sequencing Algorithm for Minimizing the Number of Late Jobs},
journal = {Management Science},
volume = {15},
number = {1},
pages = {102--109},
year = {1968},
doi = {10.1287/mnsc.15.1.102}
}

@article{johnson1954,
author = {Selmer M. Johnson},
title = {Optimal two- and three-stage production schedules with setup times included},
Expand Down
1 change: 1 addition & 0 deletions docs/src/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ pred create MaxCut --graph 0-1,1-2,2-0 -o maxcut.json
pred create Factoring --target 15 --bits-m 4 --bits-n 4 -o factoring.json
pred create Factoring --target 21 --bits-m 3 --bits-n 3 -o factoring2.json
pred create X3C --universe 9 --sets "0,1,2;0,2,4;3,4,5;3,5,7;6,7,8;1,4,6;2,5,8" -o x3c.json
pred create MinimumTardinessSequencing --n 5 --deadlines 5,5,5,3,3 --precedence-pairs "0>3,1>3,1>4,2>4" -o mts.json
```

Canonical examples are useful when you want a known-good instance from the paper/example database.
Expand Down
21 changes: 21 additions & 0 deletions docs/src/reductions/problem_schemas.json
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,27 @@
}
]
},
{
"name": "MinimumTardinessSequencing",
"description": "Schedule unit-length tasks with precedence constraints and deadlines to minimize the number of tardy tasks",
"fields": [
{
"name": "num_tasks",
"type_name": "usize",
"description": "Number of tasks |T|"
},
{
"name": "deadlines",
"type_name": "Vec<usize>",
"description": "Deadline d(t) for each task (1-indexed finish time)"
},
{
"name": "precedences",
"type_name": "Vec<(usize, usize)>",
"description": "Precedence pairs (predecessor, successor)"
}
]
},
{
"name": "MinimumVertexCover",
"description": "Find minimum weight vertex cover in a graph",
Expand Down
65 changes: 36 additions & 29 deletions docs/src/reductions/reduction_graph.json
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,13 @@
"doc_path": "models/graph/struct.MinimumSumMulticenter.html",
"complexity": "2^num_vertices"
},
{
"name": "MinimumTardinessSequencing",
"variant": {},
"category": "misc",
"doc_path": "models/misc/struct.MinimumTardinessSequencing.html",
"complexity": "2^num_tasks"
},
{
"name": "MinimumVertexCover",
"variant": {
Expand Down Expand Up @@ -542,7 +549,7 @@
},
{
"source": 4,
"target": 53,
"target": 54,
"overhead": [
{
"field": "num_spins",
Expand Down Expand Up @@ -602,7 +609,7 @@
},
{
"source": 12,
"target": 48,
"target": 49,
"overhead": [
{
"field": "num_vars",
Expand Down Expand Up @@ -643,7 +650,7 @@
},
{
"source": 19,
"target": 48,
"target": 49,
"overhead": [
{
"field": "num_vars",
Expand All @@ -669,7 +676,7 @@
},
{
"source": 20,
"target": 48,
"target": 49,
"overhead": [
{
"field": "num_vars",
Expand All @@ -695,7 +702,7 @@
},
{
"source": 21,
"target": 48,
"target": 49,
"overhead": [
{
"field": "num_vars",
Expand All @@ -706,7 +713,7 @@
},
{
"source": 21,
"target": 55,
"target": 56,
"overhead": [
{
"field": "num_elements",
Expand All @@ -717,7 +724,7 @@
},
{
"source": 22,
"target": 50,
"target": 51,
"overhead": [
{
"field": "num_clauses",
Expand All @@ -736,7 +743,7 @@
},
{
"source": 23,
"target": 48,
"target": 49,
"overhead": [
{
"field": "num_vars",
Expand All @@ -762,7 +769,7 @@
},
{
"source": 25,
"target": 53,
"target": 54,
"overhead": [
{
"field": "num_spins",
Expand Down Expand Up @@ -942,7 +949,7 @@
},
{
"source": 31,
"target": 44,
"target": 45,
"overhead": [
{
"field": "num_vertices",
Expand Down Expand Up @@ -1077,7 +1084,7 @@
},
{
"source": 37,
"target": 48,
"target": 49,
"overhead": [
{
"field": "num_vars",
Expand Down Expand Up @@ -1162,7 +1169,7 @@
"doc_path": "rules/minimumsetcovering_ilp/index.html"
},
{
"source": 44,
"source": 45,
"target": 31,
"overhead": [
{
Expand All @@ -1177,7 +1184,7 @@
"doc_path": "rules/minimumvertexcover_maximumindependentset/index.html"
},
{
"source": 44,
"source": 45,
"target": 42,
"overhead": [
{
Expand All @@ -1192,7 +1199,7 @@
"doc_path": "rules/minimumvertexcover_minimumsetcovering/index.html"
},
{
"source": 48,
"source": 49,
"target": 12,
"overhead": [
{
Expand All @@ -1207,8 +1214,8 @@
"doc_path": "rules/qubo_ilp/index.html"
},
{
"source": 48,
"target": 52,
"source": 49,
"target": 53,
"overhead": [
{
"field": "num_spins",
Expand All @@ -1218,7 +1225,7 @@
"doc_path": "rules/spinglass_qubo/index.html"
},
{
"source": 50,
"source": 51,
"target": 4,
"overhead": [
{
Expand All @@ -1233,7 +1240,7 @@
"doc_path": "rules/sat_circuitsat/index.html"
},
{
"source": 50,
"source": 51,
"target": 16,
"overhead": [
{
Expand All @@ -1248,7 +1255,7 @@
"doc_path": "rules/sat_coloring/index.html"
},
{
"source": 50,
"source": 51,
"target": 21,
"overhead": [
{
Expand All @@ -1263,7 +1270,7 @@
"doc_path": "rules/sat_ksat/index.html"
},
{
"source": 50,
"source": 51,
"target": 30,
"overhead": [
{
Expand All @@ -1278,7 +1285,7 @@
"doc_path": "rules/sat_maximumindependentset/index.html"
},
{
"source": 50,
"source": 51,
"target": 39,
"overhead": [
{
Expand All @@ -1293,8 +1300,8 @@
"doc_path": "rules/sat_minimumdominatingset/index.html"
},
{
"source": 52,
"target": 48,
"source": 53,
"target": 49,
"overhead": [
{
"field": "num_vars",
Expand All @@ -1304,7 +1311,7 @@
"doc_path": "rules/spinglass_qubo/index.html"
},
{
"source": 53,
"source": 54,
"target": 25,
"overhead": [
{
Expand All @@ -1319,8 +1326,8 @@
"doc_path": "rules/spinglass_maxcut/index.html"
},
{
"source": 53,
"target": 52,
"source": 54,
"target": 53,
"overhead": [
{
"field": "num_spins",
Expand All @@ -1334,7 +1341,7 @@
"doc_path": "rules/spinglass_casts/index.html"
},
{
"source": 56,
"source": 57,
"target": 12,
"overhead": [
{
Expand All @@ -1349,8 +1356,8 @@
"doc_path": "rules/travelingsalesman_ilp/index.html"
},
{
"source": 56,
"target": 48,
"source": 57,
"target": 49,
"overhead": [
{
"field": "num_vars",
Expand Down
Loading
Loading