CubicPathwidth.Alg.Proofs2.R1ReduceFuel
Source file CubicPathwidth/Alg/Proofs2/R1ReduceFuel.lean of the Lean project (how it is built).
import CubicPathwidth.Alg.Plan2
import CubicPathwidth.Alg.Proofs.Targets
/-!
# R1: `reduce_record` never runs out of fuel
Task R1 of the formalisation (§6.2, statement `ReduceRecordNoFallback` of
`Spec.lean` §7). Proves
* `reduceLoop_flag_false`: for every fuel, if `|stack| + 2·numNodes ≤ fuel` then the
flag of `reduceLoop` is `false` (general lemma, any well-formed start graph);
* `reduceRecordNoFallback : ReduceRecordNoFallback`, from fuel `3·numNodes + 1` and
`|initial stack| ≤ numNodes`.
Measure. `μ(G, stack) = |stack| + 2·numNodes(G)`. A skipped pop decreases `|stack|` by
one. A reduction at `v` (a node with `≤ 2` neighbours) removes one node and pushes the
`≤ 2` neighbours: `μ` drops by at least one.
Reusable helpers (namespace `CubicPathwidth.Alg.R1`): `length_eq_of_mem_iff`,
`numNodes_removeNode`, `stepL`, `stepL_props`, `redStep_wf_numNodes`, `redStep_eq`,
`reduceLoop_red_eq`, `reduceLoop_flag_false`.
-/
noncomputable section
namespace CubicPathwidth.Alg
open Graph
namespace R1
/-- Two lists without repeats and with the same members have the same length. -/
theorem length_eq_of_mem_iff {l l' : List Nat} (hl : l.Nodup) (hl' : l'.Nodup)
(h : ∀ x, x ∈ l ↔ x ∈ l') : l.length = l'.length :=
((List.perm_ext_iff_of_nodup hl hl').2 h).length_eq
/-- Two well-formed graphs with the same nodes have the same number of nodes. -/
theorem numNodes_eq_of_mem_iff {G H : Graph} (hG : G.WF) (hH : H.WF)
(h : ∀ x, x ∈ G.nodes ↔ x ∈ H.nodes) : G.numNodes = H.numNodes :=
length_eq_of_mem_iff hG.nodup hH.nodup h
/-- Removing a node lowers the node count by one. -/
theorem numNodes_removeNode {G : Graph} (hG : G.WF) {v : Nat} (hv : v ∈ G.nodes) :
(G.removeNode v).numNodes + 1 = G.numNodes := by
have hR := removeNodeSpec G v hG
have hperm : (G.removeNode v).nodes.length = (G.nodes.erase v).length :=
length_eq_of_mem_iff hR.1.nodup (hG.nodup.erase v) (fun x => by
rw [hR.2.1 x, hG.nodup.mem_erase_iff]
tauto)
simp only [Graph.numNodes]
rw [hperm, List.length_erase_of_mem hv]
have := List.length_pos_of_mem hv
omega
/-- The graph part of one step of `reduceLoop` (`G1` = `G` after `removeNode v`, `nb`
= the neighbour list of `v`). Same as `C5.stepG`. -/
def stepL (G1 : Graph) (nb : List Nat) : Graph :=
match nb with
| [a, b] => G1.addEdge a b
| _ => G1
theorem redStep_eq (G : Graph) (v : Nat) :
redStep G v = stepL (G.removeNode v) (G.nbrs v) := rfl
/-- A reduction step on a well-formed graph keeps well-formedness; the node count is
unchanged when the neighbours are nodes of `G1` (distinct). -/
theorem stepL_props {G1 : Graph} (hG1 : G1.WF) {nb : List Nat} (hnd : nb.Nodup)
(hl : ∀ a ∈ nb, a ∈ G1.nodes) :
(stepL G1 nb).WF ∧ (stepL G1 nb).numNodes = G1.numNodes := by
rcases nb with _ | ⟨a, _ | ⟨b, _ | ⟨c, t⟩⟩⟩
· exact ⟨hG1, rfl⟩
· exact ⟨hG1, rfl⟩
· have hab : a ≠ b := by
intro h
subst h
simp at hnd
have ha : a ∈ G1.nodes := hl a (by simp)
have hb : b ∈ G1.nodes := hl b (by simp)
obtain ⟨hw, hn, _⟩ := addEdgeSpec G1 a b hG1 hab
have hcount : (G1.addEdge a b).numNodes = G1.numNodes := by
apply numNodes_eq_of_mem_iff hw hG1
intro x
rw [hn x]
constructor
· rintro (h | rfl | rfl)
· exact h
· exact ha
· exact hb
· intro h
exact Or.inl h
exact ⟨hw, hcount⟩
· exact ⟨hG1, rfl⟩
/-- `redStep` keeps well-formedness and removes exactly one node, for `v` a node. -/
theorem redStep_wf_numNodes {G : Graph} (hG : G.WF) {v : Nat} (hv : v ∈ G.nodes) :
(redStep G v).WF ∧ (redStep G v).numNodes + 1 = G.numNodes := by
have hRem := removeNodeSpec G v hG
have hl : ∀ a ∈ G.nbrs v, a ∈ (G.removeNode v).nodes := by
intro a ha
have hav : a ≠ v := fun h => hG.loopless v (h ▸ ha)
exact (hRem.2.1 a).2 ⟨hG.nbrs_sub v a ha, hav⟩
obtain ⟨hw, hcount⟩ := stepL_props hRem.1 (hG.nbrs_nodup v) hl
refine ⟨?_, ?_⟩
· rw [redStep_eq]
exact hw
· rw [redStep_eq, hcount]
exact numNodes_removeNode hG hv
/-- One non-skipped pop of `reduceLoop`, written with `redStep`. -/
theorem reduceLoop_red_eq (fuel : Nat) (G : Graph) (v : Nat) (rest : List Nat)
(opsRev : List Op) (hm : G.mem v = true) (hd : G.degree v ≤ 2) :
reduceLoop (fuel + 1) G (v :: rest) opsRev =
reduceLoop fuel (redStep G v) ((G.nbrs v).reverse ++ rest)
(Op.red v (G.nbrs v) :: opsRev) := by
have hc : (!G.mem v || decide (G.degree v > 2)) = false := by
simp only [hm, Bool.not_true, Bool.false_or, decide_eq_false_iff_not, not_lt]
exact hd
simp only [reduceLoop, hc, Bool.false_eq_true, ↓reduceIte]
rfl
/-- The general measure lemma: if `|stack| + 2·numNodes ≤ fuel`, then the loop never
runs out of fuel. -/
theorem reduceLoop_flag_false : ∀ (fuel : Nat) (G : Graph) (stack : List Nat)
(opsRev : List Op), G.WF → stack.length + 2 * G.numNodes ≤ fuel →
(reduceLoop fuel G stack opsRev).2.2 = false
| 0, G, stack, opsRev, _, hfuel => by
cases stack with
| nil => rfl
| cons v rest =>
exfalso
simp only [List.length_cons] at hfuel
omega
| fuel + 1, G, stack, opsRev, hG, hfuel => by
cases stack with
| nil => rfl
| cons v rest =>
simp only [List.length_cons] at hfuel
by_cases hm : G.mem v = true
· by_cases hd : G.degree v ≤ 2
· -- a reduction
rw [reduceLoop_red_eq fuel G v rest opsRev hm hd]
obtain ⟨hw, hcount⟩ := redStep_wf_numNodes hG ((hG.mem_iff v).1 hm)
apply reduceLoop_flag_false fuel (redStep G v) _ _ hw
have hlen : (G.nbrs v).length ≤ 2 := hd
simp only [List.length_append, List.length_reverse]
omega
· -- degree above two: the pop is skipped
have hc : (!G.mem v || decide (G.degree v > 2)) = true := by
simp only [hm, Bool.not_true, Bool.false_or, decide_eq_true_eq]
omega
have hskip : reduceLoop (fuel + 1) G (v :: rest) opsRev =
reduceLoop fuel G rest opsRev := by
simp only [reduceLoop, hc, ↓reduceIte]
rw [hskip]
apply reduceLoop_flag_false fuel G rest opsRev hG
omega
· -- not a node: skipped
have hm' : G.mem v = false := by simpa using hm
have hc : (!G.mem v || decide (G.degree v > 2)) = true := by
simp [hm']
have hskip : reduceLoop (fuel + 1) G (v :: rest) opsRev =
reduceLoop fuel G rest opsRev := by
simp only [reduceLoop, hc, ↓reduceIte]
rw [hskip]
apply reduceLoop_flag_false fuel G rest opsRev hG
omega
/-- `reduce_record` (`reduceRecordF`) never falls back. The initial stack has at most
`numNodes` entries and the fuel is `3·numNodes + 1`. -/
theorem reduceRecordNoFallback_aux : ∀ G : Graph, G.WF → (reduceRecordF G).2.2 = false := by
intro G hG
unfold reduceRecordF
apply reduceLoop_flag_false _ G _ [] hG
have hfilt := List.length_filter_le (fun v => decide (G.degree v ≤ 2)) G.nodes
simp only [List.length_reverse]
simp only [Graph.numNodes]
omega
end R1
/-- `reduce_record` never falls back (`Spec.lean` §7). -/
theorem reduceRecordNoFallback : ReduceRecordNoFallback :=
R1.reduceRecordNoFallback_aux
#print axioms reduceRecordNoFallback
end CubicPathwidth.Alg
Back to top