Paper
Fair Repetitive Interval Scheduling
6 pages · 24 marked passages · pdflatex · download PDF · lax-117284
-
Fair Repetitive Interval Scheduling
An instance consists of clients and days. Every client submits one job on every day: client 's job on day has a processing time and a due date . The schedule is just-in-time, so that job occupies exactly the interval , and two jobs of the same day conflict if their intervals intersect. One machine is available on each day, so a schedule selects, for every day, a set of clients whose day's jobs are pairwise non-conflicting; the jobs of the clients left out are rejected that day.
Write for the indicator that client 's job is executed on day . The objective is the fairness of the schedule, : the decision problem asks, given a fairness parameter , whether some schedule serves every client on at least of the days. In the generalization every client carries its own parameter and must be served on at least days.
Three restrictions of the instance recur. The processing times are day-independent if for all , the due dates are day-independent if for all , and the processing times are unit if throughout.
1 import Mathlib.Data.Fintype.Card 2 import Mathlib.Order.Interval.Set.Basic 3 … module docstring, 39 lines 43 44 namespace Lax117284.Scheduling 45 46 /-- An instance of fair repetitive interval scheduling: `clients` clients each submitting 47 one job on each of `days` days, where client `j`'s job on day `i` has processing time 48 `p i j` and due date `d i j`. A job takes some time and does not start before time `0`. -/ 49 structure Instance where 50 /-- The number `n` of clients. -/ 51 clients : ℕ 52 /-- The number `m` of days. -/ 53 days : ℕ 54 /-- The processing time `p i j` of client `j`'s job on day `i`. -/ 55 p : Fin days → Fin clients → ℕ 56 /-- The due date `d i j` of client `j`'s job on day `i`. -/ 57 d : Fin days → Fin clients → ℕ 58 /-- Every job takes some time. -/ 59 p_pos : ∀ i j, 0 < p i j 60 /-- No job starts before time `0`. -/ 61 p_le_d : ∀ i j, p i j ≤ d i j 62 63 namespace Instance 64 65 variable (I : Instance) 66 67 /-- The interval `(d i j - p i j, d i j]` occupied by client `j`'s job on day `i`: the job 68 is completed exactly at its due date. -/ 69 def job (i : Fin I.days) (j : Fin I.clients) : Set ℕ := 70 Set.Ioc (I.d i j - I.p i j) (I.d i j) 71 72 /-- Clients `j` and `j'` **conflict on day `i`**: their day-`i` jobs share a time point, so 73 the machine of that day can execute at most one of them. -/ 74 def Conflict (i : Fin I.days) (j j' : Fin I.clients) : Prop := 75 (I.job i j ∩ I.job i j').Nonempty 76 77 /-- A **schedule** names, for each day, the clients whose job is executed that day. -/ 78 abbrev Schedule := Fin I.days → Finset (Fin I.clients) 79 80 variable {I} 81 82 /-- A schedule is **feasible** if the jobs it executes on any one day are pairwise 83 non-conflicting. -/ 84 def Feasible (σ : I.Schedule) : Prop := 85 ∀ i, (σ i : Set (Fin I.clients)).Pairwise fun j j' => ¬ I.Conflict i j j' 86 87 /-- `∑_i Z_{i,j}`, the number of days on which client `j`'s job is executed. -/ 88 def served (σ : I.Schedule) (j : Fin I.clients) : ℕ := 89 (Finset.univ.filter fun i => j ∈ σ i).card 90 91 /-- A schedule is **fair** for the parameters `k` if every client `j` is served on at least 92 `k j` days. -/ 93 def Fair (k : Fin I.clients → ℕ) (σ : I.Schedule) : Prop := ∀ j, k j ≤ served σ j 94 95 variable (I) 96 97 /-- **The question of `1 | k_j, rep | min_j ∑_i Z_{i,j}`**: is there a feasible schedule 98 serving every client `j` on at least `k j` days? -/ 99 def HasFairSchedule (k : Fin I.clients → ℕ) : Prop := 100 ∃ σ : I.Schedule, Feasible σ ∧ Fair k σ 101 102 /-- **The question of `1 | rep | min_j ∑_i Z_{i,j}`**: the uniform case, in which every 103 client carries the same fairness parameter `k`. -/ 104 def HasKFairSchedule (k : ℕ) : Prop := I.HasFairSchedule fun _ => k 105 106 /-- `p_{i,j} = p_j`: the processing times are **day-independent**. -/ 107 def DayIndepP : Prop := ∀ i i' j, I.p i j = I.p i' j 108 109 /-- `d_{i,j} = d_j`: the due dates are **day-independent**. -/ 110 def DayIndepD : Prop := ∀ i i' j, I.d i j = I.d i' j 111 112 /-- The processing time of client `j`'s job on day `i`, read off unnumbered indices and 113 `1` outside the instance, so that a construction reading another instance need not carry 114 the proofs that its indices are in range. -/ 115 def pAt (i j : ℕ) : ℕ := 116 if h : i < I.days then if h' : j < I.clients then I.p ⟨i, h⟩ ⟨j, h'⟩ else 1 else 1 117 118 /-- The due date of client `j`'s job on day `i`, and `1` outside the instance. -/ 119 def dAt (i j : ℕ) : ℕ := 120 if h : i < I.days then if h' : j < I.clients then I.d ⟨i, h⟩ ⟨j, h'⟩ else 1 else 1 121 122 theorem pAt_pos (i j : ℕ) : 0 < I.pAt i j := by 123 unfold pAt 124 split 125 · split 126 · exact I.p_pos _ _ 127 · exact Nat.zero_lt_one 128 · exact Nat.zero_lt_one 129 130 theorem pAt_le_dAt (i j : ℕ) : I.pAt i j ≤ I.dAt i j := by 131 unfold pAt dAt 132 split 133 · split 134 · exact I.p_le_d _ _ 135 · exact Nat.le_refl 1 136 · exact Nat.le_refl 1 137 138 @[simp] theorem pAt_coe (i : Fin I.days) (j : Fin I.clients) : I.pAt i j = I.p i j := by 139 simp [pAt, i.isLt, j.isLt] 140 141 @[simp] theorem dAt_coe (i : Fin I.days) (j : Fin I.clients) : I.dAt i j = I.d i j := by 142 simp [dAt, i.isLt, j.isLt] 143 144 /-- Whether clients `j` and `j'` conflict on day `i`, read off unnumbered indices and in 145 the arithmetic form: each of the two jobs starts before the other one is due. It is this 146 form of the condition that a construction can decide. -/ 147 def ConflictAt (i j j' : ℕ) : Prop := 148 I.dAt i j - I.pAt i j < I.dAt i j' ∧ I.dAt i j' - I.pAt i j' < I.dAt i j 149 150 instance (i j j' : ℕ) : Decidable (I.ConflictAt i j j') := by 151 unfold ConflictAt; infer_instance 152 153 /-- **The arithmetic and the geometric form of conflict agree.** -/ 154 theorem conflictAt_iff (i : Fin I.days) (j j' : Fin I.clients) : 155 I.ConflictAt i j j' ↔ I.Conflict i j j' := by 156 have h1 := I.p_pos i j 157 have h2 := I.p_le_d i j 158 have h3 := I.p_pos i j' 159 have h4 := I.p_le_d i j' 160 simp only [ConflictAt, Conflict, job, pAt_coe, dAt_coe, Set.Nonempty, Set.mem_inter_iff, 161 Set.mem_Ioc] 162 constructor 163 · intro h 164 exact ⟨min (I.d i j) (I.d i j'), by omega, by omega⟩ 165 · rintro ⟨t, ⟨h5, h6⟩, h7, h8⟩ 166 omega 167 168 /-- `p_{i,j} = 1`: the processing times are **unit**. -/ 169 def UnitP : Prop := ∀ i j, I.p i j = 1 170 171 /-- No two jobs of a day conflict, so every client can be served on every day. -/ 172 def ConflictFree : Prop := ∀ i, ∀ j j', j ≠ j' → ¬ I.Conflict i j j' 173 174 end Instance 175 176 end Lax117284.Scheduling 177 -
The Word RAM
-
The Complexity of Fair Repetitive Interval Scheduling in the Fairness Parameter
Theorem 1. The problem is solvable in polynomial time when . For every fixed pair with , the problem is NP-hard.
The three tractable values are settled separately: and by inspection of the instance, and by a reduction to 2-satisfiability. Hardness holds already for every fixed pair with and ; it is obtained for from a satisfiability problem of bounded occurrence and lifted to all other pairs by two constructions adding one day at a time.
1 import Lax117284.Problems 2 … module docstring, 26 lines 29 30 namespace Lax117284.Theorem1 31 32 open Lax117284.Scheduling Lax117284.Problems Lax434930.PolynomialTime 33 34 /-- **The tractable values of the fairness parameter.** The problem restricted to 35 instances whose fairness parameter is `0`, `m - 1` or `m` is solvable in polynomial 36 time. -/ 37 axiom uniform_extremes_mem_P : 38 Uniform (fun I k => k = 0 ∨ k + 1 = I.days ∨ k = I.days) ∈ P 39 40 /-- **Theorem 1.** For every fixed number `m ≥ 3` of days and every fairness parameter `k` 41 with `0 < k < m - 1`, the problem restricted to that pair is NP-hard. -/ 42 axiom uniform_npHard (m k : ℕ) (hm : 3 ≤ m) (hk : 0 < k) (hk' : k + 1 < m) : 43 NPHard (Uniform fun I k' => I.days = m ∧ k' = k) 44 45 end Lax117284.Theorem1 46 -
The Conflict Graphs of an Instance
Let be an instance with clients and days. The day conflict graph of is the graph on the clients in which two clients are adjacent if their jobs of day conflict. The overall conflict graph of is the graph on the clients in which two clients are adjacent if their jobs conflict on at least one day; its edge set is the union of the edge sets of the daily graphs.
A day's conflict graph is the intersection graph of that day's intervals, so it is an interval graph. The observation the results of the source rest on is that a set of clients can be served on day exactly when it is an independent set of the day conflict graph: a schedule is feasible if and only if each of its days is independent in that day's graph.
The treewidth of the overall conflict graph is the structural parameter measured in the last section of the source, and it is the archive's treewidth.
1 import Lax117284.Scheduling 2 import Lax228581.Treewidth 3 import Mathlib.Combinatorics.SimpleGraph.Clique 4 … module docstring, 31 lines 36 37 namespace Lax117284.ConflictGraph 38 39 open Lax117284.Scheduling 40 41 /-- Conflict is a symmetric relation: the two jobs share a time point either way. -/ 42 theorem conflict_symm {I : Instance} {i : Fin I.days} {j j' : Fin I.clients} 43 (h : I.Conflict i j j') : I.Conflict i j' j := 44 let ⟨t, ht⟩ := h; ⟨t, ht.2, ht.1⟩ 45 46 variable (I : Instance) 47 48 /-- **The day `i` conflict graph** of `I`: two distinct clients are adjacent when their 49 jobs of day `i` conflict. -/ 50 def dayGraph (i : Fin I.days) : SimpleGraph (Fin I.clients) where 51 Adj j j' := j ≠ j' ∧ I.Conflict i j j' 52 symm := ⟨fun _ _ h => ⟨h.1.symm, conflict_symm h.2⟩⟩ 53 loopless := ⟨fun _ h => h.1 rfl⟩ 54 55 /-- **The overall conflict graph** of `I`: two distinct clients are adjacent when their 56 jobs conflict on some day. -/ 57 def overallGraph : SimpleGraph (Fin I.clients) where 58 Adj j j' := j ≠ j' ∧ ∃ i, I.Conflict i j j' 59 symm := ⟨fun _ _ h => ⟨h.1.symm, h.2.imp fun _ => conflict_symm⟩⟩ 60 loopless := ⟨fun _ h => h.1 rfl⟩ 61 62 /-- The treewidth `τ` of the overall conflict graph of `I`. -/ 63 noncomputable def treewidth : ℕ := Lax228581.Treewidth.treewidth (overallGraph I) 64 65 /-- **A schedule is feasible exactly when every day's set of clients is an independent set 66 of that day's conflict graph.** -/ 67 axiom feasible_iff_isIndepSet {I : Instance} (σ : I.Schedule) : 68 Instance.Feasible σ ↔ ∀ i, (dayGraph I i).IsIndepSet (σ i : Set (Fin I.clients)) 69 70 end Lax117284.ConflictGraph 71 -
Nice Tree Decompositions of Small Width Are Found in Fixed-Parameter Time
A tree decomposition of a graph is a tree whose nodes carry bags of vertices such that every vertex and every edge lies in a bag and the nodes whose bags contain a fixed vertex form a connected subtree; its width is the largest bag size minus one. It is nice if its nodes are of four kinds: a leaf, an introduce node whose bag is the bag of its single child plus one vertex, a forget node whose bag is the bag of its single child minus one vertex, and a join node with two children whose bags equal its own.
Bodlaender's theorem. For every constant one can decide in linear time whether a graph has treewidth at most , and if so construct a tree decomposition of width at most ; the dependence on is . Kloks' construction turns any tree decomposition of width into a nice one of the same width in time linear in the size of the decomposition, so the same holds for nice decompositions.
Bodlaender, "A linear-time algorithm for finding tree-decompositions of small treewidth", SIAM Journal on Computing 25 (1996) 1305–1317; Kloks, "Treewidth: Computations and Approximations", Lecture Notes in Computer Science 842, Springer 1994. Cited by Heeger–Hermelin–Itzhaki–Molter–Shabtay for Theorem 4's second bullet.
1 import Lax808846.Ram 2 import Lax117284.InstanceEncoding 3 import Lax117284.ConflictGraph 4 … module docstring, 58 lines 63 64 namespace Lax117284.Bodlaender 65 66 open Lax808846.Ram Lax117284.Scheduling Lax117284.ConflictGraph 67 68 -- The word of a decomposition. 69 70 /-- The number of nodes of the decomposition word `D`: its first entry. -/ 71 def nodeCount (D : List ℕ) : ℕ := D.getD 0 0 72 73 /-- The kind of node `i`: `0` leaf, `1` introduce, `2` forget, `3` join. -/ 74 def kind (D : List ℕ) (i : ℕ) : ℕ := D.getD (1 + 3 * i) 0 75 76 /-- The vertex of node `i`, for an introduce or a forget node. -/ 77 def vertex (D : List ℕ) (i : ℕ) : ℕ := D.getD (2 + 3 * i) 0 78 79 /-- The second child of node `i`, for a join node. -/ 80 def other (D : List ℕ) (i : ℕ) : ℕ := D.getD (3 + 3 * i) 0 81 82 /-- The bag of node `i` among the vertices `0 … n - 1`, from the leaves up: the empty bag at a 83 leaf, the child's bag plus the vertex at an introduce node, minus it at a forget node, and the 84 child's bag at a join node. The first child of node `i + 1` is node `i`. -/ 85 def bagAt (n : ℕ) (D : List ℕ) : ℕ → Finset (Fin n) 86 | 0 => ∅ 87 | i + 1 => 88 if kind D (i + 1) = 1 then 89 if h : vertex D (i + 1) < n then insert ⟨vertex D (i + 1), h⟩ (bagAt n D i) 90 else bagAt n D i 91 else if kind D (i + 1) = 2 then 92 if h : vertex D (i + 1) < n then (bagAt n D i).erase ⟨vertex D (i + 1), h⟩ 93 else bagAt n D i 94 else if kind D (i + 1) = 3 then bagAt n D i 95 else ∅ 96 97 /-- Node `c` is a child of node `p`. -/ 98 def IsChild (D : List ℕ) (c p : ℕ) : Prop := 99 p < nodeCount D ∧ 100 ((kind D p = 1 ∨ kind D p = 2) ∧ c + 1 = p ∨ kind D p = 3 ∧ (c + 1 = p ∨ c = other D p)) 101 102 /-- The graph on the nodes `0 … N - 1` whose edges join a node to its children. -/ 103 def treeGraph (D : List ℕ) : SimpleGraph (Fin (nodeCount D)) where 104 Adj a b := a ≠ b ∧ (IsChild D a.val b.val ∨ IsChild D b.val a.val) 105 symm := ⟨fun _ _ h => ⟨h.1.symm, h.2.symm⟩⟩ 106 loopless := ⟨fun _ h => h.1 rfl⟩ 107 108 /-- **`D` is the word of a nice tree decomposition of the overall conflict graph of `I` of width 109 at most `w`.** -/ 110 structure NiceDecomposition (I : Instance) (w : ℕ) (D : List ℕ) : Prop where 111 /-- The word is `N` followed by three numbers for each of the `N` nodes. -/ 112 length_eq : D.length = 1 + 3 * nodeCount D 113 /-- There is a node. -/ 114 nonempty : 0 < nodeCount D 115 /-- Every node is a leaf, or an introduce node over the node before it whose vertex is not in 116 that node's bag, or a forget node over the node before it whose vertex is in that node's bag, 117 or a join node whose two children, the node before it and an earlier node, have its bag. -/ 118 shape : ∀ i, i < nodeCount D → 119 kind D i = 0 ∨ 120 (0 < i ∧ kind D i = 1 ∧ vertex D i < I.clients ∧ 121 ∀ h : vertex D i < I.clients, (⟨vertex D i, h⟩ : Fin I.clients) ∉ bagAt I.clients D (i - 1)) ∨ 122 (0 < i ∧ kind D i = 2 ∧ vertex D i < I.clients ∧ 123 ∀ h : vertex D i < I.clients, (⟨vertex D i, h⟩ : Fin I.clients) ∈ bagAt I.clients D (i - 1)) ∨ 124 (0 < i ∧ kind D i = 3 ∧ other D i + 1 < i ∧ 125 bagAt I.clients D (other D i) = bagAt I.clients D (i - 1)) 126 /-- Every node other than the last has exactly one parent. -/ 127 parent : ∀ c, c + 1 < nodeCount D → ∃! p, IsChild D c p 128 /-- The nodes form a tree. -/ 129 isTree : (treeGraph D).IsTree 130 /-- Every client is in a bag. -/ 131 covers : ∀ v : Fin I.clients, ∃ i, i < nodeCount D ∧ v ∈ bagAt I.clients D i 132 /-- Every two clients whose jobs conflict on some day are in a bag together. -/ 133 edges : ∀ u v : Fin I.clients, (overallGraph I).Adj u v → 134 ∃ i, i < nodeCount D ∧ u ∈ bagAt I.clients D i ∧ v ∈ bagAt I.clients D i 135 /-- The nodes whose bags contain a fixed client form a connected subtree. -/ 136 connected : ∀ v : Fin I.clients, 137 ((treeGraph D).induce {i : Fin (nodeCount D) | v ∈ bagAt I.clients D i}).Connected 138 /-- Every bag has at most `w + 1` clients. -/ 139 width : ∀ i, i < nodeCount D → (bagAt I.clients D i).card ≤ w + 1 140 141 open Classical in 142 /-- **`g` is the word of the overall conflict graph of `I`**: the number of clients, then the 143 adjacency matrix row by row. -/ 144 structure EncodesGraph (g : List ℕ) (I : Instance) : Prop where 145 /-- The word is the count followed by the matrix. -/ 146 length_eq : g.length = 1 + I.clients * I.clients 147 /-- The first entry is the number of clients. -/ 148 head_eq : g.getD 0 0 = I.clients 149 /-- The entry of the row `u` and column `v` is `1` when the two clients are adjacent and `0` 150 otherwise. -/ 151 adj_eq : ∀ u v : Fin I.clients, 152 g.getD (1 + u.val * I.clients + v.val) 0 = if (overallGraph I).Adj u v then 1 else 0 153 154 /-- **Bodlaender's theorem with Kloks' niceness, for the overall conflict graph of an instance 155 given as a word.** One program and one constant serve every word length `W`, every graph and every 156 bound `w`, provided the word — the graph followed by `w` — fits with room for 157 `c * 2 ^ (c * w ^ 3)` times a polynomial in its length. On such a word the program halts within 158 `c * 2 ^ (c * w ^ 3) * (|g| + 2) ^ c` instructions, writing `[0]` if the graph has no tree 159 decomposition of width at most `w`, and `1` followed by the word of a nice tree decomposition of 160 width at most `w` otherwise. -/ 161 axiom niceDecomposition_computable : 162 ∃ (prog : Program) (c : ℕ), ∀ (W w : ℕ) (g : List ℕ) (I : Instance), 163 EncodesGraph g I → 164 (∀ v ∈ g ++ [w], c * 2 ^ (c * w ^ 3) * ((g ++ [w]).length + v + 1) ^ c ≤ 2 ^ W) → 165 ∃ (out : List ℕ) (t : ℕ), t ≤ c * 2 ^ (c * w ^ 3) * (g.length + 2) ^ c ∧ 166 RunsTo W prog (g ++ [w]) out t ∧ 167 (out = [0] ∧ ¬ Lax228581.Treewidth.HasTreewidthAtMost (overallGraph I) w ∨ 168 ∃ D, out = 1 :: D ∧ NiceDecomposition I w D) 169 170 end Lax117284.Bodlaender 171 -
Three Days and the Fairness Parameter One
Theorem 7. The problem is NP-hard for and .
Let be a [2,3]-bounded 3-SAT formula with variables, clauses of two literals and clauses of three literals. Build an instance with three days and the clients: three dummies; two clients per variable ; and one client per occurrence of a literal in a clause. Every processing time is , so two jobs of a day conflict exactly when their due dates differ by at most one, and the whole construction is a matter of placing due dates on a line, three times.
On the first day the due dates are for the three dummies, for both clients of variable , for both clients of the -th clause of two literals, and for all three clients of the -th clause of three literals. Clients sharing a due date conflict and nothing else does, so the first day admits one client out of each group: one dummy, one of — this is the truth assignment — and one client of every clause. On the second day the dummies, the variable clients and the clients of the clauses of two literals all have the due date , so they are pairwise conflicting, while the clients of the -th clause of three literals sit alone at ; since a dummy runs on every day, the second day is a dead end for everything but one client of each clause of three literals. On the third day sits at and at , while an occurrence of the literal sits at or and an occurrence of at or , according to which of its at most two occurrences it is. So on the third day a client of an occurrence conflicts with exactly the variable client that falsifies its literal, and with nothing else.
The three dummies conflict with each other on all three days, so a -fair schedule runs exactly one of them on each day. This is what forces the selection on the first day and makes the second day a dead end, and a client of an occurrence can then be served only on the third day — which is possible exactly when the assignment selected on the first day makes its literal true.
- thm✓
Lax117284.Theorem7(1st statement) - thm✓
Lax117284.Theorem7(2nd statement) - thm✓
Lax117284.Theorem7(3rd statement) - thm✓
Lax117284.Theorem7(4th statement) - thm✓
Lax117284.Theorem7(5th statement) - thm✓
Lax117284.Theorem7(6th statement)
1 import Lax117284.BoundedSat 2 import Lax117284.Problems 3 … module docstring, 51 lines 55 56 namespace Lax117284.Theorem7 57 58 open Lax117284.Scheduling Lax117284.Problems Lax434930.PolynomialTime 59 open Lax429075.Reductions 60 61 /-- The number of clients of the construction: three dummies, two per variable, and one per 62 occurrence slot. -/ 63 def clients (φ : BoundedSat.Formula) : ℕ := 3 + 2 * φ.vars + BoundedSat.slots φ 64 65 /-- The due date of client `c` on day `i` of the construction. -/ 66 def due (φ : BoundedSat.Formula) (i c : ℕ) : ℕ := 67 if c < 3 then 2 68 else if c < 3 + 2 * φ.vars then 69 let v := (c - 3) / 2 70 if i = 0 then 2 * v + 5 71 else if i = 1 then 2 72 else if (c - 3) % 2 = 0 then 10 * v + 6 else 10 * v + 11 73 else 74 let o := c - 3 - 2 * φ.vars 75 let l := BoundedSat.litOfSlot φ o 76 if i = 2 then 77 (if l.2 then 10 * l.1 + 5 + 2 * BoundedSat.slotRank φ o 78 else 10 * l.1 + 10 + 2 * BoundedSat.slotRank φ o) 79 else if o < 2 * φ.twoClauses then 80 (if i = 0 then 2 * φ.vars + 2 * (o / 2) + 7 else 2) 81 else 82 (if i = 0 then 83 2 * φ.vars + 2 * φ.twoClauses + 2 * ((o - 2 * φ.twoClauses) / 3) + 9 84 else 3 * ((o - 2 * φ.twoClauses) / 3) + 6) 85 86 theorem two_le_due (φ : BoundedSat.Formula) (i c : ℕ) : 2 ≤ due φ i c := by 87 unfold due 88 simp only [] 89 split_ifs <;> omega 90 91 /-- **The instance of Theorem 7**: three days, all processing times `2`, and the fairness 92 parameter `1`. -/ 93 def inst (φ : BoundedSat.Formula) : Instance where 94 clients := clients φ 95 days := 3 96 p _ _ := 2 97 d i c := due φ i c 98 p_pos _ _ := by omega 99 p_le_d i c := two_le_due φ i c 100 101 /-- **The constructed instance has three days.** -/ 102 axiom inst_days (φ : BoundedSat.Formula) : (inst φ).days = 3 103 104 /-- **All processing times of the constructed instance are equal**, so in particular they 105 are day-independent. -/ 106 axiom inst_p_eq (φ : BoundedSat.Formula) (i i' : Fin (inst φ).days) 107 (j j' : Fin (inst φ).clients) : (inst φ).p i j = (inst φ).p i' j' 108 109 /-- **The construction is correct**: the formula is satisfiable exactly when the instance 110 admits a schedule serving every client on at least one of the three days. -/ 111 axiom correct (φ : BoundedSat.Formula) : 112 φ.Satisfiable ↔ (inst φ).HasKFairSchedule 1 113 114 open Classical in 115 /-- **The reduction**, as a map on words: a word encoding a formula with no more variables 116 than positions is sent to the encoding of the constructed instance with the fairness 117 parameter `1`, and every other word to the rejected word. -/ 118 noncomputable def reduce (w : Word) : Word := 119 if h : ∃ φ : BoundedSat.Formula, 120 BoundedSat.encodeFormula φ = w ∧ φ.vars ≤ BoundedSat.slots φ then 121 encodeUniform (inst h.choose) 1 122 else rejected 123 124 /-- **The reduction is correct.** -/ 125 axiom reduce_correct (w : Word) : 126 w ∈ BoundedSat.BoundedSat ↔ 127 reduce w ∈ Uniform fun I k => I.days = 3 ∧ k = 1 ∧ I.DayIndepP 128 129 /-- **The reduction runs in polynomial time.** -/ 130 axiom reduce_polyTime : Nonempty (Turing.TM2ComputableInPolyTime id id reduce) 131 132 /-- **Theorem 7.** The problem with three days, the fairness parameter `1` and 133 day-independent processing times is NP-hard. -/ 134 axiom uniform_three_one_npHard : 135 NPHard (Uniform fun I k => I.days = 3 ∧ k = 1 ∧ I.DayIndepP) 136 137 end Lax117284.Theorem7 138 - thm✓
-
Raising the Number of Days and the Fairness Parameter
Corollary 8. The problem is NP-hard whenever and .
Two constructions lift hardness from one pair to the next. Adding a single day on which no two jobs conflict raises the attainable fairness by exactly one, so hardness for gives hardness for : every client is served on the new day, and on the old days nothing has changed. Adding one day together with one new client raises the number of days at : the new client's job blocks every job on each of the old days, and on the new day all jobs coincide, so a -fair schedule must serve the new client on the new day and is otherwise a -fair schedule of the old instance. Together with the base case these two steps reach every pair with and .
Both constructions give the new day the processing times of the first day, so an instance with day-independent processing times is sent to one with day-independent processing times, which is what makes the corollary a statement about that restriction.
- cor✓
Lax117284.Corollary8(1st statement) - cor✓
Lax117284.Corollary8(2nd statement) - cor✓
Lax117284.Corollary8(3rd statement) - cor✓
Lax117284.Corollary8(4th statement) - cor✓
Lax117284.Corollary8(5th statement) - cor✓
Lax117284.Corollary8(6th statement) - cor✓
Lax117284.Corollary8(7th statement) - cor✓
Lax117284.Corollary8(8th statement) - cor✓
Lax117284.Corollary8(9th statement) - cor✓
Lax117284.Corollary8(10th statement)
1 import Lax117284.Problems 2 … module docstring, 41 lines 44 45 namespace Lax117284.Corollary8 46 47 open Lax117284.Scheduling Lax117284.Problems Lax434930.PolynomialTime 48 open Lax429075.Reductions 49 50 /-- A time later than every due date of `I`. -/ 51 def bound (I : Instance) : ℕ := 52 (Finset.univ.sup fun i => Finset.univ.sup fun j => I.d i j) + 1 53 54 /-- A length at least every processing time of the first day of `I`. -/ 55 def gap (I : Instance) : ℕ := Finset.univ.sup fun j : Fin I.clients => I.pAt 0 j 56 57 /-- **The instance with one conflict-free day added**: on the new day client `j`'s job has 58 the processing time it has on the first day and ends at `(j+1)` times a length at least 59 every such processing time, so no two jobs of the new day meet. -/ 60 def addFreeDay (I : Instance) : Instance where 61 clients := I.clients 62 days := I.days + 1 63 p i j := if (i : ℕ) < I.days then I.pAt i j else I.pAt 0 j 64 d i j := if (i : ℕ) < I.days then I.dAt i j else ((j : ℕ) + 1) * gap I 65 p_pos i j := by 66 have h0 := I.pAt_pos (i : ℕ) (j : ℕ) 67 have h1 := I.pAt_pos 0 (j : ℕ) 68 split_ifs <;> omega 69 p_le_d i j := by 70 have h1 := I.pAt_le_dAt (i : ℕ) (j : ℕ) 71 have h2 : I.pAt 0 (j : ℕ) ≤ gap I := by 72 have := Finset.le_sup (f := fun j' : Fin I.clients => I.pAt 0 (j' : ℕ)) 73 (Finset.mem_univ j) 74 simp only [gap] 75 omega 76 have h3 : 1 * gap I ≤ ((j : ℕ) + 1) * gap I := Nat.mul_le_mul_right _ (by omega) 77 split_ifs <;> omega 78 79 /-- **The instance with one blocking client and one blocking day added**: the new client's 80 job ends later than every due date of `I` and starts at time `0`, so on an old day it 81 conflicts with every job, and on the new day every job ends at that same time, so all jobs 82 of the new day pairwise conflict. -/ 83 def addBlockingDay (I : Instance) : Instance where 84 clients := I.clients + 1 85 days := I.days + 1 86 p i j := 87 if (j : ℕ) < I.clients then 88 (if (i : ℕ) < I.days then I.pAt i j else I.pAt 0 j) 89 else bound I 90 d i j := if (j : ℕ) < I.clients ∧ (i : ℕ) < I.days then I.dAt i j else bound I 91 p_pos i j := by 92 have h0 := I.pAt_pos (i : ℕ) (j : ℕ) 93 have h1 := I.pAt_pos 0 (j : ℕ) 94 have h2 : 0 < bound I := by simp [bound] 95 split_ifs <;> omega 96 p_le_d i j := by 97 have h1 := I.pAt_le_dAt (i : ℕ) (j : ℕ) 98 have h2 : I.dAt (i : ℕ) (j : ℕ) ≤ bound I := by 99 unfold Instance.dAt bound 100 split_ifs with h h' 101 · have h3 : I.d ⟨(i : ℕ), h⟩ ⟨(j : ℕ), h'⟩ ≤ 102 Finset.univ.sup fun j' => I.d ⟨(i : ℕ), h⟩ j' := 103 Finset.le_sup (Finset.mem_univ _) 104 have h4 : (Finset.univ.sup fun j' => I.d ⟨(i : ℕ), h⟩ j') ≤ 105 Finset.univ.sup fun i' => Finset.univ.sup fun j' => I.d i' j' := 106 Finset.le_sup (f := fun i' => Finset.univ.sup fun j' => I.d i' j') (Finset.mem_univ _) 107 omega 108 · omega 109 · omega 110 have h5 : I.pAt 0 (j : ℕ) ≤ I.dAt 0 (j : ℕ) := I.pAt_le_dAt 0 (j : ℕ) 111 have h6 : I.dAt 0 (j : ℕ) ≤ bound I := by 112 unfold Instance.dAt bound 113 split_ifs with h h' 114 · have h3 : I.d ⟨0, h⟩ ⟨(j : ℕ), h'⟩ ≤ Finset.univ.sup fun j' => I.d ⟨0, h⟩ j' := 115 Finset.le_sup (Finset.mem_univ _) 116 have h4 : (Finset.univ.sup fun j' => I.d ⟨0, h⟩ j') ≤ 117 Finset.univ.sup fun i' => Finset.univ.sup fun j' => I.d i' j' := 118 Finset.le_sup (f := fun i' => Finset.univ.sup fun j' => I.d i' j') (Finset.mem_univ _) 119 omega 120 · omega 121 · omega 122 split_ifs <;> omega 123 124 /-- **The instance without clients and with `m` days.** -/ 125 def noClients (m : ℕ) : Instance where 126 clients := 0 127 days := m 128 p _ j := j.elim0 129 d _ j := j.elim0 130 p_pos _ j := j.elim0 131 p_le_d _ j := j.elim0 132 133 /-- **Adding a conflict-free day raises the attainable fairness by exactly one.** -/ 134 axiom addFreeDay_correct (I : Instance) (hm : 0 < I.days) (k : ℕ) : 135 I.HasKFairSchedule k ↔ (addFreeDay I).HasKFairSchedule (k + 1) 136 137 /-- **Adding a blocking client and a blocking day preserves one-fairness.** -/ 138 axiom addBlockingDay_correct (I : Instance) (hm : 0 < I.days) : 139 I.HasKFairSchedule 1 ↔ (addBlockingDay I).HasKFairSchedule 1 140 141 /-- **The conflict-free day keeps the processing times day-independent.** -/ 142 axiom addFreeDay_dayIndepP {I : Instance} (h : I.DayIndepP) : (addFreeDay I).DayIndepP 143 144 /-- **The blocking day keeps the processing times day-independent.** -/ 145 axiom addBlockingDay_dayIndepP {I : Instance} (h : I.DayIndepP) : 146 (addBlockingDay I).DayIndepP 147 148 open Classical in 149 /-- **The first reduction**, as a map on words: a word encoding an instance with `m` days 150 and the fairness parameter `k` is sent to the encoding of the instance with one 151 conflict-free day added and the parameter `k + 1`. -/ 152 noncomputable def reduceFreeDay (w : Word) : Word := 153 if h : ∃ (I : Instance) (k : ℕ), encodeUniform I k = w ∧ 0 < I.days then 154 encodeUniform (addFreeDay h.choose) (h.choose_spec.choose + 1) 155 else rejected 156 157 open Classical in 158 /-- **The second reduction**, as a map on words: a word encoding an instance with `m` days 159 and the fairness parameter `1` is sent to the encoding of the instance with one blocking 160 client and one blocking day added, with the parameter `1`; an instance without clients is 161 sent to the instance without clients and one more day. -/ 162 noncomputable def reduceBlockingDay (w : Word) : Word := 163 if h : ∃ (I : Instance) (k : ℕ), encodeUniform I k = w ∧ 0 < I.days ∧ k = 1 then 164 if h.choose.clients = 0 then encodeUniform (noClients (h.choose.days + 1)) 1 165 else encodeUniform (addBlockingDay h.choose) 1 166 else rejected 167 168 /-- **The first reduction is correct.** -/ 169 axiom reduceFreeDay_correct (m k : ℕ) (hm : 0 < m) (w : Word) : 170 w ∈ Uniform (fun I k' => I.days = m ∧ k' = k ∧ I.DayIndepP) ↔ 171 reduceFreeDay w ∈ Uniform fun I k' => I.days = m + 1 ∧ k' = k + 1 ∧ I.DayIndepP 172 173 /-- **The second reduction is correct.** -/ 174 axiom reduceBlockingDay_correct (m : ℕ) (hm : 0 < m) (w : Word) : 175 w ∈ Uniform (fun I k' => I.days = m ∧ k' = 1 ∧ I.DayIndepP) ↔ 176 reduceBlockingDay w ∈ Uniform fun I k' => I.days = m + 1 ∧ k' = 1 ∧ I.DayIndepP 177 178 /-- **The first reduction runs in polynomial time.** -/ 179 axiom reduceFreeDay_polyTime : 180 Nonempty (Turing.TM2ComputableInPolyTime id id reduceFreeDay) 181 182 /-- **The second reduction runs in polynomial time.** -/ 183 axiom reduceBlockingDay_polyTime : 184 Nonempty (Turing.TM2ComputableInPolyTime id id reduceBlockingDay) 185 186 /-- **The first step of Corollary 8**: hardness for `(m, k)` gives hardness for 187 `(m+1, k+1)`. -/ 188 axiom manyOne_freeDay (m k : ℕ) (hm : 0 < m) : 189 ManyOne (Uniform fun I k' => I.days = m ∧ k' = k ∧ I.DayIndepP) 190 (Uniform fun I k' => I.days = m + 1 ∧ k' = k + 1 ∧ I.DayIndepP) 191 192 /-- **The second step of Corollary 8**: hardness for `(m, 1)` gives hardness for 193 `(m+1, 1)`. -/ 194 axiom manyOne_blockingDay (m : ℕ) (hm : 0 < m) : 195 ManyOne (Uniform fun I k' => I.days = m ∧ k' = 1 ∧ I.DayIndepP) 196 (Uniform fun I k' => I.days = m + 1 ∧ k' = 1 ∧ I.DayIndepP) 197 198 end Lax117284.Corollary8 199 - cor✓
-
The Fairness Parameter One Below the Number of Days
Theorem 9. The problem is solvable in polynomial time when .
Associate with every day and client a variable , read as "client 's job is executed on day ". Two families of clauses express the two requirements. For every day and every two distinct clients whose jobs conflict on that day, the conflict clause forbids executing both. For every client and every two distinct days , the validation clause forbids rejecting the client on both. A schedule satisfies the first family exactly when it is feasible and the second exactly when no client is rejected twice, which at is fairness. The formula is a 2-CNF formula of variables and clauses, and 2-satisfiability is decidable in linear time.
The parameter is exactly the value at which two literals suffice: " is served on at least days" says "of any two days, is served on one of them", a binary constraint.
- thm✓
Lax117284.Theorem9(1st statement) - thm✓
Lax117284.Theorem9(2nd statement) - thm✓
Lax117284.Theorem9(3rd statement) - thm✓
Lax117284.Theorem9(4th statement)
1 import Lax117284.Problems 2 import Lax117284.TwoSatisfiability 3 … module docstring, 35 lines 39 40 namespace Lax117284.Theorem9 41 42 open Lax117284.Scheduling Lax117284.Problems Lax117284.TwoSatisfiability 43 open Lax434930.PolynomialTime Lax429075.Reductions 44 45 /-- The variable "client `j`'s job is executed on day `i`", numbered `i·n + j`. The one 46 further variable, which no meaningful clause names, is the value on indices out of 47 range. -/ 48 def varIdx (I : Instance) (i j : ℕ) : Fin (I.days * I.clients + 1) := 49 ⟨min (i * I.clients + j) (I.days * I.clients), Nat.lt_succ_of_le (min_le_right _ _)⟩ 50 51 /-- The `α`-th literal of clause `c`: the conflict clauses come first, one slot for every 52 day and ordered pair of clients, then the validation clauses, one slot for every client and 53 ordered pair of days. An inadmissible slot carries a tautology. -/ 54 def clauseLit (I : Instance) (c : ℕ) (α : Fin 2) : Fin (I.days * I.clients + 1) × Bool := 55 if c < I.days * I.clients * I.clients then 56 let i := c / (I.clients * I.clients) 57 let r := c % (I.clients * I.clients) 58 let j₁ := r / I.clients 59 let j₂ := r % I.clients 60 if j₁ ≠ j₂ ∧ I.ConflictAt i j₁ j₂ then 61 (varIdx I i (if α = 0 then j₁ else j₂), false) 62 else (varIdx I i j₁, α = 0) 63 else 64 let c' := c - I.days * I.clients * I.clients 65 let j := c' / (I.days * I.days) 66 let r := c' % (I.days * I.days) 67 let i₁ := r / I.days 68 let i₂ := r % I.days 69 if i₁ ≠ i₂ then (varIdx I (if α = 0 then i₁ else i₂) j, true) 70 else (varIdx I i₁ j, α = 0) 71 72 /-- **The 2-CNF formula of Theorem 9.** -/ 73 def formula (I : Instance) : Formula where 74 vars := I.days * I.clients + 1 75 clauses := I.days * I.clients * I.clients + I.clients * I.days * I.days 76 lit c α := clauseLit I c α 77 78 /-- **The construction is correct**: at the fairness parameter `m - 1` an instance is a 79 yes-instance exactly when its formula is satisfiable. -/ 80 axiom correct (I : Instance) (k : ℕ) (hk : k + 1 = I.days) : 81 I.HasKFairSchedule k ↔ (formula I).Satisfiable 82 83 open Classical in 84 /-- **The reduction**, as a map on words: a word encoding an instance with the fairness 85 parameter one below its number of days is sent to the encoding of its formula, and every 86 other word to the encoding of an unsatisfiable formula. -/ 87 noncomputable def reduce (w : Word) : Word := 88 if h : ∃ (I : Instance) (k : ℕ), encodeUniform I k = w ∧ k + 1 = I.days then 89 encodeFormula (formula h.choose) 90 else encodeFormula unsatisfiable 91 92 /-- **The reduction is correct.** -/ 93 axiom reduce_correct (w : Word) : 94 w ∈ Uniform (fun I k => k + 1 = I.days) ↔ reduce w ∈ TwoSat 95 96 /-- **The reduction runs in polynomial time.** -/ 97 axiom reduce_polyTime : Nonempty (Turing.TM2ComputableInPolyTime id id reduce) 98 99 /-- **Theorem 9.** The problem at the fairness parameter one below the number of days 100 reduces in polynomial time to 2-SAT. -/ 101 axiom manyOne_twoSat : ManyOne (Uniform fun I k => k + 1 = I.days) TwoSat 102 103 end Lax117284.Theorem9 104 - thm✓
-
Day-Independent Processing Times
Theorem 2. The problem is NP-hard. It is solvable in polynomial time if for every client .
Hardness needs no more than three days and , and the instances the reduction produces have all their processing times equal, which is a special case of day-independence. With unit processing times two jobs of a day conflict exactly when they have the same due date, and the problem becomes one of bipartite matching.
1 import Lax117284.Problems 2 … module docstring, 21 lines 24 25 namespace Lax117284.Theorem2 26 27 open Lax117284.Scheduling Lax117284.Problems Lax434930.PolynomialTime 28 29 /-- **Theorem 2, hardness.** With day-independent processing times the problem remains 30 NP-hard. -/ 31 axiom uniform_dayIndepP_npHard : NPHard (Uniform fun I _ => I.DayIndepP) 32 33 /-- **Theorem 2, tractability.** With unit processing times the problem is solvable in 34 polynomial time. -/ 35 axiom uniform_unitP_mem_P : Uniform (fun I _ => I.UnitP) ∈ P 36 37 end Lax117284.Theorem2 38 -
Unit Processing Times as a Bipartite Matching Problem
Theorem 10. Let be an instance all of whose processing times are and let . Consider the bipartite graph whose one side holds a vertex for every day and client , and whose other side holds a vertex for every day and time together with vertices for every client ; join to and to all vertices . Then admits a -fair schedule if and only if this graph has a matching saturating every vertex .
With unit processing times two jobs of a day conflict exactly when their due dates agree, so a day's machine may execute at most one job per due date; matching to records that client is served on day , and matching it to one of the vertices records that it is not. A matching saturating the job side thus assigns every job either a slot of its day or one of the rejections client is allowed.
1 import Lax117284.Scheduling 2 … module docstring, 30 lines 33 34 namespace Lax117284.Theorem10 35 36 open Lax117284.Scheduling 37 38 variable (I : Instance) 39 40 /-- The far side of the bipartite graph: a slot vertex `u_{i,t}` for a day and a time, and 41 a rejection vertex `w_{l,j}` for a client. -/ 42 def Target : Type := (Fin I.days × ℕ) ⊕ (ℕ × Fin I.clients) 43 44 /-- The edges of the bipartite graph: the job vertex `v_{i,j}` is joined to the slot vertex 45 of its own day and due date, and to the `m - k` rejection vertices of its own client. -/ 46 def Matchable (k : ℕ) (v : Fin I.days × Fin I.clients) (t : Target I) : Prop := 47 t = Sum.inl (v.1, I.d v.1 v.2) ∨ ∃ l < I.days - k, t = Sum.inr (l, v.2) 48 49 /-- The bipartite graph has a matching saturating every job vertex. -/ 50 def HasFullMatching (k : ℕ) : Prop := 51 ∃ f : Fin I.days × Fin I.clients → Target I, 52 Function.Injective f ∧ ∀ v, Matchable I k v (f v) 53 54 /-- **With unit processing times, two jobs of a day conflict exactly when their due dates 55 agree.** -/ 56 axiom conflict_iff_d_eq_of_unitP {I : Instance} (h : I.UnitP) (i : Fin I.days) 57 (j j' : Fin I.clients) : I.Conflict i j j' ↔ I.d i j = I.d i j' 58 59 /-- **Theorem 10.** With unit processing times and `k ≤ m`, a `k`-fair schedule exists 60 exactly when the bipartite graph has a matching saturating every job vertex. -/ 61 axiom hasKFairSchedule_iff_hasFullMatching {I : Instance} (h : I.UnitP) {k : ℕ} 62 (hk : k ≤ I.days) : I.HasKFairSchedule k ↔ HasFullMatching I k 63 64 end Lax117284.Theorem10 65 -
With unit processing times, a fair schedule exists exactly when a bipartite graph has a matching that saturates its left side. The left vertices of the graph are the jobs, one for every day and client. The right vertices are the pairs of a day and a client, of which the first client of a day with a given due date stands for that due date, and, for every client, the days that the client must be served on being taken out of the days, rejection vertices. The graph is written as a flat table of zeros and ones by a word RAM program on the zeros and ones of the word of the instance, in time polynomial in the size of the word; a second word RAM program converts the table into the compressed sparse row word of the graph, in time polynomial in the size of the table; and the cited decider of () decides whether the graph has such a matching in time polynomial in the size of that word. A word that encodes no instance with unit processing times is sent to a graph with one left vertex and no right vertex.
-
Just-in-Time Scheduling on Unrelated Machines as Day-Independent Due Dates
Theorem 11. The problem is NP-hard.
An instance of is read as an instance of fair repetitive interval scheduling by taking its jobs as clients and its machines as days: client 's job on day has the processing time of job on machine and the due date of job , which does not depend on the day. Executing every job just in time on some machine is then the same thing as serving every client on at least one day, so the constructed instance is a yes-instance at exactly when the given one admits an all-just-in-time schedule. Since is NP-hard, so is the problem with day-independent due dates.
- thm✓
Lax117284.Theorem11(1st statement) - thm✓
Lax117284.Theorem11(2nd statement) - thm✓
Lax117284.Theorem11(3rd statement) - thm✓
Lax117284.Theorem11(4th statement) - thm✓
Lax117284.Theorem11(5th statement)
1 import Lax117284.JustInTime 2 import Lax117284.Problems 3 … module docstring, 22 lines 26 27 namespace Lax117284.Theorem11 28 29 open Lax117284.Scheduling Lax117284.Problems Lax434930.PolynomialTime 30 open Lax429075.Reductions 31 32 /-- **The instance of Theorem 11**: clients are jobs, days are machines, the due date of a 33 client is that of its job and so does not depend on the day. -/ 34 def inst (R : JustInTime.Instance) : Instance where 35 clients := R.jobs 36 days := R.machines 37 p i j := R.p i j 38 d _ j := R.d j 39 p_pos := R.p_pos 40 p_le_d := R.p_le_d 41 42 /-- **The constructed instance has day-independent due dates.** -/ 43 axiom inst_dayIndepD (R : JustInTime.Instance) : (inst R).DayIndepD 44 45 /-- **The construction is correct**: every job can be executed just in time exactly when 46 every client can be served on at least one day. -/ 47 axiom correct (R : JustInTime.Instance) : 48 R.AllJustInTime ↔ (inst R).HasKFairSchedule 1 49 50 open Classical in 51 /-- **The reduction**, as a map on words: a word encoding an instance of 52 `R || ∑_j Z_j` is sent to the encoding of the constructed instance with the fairness 53 parameter `1`, and every other word to the rejected word. -/ 54 noncomputable def reduce (w : Word) : Word := 55 if h : ∃ R : JustInTime.Instance, JustInTime.encodeInstance R = w then 56 encodeUniform (inst h.choose) 1 57 else rejected 58 59 /-- **The reduction is correct.** -/ 60 axiom reduce_correct (w : Word) : 61 w ∈ JustInTime.AllJIT ↔ reduce w ∈ Uniform fun I _ => I.DayIndepD 62 63 /-- **The reduction runs in polynomial time.** -/ 64 axiom reduce_polyTime : Nonempty (Turing.TM2ComputableInPolyTime id id reduce) 65 66 /-- **Theorem 11.** `R || ∑_j Z_j` reduces in polynomial time to the problem on the 67 instances with day-independent due dates. -/ 68 axiom allJIT_manyOne_dayIndepD : 69 ManyOne JustInTime.AllJIT (Uniform fun I _ => I.DayIndepD) 70 71 end Lax117284.Theorem11 72 - thm✓
-
The Dynamic Program for Day-Independent Due Dates
Theorem 12. Let be an instance whose due dates are day-independent, so that client has one due date on every day. Order the clients by due date and process them in that order, carrying as state, for every day, the time at which that day's machine becomes free. Serving a client on a set of days requires that on each of those days the machine be free early enough for its job to be completed at its due date, and leaves the machine of each of those days free from that due date onwards. The instance admits a -fair schedule if and only if the program, started with every machine free from time and applied to the clients in due-date order, can serve every client on days.
Processing the clients in due-date order is what makes the state sufficient: a client considered later has a due date at least as large, so the only thing a decision about the earlier clients can leave behind that matters is how late each machine is occupied. For a constant number of days the number of reachable states is polynomial, and the program decides the problem in polynomial time.
1 import Lax117284.Scheduling 2 … module docstring, 32 lines 35 36 namespace Lax117284.Theorem12 37 38 open Lax117284.Scheduling 39 40 variable {I : Instance} 41 42 /-- The dynamic program of Theorem 12: given the due dates `dd`, the fairness parameter 43 `k`, the clients still to be served, and for each day the time at which its machine is 44 free, can every remaining client be served on `k` days? -/ 45 def Program (dd : Fin I.clients → ℕ) (k : ℕ) : 46 List (Fin I.clients) → (Fin I.days → ℕ) → Prop 47 | [], _ => True 48 | j :: rest, free => ∃ S : Finset (Fin I.days), S.card = k ∧ 49 (∀ i ∈ S, I.p i j + free i ≤ dd j) ∧ 50 Program dd k rest fun i => if i ∈ S then dd j else free i 51 52 /-- **Theorem 12.** With day-independent due dates, an instance admits a `k`-fair schedule 53 exactly when the dynamic program, applied to the clients in due-date order with every 54 machine free from time `0`, can serve every client on `k` days. -/ 55 axiom hasKFairSchedule_iff_program (hd : I.DayIndepD) (i₀ : Fin I.days) (k : ℕ) 56 {l : List (Fin I.clients)} (hnd : l.Nodup) (hall : ∀ j, j ∈ l) 57 (hsorted : l.Pairwise fun a b => I.d i₀ a ≤ I.d i₀ b) : 58 I.HasKFairSchedule k ↔ Program (I.d i₀) k l fun _ => 0 59 60 end Lax117284.Theorem12 61 -
Day-Independent Due Dates and Processing Times
Theorem 13. Let be an instance whose due dates and processing times are both day-independent, so that all days have the same conflict graph . Then admits a -fair schedule if and only if , where is the chromatic number of .
A feasible schedule meets a clique of in at most one client per day, so a clique of vertices whose clients are each served times needs days; this gives the condition with the clique number in place of the chromatic number. Conversely a proper colouring of with colours partitions the clients into independent sets, and serving the classes in rotation serves every client on days. Both bounds meet because is an interval graph, and its chromatic number therefore equals its clique number.
- thm✓
Lax117284.Theorem13(1st statement) - thm✓
Lax117284.Theorem13(2nd statement) - thm✓
Lax117284.Theorem13(3rd statement) - thm✓
Lax117284.Theorem13(4th statement)
1 import Lax117284.ConflictGraph 2 … module docstring, 29 lines 32 33 namespace Lax117284.Theorem13 34 35 open Lax117284.Scheduling Lax117284.ConflictGraph 36 37 variable {I : Instance} 38 39 /-- **Day-independent instances have one conflict graph.** -/ 40 axiom dayGraph_eq_of_dayIndep (hd : I.DayIndepD) (hp : I.DayIndepP) (i i' : Fin I.days) : 41 dayGraph I i = dayGraph I i' 42 43 /-- **A day's conflict graph is an interval graph, so its chromatic number is its clique 44 number.** -/ 45 axiom chromaticNumber_dayGraph (I : Instance) (i : Fin I.days) : 46 (dayGraph I i).chromaticNumber = (dayGraph I i).cliqueNum 47 48 /-- **Theorem 13, with the clique number.** -/ 49 axiom hasKFairSchedule_iff_mul_cliqueNum_le (hd : I.DayIndepD) (hp : I.DayIndepP) 50 (i₀ : Fin I.days) (k : ℕ) : 51 I.HasKFairSchedule k ↔ k * (dayGraph I i₀).cliqueNum ≤ I.days 52 53 /-- **Theorem 13.** With day-independent due dates and processing times, a `k`-fair 54 schedule exists exactly when `k` times the chromatic number of the conflict graph is at 55 most the number of days. -/ 56 axiom hasKFairSchedule_iff_mul_chromaticNumber_le (hd : I.DayIndepD) (hp : I.DayIndepP) 57 (i₀ : Fin I.days) (k : ℕ) : 58 I.HasKFairSchedule k ↔ (k : ℕ∞) * (dayGraph I i₀).chromaticNumber ≤ I.days 59 60 end Lax117284.Theorem13 61 - thm✓
-
Structural Parameters of the Conflict Graph
Theorem 4. The problem
- is NP-hard for a constant treewidth of the overall conflict graph,
- is fixed-parameter tractable with respect to ,
- and is fixed-parameter tractable with respect to the number of clients.
Hardness at constant treewidth holds already at , and therefore at every larger constant: it is obtained from Multicoloured Independent Set through the per-client problem, whose instances the construction produces with treewidth at most , and a reduction back to the uniform problem that raises the treewidth by at most . Tractability for is a dynamic program over a nice tree decomposition of the overall conflict graph, whose table holds, for every bag, the restrictions to that bag of the schedules of the subtree; a bag of clients admits of them. Tractability for is a formulation as an integer program whose number of variables depends on alone, which the source solves by Lenstra's algorithm; here it is solved by an algorithm for these particular integer programs.
The third bullet is stated in two halves: the problem parameterized by fpt-reduces to the feasibility of the integer programs of the reduction, parameterized by the number of variables (, proved: the integer program of the source, written by a word RAM program), and the feasibility of these integer programs is fixed-parameter tractable (, proved by a guess-and-verify algorithm for the constraint matrices of the family, which are fixed by ; the source cites Lenstra's algorithm for it, which is not needed for this family). Fixed-parameter tractability in () is their combination.
- thm✓
Lax117284.Theorem4(1st statement) - thm✓
Lax117284.Theorem4(2nd statement) - thm✓
Lax117284.Theorem4(3rd statement) - thm✓
Lax117284.Theorem4(4th statement)
1 import Lax117284.InstanceEncoding 2 import Lax117284.IlpClients 3 import Lax117284.ParameterizedComplexity 4 import Lax117284.Problems 5 … module docstring, 58 lines 64 65 namespace Lax117284.Theorem4 66 67 open Lax117284.Scheduling Lax117284.Problems Lax117284.InstanceEncoding 68 open Lax117284.ParameterizedComplexity 69 70 /-- The problem parameterized by the number of days plus the treewidth of the overall 71 conflict graph. -/ 72 noncomputable def byDaysAndTreewidth : ParameterizedComplexity.Problem where 73 Domain := UniformInstances 74 Yes x := (decode x.dropLast).HasKFairSchedule (parameter x) 75 param x := (decode x.dropLast).days + ConflictGraph.treewidth (decode x.dropLast) 76 77 /-- The problem parameterized by the number of clients. -/ 78 noncomputable def byClients : ParameterizedComplexity.Problem where 79 Domain := UniformInstances 80 Yes x := (decode x.dropLast).HasKFairSchedule (parameter x) 81 param x := (decode x.dropLast).clients 82 83 /-- **Theorem 4, first bullet.** The problem is NP-hard on the instances whose overall 84 conflict graph has treewidth at most `6`, hence for a constant treewidth. -/ 85 axiom uniform_treewidth_npHard : 86 NPHard (Uniform fun I _ => ConflictGraph.treewidth I ≤ 6) 87 88 /-- **Theorem 4, second bullet.** The problem is fixed-parameter tractable with respect to 89 the number of days plus the treewidth of the overall conflict graph. -/ 90 axiom fpt_byDaysAndTreewidth : FPT byDaysAndTreewidth 91 92 /-- **Theorem 4, third bullet, the reduction.** The problem parameterized by the number of 93 clients fpt-reduces to the feasibility of the integer programs of the family `IlpClients.ilpClients`, 94 parameterized by the number of variables: the integer program of Theorem 21 of the source, with one 95 variable per type of day and set of clients and one slack per client, `2 ^ (n² + n) + n` variables in 96 all, computed by a word RAM program. -/ 97 axiom byClients_fptReduces_ilp : byClients ≤fpt IlpClients.ilpClients 98 99 /-- **Theorem 4, third bullet.** The problem is fixed-parameter tractable with respect to 100 the number of clients: by the reduction `byClients_fptReduces_ilp` and the algorithm for the 101 integer programs of the family, `IlpClients.ilpClients_fpt`. -/ 102 axiom fpt_byClients : FPT byClients 103 104 end Lax117284.Theorem4 105 -
Multicoloured Independent Set
An instance consists of colour classes of vertices each and a graph on those vertices whose edges join vertices of different classes. It is a yes-instance if one vertex can be chosen from every class so that no two chosen vertices are adjacent — a multicoloured independent set, necessarily of size .
The problem is NP-hard, and remains NP-hard on the instances the reduction of the last section consumes: those in which every vertex has the same number of neighbours, every class holds at least four vertices, and the number of edges is even.
1 import Lax117284.Problems 2 import Mathlib.Combinatorics.SimpleGraph.Finite 3 … module docstring, 37 lines 41 42 namespace Lax117284.MulticolouredIndepSet 43 44 open Lax434930.PolynomialTime 45 46 /-- An instance of Multicoloured Independent Set: a graph on `colours` classes of `size` 47 vertices whose edges join vertices of different classes. -/ 48 structure Instance where 49 /-- The number `ℓ` of colour classes. -/ 50 colours : ℕ 51 /-- The number `n` of vertices in each class. -/ 52 size : ℕ 53 /-- The graph, on the vertices `(class, index)`. -/ 54 graph : SimpleGraph (Fin colours × Fin size) 55 /-- Adjacent vertices lie in different classes. -/ 56 adj_colour_ne : ∀ u v, graph.Adj u v → u.1 ≠ v.1 57 58 namespace Instance 59 60 variable (G : Instance) 61 62 /-- **The question of Multicoloured Independent Set**: is there one vertex of every class 63 such that no two of them are adjacent? -/ 64 def HasIndepSet : Prop := 65 ∃ f : Fin G.colours → Fin G.size, ∀ i i', ¬ G.graph.Adj (i, f i) (i', f i') 66 67 /-- The number `ℓn` of vertices. -/ 68 def vertices : ℕ := G.colours * G.size 69 70 /-- The class of the vertex numbered `w`. -/ 71 def classOf (w : ℕ) : ℕ := w / G.size 72 73 /-- The index inside its class of the vertex numbered `w`. -/ 74 def indexOf (w : ℕ) : ℕ := w % G.size 75 76 open Classical in 77 /-- Whether the vertices numbered `w` and `w'` are adjacent, and `false` for numbers that 78 name no vertex. -/ 79 noncomputable def adjAt (w w' : ℕ) : Bool := 80 if h : w < G.vertices ∧ w' < G.vertices then 81 have hs : 0 < G.size := by 82 by_contra h0 83 have hz : G.size = 0 := by omega 84 rw [vertices, hz, Nat.mul_zero] at h 85 omega 86 decide (G.graph.Adj 87 (⟨w / G.size, (Nat.div_lt_iff_lt_mul hs).2 h.1⟩, ⟨w % G.size, Nat.mod_lt _ hs⟩) 88 (⟨w' / G.size, (Nat.div_lt_iff_lt_mul hs).2 h.2⟩, ⟨w' % G.size, Nat.mod_lt _ hs⟩)) 89 else false 90 91 /-- The neighbours of the vertex numbered `w`, in increasing order. -/ 92 noncomputable def nbrs (w : ℕ) : List ℕ := 93 (List.range G.vertices).filter fun w' => G.adjAt w w' 94 95 /-- The number of neighbours the vertex `0` has; on a regular graph, the degree `r`. -/ 96 noncomputable def degree : ℕ := (G.nbrs 0).length 97 98 /-- The edges, each listed as the pair of its endpoints from the smaller number to the 99 larger — which, since adjacent vertices lie in different classes, is from the smaller class 100 to the larger. -/ 101 noncomputable def edgeList : List (ℕ × ℕ) := 102 (List.range G.vertices).flatMap fun w => ((G.nbrs w).filter fun w' => w < w').map (w, ·) 103 104 /-- The number `|E|` of edges. -/ 105 noncomputable def edgeCount : ℕ := G.edgeList.length 106 107 /-- Every vertex of `G` has exactly `r` neighbours. -/ 108 def Regular (r : ℕ) : Prop := ∀ w < G.vertices, (G.nbrs w).length = r 109 110 /-- The normal form the reduction into fair repetitive interval scheduling consumes: the 111 graph is regular of some positive degree, every class holds at least four vertices, and the 112 number of edges is even. -/ 113 def Normal : Prop := (∃ r, 0 < r ∧ G.Regular r) ∧ 4 ≤ G.size ∧ 2 ∣ G.edgeCount 114 115 end Instance 116 117 /-- An instance as a binary word: the number of classes, the number of vertices per class, 118 and then the adjacency matrix, one bit per ordered pair of vertices. -/ 119 noncomputable def encodeInstance (G : Instance) : Word := 120 Problems.encodeNat G.colours ++ Problems.encodeNat G.size ++ 121 (List.range G.vertices).flatMap fun w => 122 (List.range G.vertices).map fun w' => G.adjAt w w' 123 124 /-- **Multicoloured Independent Set on the instances in normal form**, as a language. -/ 125 def NormalMulticolouredIndepSet : Language := 126 {w | ∃ G : Instance, encodeInstance G = w ∧ G.Normal ∧ G.HasIndepSet} 127 128 /-- **Multicoloured Independent Set is NP-hard on the instances in normal form.** -/ 129 axiom normalMulticolouredIndepSet_npHard : 130 Problems.NPHard NormalMulticolouredIndepSet 131 132 end Lax117284.MulticolouredIndepSet 133 -
Per-Client Fairness Parameters at Treewidth Four
Lemma 14. The problem is NP-hard even when the overall conflict graph has treewidth at most .
Let be an instance of Multicoloured Independent Set in normal form: -regular with , classes of vertices, and an even number of edges, every edge directed from the smaller class to the larger. The constructed instance has days — for every colour vertex days and one validation day, and one edge day per edge — and the clients: a vertex client with for every vertex; a selection client with for every colour; two edge clients and with parameter for every edge ; two interaction clients and with ; and a dummy client with .
For every colour the vertex days and the validation day form a vertex-selection gadget which compels the selection of a single vertex client for that colour, and the edge days form an incidence-checking gadget which certifies that all clients can be satisfied only if the selected vertices form a multicoloured independent set. On each day only a few clients take part in the gadget, and the job of blocks the jobs of all the others: it starts right after the last job of the gadget, and every client not taking part has a unit-length job of its own inside it. Writing for the number of clients other than and for a bijection between them and , the job of has processing time , and the job of such a client occupies the 'th time unit of it. On the vertex day of a vertex , the clients and of 's colour have due date and processing time , the job of has due date , and every other client has due date and processing time . On the validation day of colour , the client of the 'th vertex of has due date and processing time , the client of the 'th neighbour of that vertex has due date and processing time , the job of has due date , and every other client has due date and processing time . On the edge day of an edge , the client has due date and processing time , due date and processing time , due date and processing time , due date and processing time , the job of due date , and every other client due date and processing time .
The overall conflict graph of the constructed instance has a tree decomposition of width four: a root bag , a bag per colour below it, a bag per vertex of that colour below that, and a bag per neighbour of below that.
- lem✓
Lax117284.Lemma14(1st statement) - lem✓
Lax117284.Lemma14(2nd statement) - lem✓
Lax117284.Lemma14(3rd statement) - lem✓
Lax117284.Lemma14(4th statement) - lem✓
Lax117284.Lemma14(5th statement) - lem✓
Lax117284.Lemma14(6th statement)
1 import Lax117284.ConflictGraph 2 import Lax117284.MulticolouredIndepSet 3 import Lax117284.Problems 4 … module docstring, 80 lines 85 86 namespace Lax117284.Lemma14 87 88 open Lax117284.Problems Lax117284.MulticolouredIndepSet 89 open Lax434930.PolynomialTime Lax429075.Reductions 90 91 variable (G : Instance) 92 93 /-- The degree of the graph, read off the vertex `0` and raised to `1`. -/ 94 noncomputable def deg : ℕ := max 1 G.degree 95 96 /-- The number of clients: the dummy client, the two interaction clients, one per colour, 97 one per vertex, and one per pair of a vertex and a neighbour index. -/ 98 noncomputable def clientCount : ℕ := 99 3 + G.colours + G.vertices + G.vertices * deg G 100 101 /-- The number `N` of clients other than the dummy client, which is the length of its job 102 and the number of time units inside it. -/ 103 noncomputable def span : ℕ := 2 + G.colours + G.vertices + G.vertices * deg G 104 105 /-- The number of days: `n` vertex days and one validation day per colour, and one edge day 106 per edge. -/ 107 noncomputable def dayCount : ℕ := G.colours * (G.size + 1) + G.edgeCount 108 109 /-- The number of the selection client of colour `i`. -/ 110 def selId (i : ℕ) : ℕ := 3 + i 111 112 /-- The number of the vertex client of the vertex numbered `w`. -/ 113 def vtxId (w : ℕ) : ℕ := 3 + G.colours + w 114 115 /-- The number of the edge client `c_{v,u}` where `v` is the vertex numbered `w` and `u` is 116 its `q`'th neighbour. -/ 117 noncomputable def incId (w q : ℕ) : ℕ := 118 3 + G.colours + G.vertices + w * deg G + q 119 120 /-- The processing time and the due date of client `c` on the vertex day of the vertex 121 numbered `w₀`. -/ 122 noncomputable def vertexDay (w₀ c : ℕ) : ℕ × ℕ := 123 if c = 0 then (span G, deg G + span G) 124 else if c = vtxId G w₀ ∨ c = selId (G.classOf w₀) then (deg G, deg G) 125 else (1, deg G + c) 126 127 /-- The processing time and the due date of client `c` on the validation day of colour 128 `i₀`. -/ 129 noncomputable def validationDay (i₀ c : ℕ) : ℕ × ℕ := 130 if c = 0 then (span G, deg G * G.size + span G) 131 else if 3 + G.colours ≤ c ∧ c < 3 + G.colours + G.vertices ∧ 132 G.classOf (c - (3 + G.colours)) = i₀ then 133 (deg G, deg G * (G.indexOf (c - (3 + G.colours)) + 1)) 134 else if 3 + G.colours + G.vertices ≤ c ∧ 135 G.classOf ((c - (3 + G.colours + G.vertices)) / deg G) = i₀ then 136 (1, deg G * G.indexOf ((c - (3 + G.colours + G.vertices)) / deg G) 137 + (c - (3 + G.colours + G.vertices)) % deg G + 1) 138 else (1, deg G * G.size + c) 139 140 /-- The processing time and the due date of client `c` on the edge day of the edge from the 141 vertex numbered `w` to the vertex numbered `w'`. -/ 142 noncomputable def edgeDay (w w' c : ℕ) : ℕ × ℕ := 143 if c = 0 then (span G, 3 + span G) 144 else if c = 2 then (2, 2) 145 else if c = 1 then (2, 3) 146 else if c = incId G w ((G.nbrs w).idxOf w') then (1, 3) 147 else if c = incId G w' ((G.nbrs w').idxOf w) then (1, 1) 148 else (1, 3 + c) 149 150 /-- The processing time and the due date of client `c` on day `i`: the colour blocks of `n` 151 vertex days and a validation day come first, then the edge days. -/ 152 noncomputable def job (i c : ℕ) : ℕ × ℕ := 153 if i < G.colours * (G.size + 1) then 154 (if i % (G.size + 1) < G.size then 155 vertexDay G ((i / (G.size + 1)) * G.size + i % (G.size + 1)) c 156 else validationDay G (i / (G.size + 1)) c) 157 else 158 edgeDay G (G.edgeList.getD (i - G.colours * (G.size + 1)) (0, 0)).1 159 (G.edgeList.getD (i - G.colours * (G.size + 1)) (0, 0)).2 c 160 161 theorem one_le_deg : 1 ≤ deg G := le_max_left 1 _ 162 163 theorem two_le_span : 2 ≤ span G := by unfold span; omega 164 165 theorem job_pos (i c : ℕ) : 0 < (job G i c).1 := by 166 have hd := one_le_deg G 167 have hs := two_le_span G 168 unfold job vertexDay validationDay edgeDay 169 split_ifs <;> simp only [] <;> omega 170 171 theorem job_le (i c : ℕ) : (job G i c).1 ≤ (job G i c).2 := by 172 have hd := one_le_deg G 173 have hs := two_le_span G 174 have hm : ∀ a b : ℕ, a ≤ a * (b + 1) := fun a b => Nat.le_mul_of_pos_right a (Nat.succ_pos b) 175 unfold job vertexDay validationDay edgeDay 176 split_ifs <;> simp only [] <;> first 177 | omega 178 | exact hm _ _ 179 180 /-- **The instance of Lemma 14.** -/ 181 noncomputable def inst : Scheduling.Instance where 182 clients := clientCount G 183 days := dayCount G 184 p i c := (job G i c).1 185 d i c := (job G i c).2 186 p_pos i c := job_pos G i c 187 p_le_d i c := job_le G i c 188 189 /-- **The fairness parameters of the constructed instance**: the dummy client is required 190 on every day, the two interaction clients on half the edge days each, and every other 191 client once. -/ 192 noncomputable def kvec : Fin (inst G).clients → ℕ := fun c => 193 if (c : ℕ) = 0 then dayCount G 194 else if (c : ℕ) = 1 ∨ (c : ℕ) = 2 then G.edgeCount / 2 195 else 1 196 197 /-- **The construction is correct**: the graph has a multicoloured independent set exactly 198 when the constructed instance admits a schedule meeting every client's own fairness 199 parameter. -/ 200 axiom correct (hG : G.Normal) : 201 G.HasIndepSet ↔ (inst G).HasFairSchedule (kvec G) 202 203 /-- **The overall conflict graph of the constructed instance has treewidth at most 204 four.** -/ 205 axiom treewidth_le : ConflictGraph.treewidth (inst G) ≤ 4 206 207 /-- **No fairness parameter of the constructed instance exceeds its number of days.** -/ 208 axiom kvec_le_days (j : Fin (inst G).clients) : kvec G j ≤ (inst G).days 209 210 open Classical in 211 /-- **The reduction**, as a map on words: a word encoding an instance of Multicoloured 212 Independent Set *in normal form* is sent to the encoding of the constructed instance with 213 its fairness parameters, and every other word — one that encodes an instance outside the 214 normal form as much as one that encodes nothing — to the rejected word. -/ 215 noncomputable def reduce (w : Word) : Word := 216 if h : ∃ G : Instance, encodeInstance G = w ∧ G.Normal then 217 encodePerClient (inst h.choose) (kvec h.choose) 218 else rejectedPerClient 219 220 /-- **The reduction is correct.** -/ 221 axiom reduce_correct (w : Word) : 222 w ∈ NormalMulticolouredIndepSet ↔ 223 reduce w ∈ PerClient fun I k => ConflictGraph.treewidth I ≤ 4 ∧ ∀ j, k j ≤ I.days 224 225 /-- **The reduction runs in polynomial time.** -/ 226 axiom reduce_polyTime : Nonempty (Turing.TM2ComputableInPolyTime id id reduce) 227 228 /-- **Lemma 14.** The per-client problem is NP-hard on the instances whose overall conflict 229 graph has treewidth at most `4`. -/ 230 axiom perClient_npHard : 231 NPHard (PerClient fun I k => ConflictGraph.treewidth I ≤ 4 ∧ ∀ j, k j ≤ I.days) 232 233 end Lax117284.Lemma14 234 - lem✓
-
Per-Client Fairness Parameters Reduce to a Uniform One
Lemma 15. There is a polynomial-time reduction from to which increases the treewidth of the overall conflict graph by at most .
Let have clients, days and fairness parameters with . Append another days, add two clients and , and ask for the uniform parameter . The jobs of and coincide on every day, so at most one of them runs on any day and, being asked for of the days, each of them runs on exactly one day of every pair. Their common job occupies a stretch of time later than every due date of , so it conflicts with no original job on the original days. On the additional days each original client has a job of its own, which is placed inside that stretch on of them and after it on the others. A client of can therefore be served on all but of the additional days, and needs of the original days to reach — which is its original requirement.
The jobs the original clients receive on the additional days are pairwise disjoint, so the construction adds no edge between original clients: the overall conflict graph grows only by the two new vertices, and its treewidth by at most .
- lem✓
Lax117284.Lemma15(1st statement) - lem✓
Lax117284.Lemma15(2nd statement) - lem✓
Lax117284.Lemma15(3rd statement) - lem✓
Lax117284.Lemma15(4th statement) - lem✓
Lax117284.Lemma15(5th statement)
1 import Lax117284.ConflictGraph 2 import Lax117284.Corollary8 3 import Lax117284.Problems 4 … module docstring, 53 lines 58 59 namespace Lax117284.Lemma15 60 61 open Lax117284.Scheduling Lax117284.Problems Lax434930.PolynomialTime 62 open Lax429075.Reductions 63 64 /-- A time not before any due date of `I`. -/ 65 def dmax (I : Instance) : ℕ := Finset.univ.sup fun i => Finset.univ.sup fun j => I.d i j 66 67 /-- The length of the job the two new clients share: longer than the stretch of private 68 jobs, so that it contains all of them. -/ 69 def span (I : Instance) : ℕ := I.clients + 1 70 71 /-- **The instance of Lemma 15**: `2m` days, the `n` clients of `I` together with two new 72 ones, and the uniform fairness parameter `m`. -/ 73 def inst (I : Instance) (k : Fin I.clients → ℕ) : Instance where 74 clients := I.clients + 2 75 days := 2 * I.days 76 p i j := 77 if (j : ℕ) < I.clients then 78 (if (i : ℕ) < I.days then I.pAt i j else 1) 79 else span I 80 d i j := 81 if hj : (j : ℕ) < I.clients then 82 (if (i : ℕ) < I.days then I.dAt i j 83 else if (i : ℕ) - I.days < k ⟨j, hj⟩ then dmax I + ((j : ℕ) + 1) 84 else dmax I + span I + ((j : ℕ) + 1)) 85 else dmax I + span I 86 p_pos i j := by 87 have h0 := I.pAt_pos (i : ℕ) (j : ℕ) 88 have hs : span I = I.clients + 1 := rfl 89 split_ifs <;> omega 90 p_le_d i j := by 91 have h1 := I.pAt_le_dAt (i : ℕ) (j : ℕ) 92 have hs : span I = I.clients + 1 := rfl 93 split_ifs <;> omega 94 95 /-- **The construction is correct**: the instance it produces admits a schedule serving 96 every client on `m` of its `2m` days exactly when the original instance admits one serving 97 every client `j` on `k j` of its `m` days. -/ 98 axiom correct (I : Instance) (k : Fin I.clients → ℕ) (hk : ∀ j, k j ≤ I.days) : 99 I.HasFairSchedule k ↔ (inst I k).HasKFairSchedule I.days 100 101 /-- **The construction raises the treewidth of the overall conflict graph by at most 102 two.** -/ 103 axiom treewidth_le (I : Instance) (k : Fin I.clients → ℕ) : 104 ConflictGraph.treewidth (inst I k) ≤ ConflictGraph.treewidth I + 2 105 106 open Classical in 107 /-- **The reduction**, as a map on words: a word encoding an instance with per-client 108 parameters not exceeding its number of days is sent to the encoding of the constructed 109 instance with the parameter `m`, an instance without clients to a fixed yes-instance, and 110 every other word to the rejected word. -/ 111 noncomputable def reduce (w : Word) : Word := 112 if h : ∃ (I : Instance) (k : Fin I.clients → ℕ), 113 encodePerClient I k = w ∧ ∀ j, k j ≤ I.days then 114 if h.choose.clients = 0 then encodeUniform (Corollary8.noClients 0) 0 115 else encodeUniform (inst h.choose h.choose_spec.choose) h.choose.days 116 else rejected 117 118 /-- **The reduction is correct.** -/ 119 axiom reduce_correct (w : Word) : 120 w ∈ PerClient (fun I k => ∀ j, k j ≤ I.days) ↔ reduce w ∈ Uniform any 121 122 /-- **The reduction runs in polynomial time.** -/ 123 axiom reduce_polyTime : Nonempty (Turing.TM2ComputableInPolyTime id id reduce) 124 125 /-- **Lemma 15.** The per-client problem reduces in polynomial time to the uniform problem; 126 by `treewidth_le` the reduction raises the treewidth of the overall conflict graph by at 127 most two. -/ 128 axiom perClient_manyOne_uniform : 129 ManyOne (PerClient fun I k => ∀ j, k j ≤ I.days) (Uniform any) 130 131 end Lax117284.Lemma15 132 - lem✓
-
Graphs and nice tree decompositions as words
A tree decomposition of a graph is a tree whose nodes carry bags of vertices such that every vertex and every edge lies in a bag and the nodes whose bags contain a fixed vertex form a connected subtree; its width is the largest bag size minus one, and the treewidth of the graph is the least width of a decomposition (the archive's ). A tree decomposition is nice if its root and its leaves have empty bags and every other node is either an introduce node, whose bag is the bag of its single child plus one vertex, a forget node, whose bag is the bag of its single child minus one vertex, or a join node, with two children whose bags equal its own. This is the form dynamic programs over tree decompositions consume; it is due to Kloks.
1 import Mathlib.Combinatorics.SimpleGraph.Acyclic 2 import Mathlib.Combinatorics.SimpleGraph.Connectivity.Connected 3 import Lax228581.Treewidth 4 … module docstring, 42 lines 47 48 namespace Lax117284.GraphWords 49 50 open Lax228581.Treewidth 51 52 -- The word of a decomposition. 53 54 /-- The number of nodes of the decomposition word `D`: its first entry. -/ 55 def nodeCount (D : List ℕ) : ℕ := D.getD 0 0 56 57 /-- The kind of node `i`: `0` leaf, `1` introduce, `2` forget, `3` join. -/ 58 def kind (D : List ℕ) (i : ℕ) : ℕ := D.getD (1 + 3 * i) 0 59 60 /-- The vertex of node `i`, for an introduce or a forget node. -/ 61 def vertex (D : List ℕ) (i : ℕ) : ℕ := D.getD (2 + 3 * i) 0 62 63 /-- The second child of node `i`, for a join node. -/ 64 def other (D : List ℕ) (i : ℕ) : ℕ := D.getD (3 + 3 * i) 0 65 66 /-- The bag of node `i` among the vertices `0 … n - 1`, from the leaves up: the empty bag at a 67 leaf, the child's bag plus the vertex at an introduce node, minus it at a forget node, and the 68 child's bag at a join node. The first child of node `i + 1` is node `i`. -/ 69 def bagAt (n : ℕ) (D : List ℕ) : ℕ → Finset (Fin n) 70 | 0 => ∅ 71 | i + 1 => 72 if kind D (i + 1) = 1 then 73 if h : vertex D (i + 1) < n then insert ⟨vertex D (i + 1), h⟩ (bagAt n D i) 74 else bagAt n D i 75 else if kind D (i + 1) = 2 then 76 if h : vertex D (i + 1) < n then (bagAt n D i).erase ⟨vertex D (i + 1), h⟩ 77 else bagAt n D i 78 else if kind D (i + 1) = 3 then bagAt n D i 79 else ∅ 80 81 /-- Node `c` is a child of node `p`. -/ 82 def IsChild (D : List ℕ) (c p : ℕ) : Prop := 83 p < nodeCount D ∧ 84 ((kind D p = 1 ∨ kind D p = 2) ∧ c + 1 = p ∨ kind D p = 3 ∧ (c + 1 = p ∨ c = other D p)) 85 86 /-- The graph on the nodes `0 … N - 1` whose edges join a node to its children. -/ 87 def treeGraph (D : List ℕ) : SimpleGraph (Fin (nodeCount D)) where 88 Adj a b := a ≠ b ∧ (IsChild D a.val b.val ∨ IsChild D b.val a.val) 89 symm := ⟨fun _ _ h => ⟨h.1.symm, h.2.symm⟩⟩ 90 loopless := ⟨fun _ h => h.1 rfl⟩ 91 92 /-- **`D` is the word of a nice tree decomposition of `G` of width at most `w`.** -/ 93 structure NiceDecomposition {n : ℕ} (G : SimpleGraph (Fin n)) (w : ℕ) (D : List ℕ) : Prop where 94 /-- The word is `N` followed by three numbers for each of the `N` nodes. -/ 95 length_eq : D.length = 1 + 3 * nodeCount D 96 /-- There is a node. -/ 97 nonempty : 0 < nodeCount D 98 /-- Every node is a leaf, or an introduce node over the node before it whose vertex is not in 99 that node's bag, or a forget node over the node before it whose vertex is in that node's bag, 100 or a join node whose two children, the node before it and an earlier node, have its bag. -/ 101 shape : ∀ i, i < nodeCount D → 102 kind D i = 0 ∨ 103 (0 < i ∧ kind D i = 1 ∧ vertex D i < n ∧ 104 ∀ h : vertex D i < n, (⟨vertex D i, h⟩ : Fin n) ∉ bagAt n D (i - 1)) ∨ 105 (0 < i ∧ kind D i = 2 ∧ vertex D i < n ∧ 106 ∀ h : vertex D i < n, (⟨vertex D i, h⟩ : Fin n) ∈ bagAt n D (i - 1)) ∨ 107 (0 < i ∧ kind D i = 3 ∧ other D i + 1 < i ∧ 108 bagAt n D (other D i) = bagAt n D (i - 1)) 109 /-- Every node other than the last has exactly one parent. -/ 110 parent : ∀ c, c + 1 < nodeCount D → ∃! p, IsChild D c p 111 /-- The nodes form a tree. -/ 112 isTree : (treeGraph D).IsTree 113 /-- Every vertex is in a bag. -/ 114 covers : ∀ v : Fin n, ∃ i, i < nodeCount D ∧ v ∈ bagAt n D i 115 /-- Every two adjacent vertices are in a bag together. -/ 116 edges : ∀ u v : Fin n, G.Adj u v → 117 ∃ i, i < nodeCount D ∧ u ∈ bagAt n D i ∧ v ∈ bagAt n D i 118 /-- The nodes whose bags contain a fixed vertex form a connected subtree. -/ 119 connected : ∀ v : Fin n, 120 ((treeGraph D).induce {i : Fin (nodeCount D) | v ∈ bagAt n D i}).Connected 121 /-- Every bag has at most `w + 1` vertices. -/ 122 width : ∀ i, i < nodeCount D → (bagAt n D i).card ≤ w + 1 123 124 open Classical in 125 /-- **`g` is the word of the graph `G`**: the number of vertices, then the adjacency matrix row 126 by row. -/ 127 structure EncodesGraph {n : ℕ} (g : List ℕ) (G : SimpleGraph (Fin n)) : Prop where 128 /-- The word is the count followed by the matrix. -/ 129 length_eq : g.length = 1 + n * n 130 /-- The first entry is the number of vertices. -/ 131 head_eq : g.getD 0 0 = n 132 /-- The entry of the row `u` and column `v` is `1` when the two vertices are adjacent and `0` 133 otherwise. -/ 134 adj_eq : ∀ u v : Fin n, g.getD (1 + u.val * n + v.val) 0 = if G.Adj u v then 1 else 0 135 136 end Lax117284.GraphWords 137 -
Optimal tree decompositions from a given one
The theorem of Bodlaender and Kloks. For all and there is a linear-time algorithm that, given a graph together with a tree decomposition of width at most , decides whether the treewidth of the graph is at most and, if so, finds a tree decomposition of width at most . The dependence on is .
H. L. Bodlaender and T. Kloks, "Efficient and constructive algorithms for the pathwidth and treewidth of graphs", Journal of Algorithms 21 (1996) 358–402. A simpler presentation, which this module follows, is E. Althaus and S. Ziegler, "Optimal tree decompositions revisited: a simpler linear-time FPT algorithm", arXiv:1912.09144 (2020), §3; the statement is Theorem 2.10 of H. L. Bodlaender, "A linear-time algorithm for finding tree-decompositions of small treewidth", SIAM Journal on Computing 25 (1996) 1305–1317, where it is used as a black box.
This is the second stage of Bodlaender's algorithm, being the first: the latter builds the decomposition of width at most this one consumes.
1 import Lax117284.GraphWords 2 import Lax808846.Ram 3 … module docstring, 46 lines 50 51 namespace Lax117284.BodlaenderKloks 52 53 open Lax808846.Ram Lax117284.GraphWords 54 55 open Classical in 56 /-- **The theorem of Bodlaender and Kloks, on a word RAM.** One program and one constant serve 57 every word length `W`, every graph `G`, every bound `k` and every nice tree decomposition `D` of 58 `G` of width at most `l`, provided the input `g ++ [k, l] ++ D` fits with room for 59 `c * 2 ^ (c * l ^ 3)` times a polynomial in its length. On such an input the program halts 60 within `c * 2 ^ (c * l ^ 3) * (|input| + 2) ^ c` instructions, writing `[0]` if the graph has no 61 tree decomposition of width at most `k`, and `1` followed by the word of a nice tree 62 decomposition of width at most `k` otherwise. -/ 63 axiom improveDecomposition : 64 ∃ (prog : Program) (c : ℕ), ∀ (W k l : ℕ) (n : ℕ) (G : SimpleGraph (Fin n)) (g D : List ℕ), 65 EncodesGraph g G → NiceDecomposition G l D → 66 (∀ v ∈ g ++ [k, l] ++ D, 67 c * 2 ^ (c * l ^ 3) * ((g ++ [k, l] ++ D).length + v + 1) ^ c ≤ 2 ^ W) → 68 ∃ (out : List ℕ) (t : ℕ), t ≤ c * 2 ^ (c * l ^ 3) * ((g ++ [k, l] ++ D).length + 2) ^ c ∧ 69 RunsTo W prog (g ++ [k, l] ++ D) out t ∧ 70 (out = [0] ∧ ¬ Lax228581.Treewidth.HasTreewidthAtMost G k ∨ 71 ∃ D', out = 1 :: D' ∧ NiceDecomposition G k D') 72 73 end Lax117284.BodlaenderKloks 74 -
Nice tree decompositions of small width are found in fixed-parameter time
Bodlaender's theorem. For every constant one can decide in linear time whether a graph has treewidth at most , and if so construct a tree decomposition of width at most ; the dependence on is . Kloks' construction turns a tree decomposition into a nice one of the same width in time linear in its size, so the same holds for nice decompositions.
H. L. Bodlaender, "A linear-time algorithm for finding tree-decompositions of small treewidth", SIAM Journal on Computing 25 (1996) 1305–1317; T. Kloks, "Treewidth: Computations and Approximations", Lecture Notes in Computer Science 842, Springer 1994. A simplified presentation of the whole algorithm is E. Althaus and S. Ziegler, "Optimal tree decompositions revisited: a simpler linear-time FPT algorithm", arXiv:1912.09144 (2020).
The algorithm reduces to a graph with a constant fraction fewer vertices — either by contracting a maximal matching among the low-degree vertices, or by removing the "I-simplicial" vertices of the graph with the edges added between vertices that have common low-degree neighbours — solves that recursively, lifts the answer to a decomposition of width at most , and hands that to .
1 import Lax117284.GraphWords 2 import Lax808846.Ram 3 … module docstring, 42 lines 46 47 namespace Lax117284.BodlaenderGeneral 48 49 open Lax808846.Ram Lax117284.GraphWords 50 51 open Classical in 52 /-- **Bodlaender's theorem with Kloks' niceness, on a word RAM.** One program and one constant 53 serve every word length `W`, every graph `G` on `n` vertices and every bound `k`, provided the 54 word — the graph followed by `k` — fits with room for `c * 2 ^ (c * k ^ 3)` times a polynomial 55 in its length. On such a word the program halts within `c * 2 ^ (c * k ^ 3) * (|g| + 2) ^ c` 56 instructions, writing `[0]` if the graph has no tree decomposition of width at most `k`, and `1` 57 followed by the word of a nice tree decomposition of width at most `k` otherwise. -/ 58 axiom niceDecomposition_computable : 59 ∃ (prog : Program) (c : ℕ), ∀ (W k n : ℕ) (G : SimpleGraph (Fin n)) (g : List ℕ), 60 EncodesGraph g G → 61 (∀ v ∈ g ++ [k], c * 2 ^ (c * k ^ 3) * ((g ++ [k]).length + v + 1) ^ c ≤ 2 ^ W) → 62 ∃ (out : List ℕ) (t : ℕ), t ≤ c * 2 ^ (c * k ^ 3) * (g.length + 2) ^ c ∧ 63 RunsTo W prog (g ++ [k]) out t ∧ 64 (out = [0] ∧ ¬ Lax228581.Treewidth.HasTreewidthAtMost G k ∨ 65 ∃ D, out = 1 :: D ∧ NiceDecomposition G k D) 66 67 end Lax117284.BodlaenderGeneral 68 -
no assumptions
The problem of the number of days plus the treewidth is fixed-parameter tractable. A word RAM program computes the answer within instructions, where is the number of days plus the treewidth of the overall conflict graph, for a function of alone. The program is compiled from an IMP+ program whose correctness and cost are proved in ; the theorem of Bodlaender and Kloks that finds a nice tree decomposition of small width in time times a polynomial () is proved, in , from its proof in lax-689794.
-
no assumptions
The problem is decided by a word RAM program that reads the word, with clients and days, and tests whether it is at least long, by doubling a counter held at the length. If it is, and the fairness parameter is at most , the program writes the word of an integer program with variables: one per pair of a type of day (its conflict relation) and a set of clients, which the pair says run together on the days of that type, and one slack per client; one constraint fixes, for each type, the number of days of that type, and one says, for each client, that the days it is not served on are at most . It then chooses a word length for which that word fits, and runs the program that solves the integer programs of the family (, proved) on it at that word length, by an interpreter of word RAM programs written in the IMP+ language, the memory of the interpreted machine being an array of
2 ^ w'cells. The program is read as data, so it is the same for every word length. If the word is too short, the number of schedules is at most a function of alone, and the program enumerates them. A parameter above is answered at once when there is a client. No result is cited. -
The Integer Programs of the Clients' Reduction Are Fixed-Parameter Tractable
An integer program here is a system of linear equality constraints over non-negative integer variables, with non-negative integer coefficients and right-hand sides: find with for every constraint . It is feasible if such an exists.
The integer programs that the paper solves with Lenstra's algorithm (Heeger–Hermelin–Itzhaki–Molter–Shabtay, proof of Theorem 4's third bullet, via the integer program of 's ) form one explicit family, one constraint matrix for each number of clients: types of day (a type is a conflict relation on the clients), subsets of clients, one variable for every pair of a type and a subset and one slack variable for every client, and one constraint for each type and one for each client. This module states, for exactly this family, that feasibility is fixed-parameter tractable in the number of variables: . The matrix is fixed by ; only the right-hand side (the number of days of each type and the number of days a client may go unserved) is free. The reduction of the problem of the clients to this problem is , and is proved in the proofs package, by a guess-and-verify algorithm specific to these matrices, not cited.
1 import Mathlib.Algebra.BigOperators.Group.Finset.Basic 2 import Mathlib.Data.Fintype.Fin 3 import Mathlib.Data.Nat.Bitwise 4 import Lax117284.ParameterizedComplexity 5 … module docstring, 47 lines 53 54 namespace Lax117284.IlpClients 55 56 open Lax117284.ParameterizedComplexity 57 58 /-- **An integer program**: `M` linear equality constraints over `N` non-negative integer 59 variables, with non-negative integer coefficients `a` and right-hand sides `b`. -/ 60 structure ILP where 61 /-- The number of variables. -/ 62 N : ℕ 63 /-- The number of constraints. -/ 64 M : ℕ 65 /-- The coefficient of variable `i` in constraint `j`. -/ 66 a : Fin M → Fin N → ℕ 67 /-- The right-hand side of constraint `j`. -/ 68 b : Fin M → ℕ 69 70 /-- **`E` is feasible**: some assignment of its variables satisfies every constraint. -/ 71 def ILP.Feasible (E : ILP) : Prop := ∃ x : Fin E.N → ℕ, ∀ j, ∑ i, E.a j i * x i = E.b j 72 73 /-- The numbers of an integer program: the two counts, then the coefficients row by row, 74 then the right-hand sides. Total and well-defined on every list, reading absent entries as 75 `0`, exactly like `Instance.pAt`/`Instance.dAt`. -/ 76 def decodeILP (w : List ℕ) : ILP where 77 N := w.getD 0 0 78 M := w.getD 1 0 79 a := fun j i => w.getD (2 + j.val * w.getD 0 0 + i.val) 0 80 b := fun j => w.getD (2 + w.getD 1 0 * w.getD 0 0 + j.val) 0 81 82 /-- The number of types of day for `n` clients: `2 ^ (n * n)` (bit `a * n + b` of a type says 83 that clients `a` and `b` conflict on the days of that type). -/ 84 def nT (n : ℕ) : ℕ := 2 ^ (n * n) 85 86 /-- The number of subsets of the `n` clients, `2 ^ n` (bit `j` of a subset says that client `j` is served). -/ 87 def nZ (n : ℕ) : ℕ := 2 ^ n 88 89 /-- The number of pairs of a type and a subset, `2 ^ (n * n) * 2 ^ n`. -/ 90 def nV (n : ℕ) : ℕ := nT n * nZ n 91 92 /-- The number of variables, `2 ^ (n * n) * 2 ^ n + n`: one for every pair of a type and a 93 subset, and one slack for every client. -/ 94 def nN (n : ℕ) : ℕ := nV n + n 95 96 /-- The number of constraints, `2 ^ (n * n) + n`: one for every type and one for every client. -/ 97 def nM (n : ℕ) : ℕ := nT n + n 98 99 /-- The subset `S` (a number) is independent for the type `t` (a number): it contains no two distinct 100 clients `a`, `b` that the type makes conflict (bit `a * n + b` of `t`). -/ 101 def indepB (n t S : ℕ) : Bool := 102 decide (∀ a < n, ∀ b < n, a ≠ b → S.testBit a = true → S.testBit b = true → 103 t.testBit (a * n + b) = false) 104 105 /-- The coefficient of variable `c` in constraint `r`: the variable `c < nV n` is the pair 106 of the type `c / 2 ^ n` and the subset `c % 2 ^ n`, whose column has a `1` in the row of its type and in the row 107 of every client outside the subset, unless the subset is not independent for the type, when the column is 108 zero; the other variables are the slacks, `1` in the row of their client. -/ 109 def coef (n r c : ℕ) : ℕ := 110 if c < nV n then 111 (if indepB n (c / nZ n) (c % nZ n) then 112 (if r < nT n then (if c / nZ n = r then 1 else 0) 113 else (if (c % nZ n).testBit (r - nT n) = false then 1 else 0)) 114 else 0) 115 else (if nT n ≤ r ∧ c - nV n = r - nT n then 1 else 0) 116 117 /-- **The word of the integer program** of `n` clients with `cnt t` days of type `t` and `B` days that a 118 client may go unserved: the two counts, the coefficients row by row, and the right-hand sides, `cnt r` in the row 119 of type `r` and `B` in each client row. -/ 120 def ilpWord (n : ℕ) (cnt : ℕ → ℕ) (B : ℕ) : List ℕ := 121 [nN n, nM n] ++ (List.range (nM n * nN n)).map (fun i => coef n (i / nN n) (i % nN n)) 122 ++ (List.range (nM n)).map (fun r => if r < nT n then cnt r else B) 123 124 /-- **The integer programs of the clients' reduction, as a parameterized problem**: the domain is the words 125 `ilpWord n cnt B` of the family and the fixed word `[1, 1, 0, 1]` of the program `0 · x = 1`; a yes-instance 126 is a word whose integer program is feasible; the parameter is the number of variables. -/ 127 def ilpClients : Problem where 128 Domain := {z | (∃ n cnt B, z = ilpWord n cnt B) ∨ z = [1, 1, 0, 1]} 129 Yes z := (decodeILP z).Feasible 130 param z := (decodeILP z).N 131 132 /-- **Integer programs of the clients' family are fixed-parameter tractable** in the number of variables. -/ 133 axiom ilpClients_fpt : FPT ilpClients 134 135 end Lax117284.IlpClients 136
Loading the paper…