From 4a1b3c4f6fe0ea894ffcdf53a7d6e61e75178fa8 Mon Sep 17 00:00:00 2001 From: Samuel Schlesinger Date: Wed, 11 Nov 2026 14:45:55 -0500 Subject: [PATCH 1/8] refactor(Problems/Graph): migrate Graph to structure with enc_structure (#4385) --- .../GeneralizedGeography/Membership.lean | 64 +- .../Space/GeneralizedGeography/Reduction.lean | 5 +- .../FineGrained/ContractionMatching.lean | 5 +- .../Complexity/FineGrained/Diameter.lean | 91 +- .../Complexity/FineGrained/GraphProblems.lean | 18 +- .../PermutationIndependentSet.lean | 68 +- .../Complexity/Parameterized/ColorCoding.lean | 6 +- .../Parameterized/LongPathKernel.lean | 47 +- .../Complexity/Parameterized/VertexCover.lean | 23 +- .../Parameterized/VertexCover/BussKernel.lean | 20 +- .../WHierarchy/IndependentSet.lean | 6 +- .../CSP/RotationSystem/Primal/Euler.lean | 14 +- .../CSP/RotationSystem/Primal/Graph.lean | 24 +- .../Complexity/Problems/ChromaticNumber.lean | 15 +- .../Complexity/Problems/Clique.lean | 51 +- .../Complexity/Problems/Clique/OfAdj.lean | 15 +- .../Complexity/Problems/CliqueCover.lean | 4 +- .../Complexity/Problems/Coloring.lean | 50 +- .../Complexity/Problems/DominatingSet.lean | 68 +- .../Complexity/Problems/FeedbackArcSet.lean | 37 +- .../Problems/FeedbackVertexSet.lean | 62 +- .../Problems/GeneralizedGeography.lean | 21 +- .../Complexity/Problems/Graph.lean | 98 ++- .../Problems/Graph/DartConnectivity.lean | 6 +- .../Problems/Graph/DirectedCycle.lean | 45 +- .../Problems/Graph/OrthogonalDrawing.lean | 32 +- .../Problems/Graph/OrthogonalLayout.lean | 90 +- .../Problems/Graph/OrthogonalPath.lean | 271 +++--- .../Problems/Graph/PerfectMatching.lean | 30 +- .../Complexity/Problems/Graph/Ports.lean | 36 +- .../Problems/Graph/RotationSystem.lean | 4 +- .../Graph/RotationSystem/Certificate.lean | 116 +-- .../Graph/RotationSystem/ComponentCounts.lean | 42 +- .../Graph/RotationSystem/Components.lean | 2 +- .../Graph/RotationSystem/Differences.lean | 16 +- .../Problems/Graph/RotationSystem/Dual.lean | 32 +- .../Graph/RotationSystem/FaceCounts.lean | 10 +- .../Graph/RotationSystem/Lifting.lean | 72 +- .../Problems/Graph/SpanningTrees.lean | 22 +- .../Problems/Graph/VertexColors.lean | 53 +- .../Complexity/Problems/GraphIsomorphism.lean | 9 +- .../Complexity/Problems/Hamiltonian.lean | 67 +- .../Problems/Hamiltonian/Diamond/Defs.lean | 4 +- .../Problems/Hamiltonian/Undirected.lean | 89 +- .../Problems/IndependentSet/Sparse.lean | 9 +- .../Complexity/Problems/LongPath.lean | 8 +- .../Problems/LongPath/Hardness.lean | 125 +-- .../Problems/SAT/ExactOne/DrawingSource.lean | 53 +- .../Complexity/Problems/SAT/Incidence.lean | 44 +- .../Problems/SAT/Planar/PathAssignments.lean | 10 +- .../Problems/SAT/Planar/PathGeometry.lean | 20 +- .../Problems/SAT/Planar/PathPolyTime.lean | 41 +- .../Complexity/Problems/TSP.lean | 5 +- .../Complexity/Problems/VertexCover.lean | 80 +- .../Problems/VertexCover/Approximation.lean | 15 +- .../PCP/Approximation/KCenter.lean | 2 +- .../PolyTime/Graph/RotationSystem.lean | 40 +- CslibFrontierTests/CSPOccurrence.lean | 10 +- CslibFrontierTests/CSPPrimalRotation.lean | 10 +- CslibFrontierTests/ChromaticNumber.lean | 14 +- CslibFrontierTests/CliqueCover.lean | 10 +- CslibFrontierTests/ColorCoding.lean | 2 +- CslibFrontierTests/ContractionMatching.lean | 10 +- CslibFrontierTests/Copositivity.lean | 6 +- CslibFrontierTests/DominatingSet.lean | 14 +- CslibFrontierTests/FeedbackArcSet.lean | 10 +- CslibFrontierTests/FeedbackVertexSet.lean | 23 +- CslibFrontierTests/FineGrained/Diameter.lean | 11 +- .../FineGrained/GraphProblems.lean | 20 +- CslibFrontierTests/GapGraphsGeography.lean | 20 +- CslibFrontierTests/InteractiveProofs.lean | 15 +- CslibFrontierTests/KCenterHardness.lean | 4 +- CslibFrontierTests/KarpReductions.lean | 38 +- CslibFrontierTests/LongPathHardness.lean | 4 +- CslibFrontierTests/LongPathKernel.lean | 8 +- CslibFrontierTests/NPCompleteGraphs.lean | 74 +- .../NPCompleteGraphs/Hamiltonian.lean | 72 +- CslibFrontierTests/NPCompleteGraphs/TSP.lean | 17 +- .../NPCompleteGraphs/Undirected.lean | 38 +- CslibFrontierTests/NPCompleteness.lean | 7 +- CslibFrontierTests/NPWitness.lean | 8 +- CslibFrontierTests/OrthogonalPath.lean | 27 +- CslibFrontierTests/Parameterized.lean | 2 +- CslibFrontierTests/PartialMCSP.lean | 10 +- CslibFrontierTests/PlanarCompile.lean | 2 +- .../PlanarNonSelfDualRotation.lean | 4 +- CslibFrontierTests/RotationCertificate.lean | 40 +- CslibFrontierTests/RotationDual.lean | 33 +- CslibFrontierTests/RotationSystem.lean | 10 +- CslibFrontierTests/SATIncidence.lean | 12 +- CslibFrontierTests/SpanningTrees.lean | 26 +- CslibFrontierTests/TSPHeldKarp.lean | 4 +- .../VertexCoverApproximation.lean | 12 +- CslibFrontierTests/VertexCoverKernel.lean | 12 +- CslibFrontierTests/WHierarchy.lean | 8 +- guide/CslibFrontierGuide/NPCompleteness.lean | 15 +- scripts/projection_chains_baseline.json | 826 +++++++++--------- 97 files changed, 1963 insertions(+), 1837 deletions(-) diff --git a/CslibFrontier/Computability/Complexity/Classes/Space/GeneralizedGeography/Membership.lean b/CslibFrontier/Computability/Complexity/Classes/Space/GeneralizedGeography/Membership.lean index 2260227077..310eab71ff 100644 --- a/CslibFrontier/Computability/Complexity/Classes/Space/GeneralizedGeography/Membership.lean +++ b/CslibFrontier/Computability/Complexity/Classes/Space/GeneralizedGeography/Membership.lean @@ -194,20 +194,20 @@ private theorem choice_indicator (B i : ℕ) : choice (indicator B i) = i := by simp /-- The arc of index `i`, or `(0, 0)` past the last arc. -/ -def arc (G : Graph) (i : ℕ) : ℕ × ℕ := G.2.getD i (0, 0) +def arc (G : Graph) (i : ℕ) : ℕ × ℕ := G.edges.getD i (0, 0) /-- The arc of index `i` is a legal move with the token at `v` and the vertices `vis` visited: it leaves `v` for an unvisited declared vertex. -/ def legal (G : Graph) (vis : List ℕ) (v i : ℕ) : Bool := - decide (i < G.2.length) && decide ((arc G i).1 = v) && decide ((arc G i).2 ∈ G.1) && + decide (i < G.edges.length) && decide ((arc G i).1 = v) && decide ((arc G i).2 ∈ G.vertices) && !decide ((arc G i).2 ∈ vis) -private theorem arc_eq {G : Graph} {i : ℕ} (hi : i < G.2.length) : arc G i = G.2[i] := +private theorem arc_eq {G : Graph} {i : ℕ} (hi : i < G.edges.length) : arc G i = G.edges[i] := List.getD_eq_getElem _ _ hi theorem legal_eq_true_iff {G : Graph} {vis : List ℕ} {v i : ℕ} : - legal G vis v i = true ↔ ∃ h : i < G.2.length, G.2[i].1 = v ∧ G.2[i].2 ∈ G.1 ∧ - G.2[i].2 ∉ vis := by + legal G vis v i = true ↔ ∃ h : i < G.edges.length, + G.edges[i].1 = v ∧ G.edges[i].2 ∈ G.vertices ∧ G.edges[i].2 ∉ vis := by unfold legal simp only [Bool.and_eq_true, decide_eq_true_eq, Bool.not_eq_true', decide_eq_false_iff_not] constructor @@ -220,9 +220,9 @@ theorem legal_eq_true_iff {G : Graph} {vis : List ℕ} {v i : ℕ} : /-- Legal arcs are the moves of Generalized Geography. -/ theorem exists_legal_iff {G : Graph} {vis : List ℕ} {v : ℕ} {R : ℕ → Prop} : - (∃ x : List Bool, x.length = G.2.length ∧ legal G vis v (choice x) = true ∧ + (∃ x : List Bool, x.length = G.edges.length ∧ legal G vis v (choice x) = true ∧ R (arc G (choice x)).2) ↔ - ∃ w, (v, w) ∈ G.2 ∧ w ∈ G.1 ∧ w ∉ vis ∧ R w := by + ∃ w, (v, w) ∈ G.edges ∧ w ∈ G.vertices ∧ w ∉ vis ∧ R w := by constructor · rintro ⟨x, -, hlegal, hR⟩ obtain ⟨hi, h₁, h₂, h₃⟩ := legal_eq_true_iff.1 hlegal @@ -232,7 +232,7 @@ theorem exists_legal_iff {G : Graph} {vis : List ℕ} {v : ℕ} {R : ℕ → Pro exact List.getElem_mem hi · rintro ⟨w, hvw, hw, hwvis, hR⟩ obtain ⟨i, hi, hiw⟩ := List.getElem_of_mem hvw - refine ⟨indicator G.2.length i, length_indicator hi, ?_, ?_⟩ + refine ⟨indicator G.edges.length i, length_indicator hi, ?_, ?_⟩ · rw [choice_indicator] exact legal_eq_true_iff.2 ⟨hi, by rw [hiw], by rw [hiw]; exact hw, by rw [hiw]; exact hwvis⟩ · rw [choice_indicator, arc_eq hi, hiw] @@ -240,9 +240,9 @@ theorem exists_legal_iff {G : Graph} {vis : List ℕ} {v : ℕ} {R : ℕ → Pro /-- The second player's legal arcs are the moves of Generalized Geography. -/ theorem forall_legal_iff {G : Graph} {vis : List ℕ} {v : ℕ} {R : ℕ → Prop} : - (∀ y : List Bool, y.length = G.2.length → legal G vis v (choice y) = true → + (∀ y : List Bool, y.length = G.edges.length → legal G vis v (choice y) = true → R (arc G (choice y)).2) ↔ - ∀ w, (v, w) ∈ G.2 → w ∈ G.1 → w ∉ vis → R w := by + ∀ w, (v, w) ∈ G.edges → w ∈ G.vertices → w ∉ vis → R w := by have h := exists_legal_iff (G := G) (vis := vis) (v := v) (R := fun w => ¬R w) constructor · intro hy w hvw hw hwvis @@ -313,14 +313,14 @@ Geography from the position (if the first player is to move) or loses it (if the to move). -/ theorem alternation_foldl_step (G : Graph) : ∀ (r : ℕ) (first : Bool) (vis : List ℕ) (v : ℕ), - (G.1.toFinset \ vis.toFinset).card < r → - (Alternation true (List.replicate (2 * G.2.length * r) 1) (fun z => - ((blocks G.2.length r z).foldl (step G G.2.length) (none, first, vis, v)).1.getD false = - true) ↔ + (G.vertices.toFinset \ vis.toFinset).card < r → + (Alternation true (List.replicate (2 * G.edges.length * r) 1) (fun z => + ((blocks G.edges.length r z).foldl (step G G.edges.length) + (none, first, vis, v)).1.getD false = true) ↔ (first = true ↔ G.GeographyWins vis v)) | 0, _, _, _, h => absurd h (Nat.not_lt_zero _) | r + 1, first, vis, v, hr => by - set B := G.2.length + set B := G.edges.length -- the outcome after the first block have hP (z : List Bool) : ((blocks B (r + 1) z).foldl (step G B) (none, first, vis, v)).1.getD false = true ↔ @@ -334,7 +334,7 @@ theorem alternation_foldl_step (G : Graph) : ((!first) = true ↔ G.GeographyWins ((arc G (choice bits)).2 :: vis) (arc G (choice bits)).2) := by obtain ⟨hi, h₁, h₂, h₃⟩ := legal_eq_true_iff.1 hl - have hw : (arc G (choice bits)).2 ∈ G.1 := by rwa [arc_eq hi] + have hw : (arc G (choice bits)).2 ∈ G.vertices := by rwa [arc_eq hi] have hwvis : (arc G (choice bits)).2 ∉ vis := by rwa [arc_eq hi] rw [next, ite_eq_left hl] exact alternation_foldl_step G r _ _ _ (by have := Graph.card_sdiff_cons_lt hw hwvis; omega) @@ -392,12 +392,12 @@ def verifier (x : GeneralizedGeographyInstance × List Bool) : Bool := /-- The arcs and the declared vertex entries of a graph are no more than the length of the encoded instance. -/ private theorem length_le_length_encode (x : GeneralizedGeographyInstance) : - x.graph.2.length ≤ (encGeneralizedGeography x).length ∧ - x.graph.1.length ≤ (encGeneralizedGeography x).length := by - have h₁ := length_le_length_encList (encPair encNat encNat) x.graph.2 - have h₂ := length_le_length_encList encNat x.graph.1 + x.graph.edges.length ≤ (encGeneralizedGeography x).length ∧ + x.graph.vertices.length ≤ (encGeneralizedGeography x).length := by + have h₁ := length_le_length_encList (encPair encNat encNat) x.graph.edges + have h₂ := length_le_length_encList encNat x.graph.vertices rw [encGeneralizedGeography_apply] - enc_len [encGraph] + enc_len [encGraph_apply] /-- **The replay is a game verifier of Generalized Geography**, with `2 * (n + 1) ^ 2` single-bit moves on instances of encoded length `n`. -/ @@ -408,14 +408,14 @@ theorem isGameVerifier_verifier : set n := (encGeneralizedGeography ⟨G, s⟩).length obtain ⟨hB, hV⟩ := length_le_length_encode ⟨G, s⟩ dsimp only at hB hV - have hle : 2 * G.2.length * (G.1.length + 1) ≤ + have hle : 2 * G.edges.length * (G.vertices.length + 1) ≤ 2 * ((Polynomial.X + 1) ^ 2 : Polynomial ℕ).eval n := by simp only [Polynomial.eval_pow, Polynomial.eval_add, Polynomial.eval_X, Polynomial.eval_one] rw [Nat.mul_assoc] refine Nat.mul_le_mul_left 2 ?_ rw [sq] exact Nat.mul_le_mul (by omega) (by omega) - have hcount : (G.1.toFinset \ [s].toFinset).card < G.1.length + 1 := + have hcount : (G.vertices.toFinset \ [s].toFinset).card < G.vertices.length + 1 := Nat.lt_succ_of_le ((Finset.card_le_card Finset.sdiff_subset).trans (List.toFinset_card_le _)) rw [mem_GeneralizedGeography, ← Nat.add_sub_cancel' hle, List.replicate_add, alternation_append_iff fun z => by rw [sum_replicate_one, verifier, verifier, blocks_take]] @@ -453,10 +453,10 @@ private theorem computableInPolyTime_next : set encQ := encPair (encPair encGraph encPlay) (encList encBool) have hG : ComputableInPolyTime (fun p : (Graph × Play) × List Bool => p.1.1) encQ encGraph := by fun_prop - have hV : ComputableInPolyTime (fun p : (Graph × Play) × List Bool => p.1.1.1) encQ - (encList encNat) := (fst _ _).comp hG - have hE : ComputableInPolyTime (fun p : (Graph × Play) × List Bool => p.1.1.2) encQ - (encList (encPair encNat encNat)) := (snd _ _).comp hG + have hV : ComputableInPolyTime (fun p : (Graph × Play) × List Bool => p.1.1.vertices) encQ + (encList encNat) := encGraph_vertices_polyTime hG + have hE : ComputableInPolyTime (fun p : (Graph × Play) × List Bool => p.1.1.edges) encQ + (encList (encPair encNat encNat)) := encGraph_edges_polyTime hG have hfirst : ComputableInPolyTime (fun p : (Graph × Play) × List Bool => p.1.2.2.1) encQ encBool := by fun_prop have hvis : ComputableInPolyTime (fun p : (Graph × Play) × List Bool => p.1.2.2.2.1) @@ -465,7 +465,7 @@ private theorem computableInPolyTime_next : encQ encNat := by fun_prop have hi : ComputableInPolyTime (fun p : (Graph × Play) × List Bool => choice p.2) encQ encUnary := idxOfWithInput (snd _ _) (const true) - have hN : ComputableInPolyTime (fun p : (Graph × Play) × List Bool => p.1.1.2.length) + have hN : ComputableInPolyTime (fun p : (Graph × Play) × List Bool => p.1.1.edges.length) encQ encUnary := (length_list _).comp hE have harc : ComputableInPolyTime (fun p : (Graph × Play) × List Bool => arc p.1.1 (choice p.2)) encQ @@ -493,13 +493,13 @@ private theorem computableInPolyTime_next : (((optionSome encBool).comp (bool_not.comp hfirst)).prod (hfirst.prod (hvis.prod hv))) /-- A target of an arc is no longer in binary than the encoded graph. -/ -private theorem length_encNat_le_of_mem {G : Graph} {e : ℕ × ℕ} (he : e ∈ G.2) : +private theorem length_encNat_le_of_mem {G : Graph} {e : ℕ × ℕ} (he : e ∈ G.edges) : (encNat e.2).length ≤ (encGraph G).length := by - have h₁ := sum_length_le_length_encList (encPair encNat encNat) G.2 + have h₁ := sum_length_le_length_encList (encPair encNat encNat) G.edges have h₂ := List.le_sum_of_mem (List.mem_map_of_mem (f := fun a => (encPair encNat encNat a).length) he) have h₃ := length_le_encPair_right encNat encNat e - enc_len [encGraph] at h₁ h₂ h₃ ⊢ + enc_len [encGraph_apply] at h₁ h₂ h₃ ⊢ /-- **A block adds at most one vertex to a play.** -/ theorem length_encPlay_step_le (G : Graph) (B : ℕ) (st : Play) (block : List Bool) : @@ -514,7 +514,7 @@ theorem length_encPlay_step_le (G : Graph) (B : ℕ) (st : Play) (block : List B split_ifs with hl · obtain ⟨hi, -, -, -⟩ := legal_eq_true_iff.1 hl have hw := length_encNat_le_of_mem (G := G) - (List.getElem_mem (l := G.2) (n := choice (moverBits B first block)) hi) + (List.getElem_mem (l := G.edges) (n := choice (moverBits B first block)) hi) rw [← arc_eq hi] at hw enc_len [encList_cons] at hw ⊢ · enc_len diff --git a/CslibFrontier/Computability/Complexity/Classes/Space/GeneralizedGeography/Reduction.lean b/CslibFrontier/Computability/Complexity/Classes/Space/GeneralizedGeography/Reduction.lean index b755c362c7..25bba94f5d 100644 --- a/CslibFrontier/Computability/Complexity/Classes/Space/GeneralizedGeography/Reduction.lean +++ b/CslibFrontier/Computability/Complexity/Classes/Space/GeneralizedGeography/Reduction.lean @@ -121,14 +121,14 @@ def formulaArcs (xs : List ℕ) (ψ : CNF ℕ) : List (ℕ × ℕ) := /-- **The graph of a formula**: its arcs, and as vertices the top of the first diamond and the targets of the arcs. -/ def formulaGraph (xs : List ℕ) (ψ : CNF ℕ) : Graph := - (topNode 0 :: (formulaArcs xs ψ).map Prod.snd, formulaArcs xs ψ) + ⟨topNode 0 :: (formulaArcs xs ψ).map Prod.snd, formulaArcs xs ψ⟩ /-- **The reduction from `TQBFCNF`**: the graph of the formula, from the top of the first diamond, when the formula is closed, and a graph without arcs otherwise. -/ def reduce (x : List (ℕ × ℕ) × CNF ℕ) : GeneralizedGeographyInstance := if x.2.all (fun c => c.all fun l => decide (l.1 ∈ QBF.roundVars x.1)) then ⟨formulaGraph (QBF.roundVars x.1) x.2, topNode 0⟩ - else ⟨([0], []), 0⟩ + else ⟨⟨[0], []⟩, 0⟩ /-! ### The arcs -/ @@ -322,6 +322,7 @@ theorem geographyWins_formulaGraph_iff {vis : List ℕ} {v : ℕ} : (formulaGraph xs ψ).GeographyWins vis v ↔ ∃ w, (v, w) ∈ formulaArcs xs ψ ∧ w ∉ vis ∧ ¬(formulaGraph xs ψ).GeographyWins (w :: vis) w := by rw [Graph.geographyWins_iff] + dsimp only [formulaGraph] refine exists_congr fun w => ⟨fun ⟨h, _, h'⟩ => ⟨h, h'⟩, fun ⟨h, h'⟩ => ⟨h, ?_, h'⟩⟩ exact List.mem_cons_of_mem _ (List.mem_map_of_mem (f := Prod.snd) h) diff --git a/CslibFrontier/Computability/Complexity/FineGrained/ContractionMatching.lean b/CslibFrontier/Computability/Complexity/FineGrained/ContractionMatching.lean index 71ce6da91b..9be9c9d949 100644 --- a/CslibFrontier/Computability/Complexity/FineGrained/ContractionMatching.lean +++ b/CslibFrontier/Computability/Complexity/FineGrained/ContractionMatching.lean @@ -282,11 +282,12 @@ theorem exists_perfectMatchingCounter (d : ℕ) : (∀ G : Graph, (∀ v, Nat.card (G.induced.neighborSet v) ≤ d) → count G = Nat.card {M : G.induced.Subgraph // M.IsPerfectMatching}) ∧ ComputableInO count encGraph encNat - (fun G => 2 ^ (A * (G.1.toFinset.card + 1) / Nat.sqrt (Nat.log 2 (G.1.toFinset.card + 2))) * + (fun G => 2 ^ (A * (G.vertices.toFinset.card + 1) / + Nat.sqrt (Nat.log 2 (G.vertices.toFinset.card + 2))) * ((encGraph G).length + 1) ^ e) (fun G => ((encGraph G).length + 1) ^ b) := by obtain ⟨count, A, e, b, hcount, hc⟩ := exists_exactlyOneCounter d 1 - (size := fun G : Graph => G.1.toFinset.card) + (size := fun G : Graph => G.vertices.toFinset.card) Graph.computableInPolyTime_perfectMatchingInstance (fun G => Graph.count_vars_perfectMatchingInstance G) fun G => by rw [Graph.length_perfectMatchingInstance] diff --git a/CslibFrontier/Computability/Complexity/FineGrained/Diameter.lean b/CslibFrontier/Computability/Complexity/FineGrained/Diameter.lean index edc7344500..66c8202d85 100644 --- a/CslibFrontier/Computability/Complexity/FineGrained/Diameter.lean +++ b/CslibFrontier/Computability/Complexity/FineGrained/Diameter.lean @@ -205,10 +205,10 @@ each vector and each coordinate, the hubs `0` and `1` and the padding vertices ` each other, to the coordinates and to the padding vertices, and a vector to the coordinates where it holds `true`. -/ def toDiameter (p : List (List Bool) × List (List Bool)) : Graph := - (vecsOut bitVertices (endVertices false) 0 p.1 ++ + ⟨vecsOut bitVertices (endVertices false) 0 p.1 ++ vecsOut bitVertices (endVertices true) p.1.length p.2 ++ constVertices, vecsOut bitEdges (endEdges false) 0 p.1 ++ - vecsOut bitEdges (endEdges true) p.1.length p.2 ++ constEdges) + vecsOut bitEdges (endEdges true) p.1.length p.2 ++ constEdges⟩ /-! ### Vertices and edges of the gadget -/ @@ -217,10 +217,10 @@ section Gadget variable {p : List (List Bool) × List (List Bool)} /-- The vertices of the gadget: four constants, coordinates, and vectors. -/ -theorem mem_vertices_toDiameter {v : ℕ} (hv : v ∈ (toDiameter p).1) : +theorem mem_vertices_toDiameter {v : ℕ} (hv : v ∈ (toDiameter p).vertices) : v = 0 ∨ v = 1 ∨ v = 2 ∨ v = 3 ∨ - (∃ i, v = coordVertex i ∧ (0, coordVertex i) ∈ (toDiameter p).2 ∧ - (1, coordVertex i) ∈ (toDiameter p).2) ∨ + (∃ i, v = coordVertex i ∧ (0, coordVertex i) ∈ (toDiameter p).edges ∧ + (1, coordVertex i) ∈ (toDiameter p).edges) ∨ (∃ k < p.1.length, v = vecVertex k) ∨ (∃ j < p.2.length, v = vecVertex (p.1.length + j)) := by simp only [toDiameter, List.mem_append, mem_vecsOut, bitVertices, endVertices, constVertices, @@ -237,7 +237,7 @@ theorem mem_vertices_toDiameter {v : ℕ} (hv : v ∈ (toDiameter p).1) : · omega /-- The edges of the gadget. -/ -theorem mem_edges_toDiameter {e : ℕ × ℕ} (he : e ∈ (toDiameter p).2) : +theorem mem_edges_toDiameter {e : ℕ × ℕ} (he : e ∈ (toDiameter p).edges) : e ∈ constEdges ∨ (∃ i, e = (0, coordVertex i) ∨ e = (1, coordVertex i)) ∨ (∃ k < p.1.length, e = (0, vecVertex k)) ∨ (∃ j < p.2.length, e = (1, vecVertex (p.1.length + j))) ∨ @@ -270,7 +270,7 @@ theorem mem_edges_toDiameter {e : ℕ × ℕ} (he : e ∈ (toDiameter p).2) : · exact Or.inl h /-- An edge starts at a hub or at a coordinate. -/ -theorem fst_mem_edges_toDiameter {a b : ℕ} (h : (a, b) ∈ (toDiameter p).2) : +theorem fst_mem_edges_toDiameter {a b : ℕ} (h : (a, b) ∈ (toDiameter p).edges) : a = 0 ∨ a = 1 ∨ ∃ i, a = coordVertex i := by rcases mem_edges_toDiameter h with h | ⟨i, h | h⟩ | ⟨k, -, h⟩ | ⟨j, -, h⟩ | ⟨k, -, i, -, -, h⟩ | ⟨j, -, i, -, -, h⟩ @@ -283,7 +283,7 @@ theorem fst_mem_edges_toDiameter {a b : ℕ} (h : (a, b) ∈ (toDiameter p).2) : /-- The neighbours of a vector of the first list: the hub `0` and its coordinates. -/ theorem eq_of_mem_edges_toDiameter_vec₁ {w k : ℕ} (hk : k < p.1.length) - (h : (w, vecVertex k) ∈ (toDiameter p).2 ∨ (vecVertex k, w) ∈ (toDiameter p).2) : + (h : (w, vecVertex k) ∈ (toDiameter p).edges ∨ (vecVertex k, w) ∈ (toDiameter p).edges) : w = 0 ∨ ∃ i, ∃ hi : i < p.1[k].length, p.1[k][i] = true ∧ w = coordVertex i := by rcases h with h | h · rcases mem_edges_toDiameter h with h | ⟨i, h | h⟩ | ⟨k', hk', h⟩ | ⟨j, hj, h⟩ | @@ -307,8 +307,8 @@ theorem eq_of_mem_edges_toDiameter_vec₁ {w k : ℕ} (hk : k < p.1.length) /-- The neighbours of a vector of the second list: the hub `1` and its coordinates. -/ theorem eq_of_mem_edges_toDiameter_vec₂ {w j : ℕ} (hj : j < p.2.length) - (h : (w, vecVertex (p.1.length + j)) ∈ (toDiameter p).2 ∨ - (vecVertex (p.1.length + j), w) ∈ (toDiameter p).2) : + (h : (w, vecVertex (p.1.length + j)) ∈ (toDiameter p).edges ∨ + (vecVertex (p.1.length + j), w) ∈ (toDiameter p).edges) : w = 1 ∨ ∃ i, ∃ hi : i < p.2[j].length, p.2[j][i] = true ∧ w = coordVertex i := by rcases h with h | h · rcases mem_edges_toDiameter h with h | ⟨i, h | h⟩ | ⟨k', hk', h⟩ | ⟨j', hj', h⟩ | @@ -331,60 +331,61 @@ theorem eq_of_mem_edges_toDiameter_vec₂ {w j : ℕ} (hj : j < p.2.length) simp only [vecVertex, coordVertex] at h <;> omega /-- The constant vertices are vertices of the gadget. -/ -theorem const_mem_toDiameter {v : ℕ} (hv : v ∈ constVertices) : v ∈ (toDiameter p).1 := +theorem const_mem_toDiameter {v : ℕ} (hv : v ∈ constVertices) : v ∈ (toDiameter p).vertices := List.mem_append_right _ hv /-- The constant edges are edges of the gadget. -/ -theorem const_mem_edges_toDiameter {e : ℕ × ℕ} (he : e ∈ constEdges) : e ∈ (toDiameter p).2 := +theorem const_mem_edges_toDiameter {e : ℕ × ℕ} (he : e ∈ constEdges) : e ∈ (toDiameter p).edges := List.mem_append_right _ he /-- The hub `0` is adjacent to the vectors of the first list. -/ theorem vec_mem_edges_toDiameter₁ {k : ℕ} (hk : k < p.1.length) : - (0, vecVertex k) ∈ (toDiameter p).2 := by + (0, vecVertex k) ∈ (toDiameter p).edges := by simp only [toDiameter, List.mem_append, mem_vecsOut] exact Or.inl (Or.inl ⟨k, hk, Or.inr (by simp [endEdges, sideVertex])⟩) /-- The hub `1` is adjacent to the vectors of the second list. -/ theorem vec_mem_edges_toDiameter₂ {j : ℕ} (hj : j < p.2.length) : - (1, vecVertex (p.1.length + j)) ∈ (toDiameter p).2 := by + (1, vecVertex (p.1.length + j)) ∈ (toDiameter p).edges := by simp only [toDiameter, List.mem_append, mem_vecsOut] exact Or.inl (Or.inr ⟨j, hj, Or.inr (by simp [endEdges, sideVertex])⟩) /-- A vector of the first list is adjacent to the coordinates where it holds `true`. -/ theorem coord_mem_edges_toDiameter₁ {k i : ℕ} (hk : k < p.1.length) (hi : i < p.1[k].length) - (hb : p.1[k][i] = true) : (coordVertex i, vecVertex k) ∈ (toDiameter p).2 := by + (hb : p.1[k][i] = true) : (coordVertex i, vecVertex k) ∈ (toDiameter p).edges := by simp only [toDiameter, List.mem_append, mem_vecsOut] exact Or.inl (Or.inl ⟨k, hk, Or.inl ⟨i, hi, by simp [bitEdges, hb]⟩⟩) /-- A vector of the second list is adjacent to the coordinates where it holds `true`. -/ theorem coord_mem_edges_toDiameter₂ {j i : ℕ} (hj : j < p.2.length) (hi : i < p.2[j].length) (hb : p.2[j][i] = true) : - (coordVertex i, vecVertex (p.1.length + j)) ∈ (toDiameter p).2 := by + (coordVertex i, vecVertex (p.1.length + j)) ∈ (toDiameter p).edges := by simp only [toDiameter, List.mem_append, mem_vecsOut] exact Or.inl (Or.inr ⟨j, hj, Or.inl ⟨i, hi, by simp [bitEdges, hb]⟩⟩) /-- The coordinates of the vectors of the first list are vertices of the gadget. -/ theorem coord_mem_toDiameter₁ {k i : ℕ} (hk : k < p.1.length) (hi : i < p.1[k].length) : - coordVertex i ∈ (toDiameter p).1 := by + coordVertex i ∈ (toDiameter p).vertices := by simp only [toDiameter, List.mem_append, mem_vecsOut] exact Or.inl (Or.inl ⟨k, hk, Or.inl ⟨i, hi, by simp [bitVertices]⟩⟩) /-- The vectors of the first list are vertices of the gadget. -/ -theorem vec_mem_toDiameter₁ {k : ℕ} (hk : k < p.1.length) : vecVertex k ∈ (toDiameter p).1 := by +theorem vec_mem_toDiameter₁ {k : ℕ} (hk : k < p.1.length) : + vecVertex k ∈ (toDiameter p).vertices := by simp only [toDiameter, List.mem_append, mem_vecsOut] exact Or.inl (Or.inl ⟨k, hk, Or.inr (by simp [endVertices])⟩) /-- The vectors of the second list are vertices of the gadget. -/ theorem vec_mem_toDiameter₂ {j : ℕ} (hj : j < p.2.length) : - vecVertex (p.1.length + j) ∈ (toDiameter p).1 := by + vecVertex (p.1.length + j) ∈ (toDiameter p).vertices := by simp only [toDiameter, List.mem_append, mem_vecsOut] exact Or.inl (Or.inr ⟨j, hj, Or.inr (by simp [endVertices])⟩) /-! ### Distances in the gadget -/ /-- Two vertices joined by an edge, or equal, are at distance at most one. -/ -private theorem edist_le_one_of {u v : {v // v ∈ (toDiameter p).1}} - (h : u.1 = v.1 ∨ (u.1, v.1) ∈ (toDiameter p).2 ∨ (v.1, u.1) ∈ (toDiameter p).2) : +private theorem edist_le_one_of {u v : {v // v ∈ (toDiameter p).vertices}} + (h : u.1 = v.1 ∨ (u.1, v.1) ∈ (toDiameter p).edges ∨ (v.1, u.1) ∈ (toDiameter p).edges) : (toDiameter p).induced.edist u v ≤ 1 := by rw [SimpleGraph.edist_le_one_iff_adj_or_eq] by_cases huv : u.1 = v.1 @@ -396,7 +397,7 @@ private theorem edist_le_one_of {u v : {v // v ∈ (toDiameter p).1}} · exact Or.inr h /-- Two vertices with a common neighbour are at distance at most two. -/ -private theorem edist_le_two_of {u v w : {v // v ∈ (toDiameter p).1}} +private theorem edist_le_two_of {u v w : {v // v ∈ (toDiameter p).vertices}} (hu : (toDiameter p).induced.edist u w ≤ 1) (hv : (toDiameter p).induced.edist w v ≤ 1) : (toDiameter p).induced.edist u v ≤ 2 := (SimpleGraph.edist_triangle).trans (by @@ -404,7 +405,7 @@ private theorem edist_le_two_of {u v w : {v // v ∈ (toDiameter p).1}} _ = 2 := one_add_one_eq_two) /-- Two vertices joined by a path of three edges are at distance at most three. -/ -private theorem edist_le_three_of {u v w x : {v // v ∈ (toDiameter p).1}} +private theorem edist_le_three_of {u v w x : {v // v ∈ (toDiameter p).vertices}} (hu : (toDiameter p).induced.edist u w ≤ 1) (hw : (toDiameter p).induced.edist w x ≤ 1) (hv : (toDiameter p).induced.edist x v ≤ 1) : (toDiameter p).induced.edist u v ≤ 3 := @@ -416,23 +417,23 @@ private theorem edist_le_three_of {u v w x : {v // v ∈ (toDiameter p).1}} _ = 3 := by norm_num) /-- The hub `0` of the first list, a vertex of the gadget. -/ -def toDiameterHub₀ (p : List (List Bool) × List (List Bool)) : {v // v ∈ (toDiameter p).1} := +def toDiameterHub₀ (p : List (List Bool) × List (List Bool)) : {v // v ∈ (toDiameter p).vertices} := ⟨0, const_mem_toDiameter (by simp [constVertices])⟩ /-- The hub `1` of the second list, a vertex of the gadget. -/ -def toDiameterHub₁ (p : List (List Bool) × List (List Bool)) : {v // v ∈ (toDiameter p).1} := +def toDiameterHub₁ (p : List (List Bool) × List (List Bool)) : {v // v ∈ (toDiameter p).vertices} := ⟨1, const_mem_toDiameter (by simp [constVertices])⟩ /-- **The vertices of the gadget**: every vertex is a neighbour of both hubs `0` and `1`, or a vector of the first list, adjacent to `0`, or a vector of the second list, adjacent to `1`. -/ -theorem classify_toDiameter (u : {v // v ∈ (toDiameter p).1}) : +theorem classify_toDiameter (u : {v // v ∈ (toDiameter p).vertices}) : ((toDiameter p).induced.edist (toDiameterHub₀ p) u ≤ 1 ∧ (toDiameter p).induced.edist (toDiameterHub₁ p) u ≤ 1) ∨ (∃ k < p.1.length, u.1 = vecVertex k ∧ (toDiameter p).induced.edist (toDiameterHub₀ p) u ≤ 1) ∨ (∃ j < p.2.length, u.1 = vecVertex (p.1.length + j) ∧ (toDiameter p).induced.edist (toDiameterHub₁ p) u ≤ 1) := by - have hc {a b : ℕ} (h : (a, b) ∈ constEdges) : (a, b) ∈ (toDiameter p).2 := + have hc {a b : ℕ} (h : (a, b) ∈ constEdges) : (a, b) ∈ (toDiameter p).edges := const_mem_edges_toDiameter h obtain ⟨v, hv⟩ := u rcases mem_vertices_toDiameter hv with rfl | rfl | rfl | rfl | ⟨i, rfl, h₀, h₁⟩ | @@ -469,12 +470,12 @@ theorem exists_getElem_of_not_orthogonal {a b : List Bool} (h : ¬OV.Orthogonal · rwa [List.getD_eq_getElem _ _ hib] at hb /-- **Diameter at most three**: through the hubs `0` and `1`. -/ -theorem edist_toDiameter_le_three (u v : {v // v ∈ (toDiameter p).1}) : +theorem edist_toDiameter_le_three (u v : {v // v ∈ (toDiameter p).vertices}) : (toDiameter p).induced.edist u v ≤ 3 := by have h01 : (toDiameter p).induced.edist (toDiameterHub₀ p) (toDiameterHub₁ p) ≤ 1 := edist_le_one_of (Or.inr (Or.inl (const_mem_edges_toDiameter (by simp [constEdges, toDiameterHub₀, toDiameterHub₁])))) - have near (u : {v // v ∈ (toDiameter p).1}) : + have near (u : {v // v ∈ (toDiameter p).vertices}) : (toDiameter p).induced.edist u (toDiameterHub₀ p) ≤ 1 ∨ (toDiameter p).induced.edist u (toDiameterHub₁ p) ≤ 1 := by rcases classify_toDiameter u with ⟨h, -⟩ | ⟨-, -, -, h⟩ | ⟨-, -, -, h⟩ @@ -490,9 +491,9 @@ theorem edist_toDiameter_le_three (u v : {v // v ∈ (toDiameter p).1}) : /-- **Diameter at most two without an orthogonal pair**: a vector of each list share a coordinate holding `true` in both, and all other pairs of vertices share a hub. -/ -theorem edist_toDiameter_le_two (hp : p ∉ OV) (u v : {v // v ∈ (toDiameter p).1}) : +theorem edist_toDiameter_le_two (hp : p ∉ OV) (u v : {v // v ∈ (toDiameter p).vertices}) : (toDiameter p).induced.edist u v ≤ 2 := by - have cross {u v : {v // v ∈ (toDiameter p).1}} {k j : ℕ} (hk : k < p.1.length) + have cross {u v : {v // v ∈ (toDiameter p).vertices}} {k j : ℕ} (hk : k < p.1.length) (hj : j < p.2.length) (hu : u.1 = vecVertex k) (hv : v.1 = vecVertex (p.1.length + j)) : (toDiameter p).induced.edist u v ≤ 2 := by have hno : ¬OV.Orthogonal p.1[k] p.2[j] := fun h => @@ -524,18 +525,19 @@ theorem two_lt_edist_toDiameter {k j : ℕ} (hk : k < p.1.length) (hj : j < p.2. simp only [vecVertex] at this omega · obtain ⟨-, he⟩ := Graph.toSimpleGraph_adj.mp he - have he' : (vecVertex (p.1.length + j), vecVertex k) ∈ (toDiameter p).2 ∨ - (vecVertex k, vecVertex (p.1.length + j)) ∈ (toDiameter p).2 := he.symm + have he' : (vecVertex (p.1.length + j), vecVertex k) ∈ (toDiameter p).edges ∨ + (vecVertex k, vecVertex (p.1.length + j)) ∈ (toDiameter p).edges := he.symm rcases eq_of_mem_edges_toDiameter_vec₁ hk he' with he | ⟨i, -, -, he⟩ <;> simp only [vecVertex, coordVertex] at he <;> omega · refine Set.eq_empty_of_forall_notMem fun w hw => ?_ rw [SimpleGraph.mem_commonNeighbors] at hw obtain ⟨-, hu⟩ := Graph.toSimpleGraph_adj.mp hw.1 obtain ⟨-, hv⟩ := Graph.toSimpleGraph_adj.mp hw.2 - have hu' : (w.1, vecVertex k) ∈ (toDiameter p).2 ∨ (vecVertex k, w.1) ∈ (toDiameter p).2 := + have hu' : (w.1, vecVertex k) ∈ (toDiameter p).edges ∨ + (vecVertex k, w.1) ∈ (toDiameter p).edges := hu.symm - have hv' : (w.1, vecVertex (p.1.length + j)) ∈ (toDiameter p).2 ∨ - (vecVertex (p.1.length + j), w.1) ∈ (toDiameter p).2 := hv.symm + have hv' : (w.1, vecVertex (p.1.length + j)) ∈ (toDiameter p).edges ∨ + (vecVertex (p.1.length + j), w.1) ∈ (toDiameter p).edges := hv.symm rcases eq_of_mem_edges_toDiameter_vec₁ hk hu' with hu | ⟨i, hia, ha, hu⟩ <;> rcases eq_of_mem_edges_toDiameter_vec₂ hj hv' with hv | ⟨i', hib, hb, hv⟩ · omega @@ -557,7 +559,8 @@ theorem one_lt_edist_toDiameter_two_three : refine lt_of_not_ge fun h => ?_ rcases SimpleGraph.edist_le_one_iff_adj_or_eq.mp h with he | he · obtain ⟨-, he⟩ := Graph.toSimpleGraph_adj.mp he - have he' : ((2 : ℕ), (3 : ℕ)) ∈ (toDiameter p).2 ∨ ((3 : ℕ), (2 : ℕ)) ∈ (toDiameter p).2 := he + have he' : ((2 : ℕ), (3 : ℕ)) ∈ (toDiameter p).edges ∨ + ((3 : ℕ), (2 : ℕ)) ∈ (toDiameter p).edges := he rcases he' with he | he <;> rcases fst_mem_edges_toDiameter he with h | h | ⟨i, h⟩ <;> first | omega | (simp only [coordVertex] at h; omega) · have := congrArg Subtype.val he @@ -1098,23 +1101,23 @@ theorem not_decidableInTimeO_diameter_size_of_OVH (hOVH : OVH) {ε : ℝ} (hε : {S : Set Graph} (hS₂ : ∀ G : Graph, G.induced.ediam ≤ 2 → G ∈ S) (hS₃ : ∀ G : Graph, G.induced.ediam = 3 → G ∉ S) : ¬DecidableInTimeO S encGraph fun G => - ((G.1.length + G.2.length : ℕ) : ℝ) ^ (2 - ε) + (encGraph G).length := by + ((G.vertices.length + G.edges.length : ℕ) : ℝ) ^ (2 - ε) + (encGraph G).length := by intro hS refine not_decidableInTimeO_diameter_of_OVH hOVH (lt_min hε one_pos) hS₂ hS₃ (hS.mono 2 (fun G => ?_) (fun G => Real.rpow_nonneg (Nat.cast_nonneg _) _)) - have hle : ((G.1.length + G.2.length : ℕ) : ℝ) ≤ (encGraph G).length := by + have hle : ((G.vertices.length + G.edges.length : ℕ) : ℝ) ≤ (encGraph G).length := by exact_mod_cast G.length_vertices_add_length_edges_le_encGraph have hL : (1 : ℝ) ≤ (encGraph G).length := by have : 0 < (encGraph G).length := by - rw [encGraph, length_encPair] + rw [encGraph_apply, length_encPair] omega exact_mod_cast this have hpos : (0 : ℝ) ≤ ((encGraph G).length : ℝ) ^ (2 - min ε 1) := Real.rpow_nonneg (Nat.cast_nonneg _) _ -- the size term - have hA : ((G.1.length + G.2.length : ℕ) : ℝ) ^ (2 - ε) ≤ + have hA : ((G.vertices.length + G.edges.length : ℕ) : ℝ) ^ (2 - ε) ≤ ((encGraph G).length : ℝ) ^ (2 - min ε 1) + 1 := by - rcases Nat.eq_zero_or_pos (G.1.length + G.2.length) with h0 | h0 + rcases Nat.eq_zero_or_pos (G.vertices.length + G.edges.length) with h0 | h0 · rw [h0, Nat.cast_zero] have h₁ : (0 : ℝ) ^ (2 - ε) ≤ 1 := by by_cases h2 : 2 - ε = 0 @@ -1122,8 +1125,8 @@ theorem not_decidableInTimeO_diameter_size_of_OVH (hOVH : OVH) {ε : ℝ} (hε : · rw [Real.zero_rpow h2] exact zero_le_one linarith - · have h1 : (1 : ℝ) ≤ ((G.1.length + G.2.length : ℕ) : ℝ) := by exact_mod_cast h0 - have h₁ : ((G.1.length + G.2.length : ℕ) : ℝ) ^ (2 - ε) ≤ + · have h1 : (1 : ℝ) ≤ ((G.vertices.length + G.edges.length : ℕ) : ℝ) := by exact_mod_cast h0 + have h₁ : ((G.vertices.length + G.edges.length : ℕ) : ℝ) ^ (2 - ε) ≤ ((encGraph G).length : ℝ) ^ (2 - min ε 1) := (Real.rpow_le_rpow_of_exponent_le h1 (by linarith [min_le_left ε 1])).trans (Real.rpow_le_rpow (by linarith) hle (by linarith [min_le_right ε 1])) diff --git a/CslibFrontier/Computability/Complexity/FineGrained/GraphProblems.lean b/CslibFrontier/Computability/Complexity/FineGrained/GraphProblems.lean index 2a4faefead..002a863607 100644 --- a/CslibFrontier/Computability/Complexity/FineGrained/GraphProblems.lean +++ b/CslibFrontier/Computability/Complexity/FineGrained/GraphProblems.lean @@ -97,7 +97,7 @@ for some `δ > 0`, `ThreeColoring` is not decidable within time `2 ^ (δ (|V| + polynomial in the length of the encoded graph, for graphs with `|V|` declared vertices and `|E|` edges. -/ theorem ETH.not_decidableInSubexpTime_ThreeColoring (h : ETH) : - ¬DecidableInSubexpTime ThreeColoring encGraph fun G => G.1.length + G.2.length := + ¬DecidableInSubexpTime ThreeColoring encGraph fun G => G.vertices.length + G.edges.length := h.not_decidableInSubexpTime_of_reduction SAT.computableInPolyTime_toThreeColoring -- `(24 * 3 + 12) m + 9 ≤ 93 m + 93` (fun φ _ => SAT.toThreeColoring_mem_iff φ) (k := 93) fun φ hφ => by @@ -110,7 +110,7 @@ polynomial in the length of the encoded instance, for graphs with `|V|` declared edges. -/ theorem ETH.not_decidableInSubexpTime_IndependentSet (h : ETH) : ¬DecidableInSubexpTime IndependentSet (encPair encGraph encNat) - fun x => x.1.1.length + x.1.2.length := + fun x => x.1.vertices.length + x.1.edges.length := h.not_decidableInSubexpTime_of_reduction SAT.computableInPolyTime_toSparseIndependentSet (fun φ _ => SAT.toSparseIndependentSet_mem_iff φ) (k := 24) fun φ hφ => by have := SAT.length_toSparseIndependentSet_le hφ @@ -120,7 +120,7 @@ theorem ETH.not_decidableInSubexpTime_IndependentSet (h : ETH) : some `δ > 0`, `IndependentSet` is not decidable within time `2 ^ (δ |V|)` times any polynomial in the length of the encoded instance, for graphs with `|V|` declared vertices. -/ theorem ETH.not_decidableInSubexpTime_IndependentSet_vertices (h : ETH) : - ¬DecidableInSubexpTime IndependentSet (encPair encGraph encNat) fun x => x.1.1.length := + ¬DecidableInSubexpTime IndependentSet (encPair encGraph encNat) fun x => x.1.vertices.length := h.not_decidableInSubexpTime_of_reduction SAT.computableInPolyTime_toSparseIndependentSet (fun φ _ => SAT.toSparseIndependentSet_mem_iff φ) (k := 24) fun φ hφ => by have := SAT.length_toSparseIndependentSet_le hφ @@ -132,7 +132,7 @@ polynomial in the length of the encoded instance, for graphs with `|V|` declared edges. -/ theorem ETH.not_decidableInSubexpTime_VertexCover (h : ETH) : ¬DecidableInSubexpTime VertexCover (encPair encGraph encNat) - fun x => x.1.1.length + x.1.2.length := + fun x => x.1.vertices.length + x.1.edges.length := h.not_decidableInSubexpTime_of_reduction (IndependentSet.computableInPolyTime_toVertexCover.comp SAT.computableInPolyTime_toSparseIndependentSet) @@ -146,7 +146,7 @@ theorem ETH.not_decidableInSubexpTime_VertexCover (h : ETH) : `δ > 0`, `Clique` is not decidable within time `2 ^ (δ |V|)` times any polynomial in the length of the encoded instance, for graphs with `|V|` declared vertices. -/ theorem ETH.not_decidableInSubexpTime_Clique (h : ETH) : - ¬DecidableInSubexpTime Clique (encPair encGraph encNat) fun x => x.1.1.length := + ¬DecidableInSubexpTime Clique (encPair encGraph encNat) fun x => x.1.vertices.length := h.not_decidableInSubexpTime_of_reduction (f := fun φ => ((SAT.toSparseIndependentSet φ).1.compl, (SAT.toSparseIndependentSet φ).2)) ((show ComputableInPolyTime (fun x : Graph × ℕ => (x.1.compl, x.2)) @@ -163,7 +163,7 @@ Chapter 14]: for some `δ > 0`, `HamCycle` is not decidable within time `2 ^ (δ any polynomial in the length of the encoded graph, for graphs with `|V|` declared vertices and `|E|` edges. -/ theorem ETH.not_decidableInSubexpTime_HamCycle (h : ETH) : - ¬DecidableInSubexpTime HamCycle encGraph fun G => G.1.length + G.2.length := + ¬DecidableInSubexpTime HamCycle encGraph fun G => G.vertices.length + G.edges.length := h.not_decidableInSubexpTime_of_reduction SAT.computableInPolyTime_toSparseHamCycle (fun φ _ => SAT.toSparseHamCycle_mem_iff φ) (k := 350) fun φ hφ => by have := SAT.length_toSparseHamCycle_le hφ @@ -174,7 +174,7 @@ theorem ETH.not_decidableInSubexpTime_HamCycle (h : ETH) : `2 ^ (δ (|V| + |E|))` times any polynomial in the length of the encoded graph, for graphs with `|V|` declared vertices and `|E|` arcs. -/ theorem ETH.not_decidableInSubexpTime_DirHamCycle (h : ETH) : - ¬DecidableInSubexpTime DirHamCycle encGraph fun G => G.1.length + G.2.length := + ¬DecidableInSubexpTime DirHamCycle encGraph fun G => G.vertices.length + G.edges.length := h.not_decidableInSubexpTime_of_reduction SAT.computableInPolyTime_toSparseDirHamCycle (fun φ _ => SAT.toSparseDirHamCycle_mem_iff φ) (k := 70) fun φ hφ => by have := SAT.length_toSparseDirHamCycle_le hφ @@ -186,7 +186,7 @@ theorem ETH.not_decidableInSubexpTime_DirHamCycle (h : ETH) : `|V|` declared vertices and `|E|` arcs. -/ theorem ETH.not_decidableInSubexpTime_DirHamPath (h : ETH) : ¬DecidableInSubexpTime DirHamPath encPathInstance - fun x => x.1.1.length + x.1.2.length := + fun x => x.1.vertices.length + x.1.edges.length := h.not_decidableInSubexpTime_of_reduction SAT.computableInPolyTime_toSparseDirHamPath (fun φ _ => SAT.toSparseDirHamPath_mem_iff φ) (k := 70) fun φ hφ => by have := SAT.length_sparseDiamondGraph_le hφ @@ -199,7 +199,7 @@ any polynomial in the length of the encoded instance, for graphs with `|V|` decl `|E|` edges. -/ theorem ETH.not_decidableInSubexpTime_HamPath (h : ETH) : ¬DecidableInSubexpTime HamPath encPathInstance - fun x => x.1.1.length + x.1.2.length := + fun x => x.1.vertices.length + x.1.edges.length := h.not_decidableInSubexpTime_of_reduction (DirHamPath.computableInPolyTime_toHamPath.comp SAT.computableInPolyTime_toSparseDirHamPath) diff --git a/CslibFrontier/Computability/Complexity/FineGrained/PermutationIndependentSet.lean b/CslibFrontier/Computability/Complexity/FineGrained/PermutationIndependentSet.lean index d32d574a16..b2cc27d54f 100644 --- a/CslibFrontier/Computability/Complexity/FineGrained/PermutationIndependentSet.lean +++ b/CslibFrontier/Computability/Complexity/FineGrained/PermutationIndependentSet.lean @@ -303,33 +303,36 @@ theorem BipartitePermutationIndependentSet.mem_iff_exists_satisfiesUndirected /-- Membership in `ThreeColoring` is equivalent to a proper 3-coloring of the vertex positions `Fin G.1.length` with respect to `G.adjBool`. -/ theorem mem_ThreeColoring_iff_fin (G : Graph) : - G ∈ ThreeColoring ↔ ∃ f : Fin G.1.length → Fin 3, - ∀ p q : Fin G.1.length, G.adjBool G.1[p] G.1[q] = true → f p ≠ f q := by + G ∈ ThreeColoring ↔ ∃ f : Fin G.vertices.length → Fin 3, + ∀ p q : Fin G.vertices.length, G.adjBool G.vertices[p] G.vertices[q] = true → f p ≠ f q := by classical rw [ThreeColoring, mem_KColoring_iff] constructor · rintro ⟨c, hc, hadj⟩ - refine ⟨fun p => ⟨c G.1[p], hc G.1[p] (List.getElem_mem p.isLt)⟩, fun p q hpq h => ?_⟩ + refine ⟨fun p => ⟨c G.vertices[p], hc G.vertices[p] (List.getElem_mem p.isLt)⟩, + fun p q hpq h => ?_⟩ rw [Graph.adjBool_eq_true_iff] at hpq - exact hadj G.1[p] (List.getElem_mem p.isLt) G.1[q] (List.getElem_mem q.isLt) hpq + exact hadj G.vertices[p] (List.getElem_mem p.isLt) G.vertices[q] (List.getElem_mem q.isLt) hpq (congrArg Fin.val h) · rintro ⟨f, hf⟩ refine ⟨fun u => - if hu : u ∈ G.1 then (f ⟨G.1.idxOf u, List.idxOf_lt_length_iff.2 hu⟩).val else 0, + if hu : u ∈ G.vertices then + (f ⟨G.vertices.idxOf u, List.idxOf_lt_length_iff.2 hu⟩).val + else 0, fun v hv => by simp only [hv, ↓reduceDIte]; exact (f _).isLt, fun u hu v hv huv h => ?_⟩ simp only [hu, hv, ↓reduceDIte] at h have hu' := List.idxOf_lt_length_iff.2 hu have hv' := List.idxOf_lt_length_iff.2 hv - have hpq : G.adjBool G.1[G.1.idxOf u] G.1[G.1.idxOf v] = true := by + have hpq : G.adjBool G.vertices[G.vertices.idxOf u] G.vertices[G.vertices.idxOf v] = true := by simpa only [List.getElem_idxOf, Graph.adjBool_eq_true_iff] using huv - exact hf ⟨G.1.idxOf u, hu'⟩ ⟨G.1.idxOf v, hv'⟩ hpq (Fin.ext h) + exact hf ⟨G.vertices.idxOf u, hu'⟩ ⟨G.vertices.idxOf v, hv'⟩ hpq (Fin.ext h) /-- **No $2^{o(|V|)}$ algorithm for 3-coloring under ETH** [CyganEtAl2015, Chapter 14]: for some $\delta > 0$, `ThreeColoring` is not decidable within time $2^{\delta |V|}$ times any polynomial in the length of the encoded graph, for graphs with $|V|$ declared vertices. -/ theorem ETH.not_decidableInSubexpTime_ThreeColoring_vertices (h : ETH) : - ¬DecidableInSubexpTime ThreeColoring encGraph fun G => G.1.length := + ¬DecidableInSubexpTime ThreeColoring encGraph fun G => G.vertices.length := h.not_decidableInSubexpTime_of_reduction SAT.computableInPolyTime_toThreeColoring (fun φ _ => SAT.toThreeColoring_mem_iff φ) (k := 93) fun φ hφ => by have := SAT.length_toThreeColoring_le hφ @@ -526,29 +529,30 @@ private def pairConflictEdges (N r p q : ℕ) : List ((ℕ × ℕ) × (ℕ × /-- Conflict edges incident to vertex position $p$ across all adjacent vertex positions $q$. -/ private def vertexConflictEdges (H : Graph) (r p : ℕ) : List ((ℕ × ℕ) × (ℕ × ℕ)) := - let N := H.1.length - ((List.range N).filter fun q => H.adjBool (H.1.getD p 0) (H.1.getD q 0)).flatMap fun q => + let N := H.vertices.length + ((List.range N).filter fun q => + H.adjBool (H.vertices.getD p 0) (H.vertices.getD q 0)).flatMap fun q => pairConflictEdges N r p q /-- Edges forbidding adjacent vertices $p, q < N$ of $H$ from receiving the same color under the block assignments selected by $\pi_1(p / b) = j_1$ and $\pi_2(q / b) = j_2$. -/ private def conflictEdges (H : Graph) (r : ℕ) : List ((ℕ × ℕ) × (ℕ × ℕ)) := - (List.range H.1.length).flatMap fun p => vertexConflictEdges H r p + (List.range H.vertices.length).flatMap fun p => vertexConflictEdges H r p /-- The instance of `BipartitePermutationIndependentSet` corresponding to a graph $H$ and a threshold-table number $r$. -/ def bpisOfColoring (x : Graph × ℕ) : BipartitePermutationGraph := - let N := x.1.1.length + let N := x.1.vertices.length (numBlocks N, consistencyEdges (numBlocks N) ++ invalidBucketEdges N x.2 ++ conflictEdges x.1 x.2) /-- **Parameter bound**: the output parameter $n = (\mathrm{bpisOfColoring}\,(H, r)).1$ satisfies $n \log_2 n \le 10 |V| + 5$. -/ theorem fst_bpisOfColoring_mul_log_le (H : Graph) (r : ℕ) : - (bpisOfColoring (H, r)).1 * Nat.log 2 (bpisOfColoring (H, r)).1 ≤ 10 * H.1.length + 5 := - numBlocks_mul_log_le H.1.length + (bpisOfColoring (H, r)).1 * Nat.log 2 (bpisOfColoring (H, r)).1 ≤ 10 * H.vertices.length + 5 := + numBlocks_mul_log_le H.vertices.length @[simp] theorem bpisOfColoring_empty_zero : - bpisOfColoring (([], []), 0) = (1, [((0, 0), (0, 0))]) := by + bpisOfColoring (⟨[], []⟩, 0) = (1, [((0, 0), (0, 0))]) := by decide /-! ### Correctness of the reduction -/ @@ -685,9 +689,9 @@ if and only if some $r < 2^{\mathrm{searchBits}\, |V|}$ yields a satisfiable ins `bpisOfColoring (H, r)`. -/ theorem mem_ThreeColoring_iff_exists_bpisOfColoring (H : Graph) : H ∈ ThreeColoring ↔ - ∃ r < 2 ^ searchBits H.1.length, + ∃ r < 2 ^ searchBits H.vertices.length, bpisOfColoring (H, r) ∈ BipartitePermutationIndependentSet := by - set N := H.1.length + set N := H.vertices.length set b := blockSize N set B := numAssignments N set n := numBlocks N @@ -985,9 +989,9 @@ private theorem computableInPolyTime_vertexConflictEdges ComputableInPolyTime (fun x => vertexConflictEdges (H x) (r x) (p x)) encIn (encList (encPair (encPair encUnary encUnary) (encPair encUnary encUnary))) := by - have hverts : ComputableInPolyTime (fun x => (H x).1) encIn (encList encNat) := - (fst (encList encNat) (encList (encPair encNat encNat))).comp hH - have hlen : ComputableInPolyTime (fun x => (H x).1.length) encIn encUnary := + have hverts : ComputableInPolyTime (fun x => (H x).vertices) encIn (encList encNat) := + encGraph_vertices_polyTime hH + have hlen : ComputableInPolyTime (fun x => (H x).vertices.length) encIn encUnary := (length_list encNat).comp hverts unfold vertexConflictEdges fun_prop @@ -997,9 +1001,9 @@ private theorem computableInPolyTime_conflictEdges : (encPair encGraph encNat) (encList (encPair (encPair encUnary encUnary) (encPair encUnary encUnary))) := by have hH := fst encGraph encNat - have hlen : ComputableInPolyTime (fun p : Graph × ℕ => p.1.1.length) + have hlen : ComputableInPolyTime (fun p : Graph × ℕ => p.1.vertices.length) (encPair encGraph encNat) encUnary := - (length_list encNat).comp ((fst (encList encNat) (encList (encPair encNat encNat))).comp hH) + (length_list encNat).comp (encGraph_vertices_polyTime hH) exact flatMapWithInput (range.comp hlen) (computableInPolyTime_vertexConflictEdges (hH.comp (fst _ _)) ((snd encGraph encNat).comp (fst _ _)) (snd _ _)) @@ -1009,10 +1013,9 @@ private theorem computableInPolyTime_conflictEdges : theorem computableInPolyTime_bpisOfColoring : ComputableInPolyTime bpisOfColoring (encPair encGraph encNat) encBipartitePermutationGraph := by - have hN : ComputableInPolyTime (fun x : Graph × ℕ => x.1.1.length) + have hN : ComputableInPolyTime (fun x : Graph × ℕ => x.1.vertices.length) (encPair encGraph encNat) encUnary := - (length_list encNat).comp - ((fst (encList encNat) (encList (encPair encNat encNat))).comp (fst encGraph encNat)) + (length_list encNat).comp (encGraph_vertices_polyTime (fst encGraph encNat)) have hn := computableInPolyTime_numBlocks.comp hN have hc1 := computableInPolyTime_consistencyEdges.comp hn have hc2 := computableInPolyTime_invalidBucketEdges.comp (hN.prod (snd encGraph encNat)) @@ -1040,13 +1043,12 @@ theorem computableInPolyTime_bpisOfColoring_binary : encBipartitePermutationGraphBinary := computableInPolyTime_toBinary.comp computableInPolyTime_bpisOfColoring -/-- Computing `searchBits H.1.length` from a graph `H` is polynomial-time in the plain machine -model. -/ +/-- Computing `searchBits H.vertices.length` from a graph `H` is polynomial-time in the plain +machine model. -/ @[fun_prop] theorem computableInPolyTime_searchBits_length : - ComputableInPolyTime (fun H : Graph => searchBits H.1.length) encGraph encUnary := by + ComputableInPolyTime (fun H : Graph => searchBits H.vertices.length) encGraph encUnary := by refine computableInPolyTime_searchBits.comp ?_ - unfold encGraph fun_prop /-! ### Exhaustive search over threshold tables and the ETH lower bound -/ @@ -1058,11 +1060,11 @@ $2^{(1 / M + 10 \delta) |V|} \cdot \mathrm{poly}(|H|)$. -/ theorem decidableInExpTime_ThreeColoring_of_bpis {M : ℕ} (hM : 0 < M) {δ : ℝ} (hδ : 0 ≤ δ) (h : DecidableInExpTime BipartitePermutationIndependentSet encBipartitePermutationGraph (fun G => G.1 * Nat.log 2 G.1) δ) : - DecidableInExpTime ThreeColoring encGraph (fun H => H.1.length) (1 / M + 10 * δ) := by + DecidableInExpTime ThreeColoring encGraph (fun H => H.vertices.length) (1 / M + 10 * δ) := by simpa using h.of_exhaustiveSearch computableInPolyTime_bpisOfColoring computableInPolyTime_searchBits_length mem_ThreeColoring_iff_exists_bpisOfColoring (c := 10) (fun H r => by have := fst_bpisOfColoring_mul_log_le H r; omega) - (fun H => searchBits_le_div_add H.1.length hM) hδ + (fun H => searchBits_le_div_add H.vertices.length hM) hδ end BipartitePermutationIndependentSet @@ -1073,11 +1075,11 @@ if `BipartitePermutationIndependentSet` is decidable in time $2^{o(n \log n)}$, theorem DecidableInSubexpTime.threeColoring_of_bipartitePermutationIndependentSet (h : DecidableInSubexpTime BipartitePermutationIndependentSet encBipartitePermutationGraph fun G => G.1 * Nat.log 2 G.1) : - DecidableInSubexpTime ThreeColoring encGraph fun H => H.1.length := + DecidableInSubexpTime ThreeColoring encGraph fun H => H.vertices.length := h.of_exhaustiveSearch computableInPolyTime_bpisOfColoring computableInPolyTime_searchBits_length mem_ThreeColoring_iff_exists_bpisOfColoring (c := 10) (fun H r => by have := fst_bpisOfColoring_mul_log_le H r; omega) - (fun M hM => ⟨48 * (48 * M) ^ 3, fun H => searchBits_le_div_add H.1.length hM⟩) + (fun M hM => ⟨48 * (48 * M) ^ 3, fun H => searchBits_le_div_add H.vertices.length hM⟩) /-- **Lokshtanov–Marx–Saurabh ETH Lower Bound for $2n \times 2n$ Bipartite Permutation Independent Set** [LokshtanovMarxSaurabh2018, Theorem 2.11] [Ilango2020FOCS, Section III.B, p. 427; ECCC diff --git a/CslibFrontier/Computability/Complexity/Parameterized/ColorCoding.lean b/CslibFrontier/Computability/Complexity/Parameterized/ColorCoding.lean index 8be872f460..a9921da5a6 100644 --- a/CslibFrontier/Computability/Complexity/Parameterized/ColorCoding.lean +++ b/CslibFrontier/Computability/Complexity/Parameterized/ColorCoding.lean @@ -95,7 +95,7 @@ colors `colors` parallel to its vertex list (`Cslib.Complexity.colorOf`), and a structure ColorfulPathInstance where /-- The input graph. -/ graph : Graph - /-- Vertex colors listed parallel to `graph.1` (`Cslib.Complexity.colorOf`). -/ + /-- Vertex colors listed parallel to `graph.vertices` (`Cslib.Complexity.colorOf`). -/ colors : List ℕ /-- The target number of vertices (and available colors). -/ length : ℕ @@ -149,7 +149,7 @@ variable {G χ} color set `m` ends at `v` exactly when `χ v ∈ m`, and either `m = {χ v}` or a colorful path with color set `m \ {χ v}` ends at a neighbor of `v`. -/ theorem reaches_iff {m v : ℕ} : - Reaches G χ m v ↔ v ∈ G.1 ∧ m.testBit (χ v) = true ∧ + Reaches G χ m v ↔ v ∈ G.vertices ∧ m.testBit (χ v) = true ∧ (m = 2 ^ χ v ∨ ∃ u, Reaches G χ (m - 2 ^ χ v) u ∧ G.toSimpleGraph.Adj u v) := by constructor · rintro ⟨p, hp, hnd, hm⟩ @@ -302,7 +302,7 @@ namespace ColorCoding structure TableInput where /-- The input graph. -/ graph : Graph - /-- Vertex colors listed parallel to `graph.1` (`Cslib.Complexity.colorOf`). -/ + /-- Vertex colors listed parallel to `graph.vertices` (`Cslib.Complexity.colorOf`). -/ colors : List ℕ /-- Number of color-set masks to tabulate (encoded in unary). -/ numSets : ℕ diff --git a/CslibFrontier/Computability/Complexity/Parameterized/LongPathKernel.lean b/CslibFrontier/Computability/Complexity/Parameterized/LongPathKernel.lean index f616cfdaf2..92a643e8b1 100644 --- a/CslibFrontier/Computability/Complexity/Parameterized/LongPathKernel.lean +++ b/CslibFrontier/Computability/Complexity/Parameterized/LongPathKernel.lean @@ -85,8 +85,8 @@ namespace HamPath /-- The disjoint union of the pendant copies of a list of instances, copy `i` for entry `i`. -/ def pendantUnion (l : List (Graph × ℕ × ℕ)) : Graph := - (l.zipIdx.flatMap fun p => (pendantCopy p.1 p.2).1, - l.zipIdx.flatMap fun p => (pendantCopy p.1 p.2).2) + ⟨l.zipIdx.flatMap fun p => (pendantCopy p.1 p.2).vertices, + l.zipIdx.flatMap fun p => (pendantCopy p.1 p.2).edges⟩ /-- The cross-composition of `HamPath` into `LongPath`: the disjoint union of the pendant copies, with target two more than the number of distinct vertices of the first entry. -/ @@ -100,11 +100,12 @@ theorem computableInPolyTime_toLongPath : (encList encPathInstance) (encList (encPair encPathInstance encUnary)) := (ComputableTidilyInPolyTime.zipIdx encPathInstance).computableInPolyTime have hc := computableInPolyTime_pendantCopy - unfold encGraph at hc have hU : ComputableInPolyTime pendantUnion (encList encPathInstance) encGraph := by - unfold pendantUnion encGraph - exact (flatMapWithInput hz (hc.fst'.comp (snd _ _))).prod - (flatMapWithInput hz (hc.snd'.comp (snd _ _))) + have hV := flatMapWithInput (f := fun _ p => (pendantCopy p.1 p.2).vertices) hz + ((encGraph_vertices_polyTime hc).comp (snd _ _)) + have hE := flatMapWithInput (f := fun _ p => (pendantCopy p.1 p.2).edges) hz + ((encGraph_edges_polyTime hc).comp (snd _ _)) + exact encGraph_mk_polyTime hV hE have hk : ComputableInPolyTime (fun l : List (Graph × ℕ × ℕ) => (l.headD default).1.distinctVertices.length + 2) (encList encPathInstance) encNat := by @@ -120,17 +121,17 @@ theorem computableInPolyTime_toLongPath : /-! ### The disjoint union -/ private theorem mem_pendantUnion_fst {l : List (Graph × ℕ × ℕ)} {w : ℕ} : - w ∈ (pendantUnion l).1 ↔ ∃ x i, l[i]? = some x ∧ w ∈ (pendantCopy x i).1 := by + w ∈ (pendantUnion l).vertices ↔ ∃ x i, l[i]? = some x ∧ w ∈ (pendantCopy x i).vertices := by simp only [pendantUnion, List.mem_flatMap, List.mem_zipIdx_iff_getElem?, Prod.exists] private theorem mem_pendantUnion_snd {l : List (Graph × ℕ × ℕ)} {e : ℕ × ℕ} : - e ∈ (pendantUnion l).2 ↔ ∃ x i, l[i]? = some x ∧ e ∈ (pendantCopy x i).2 := by + e ∈ (pendantUnion l).edges ↔ ∃ x i, l[i]? = some x ∧ e ∈ (pendantCopy x i).edges := by simp only [pendantUnion, List.mem_flatMap, List.mem_zipIdx_iff_getElem?, Prod.exists] /-- On the vertices of copy `i`, the union has the vertices of copy `i`. -/ -private theorem mem_pendantUnion_fst_iff {l : List (Graph × ℕ × ℕ)} {x : Graph × ℕ × ℕ} - {i w : ℕ} (hx : l[i]? = some x) (hw : (Nat.unpair w).1 = i) : - w ∈ (pendantUnion l).1 ↔ w ∈ (pendantCopy x i).1 := by +private theorem mem_pendantUnion_fst_iff {l : List (Graph × ℕ × ℕ)} + {x : Graph × ℕ × ℕ} {i w : ℕ} (hx : l[i]? = some x) (hw : (Nat.unpair w).1 = i) : + w ∈ (pendantUnion l).vertices ↔ w ∈ (pendantCopy x i).vertices := by refine ⟨fun h => ?_, fun h => mem_pendantUnion_fst.2 ⟨x, i, hx, h⟩⟩ obtain ⟨y, j, hy, hwy⟩ := mem_pendantUnion_fst.1 h obtain rfl : j = i := (unpair_of_mem_pendantCopy_fst hwy).symm.trans hw @@ -138,9 +139,10 @@ private theorem mem_pendantUnion_fst_iff {l : List (Graph × ℕ × ℕ)} {x : G exact hwy /-- On the edges at copy `i`, the union has the edges of copy `i`. -/ -private theorem mem_pendantUnion_snd_iff {l : List (Graph × ℕ × ℕ)} {x : Graph × ℕ × ℕ} - {i : ℕ} {e : ℕ × ℕ} (hx : l[i]? = some x) (he : (Nat.unpair e.1).1 = i) : - e ∈ (pendantUnion l).2 ↔ e ∈ (pendantCopy x i).2 := by +private theorem mem_pendantUnion_snd_iff {l : List (Graph × ℕ × ℕ)} + {x : Graph × ℕ × ℕ} {i : ℕ} {e : ℕ × ℕ} (hx : l[i]? = some x) + (he : (Nat.unpair e.1).1 = i) : + e ∈ (pendantUnion l).edges ↔ e ∈ (pendantCopy x i).edges := by refine ⟨fun h => ?_, fun h => mem_pendantUnion_snd.2 ⟨x, i, hx, h⟩⟩ obtain ⟨y, j, hy, hey⟩ := mem_pendantUnion_snd.1 h obtain rfl : j = i := (unpair_of_mem_pendantCopy_snd hey).1.symm.trans he @@ -160,9 +162,9 @@ private theorem unpair_fst_eq_of_adj {l : List (Graph × ℕ × ℕ)} {u v : ℕ exact this.2.trans this.1.symm /-- Adjacency of the union at copy `i` is adjacency of copy `i`. -/ -private theorem adj_pendantUnion_iff {l : List (Graph × ℕ × ℕ)} {x : Graph × ℕ × ℕ} - {i u v : ℕ} (hx : l[i]? = some x) (hu : (Nat.unpair u).1 = i) - (hv : (Nat.unpair v).1 = i) : +private theorem adj_pendantUnion_iff {l : List (Graph × ℕ × ℕ)} + {x : Graph × ℕ × ℕ} {i u v : ℕ} (hx : l[i]? = some x) + (hu : (Nat.unpair u).1 = i) (hv : (Nat.unpair v).1 = i) : (pendantUnion l).toSimpleGraph.Adj u v ↔ (pendantCopy x i).toSimpleGraph.Adj u v := by rw [Graph.toSimpleGraph_adj, Graph.toSimpleGraph_adj, mem_pendantUnion_snd_iff (e := (u, v)) hx hu, mem_pendantUnion_snd_iff (e := (v, u)) hx hv] @@ -202,14 +204,14 @@ theorem toLongPath_mem_iff {l : List (Graph × ℕ × ℕ)} (hl : l ≠ []) change (pendantUnion (y :: l'), y.1.distinctVertices.length + 1 + 1) ∈ LongPath ↔ _ rw [pendantUnion_mem_LongPath_iff] constructor - · rintro ⟨⟨G, s, t⟩, j, hx, hmem⟩ + · rintro ⟨x, j, hx, hmem⟩ have hxl := List.mem_of_getElem? hx - refine ⟨(G, s, t), hxl, ?_⟩ + refine ⟨x, hxl, ?_⟩ rw [hkey y List.mem_cons_self _ hxl] at hmem exact pendantCopy_mem_LongPath_iff.1 hmem - · rintro ⟨⟨G, s, t⟩, hxl, hx⟩ + · rintro ⟨x, hxl, hx⟩ obtain ⟨j, hj⟩ := List.mem_iff_getElem?.1 hxl - refine ⟨(G, s, t), j, hj, ?_⟩ + refine ⟨x, j, hj, ?_⟩ rw [hkey y List.mem_cons_self _ hxl] exact pendantCopy_mem_LongPath_iff.2 hx @@ -223,7 +225,8 @@ the disjoint union of the pendant copies, whose target is two more than the comm distinct vertices. -/ theorem HamPath.orCrossComposes_LongPath : ORCrossComposes encPathInstance HamPath (encPair encGraph encNat) Prod.snd LongPath := by - have hkey : ComputableInPolyTime (fun x : Graph × ℕ × ℕ => x.1.distinctVertices.length) + have hkey : ComputableInPolyTime + (fun x : Graph × ℕ × ℕ => x.1.distinctVertices.length) encPathInstance encUnary := (length_list encNat).comp (Graph.computableInPolyTime_distinctVertices.comp (by fun_prop)) refine ⟨_, HamPath.toLongPath, fun n => n + 2, IsPolyEquivalence.of_key hkey, by fun_prop, diff --git a/CslibFrontier/Computability/Complexity/Parameterized/VertexCover.lean b/CslibFrontier/Computability/Complexity/Parameterized/VertexCover.lean index 0cd52f7a6f..c0a38098af 100644 --- a/CslibFrontier/Computability/Complexity/Parameterized/VertexCover.lean +++ b/CslibFrontier/Computability/Complexity/Parameterized/VertexCover.lean @@ -102,13 +102,14 @@ private theorem headD_mem_uncoveredEdges {G : Graph} {C : List ℕ} /-- Outside a cover, a branching step adds one vertex of `G` to `C`. -/ private theorem exists_branchStep_eq_cons {G : Graph} {C : List ℕ} (b : Bool) - (h : isCover G C = false) : ∃ w ∈ G.1, branchStep G C b = w :: C := by + (h : isCover G C = false) : ∃ w ∈ G.vertices, branchStep G C b = w :: C := by obtain ⟨_, he₁, he₂, _, _, _⟩ := mem_uncoveredEdges_iff.mp (headD_mem_uncoveredEdges h) refine ⟨_, ?_, by rw [branchStep, h]; rfl⟩ split_ifs <;> assumption -private theorem mem_of_mem_branchStep {G : Graph} {C : List ℕ} {b : Bool} (hC : ∀ v ∈ C, v ∈ G.1) : - ∀ v ∈ branchStep G C b, v ∈ G.1 := by +private theorem mem_of_mem_branchStep {G : Graph} {C : List ℕ} {b : Bool} + (hC : ∀ v ∈ C, v ∈ G.vertices) : + ∀ v ∈ branchStep G C b, v ∈ G.vertices := by cases h : isCover G C · obtain ⟨w, hw, hstep⟩ := exists_branchStep_eq_cons b h rw [hstep] @@ -122,7 +123,7 @@ private theorem length_branchStep_le (G : Graph) (C : List ℕ) (b : Bool) : /-- Every vertex added along a search-tree path is a declared vertex of `G`. -/ private theorem mem_of_mem_foldBranch (G : Graph) (bs : List Bool) : - ∀ v ∈ foldBranch G bs, v ∈ G.1 := by + ∀ v ∈ foldBranch G bs, v ∈ G.vertices := by induction bs using List.reverseRecOn with | nil => simp [foldBranch] | append_singleton bs b ih => @@ -142,7 +143,7 @@ private theorem length_foldBranch_le (G : Graph) (bs : List Bool) : /-- Along any vertex cover `S`, at each depth `m` some path of length `m` either has already found a cover or has accumulated `m` distinct vertices of `S`. -/ private theorem exists_foldBranch_subset (G : Graph) {S : Finset ℕ} - (hS : ∀ u ∈ G.1, ∀ v ∈ G.1, G.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) (m : ℕ) : + (hS : ∀ u ∈ G.vertices, ∀ v ∈ G.vertices, G.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) (m : ℕ) : ∃ bs : List Bool, bs.length = m ∧ (∀ v ∈ foldBranch G bs, v ∈ S) ∧ (foldBranch G bs).Nodup ∧ (isCover G (foldBranch G bs) = true ∨ (foldBranch G bs).length = m) := by @@ -185,7 +186,7 @@ private theorem exists_foldBranch_subset (G : Graph) {S : Finset ℕ} /-- If `G` has a vertex cover of at most `m` vertices, some branching word `bs` of length `m` produces a cover. -/ private theorem exists_isCover_foldBranch {G : Graph} {S : Finset ℕ} - (hS : ∀ u ∈ G.1, ∀ v ∈ G.1, G.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) {m : ℕ} + (hS : ∀ u ∈ G.vertices, ∀ v ∈ G.vertices, G.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) {m : ℕ} (hm : S.card ≤ m) : ∃ bs : List Bool, bs.length = m ∧ isCover G (foldBranch G bs) = true := by obtain ⟨bs, hbs, hsub, hnodup, hcov | hlen⟩ := exists_foldBranch_subset G hS m @@ -205,8 +206,8 @@ private theorem exists_isCover_foldBranch {G : Graph} {S : Finset ℕ} declared vertices if and only if some `i < 2 ^ m` leads to a cover along the path of length `m` given by the binary digits of `i`. -/ theorem exists_lt_two_pow_isCover_foldBranch (G : Graph) (m : ℕ) : - (∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.1) ∧ - (∀ u ∈ G.1, ∀ v ∈ G.1, G.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) ∧ S.card ≤ m) ↔ + (∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.vertices) ∧ + (∀ u ∈ G.vertices, ∀ v ∈ G.vertices, G.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) ∧ S.card ≤ m) ↔ ∃ i < 2 ^ m, isCover G (foldBranch G ((List.range m).map i.testBit)) = true := by constructor · rintro ⟨S, _, hS, hm⟩ @@ -233,7 +234,7 @@ theorem exists_lt_two_pow_isCover_foldBranch (G : Graph) (m : ℕ) : def cap (x : Graph × ℕ) : ℕ := min x.1.distinctVertices.length x.2 -theorem cap_eq (x : Graph × ℕ) : cap x = min x.1.1.toFinset.card x.2 := by +theorem cap_eq (x : Graph × ℕ) : cap x = min x.1.vertices.toFinset.card x.2 := by rw [cap, Graph.length_distinctVertices] theorem cap_le_snd (x : Graph × ℕ) : cap x ≤ x.2 := @@ -283,8 +284,8 @@ private theorem length_encList_branchStep_le (G : Graph) (C : List ℕ) (b : Boo · obtain ⟨w, hw, hstep⟩ := exists_branchStep_eq_cons b h have hlen : (encNat w).length ≤ (encGraph G).length := by calc (encNat w).length - ≤ (encList encNat G.1).length := length_le_encList_of_mem encNat hw - _ ≤ (encGraph G).length := by simp only [encGraph, length_encPair]; omega + ≤ (encList encNat G.vertices).length := length_le_encList_of_mem encNat hw + _ ≤ (encGraph G).length := by rw [encGraph_apply, length_encPair]; dsimp only; omega simp only [hstep, encList_cons, List.length_append, length_frame] omega · simp only [branchStep, h, ↓reduceIte] diff --git a/CslibFrontier/Computability/Complexity/Parameterized/VertexCover/BussKernel.lean b/CslibFrontier/Computability/Complexity/Parameterized/VertexCover/BussKernel.lean index ca9378a5d6..fa02f5cb5c 100644 --- a/CslibFrontier/Computability/Complexity/Parameterized/VertexCover/BussKernel.lean +++ b/CslibFrontier/Computability/Complexity/Parameterized/VertexCover/BussKernel.lean @@ -122,7 +122,7 @@ def edges (x : Graph × ℕ) : List (ℕ × ℕ) := /-- The graph of the kernel, on the vertices `0, …, numVertices x - 1`. -/ def graph (x : Graph × ℕ) : Graph := - (List.range (numVertices x), edges x) + ⟨List.range (numVertices x), edges x⟩ /-- Whether the kernel is the reduced graph: the target is at most the number of distinct declared vertices, there are at most `k` high vertices, and at most `k (k + 1)` active vertices. -/ @@ -131,7 +131,7 @@ def accept (x : Graph × ℕ) : Bool := decide ((active x).length ≤ cap x * (cap x + 1)) /-- A fixed instance outside `VertexCover`: one cover vertex in the empty graph. -/ -def noInstance : Graph × ℕ := (([], []), 1) +def noInstance : Graph × ℕ := (⟨[], []⟩, 1) /-! ### Correctness -/ @@ -144,22 +144,22 @@ private def neighbors (G : Graph) (v : ℕ) : Finset ℕ := (G.distinctVertices.filter (G.adjBool v)).toFinset private theorem mem_neighbors {G : Graph} {u v : ℕ} : - u ∈ neighbors G v ↔ u ∈ G.1 ∧ G.toSimpleGraph.Adj v u := by + u ∈ neighbors G v ↔ u ∈ G.vertices ∧ G.toSimpleGraph.Adj v u := by simp [neighbors, Graph.mem_distinctVertices] private theorem card_neighbors (G : Graph) (v : ℕ) : (neighbors G v).card = degree G v := by rw [neighbors, List.toFinset_card_of_nodup ((Graph.nodup_distinctVertices G).filter _), degree, List.countP_eq_length_filter] -private theorem degree_le_card (G : Graph) (v : ℕ) : degree G v ≤ G.1.toFinset.card := +private theorem degree_le_card (G : Graph) (v : ℕ) : degree G v ≤ G.vertices.toFinset.card := (List.countP_le_length).trans (Graph.length_distinctVertices G).le variable (x : Graph × ℕ) -private theorem mem_high {v : ℕ} : v ∈ high x ↔ v ∈ x.1.1 ∧ isHigh x v = true := by +private theorem mem_high {v : ℕ} : v ∈ high x ↔ v ∈ x.1.vertices ∧ isHigh x v = true := by simp [high, Graph.mem_distinctVertices] -private theorem mem_low {v : ℕ} : v ∈ low x ↔ v ∈ x.1.1 ∧ isHigh x v = false := by +private theorem mem_low {v : ℕ} : v ∈ low x ↔ v ∈ x.1.vertices ∧ isHigh x v = false := by simp [low, Graph.mem_distinctVertices] private theorem mem_active {v : ℕ} : @@ -444,7 +444,7 @@ private theorem length_encode_le_of_accept (h : accept x = true) : have : x.2 * (x.2 + 1) ≤ x.2 * (x.2 + 2) := Nat.mul_le_mul_left _ (by omega) exact Nat.mul_le_mul this this have := length_encNat_le_self (param x) - simp only [graph, encGraph, length_encPair] + simp only [graph, encGraph_apply, length_encPair] exact le_trans (by omega) (size_arith (k := x.2) (K := K) le_rfl) end VertexCover.Buss @@ -549,7 +549,7 @@ theorem computableInPolyTime_graph : (encPair encGraph encNat) (encList encNat) := by simpa only [Function.comp_def, List.map_id'] using (map unary_to_binary).comp (range.comp computableInPolyTime_numVertices) - exact (hV.prod computableInPolyTime_edges).of_enc_eq fun _ => rfl + exact encGraph_mk_polyTime hV computableInPolyTime_edges end VertexCover.Buss @@ -571,7 +571,7 @@ theorem bussKernel_mem_iff (x : Graph × ℕ) : bussKernel x ∈ VertexCover ↔ /-- Buss's kernel has at most `k (k + 2)` vertices. -/ theorem length_bussKernel_vertices_le (x : Graph × ℕ) : - (bussKernel x).1.1.length ≤ x.2 * (x.2 + 2) := by + (bussKernel x).1.vertices.length ≤ x.2 * (x.2 + 2) := by unfold bussKernel split_ifs with h · simpa [graph] using numVertices_le_of_accept x h @@ -579,7 +579,7 @@ theorem length_bussKernel_vertices_le (x : Graph × ℕ) : /-- Buss's kernel lists at most `(k (k + 1)) ^ 2` edges. -/ theorem length_bussKernel_edges_le (x : Graph × ℕ) : - (bussKernel x).1.2.length ≤ (x.2 * (x.2 + 1)) * (x.2 * (x.2 + 1)) := by + (bussKernel x).1.edges.length ≤ (x.2 * (x.2 + 1)) * (x.2 * (x.2 + 1)) := by unfold bussKernel split_ifs with h · exact length_edges_le_of_accept x h diff --git a/CslibFrontier/Computability/Complexity/Parameterized/WHierarchy/IndependentSet.lean b/CslibFrontier/Computability/Complexity/Parameterized/WHierarchy/IndependentSet.lean index fcb553b555..94a1954e1c 100644 --- a/CslibFrontier/Computability/Complexity/Parameterized/WHierarchy/IndependentSet.lean +++ b/CslibFrontier/Computability/Complexity/Parameterized/WHierarchy/IndependentSet.lean @@ -68,7 +68,7 @@ namespace IndependentSet /-- The edges of `G` between distinct declared vertices, as pairs of positions in `G.distinctVertices`. -/ def edgeIndices (G : Graph) : List (ℕ × ℕ) := - (G.2.filter fun e => decide (e.1 ∈ G.1 ∧ e.2 ∈ G.1 ∧ e.1 ≠ e.2)).map fun e => + (G.edges.filter fun e => decide (e.1 ∈ G.vertices ∧ e.2 ∈ G.vertices ∧ e.1 ≠ e.2)).map fun e => (G.distinctVertices.idxOf e.1, G.distinctVertices.idxOf e.2) /-- The gates of the circuit on `n` inputs for the edges `E`: the negation of every input, the @@ -218,7 +218,7 @@ private def vertexSet (G : Graph) (x : BitString (toCircuit G).1) : Finset ℕ : Fin.ext ((List.Nodup.getElem_inj_iff G.nodup_distinctVertices).1 h)⟩ private theorem mem_vertexSet {G : Graph} {x : BitString (toCircuit G).1} {v : ℕ} : - v ∈ vertexSet G x ↔ ∃ h : v ∈ G.1, + v ∈ vertexSet G x ↔ ∃ h : v ∈ G.vertices, x ⟨G.distinctVertices.idxOf v, List.idxOf_lt_length_iff.2 (Graph.mem_distinctVertices.2 h)⟩ = true := by simp only [vertexSet, Finset.mem_map, Finset.mem_filter, Finset.mem_univ, true_and] @@ -234,7 +234,7 @@ private theorem edgeIndices_iff (G : Graph) (x : BitString (toCircuit G).1) : (∀ e ∈ edgeIndices G, ¬((List.ofFn x).getD e.1 false = true ∧ (List.ofFn x).getD e.2 false = true)) ↔ G.toSimpleGraph.IsIndepSet (vertexSet G x : Set ℕ) := by - have hget : ∀ a (ha : a ∈ G.1), (List.ofFn x).getD (G.distinctVertices.idxOf a) false = + have hget : ∀ a (ha : a ∈ G.vertices), (List.ofFn x).getD (G.distinctVertices.idxOf a) false = x ⟨G.distinctVertices.idxOf a, List.idxOf_lt_length_iff.2 (Graph.mem_distinctVertices.2 ha)⟩ := fun a ha => by rw [List.getD_eq_getElem _ _ (by diff --git a/CslibFrontier/Computability/Complexity/Problems/CSP/RotationSystem/Primal/Euler.lean b/CslibFrontier/Computability/Complexity/Problems/CSP/RotationSystem/Primal/Euler.lean index 086bc819db..4a0d03c98f 100644 --- a/CslibFrontier/Computability/Complexity/Problems/CSP/RotationSystem/Primal/Euler.lean +++ b/CslibFrontier/Computability/Complexity/Problems/CSP/RotationSystem/Primal/Euler.lean @@ -113,9 +113,9 @@ open Classical in /-- The component edge counts agree exactly: both lists retain one edge position per occurrence, and its first endpoint is the same dense variable. -/ theorem polygon_component_edge_count (root : ℕ) (hr : root < F.numVars) : - (polygonGraph F).2.countP + (polygonGraph F).edges.countP (fun e => decide (SameComponent (polygonGraph F) root e.1)) = - (incidenceGraph F).2.countP + (incidenceGraph F).edges.countP (fun e => decide (SameComponent (incidenceGraph F) root e.1)) := by classical rw [incidenceGraph_edges] @@ -139,10 +139,10 @@ theorem numericalGenusZero_polygon_iff (r : Graph.RotationSystem (incidenceGraph classical let Euler (G : Graph) (s : Graph.RotationSystem G) (root : ℕ) : Prop := G.distinctVertices.countP (fun v => decide (SameComponent G root v)) + - ((List.finRange (2 * G.2.length)).countP fun i => + ((List.finRange (2 * G.edges.length)).countP fun i => decide (SameComponent G root ((Graph.Dart.indexEquiv G).symm i).vertex ∧ s.IsFaceRepresentative ((Graph.Dart.indexEquiv G).symm i))) = - G.2.countP (fun e => decide (SameComponent G root e.1)) + 2 + G.edges.countP (fun e => decide (SameComponent G root e.1)) + 2 have hdense (o : F.Occurrence) : F.distinctVars.idxOf o.var < F.numVars := by rw [← CSP.length_distinctVars] exact List.idxOf_lt_length_of_mem (CSP.mem_distinctVars.mpr o.var_mem) @@ -156,11 +156,11 @@ theorem numericalGenusZero_polygon_iff (r : Graph.RotationSystem (incidenceGraph have hE := polygon_component_edge_count root hr dsimp only [ComponentFace] at * omega - have hPdart (root : ℕ) (hr : root ∈ (polygonGraph F).1) : + have hPdart (root : ℕ) (hr : root ∈ (polygonGraph F).vertices) : ∃ d : Graph.Dart (polygonGraph F), d.vertex = root := by obtain ⟨o, ho⟩ := exists_occurrence_dense root (List.mem_range.mp hr) exact ⟨polygonDart o false, (vertex_polygonDart o false).trans ho⟩ - have hIdart (root : ℕ) (hr : root ∈ (incidenceGraph F).1) : + have hIdart (root : ℕ) (hr : root ∈ (incidenceGraph F).vertices) : ∃ o : F.Occurrence, ∃ b : Bool, (incidenceDart o b).vertex = root := by by_cases hv : root < F.numVars · obtain ⟨o, ho⟩ := exists_occurrence_dense root hv @@ -203,7 +203,7 @@ theorem numericalGenusZero_polygon_iff (r : Graph.RotationSystem (incidenceGraph unfold Graph.RotationSystem.NumericalGenusZero at hI ⊢ intro root hr rw [ite_eq_left (hPdart root hr)] - have hroot : root ∈ (incidenceGraph F).1 := by + have hroot : root ∈ (incidenceGraph F).vertices := by rw [incidenceGraph_vertices, List.mem_range] have := List.mem_range.mp hr omega diff --git a/CslibFrontier/Computability/Complexity/Problems/CSP/RotationSystem/Primal/Graph.lean b/CslibFrontier/Computability/Complexity/Problems/CSP/RotationSystem/Primal/Graph.lean index 392cb0be68..81a8921eb8 100644 --- a/CslibFrontier/Computability/Complexity/Problems/CSP/RotationSystem/Primal/Graph.lean +++ b/CslibFrontier/Computability/Complexity/Problems/CSP/RotationSystem/Primal/Graph.lean @@ -73,12 +73,12 @@ Invalid records return the supplied variable's dense name; actual occurrences ne /-- Dense occurring variables and one listed cyclic-scope edge per occurrence. -/ @[expose] def polygonGraph (F : CSP Rel V) : Graph := - (List.range F.numVars, F.occurrences.map fun (i, j, v) => - (F.distinctVars.idxOf v, polygonNext F i j v)) + ⟨List.range F.numVars, F.occurrences.map fun (i, j, v) => + (F.distinctVars.idxOf v, polygonNext F i j v)⟩ /-- The polygon construction keeps exactly the incidence edge positions. -/ @[simp] theorem length_edges_polygonGraph (F : CSP Rel V) : - (polygonGraph F).2.length = (incidenceGraph F).2.length := by + (polygonGraph F).edges.length = (incidenceGraph F).edges.length := by simp [polygonGraph, incidenceGraph_edges] /-- Every argument occurrence retains its own listed polygon edge. -/ @@ -88,7 +88,7 @@ Invalid records return the supplied variable's dense name; actual occurrences ne /-- The listed edge at an occurrence has its two prescribed dense endpoints. -/ theorem getElem_polygonEdge (o : F.Occurrence) : - (polygonGraph F).2[incidenceEdgeIndex o]'((polygonDart o false).1.isLt) = + (polygonGraph F).edges[incidenceEdgeIndex o]'((polygonDart o false).1.isLt) = (F.distinctVars.idxOf o.var, F.distinctVars.idxOf (argumentSuccessor o).var) := by simp only [polygonGraph, List.getElem_map, incidenceEdgeIndex] rw [List.getElem_idxOf (List.idxOf_lt_length_of_mem (CSP.mem_occurrences.mpr ⟨o, rfl⟩))] @@ -99,8 +99,8 @@ theorem getElem_polygonEdge (o : F.Occurrence) : (polygonDart o side).vertex = if side then F.distinctVars.idxOf (argumentSuccessor o).var else F.distinctVars.idxOf o.var := by - change (if side then ((polygonGraph F).2.getD (incidenceEdgeIndex o) (0, 0)).2 - else ((polygonGraph F).2.getD (incidenceEdgeIndex o) (0, 0)).1) = _ + change (if side then ((polygonGraph F).edges.getD (incidenceEdgeIndex o) (0, 0)).2 + else ((polygonGraph F).edges.getD (incidenceEdgeIndex o) (0, 0)).1) = _ rw [List.getD_eq_getElem _ _ (by rw [length_edges_polygonGraph]; exact incidenceEdgeIndex_lt o)] exact congrArg (fun edge : ℕ × ℕ => if side then edge.2 else edge.1) @@ -131,7 +131,7 @@ indices from the existing occurrence enumeration; it chooses no graph or rotatio /-- The exact edge-position map: one edge for each actual argument occurrence. -/ @[expose] noncomputable def polygonEdgeEquiv (F : CSP Rel V) : - F.Occurrence ≃ Fin (polygonGraph F).2.length := + F.Occurrence ≃ Fin (polygonGraph F).edges.length := Equiv.ofBijective (fun o => (polygonDart o false).1) ⟨by intro o p h exact incidenceEdgeIndex_injective (congrArg Fin.val h), fun i => by @@ -141,7 +141,7 @@ indices from the existing occurrence enumeration; it chooses no graph or rotatio /-- Edge count is exactly the number of ordered occurrences, rather than distinct pairs. -/ theorem length_edges_polygonGraph_eq_card (F : CSP Rel V) : - (polygonGraph F).2.length = Fintype.card F.Occurrence := by + (polygonGraph F).edges.length = Fintype.card F.Occurrence := by simpa only [Fintype.card_fin] using (Fintype.card_congr (polygonEdgeEquiv F)).symm /-- Occurrence/end coordinates on the existing incidence graph. -/ @@ -193,8 +193,8 @@ theorem sameCycle_argumentPermutation (o p : F.Occurrence) : rw [zpow_natCast, ← Equiv.Perm.iterate_eq_pow, hi, Equiv.Perm.iterate_eq_pow, hn] /-- Both ends of every polygon edge are declared dense occurring variables. -/ -theorem polygonGraph_endpoints {edge : ℕ × ℕ} (he : edge ∈ (polygonGraph F).2) : - edge.1 ∈ (polygonGraph F).1 ∧ edge.2 ∈ (polygonGraph F).1 := by +theorem polygonGraph_endpoints {edge : ℕ × ℕ} (he : edge ∈ (polygonGraph F).edges) : + edge.1 ∈ (polygonGraph F).vertices ∧ edge.2 ∈ (polygonGraph F).vertices := by obtain ⟨record, hrecord, rfl⟩ := List.mem_map.mp he obtain ⟨o, rfl⟩ := CSP.mem_occurrences.mp hrecord simp only [CSP.Occurrence.record, polygonNext_occurrence, polygonGraph, List.mem_range] @@ -207,7 +207,7 @@ theorem polygonGraph_endpoints {edge : ℕ × ℕ} (he : edge ∈ (polygonGraph /-- Dense vertex count is bounded by actual scope occurrences, independent of label sizes. -/ theorem length_vertices_polygonGraph_le (F : CSP Rel V) : - (polygonGraph F).1.length ≤ (polygonGraph F).2.length := by + (polygonGraph F).vertices.length ≤ (polygonGraph F).edges.length := by have h := F.vars.toFinset_card_le simpa only [polygonGraph, List.length_range, List.length_map, CSP.length_occurrences, CSP.numVars, CSP.vars, List.length_flatMap] using h @@ -224,7 +224,7 @@ theorem polygon_scope_word (i : Fin F.length) : /-- With positive actual scopes, constraint positions are also bounded by occurrences. -/ theorem length_constraints_le_polygon_edges (hPos : ∀ i : Fin F.length, 0 < F[i.val].2.length) : - F.length ≤ (polygonGraph F).2.length := by + F.length ≤ (polygonGraph F).edges.length := by have h := CSP.length_le_length_vars (F := F) (fun c hc => by obtain ⟨i, hi, rfl⟩ := List.mem_iff_getElem.mp hc exact hPos ⟨i, hi⟩) diff --git a/CslibFrontier/Computability/Complexity/Problems/ChromaticNumber.lean b/CslibFrontier/Computability/Complexity/Problems/ChromaticNumber.lean index 8798b8a389..08c8d9fd23 100644 --- a/CslibFrontier/Computability/Complexity/Problems/ChromaticNumber.lean +++ b/CslibFrontier/Computability/Complexity/Problems/ChromaticNumber.lean @@ -104,18 +104,18 @@ namespace ChromaticNumber /-- The check of a list of colors: every declared vertex gets a color below the bound, and adjacent declared vertices get different colors. -/ private def check (x : ChromaticNumberInstance × List ℕ) : Bool := - x.1.graph.1.all fun u => decide (colorOf x.1.graph x.2 u < x.1.colors) && - x.1.graph.1.all fun v => + x.1.graph.vertices.all fun u => decide (colorOf x.1.graph x.2 u < x.1.colors) && + x.1.graph.vertices.all fun v => !(x.1.graph.adjBool u v && decide (colorOf x.1.graph x.2 u = colorOf x.1.graph x.2 v)) private theorem computableInPolyTime_check : ComputableInPolyTime check (encPair encChromaticNumber (encList encNat)) encBool := by unfold check - fun_prop [Graph.computableInPolyTime_fst] + fun_prop private theorem check_iff (x : ChromaticNumberInstance × List ℕ) : - check x = true ↔ (∀ v ∈ x.1.graph.1, colorOf x.1.graph x.2 v < x.1.colors) ∧ - ∀ u ∈ x.1.graph.1, ∀ v ∈ x.1.graph.1, x.1.graph.toSimpleGraph.Adj u v → + check x = true ↔ (∀ v ∈ x.1.graph.vertices, colorOf x.1.graph x.2 v < x.1.colors) ∧ + ∀ u ∈ x.1.graph.vertices, ∀ v ∈ x.1.graph.vertices, x.1.graph.toSimpleGraph.Adj u v → colorOf x.1.graph x.2 u ≠ colorOf x.1.graph x.2 v := by simp only [check, List.all_eq_true, Bool.and_eq_true, decide_eq_true_eq, Bool.not_eq_true', Bool.and_eq_false_iff, Graph.adjBool_eq_false_iff, decide_eq_false_iff_not] @@ -134,10 +134,11 @@ below the number of declared vertices, so witnesses stay polynomial although the is written in binary. -/ theorem mem_NP : ChromaticNumber ∈ NP encChromaticNumber := by have hcert : ∀ (G : Graph) (k : ℕ), (⟨G, k⟩ : ChromaticNumberInstance) ∈ ChromaticNumber → - ∃ g : ℕ → ℕ, (∀ v ∈ G.1, g v ≤ G.1.length) ∧ check (⟨G, k⟩, G.1.map g) = true := by + ∃ g : ℕ → ℕ, (∀ v ∈ G.vertices, g v ≤ G.vertices.length) ∧ + check (⟨G, k⟩, G.vertices.map g) = true := by intro G k h obtain ⟨c, hc, hadj⟩ := mem_KColoring_iff.1 h - refine ⟨fun v => if k ≤ G.1.length then c v else G.1.idxOf v, fun v hv => ?_, + refine ⟨fun v => if k ≤ G.vertices.length then c v else G.vertices.idxOf v, fun v hv => ?_, (check_iff _).2 ⟨fun v hv => ?_, fun u hu v hv huv => ?_⟩⟩ · split · exact (hc v hv).le.trans ‹_› diff --git a/CslibFrontier/Computability/Complexity/Problems/Clique.lean b/CslibFrontier/Computability/Complexity/Problems/Clique.lean index 98c7a2951d..e1e2ce5528 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Clique.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Clique.lean @@ -67,13 +67,13 @@ open Turing.MultiTapeTM /-- The graphs with a clique of `m` of their vertices. -/ def Clique : Set (Graph × ℕ) := - {x | ∃ S : Finset {v // v ∈ x.1.1}, x.1.induced.IsNClique x.2 S} + {x | ∃ S : Finset {v // v ∈ x.1.vertices}, x.1.induced.IsNClique x.2 S} /-- Membership in `Clique` as a finite set of `m` declared vertices that is a clique in `G.toSimpleGraph`. -/ theorem mem_Clique_iff {G : Graph} {m : ℕ} : (G, m) ∈ Clique ↔ - ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.1) ∧ G.toSimpleGraph.IsNClique m S := by + ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.vertices) ∧ G.toSimpleGraph.IsNClique m S := by simp only [Clique, Set.mem_ofPred_eq, SimpleGraph.isNClique_iff, SimpleGraph.IsClique] exact Graph.exists_subtype_pairwise_iff (P := G.toSimpleGraph.Adj) @@ -82,14 +82,15 @@ namespace Graph /-- The complement of a graph on its vertices: an edge between any two distinct vertices that are not adjacent. -/ def compl (G : Graph) : Graph := - (G.1, G.1.flatMap fun u => - (G.1.filter fun v => decide (u ≠ v ∧ (u, v) ∉ G.2 ∧ (v, u) ∉ G.2)).map fun v => (u, v)) + ⟨G.vertices, G.vertices.flatMap fun u => + (G.vertices.filter fun v => + decide (u ≠ v ∧ (u, v) ∉ G.edges ∧ (v, u) ∉ G.edges)).map fun v => (u, v)⟩ /-- On the vertices of a graph, adjacency in the complement is non-adjacency in the graph. -/ -theorem compl_adj_iff {G : Graph} {u v : ℕ} (hu : u ∈ G.1) (hv : v ∈ G.1) : +theorem compl_adj_iff {G : Graph} {u v : ℕ} (hu : u ∈ G.vertices) (hv : v ∈ G.vertices) : G.compl.toSimpleGraph.Adj u v ↔ u ≠ v ∧ ¬G.toSimpleGraph.Adj u v := by - have hedge : ∀ {a b}, (a, b) ∈ G.compl.2 ↔ a ∈ G.1 ∧ b ∈ G.1 ∧ - (a ≠ b ∧ (a, b) ∉ G.2 ∧ (b, a) ∉ G.2) := by + have hedge : ∀ {a b}, (a, b) ∈ G.compl.edges ↔ a ∈ G.vertices ∧ b ∈ G.vertices ∧ + (a ≠ b ∧ (a, b) ∉ G.edges ∧ (b, a) ∉ G.edges) := by intro a b simp only [compl, List.mem_flatMap, List.mem_map, List.mem_filter, decide_eq_true_eq, Prod.mk.injEq] @@ -109,7 +110,7 @@ theorem compl_adj_iff {G : Graph} {u v : ℕ} (hu : u ∈ G.1) (hv : v ∈ G.1) /-- The induced graph of the complement is the complement of the induced graph. -/ @[simp] theorem induced_compl (G : Graph) : - (G.compl.induced : SimpleGraph {v // v ∈ G.1}) = G.inducedᶜ := by + (G.compl.induced : SimpleGraph {v // v ∈ G.vertices}) = G.inducedᶜ := by ext u v exact (compl_adj_iff (G := G) u.2 v.2).trans ((and_congr_left' Subtype.ext_iff.not.symm).trans (G.induced.compl_adj u v).symm) @@ -130,36 +131,42 @@ theorem compl_mem_IndependentSet_iff (G : Graph) (m : ℕ) : @[fun_prop] theorem computableInPolyTime_compl : ComputableInPolyTime compl encGraph encGraph := by - unfold compl encGraph have hne : ComputableInPolyTime (fun x : (Graph × ℕ) × ℕ => decide (x.1.2 ≠ x.2)) - (encPair (encPair (encPair (encList encNat) (encList (encPair encNat encNat))) encNat) encNat) + (encPair (encPair encGraph encNat) encNat) encBool := by simpa only [ne_eq, decide_not, Function.comp_def] using ComputableInPolyTime.bool_not.comp (ComputableInPolyTime.decide_eq' (by fun_prop) (by fun_prop)) have h1 : ComputableInPolyTime - (fun x : (Graph × ℕ) × ℕ => decide ((x.1.2, x.2) ∉ x.1.1.2)) - (encPair (encPair (encPair (encList encNat) (encList (encPair encNat encNat))) encNat) encNat) + (fun x : (Graph × ℕ) × ℕ => decide ((x.1.2, x.2) ∉ x.1.1.edges)) + (encPair (encPair encGraph encNat) encNat) encBool := by fun_prop have h2 : ComputableInPolyTime - (fun x : (Graph × ℕ) × ℕ => decide ((x.2, x.1.2) ∉ x.1.1.2)) - (encPair (encPair (encPair (encList encNat) (encList (encPair encNat encNat))) encNat) encNat) + (fun x : (Graph × ℕ) × ℕ => decide ((x.2, x.1.2) ∉ x.1.1.edges)) + (encPair (encPair encGraph encNat) encNat) encBool := by fun_prop have hpred : ComputableInPolyTime (fun x : (Graph × ℕ) × ℕ => - decide (x.1.2 ≠ x.2 ∧ (x.1.2, x.2) ∉ x.1.1.2 ∧ (x.2, x.1.2) ∉ x.1.1.2)) - (encPair (encPair (encPair (encList encNat) (encList (encPair encNat encNat))) encNat) encNat) + decide (x.1.2 ≠ x.2 ∧ (x.1.2, x.2) ∉ x.1.1.edges ∧ (x.2, x.1.2) ∉ x.1.1.edges)) + (encPair (encPair encGraph encNat) encNat) encBool := by simpa only [Bool.decide_and, Function.comp_def] using ComputableInPolyTime.bool_and.comp (hne.prod (ComputableInPolyTime.bool_and.comp (h1.prod h2))) - exact (ComputableInPolyTime.fst _ _).prod - (ComputableInPolyTime.flatMapWithInput (ComputableInPolyTime.fst _ _) + have hV : ComputableInPolyTime (fun G : Graph => G.vertices) encGraph (encList encNat) := by + fun_prop + have hE : ComputableInPolyTime + (fun G : Graph => G.vertices.flatMap fun u => + (G.vertices.filter fun v => + decide (u ≠ v ∧ (u, v) ∉ G.edges ∧ (v, u) ∉ G.edges)).map fun v => (u, v)) + encGraph (encList (encPair encNat encNat)) := by + exact ComputableInPolyTime.flatMapWithInput (by fun_prop) (ComputableInPolyTime.mapWithInput - (ComputableInPolyTime.filterWithInput - ((ComputableInPolyTime.fst _ _).comp (ComputableInPolyTime.fst _ _)) hpred) - (by fun_prop))) + (ComputableInPolyTime.filterWithInput (by fun_prop) hpred) + (by fun_prop)) + unfold compl + fun_prop end Graph @@ -236,7 +243,7 @@ theorem le_cliqueNum_iff : k ≤ G.cliqueNum ↔ (G, k) ∈ Clique := fun ⟨_, hS, (hcard : _ = k)⟩ => hcard ▸ hS.card_le_cliqueNum⟩ /-- A clique has at most as many vertices as the distinct declared vertices. -/ -theorem le_card_of_mem_Clique (h : (G, k) ∈ Clique) : k ≤ G.1.toFinset.card := +theorem le_card_of_mem_Clique (h : (G, k) ∈ Clique) : k ≤ G.vertices.toFinset.card := (le_cliqueNum_iff.2 h).trans (G.natCard_vertexSubtype ▸ G.induced.cliqueNum_le_natCard) /-- **Cliques of the complement are independent sets**: the clique number of the complement on diff --git a/CslibFrontier/Computability/Complexity/Problems/Clique/OfAdj.lean b/CslibFrontier/Computability/Complexity/Problems/Clique/OfAdj.lean index 7616cd6825..5e65e689fd 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Clique/OfAdj.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Clique/OfAdj.lean @@ -48,12 +48,12 @@ variable {V : Type} /-- The graph on the positions of `vs`, with an edge from `i` to `j` when `adj` holds of the entries at `i` and `j`. -/ def ofAdj (vs : List V) (adj : V → V → Bool) : Graph := - (List.range vs.length, - vs.zipIdx.flatMap fun a => (vs.zipIdx.filter fun b => adj a.1 b.1).map fun b => (a.2, b.2)) + ⟨List.range vs.length, + vs.zipIdx.flatMap fun a => (vs.zipIdx.filter fun b => adj a.1 b.1).map fun b => (a.2, b.2)⟩ /-- The edges of `ofAdj vs adj`. -/ theorem mem_ofAdj_edges {vs : List V} {adj : V → V → Bool} {i j : ℕ} : - (i, j) ∈ (ofAdj vs adj).2 ↔ + (i, j) ∈ (ofAdj vs adj).edges ↔ ∃ (hi : i < vs.length) (hj : j < vs.length), adj vs[i] vs[j] = true := by simp only [ofAdj, List.mem_flatMap, List.mem_map, List.mem_filter, Prod.mk.injEq, List.mem_zipIdx_iff_getElem?] @@ -153,15 +153,16 @@ theorem computableInPolyTime_ofAdj {vs : γ → List V} {adj : γ → V → V have hb := ComputableInPolyTime.unary_to_binary have hlen : ComputableInPolyTime (fun a => (vs a).length) encIn encUnary := by fun_prop - have hverts : ComputableInPolyTime (fun a => (List.range (vs a).length).map id) + have hverts : ComputableInPolyTime (fun a => List.range (vs a).length) encIn (encList encNat) := by - fun_prop + simpa using (show ComputableInPolyTime (fun a => (List.range (vs a).length).map id) + encIn (encList encNat) by fun_prop) have hedges : ComputableInPolyTime (fun a => (vs a).zipIdx.flatMap fun c => ((vs a).zipIdx.filter fun b => adj a c.1 b.1).map fun b => (c.2, b.2)) encIn (encList (encPair encNat encNat)) := by fun_prop - unfold ofAdj encGraph - simpa using hverts.prod hedges + unfold ofAdj + exact encGraph_mk_polyTime hverts hedges end Graph diff --git a/CslibFrontier/Computability/Complexity/Problems/CliqueCover.lean b/CslibFrontier/Computability/Complexity/Problems/CliqueCover.lean index 20b5deaee5..2e948f3cc0 100644 --- a/CslibFrontier/Computability/Complexity/Problems/CliqueCover.lean +++ b/CslibFrontier/Computability/Complexity/Problems/CliqueCover.lean @@ -76,8 +76,8 @@ attribute [fun_prop] encCliqueCover_graph_polyTime encCliqueCover_cliques_polyTi them. -/ @[expose] def Graph.CoveredByCliques (G : Graph) (l : ℕ) : Prop := - ∃ S : Finset (Finset {v // v ∈ G.1}), S.card ≤ l ∧ - (∀ s ∈ S, G.induced.IsClique (s : Set {v // v ∈ G.1})) ∧ ∀ v, ∃ s ∈ S, v ∈ s + ∃ S : Finset (Finset {v // v ∈ G.vertices}), S.card ≤ l ∧ + (∀ s ∈ S, G.induced.IsClique (s : Set {v // v ∈ G.vertices})) ∧ ∀ v, ∃ s ∈ S, v ∈ s /-- **Clique cover** [Karp1972]: the graphs whose declared vertices are the union of at most the given number of cliques. -/ diff --git a/CslibFrontier/Computability/Complexity/Problems/Coloring.lean b/CslibFrontier/Computability/Complexity/Problems/Coloring.lean index f770c203b6..0c2a62259d 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Coloring.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Coloring.lean @@ -87,7 +87,8 @@ open Cslib.Logic.PL Turing.MultiTapeTM ComputableInPolyTime /-- The graphs whose declared vertices can be colored with `k` colors, adjacent vertices getting different colors [Karp1972]. -/ -def KColoring (k : ℕ) : Set Graph := {G | (G.toSimpleGraph.induce {v | v ∈ G.1}).Colorable k} +def KColoring (k : ℕ) : Set Graph := + {G | (G.toSimpleGraph.induce {v | v ∈ G.vertices}).Colorable k} /-- The graphs whose declared vertices can be colored with three colors, adjacent vertices getting different colors [Sipser, Chapter 7][Sipser2013]. -/ @@ -95,13 +96,13 @@ abbrev ThreeColoring : Set Graph := KColoring 3 /-- Membership in `KColoring k` as a coloring by natural numbers below `k`. -/ theorem mem_KColoring_iff {k : ℕ} {G : Graph} : - G ∈ KColoring k ↔ ∃ c : ℕ → ℕ, (∀ v ∈ G.1, c v < k) ∧ - ∀ u ∈ G.1, ∀ v ∈ G.1, G.toSimpleGraph.Adj u v → c u ≠ c v := by + G ∈ KColoring k ↔ ∃ c : ℕ → ℕ, (∀ v ∈ G.vertices, c v < k) ∧ + ∀ u ∈ G.vertices, ∀ v ∈ G.vertices, G.toSimpleGraph.Adj u v → c u ≠ c v := by classical rw [KColoring, Set.mem_ofPred_eq, SimpleGraph.colorable_iff_exists_bdd_nat_coloring] constructor · rintro ⟨C, hC⟩ - refine ⟨fun v => if h : v ∈ G.1 then C ⟨v, h⟩ else 0, fun v hv => ?_, + refine ⟨fun v => if h : v ∈ G.vertices then C ⟨v, h⟩ else 0, fun v hv => ?_, fun u hu v hv huv => ?_⟩ · simpa only [hv, dite_true] using hC ⟨v, hv⟩ · simpa only [hu, hv, dite_true] using C.valid (v := ⟨u, hu⟩) (w := ⟨v, hv⟩) huv @@ -121,8 +122,9 @@ def colorOf (k : ℕ) (V : List ℕ) (w : List Bool) (v : ℕ) : ℕ := /-- The verifier for `KColoring k`: every declared vertex gets a color below `k` from the certificate, and no two adjacent declared vertices get the same color. -/ def verifier (k : ℕ) (x : Graph × List Bool) : Bool := - x.1.1.all fun u => decide (colorOf k x.1.1 x.2 u < k) && - x.1.1.all fun v => !(x.1.adjBool u v && decide (colorOf k x.1.1 x.2 u = colorOf k x.1.1 x.2 v)) + x.1.vertices.all fun u => decide (colorOf k x.1.vertices x.2 u < k) && + x.1.vertices.all fun v => + !(x.1.adjBool u v && decide (colorOf k x.1.vertices x.2 u = colorOf k x.1.vertices x.2 v)) /-- `KColoring.verifier k` verifies `KColoring k` with certificates of `k` bits per symbol of the encoded graph. -/ @@ -134,12 +136,12 @@ theorem isVerifier (k : ℕ) : Bool.and_eq_false_iff, Graph.adjBool_eq_false_iff, decide_eq_false_iff_not] constructor · rintro ⟨c, hc, hadj⟩ - have hcol : ∀ v ∈ G.1, - colorOf k G.1 (unaryBlocks k (k * (encGraph G).length) fun i => c (G.1.getD i 0)) v = - c v := by + have hcol : ∀ v ∈ G.vertices, + colorOf k G.vertices (unaryBlocks k (k * (encGraph G).length) + fun i => c (G.vertices.getD i 0)) v = c v := by intro v hv - have hlt : G.1.idxOf v < G.1.length := List.idxOf_lt_length_iff.2 hv - have hget : G.1.getD (G.1.idxOf v) 0 = v := by + have hlt : G.vertices.idxOf v < G.vertices.length := List.idxOf_lt_length_iff.2 hv + have hget : G.vertices.getD (G.vertices.idxOf v) 0 = v := by rw [List.getD_eq_getElem _ _ hlt, List.getElem_idxOf hlt] have hlen := G.length_vertices_le_encGraph rw [colorOf, countP_unaryBlocks (by rw [hget]; exact (hc v hv).le) @@ -151,7 +153,7 @@ theorem isVerifier (k : ℕ) : · exact Or.inr (hadj u hu v hv huv) · exact Or.inl huv · rintro ⟨w, -, hw⟩ - refine ⟨colorOf k G.1 w, fun v hv => (hw v hv).1, fun u hu v hv huv => ?_⟩ + refine ⟨colorOf k G.vertices w, fun v hv => (hw v hv).1, fun u hu v hv huv => ?_⟩ rcases (hw u hu).2 v hv with h | h · exact (h huv).elim · exact h @@ -171,16 +173,15 @@ theorem computableInPolyTime_colorOf (k : ℕ) : @[fun_prop] theorem computableInPolyTime_verifier (k : ℕ) : ComputableInPolyTime (verifier k) (encPair encGraph encBits) encBool := by - have hV : ComputableInPolyTime (fun x : Graph × List Bool => x.1.1) + have hV : ComputableInPolyTime (fun x : Graph × List Bool => x.1.vertices) (encPair encGraph encBits) (encList encNat) := by - unfold encGraph fun_prop have hcol : ComputableInPolyTime - (fun p : (Graph × List Bool) × ℕ => colorOf k p.1.1.1 p.1.2 p.2) + (fun p : (Graph × List Bool) × ℕ => colorOf k p.1.1.vertices p.1.2 p.2) (encPair (encPair encGraph encBits) encNat) encUnary := (computableInPolyTime_colorOf k).comp - (g := fun p : (Graph × List Bool) × ℕ => ((p.1.1.1, p.1.2), p.2)) - (by unfold encGraph; fun_prop) + (g := fun p : (Graph × List Bool) × ℕ => ((p.1.1.vertices, p.1.2), p.2)) + (by fun_prop) have hproj : ComputableInPolyTime (fun p : ((Graph × List Bool) × ℕ) × ℕ => ((p.1.1.1, p.1.2), p.2)) (encPair (encPair (encPair encGraph encBits) encNat) encNat) @@ -247,7 +248,7 @@ def coloringEdges (φ : CNF ℕ) : List (ℕ × ℕ) := /-- The reduction from `SAT` to `ThreeColoring`: the gadget graph of `φ`, whose declared vertices are the endpoints of its edges. -/ def toThreeColoring (φ : CNF ℕ) : Graph := - ((coloringEdges φ).flatMap fun e => [e.1, e.2], coloringEdges φ) + ⟨(coloringEdges φ).flatMap fun e => [e.1, e.2], coloringEdges φ⟩ /-! ### Correctness of the reduction -/ @@ -582,7 +583,7 @@ unary from clause, position and first-occurrence indices, then converted to bina @[fun_prop] theorem computableInPolyTime_toThreeColoring : ComputableInPolyTime toThreeColoring encCNF encGraph := by - unfold toThreeColoring encGraph + unfold toThreeColoring fun_prop /-! ### Size -/ @@ -591,7 +592,8 @@ theorem computableInPolyTime_toThreeColoring : literals, the graph of `SAT.toThreeColoring` has at most `(24 k + 12) m + 9` vertices and edges in total, counted as listed. -/ theorem length_toThreeColoring_le {k : ℕ} {φ : CNF ℕ} (hφ : ∀ c ∈ φ, c.length ≤ k) : - (toThreeColoring φ).1.length + (toThreeColoring φ).2.length ≤ (24 * k + 12) * φ.length + 9 := by + (toThreeColoring φ).vertices.length + (toThreeColoring φ).edges.length ≤ + (24 * k + 12) * φ.length + 9 := by have hzip : ∀ c ∈ φ.zipIdx, c.1.length ≤ k := fun c hc => by obtain ⟨hi, hcl⟩ := List.getElem?_eq_some_iff.1 (List.mem_zipIdx_iff_getElem?.1 hc) exact hcl ▸ hφ _ (List.getElem_mem hi) @@ -610,7 +612,7 @@ theorem length_toThreeColoring_le {k : ℕ} {φ : CNF ℕ} (hφ : ∀ c ∈ φ, have hE : (coloringEdges φ).length ≤ (8 * k + 4) * φ.length + 3 := by simp only [coloringEdges, List.length_append, List.length_cons, List.length_nil] nlinarith - have hV : (toThreeColoring φ).1.length = 2 * (coloringEdges φ).length := by + have hV : (toThreeColoring φ).vertices.length = 2 * (coloringEdges φ).length := by simp only [toThreeColoring, List.length_flatMap, List.length_cons, List.length_nil, List.map_const', List.sum_replicate, smul_eq_mul] ring @@ -633,7 +635,7 @@ def kColoringEdges (m : ℕ) (φ : CNF ℕ) : List (ℕ × ℕ) := /-- The reduction from `SAT` to `KColoring (m + 3)`: the gadget graph of `φ` with `m` universal vertices, which take `m` colors of their own. -/ def toKColoring (m : ℕ) (φ : CNF ℕ) : Graph := - ((kColoringEdges m φ).flatMap fun e => [e.1, e.2], kColoringEdges m φ) + ⟨(kColoringEdges m φ).flatMap fun e => [e.1, e.2], kColoringEdges m φ⟩ /-- Gadget vertices are labelled with tags `0`, `1` and `2`. -/ private theorem unpair_fst_lt_of_mem_coloringEdges {φ : CNF ℕ} {e : ℕ × ℕ} @@ -806,8 +808,8 @@ theorem computableInPolyTime_toKColoring (m : ℕ) : listAppend' computableInPolyTime_coloringEdges (flatMapWithInput hrange (listAppend' (mapWithInput hends (by fun_prop)) (mapWithInput hr2 (by fun_prop)))) - unfold toKColoring encGraph - exact (flatMapWithInput hke (by fun_prop)).prod hke + unfold toKColoring + exact encGraph_mk_polyTime (flatMapWithInput hke (by fun_prop)) hke end SAT diff --git a/CslibFrontier/Computability/Complexity/Problems/DominatingSet.lean b/CslibFrontier/Computability/Complexity/Problems/DominatingSet.lean index de5f210f35..bd73d1a662 100644 --- a/CslibFrontier/Computability/Complexity/Problems/DominatingSet.lean +++ b/CslibFrontier/Computability/Complexity/Problems/DominatingSet.lean @@ -119,15 +119,16 @@ GT2][GareyJohnson1979]: `k` declared vertices such that every declared vertex is to a chosen one. -/ @[expose] def DominatingSet : Set DominatingSetInstance := - {x | ∃ S : Finset {v // v ∈ x.graph.1}, x.graph.induced.IsDominatingSet S ∧ S.card = x.bound} + {x | ∃ S : Finset {v // v ∈ x.graph.vertices}, + x.graph.induced.IsDominatingSet S ∧ S.card = x.bound} /-- Membership in `DominatingSet` as a finite set of `k` declared vertices such that every declared vertex is chosen or adjacent to a chosen one. -/ @[simp] theorem mem_DominatingSet_iff {G : Graph} {k : ℕ} : (⟨G, k⟩ : DominatingSetInstance) ∈ DominatingSet ↔ - ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.1) ∧ - (∀ v ∈ G.1, v ∈ S ∨ ∃ s ∈ S, G.toSimpleGraph.Adj v s) ∧ S.card = k := by + ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.vertices) ∧ + (∀ v ∈ G.vertices, v ∈ S ∨ ∃ s ∈ S, G.toSimpleGraph.Adj v s) ∧ S.card = k := by constructor · rintro ⟨S, hS, hcard⟩ refine ⟨S.map (.subtype _), @@ -137,9 +138,9 @@ theorem mem_DominatingSet_iff {G : Graph} {k : ℕ} : · exact Or.inl (Finset.mem_map_of_mem _ h) · exact Or.inr ⟨s.1, Finset.mem_map_of_mem (.subtype _) hs, hadj⟩ · rintro ⟨S, hSV, hS, rfl⟩ - have hmap : (S.subtype (· ∈ G.1)).map (.subtype _) = S := + have hmap : (S.subtype (· ∈ G.vertices)).map (.subtype _) = S := (Finset.subtype_map _).trans (Finset.filter_eq_self.mpr hSV) - refine ⟨S.subtype (· ∈ G.1), SimpleGraph.isDominatingSet_iff.2 fun v => ?_, + refine ⟨S.subtype (· ∈ G.vertices), SimpleGraph.isDominatingSet_iff.2 fun v => ?_, by rw [← Finset.card_map (.subtype _), hmap]⟩ rcases hS v.1 v.2 with h | ⟨s, hs, hadj⟩ · exact Or.inl (Finset.mem_coe.2 (Finset.mem_subtype.2 h)) @@ -150,8 +151,8 @@ exactly when `k` is at most the number of distinct declared vertices and some do at most `k` vertices. -/ theorem mem_DominatingSet_iff_exists_card_le {G : Graph} {k : ℕ} : (⟨G, k⟩ : DominatingSetInstance) ∈ DominatingSet ↔ - k ≤ G.1.toFinset.card ∧ ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.1) ∧ - (∀ v ∈ G.1, v ∈ S ∨ ∃ s ∈ S, G.toSimpleGraph.Adj v s) ∧ S.card ≤ k := by + k ≤ G.vertices.toFinset.card ∧ ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.vertices) ∧ + (∀ v ∈ G.vertices, v ∈ S ∨ ∃ s ∈ S, G.toSimpleGraph.Adj v s) ∧ S.card ≤ k := by rw [mem_DominatingSet_iff] constructor · rintro ⟨S, hSV, hS, rfl⟩ @@ -169,11 +170,11 @@ namespace DominatingSet /-- Whether every declared vertex of `G` is in `C` or adjacent to a vertex of `C`. -/ @[expose] def isDominating (G : Graph) (C : List ℕ) : Bool := - G.1.all fun v => decide (v ∈ C) || C.any fun s => G.adjBool v s + G.vertices.all fun v => decide (v ∈ C) || C.any fun s => G.adjBool v s /-- `isDominating G C` holds exactly when `C` dominates the declared vertices of `G`. -/ theorem isDominating_eq_true_iff {G : Graph} {C : List ℕ} : - isDominating G C = true ↔ ∀ v ∈ G.1, v ∈ C ∨ ∃ s ∈ C, G.toSimpleGraph.Adj v s := by + isDominating G C = true ↔ ∀ v ∈ G.vertices, v ∈ C ∨ ∃ s ∈ C, G.toSimpleGraph.Adj v s := by simp only [isDominating, List.all_eq_true, Bool.or_eq_true, decide_eq_true_eq, List.any_eq_true, Graph.adjBool_eq_true_iff] @@ -188,14 +189,15 @@ theorem computableInPolyTime_isDominating : /-- `DominatingSet` belongs to `NP`: the certificate selects distinct declared vertices, and the verifier checks their number and `isDominating`. -/ theorem mem_NP : DominatingSet ∈ NP encDominatingSet := by - have hset : DominatingSet = {x | ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ x.graph.1) ∧ - ((∀ v ∈ x.graph.1, v ∈ S ∨ ∃ s ∈ S, x.graph.toSimpleGraph.Adj v s) ∧ + have hset : DominatingSet = {x | ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ x.graph.vertices) ∧ + ((∀ v ∈ x.graph.vertices, v ∈ S ∨ ∃ s ∈ S, x.graph.toSimpleGraph.Adj v s) ∧ S.card = x.bound)} := Set.ext fun ⟨_, _⟩ => mem_DominatingSet_iff rw [hset] - refine mem_NP_of_subsetWitness (encγ := encNat) (vs := fun x : DominatingSetInstance => x.graph.1) + refine mem_NP_of_subsetWitness (encγ := encNat) + (vs := fun x : DominatingSetInstance => x.graph.vertices) (check := fun y => decide (y.2.length = y.1.bound) && isDominating y.1.graph y.2) - ((fst _ _).comp (encDominatingSet_graph_polyTime (by fun_prop))) + (by fun_prop) (by fun_prop) fun x l hl _ => ?_ · simp only [Bool.and_eq_true, decide_eq_true_eq, isDominating_eq_true_iff, List.mem_toFinset, List.toFinset_card_of_nodup hl, and_comm] @@ -213,7 +215,7 @@ def properEdges (G : Graph) : List (ℕ × ℕ) := /-- An edge is proper when it is listed and joins two distinct declared vertices. -/ theorem mem_properEdges {G : Graph} {e : ℕ × ℕ} : - e ∈ properEdges G ↔ e ∈ G.2 ∧ e.1 ∈ G.1 ∧ e.2 ∈ G.1 ∧ e.1 ≠ e.2 := by + e ∈ properEdges G ↔ e ∈ G.edges ∧ e.1 ∈ G.vertices ∧ e.2 ∈ G.vertices ∧ e.1 ≠ e.2 := by simp only [properEdges, List.mem_filter, mem_declaredEdges, decide_eq_true_eq, and_assoc] /-- The vertex of the edge `e` in `dominationGraph`: an odd number at least `5`. -/ @@ -232,19 +234,20 @@ declared vertex `v`, a hub `1` adjacent to every copy, a pendant `3` adjacent to every proper edge `e` a vertex `edgeVertex e` adjacent to the copies of the endpoints of `e`. -/ @[expose] def dominationGraph (G : Graph) : Graph := - (1 :: 3 :: G.1.map (2 * ·) ++ (properEdges G).map edgeVertex, - (1, 3) :: G.1.map (fun v => (1, 2 * v)) ++ - (properEdges G).flatMap fun e => [(edgeVertex e, 2 * e.1), (edgeVertex e, 2 * e.2)]) + ⟨1 :: 3 :: G.vertices.map (2 * ·) ++ (properEdges G).map edgeVertex, + (1, 3) :: G.vertices.map (fun v => (1, 2 * v)) ++ + (properEdges G).flatMap fun e => [(edgeVertex e, 2 * e.1), (edgeVertex e, 2 * e.2)]⟩ /-- The vertices of `dominationGraph G`. -/ theorem mem_dominationGraph_fst {G : Graph} {w : ℕ} : - w ∈ (dominationGraph G).1 ↔ w = 1 ∨ w = 3 ∨ (∃ v ∈ G.1, 2 * v = w) ∨ + w ∈ (dominationGraph G).vertices ↔ w = 1 ∨ w = 3 ∨ (∃ v ∈ G.vertices, 2 * v = w) ∨ ∃ e ∈ properEdges G, edgeVertex e = w := by simp only [dominationGraph, List.mem_append, List.mem_cons, List.mem_map, or_assoc] /-- The edges of `dominationGraph G`. -/ theorem mem_dominationGraph_snd {G : Graph} {a b : ℕ} : - (a, b) ∈ (dominationGraph G).2 ↔ (a = 1 ∧ b = 3) ∨ (a = 1 ∧ ∃ v ∈ G.1, b = 2 * v) ∨ + (a, b) ∈ (dominationGraph G).edges ↔ (a = 1 ∧ b = 3) ∨ + (a = 1 ∧ ∃ v ∈ G.vertices, b = 2 * v) ∨ ∃ e ∈ properEdges G, a = edgeVertex e ∧ (b = 2 * e.1 ∨ b = 2 * e.2) := by simp only [dominationGraph, List.mem_append, List.mem_cons, List.mem_map, List.mem_flatMap, Prod.mk.injEq, List.not_mem_nil, or_false] @@ -263,7 +266,7 @@ theorem mem_dominationGraph_snd {G : Graph} {a b : ℕ} : private theorem adj_three_one (G : Graph) : (dominationGraph G).toSimpleGraph.Adj 3 1 := Graph.toSimpleGraph_adj.2 ⟨by decide, Or.inr (mem_dominationGraph_snd.2 (Or.inl ⟨rfl, rfl⟩))⟩ -private theorem adj_double_one {G : Graph} {v : ℕ} (hv : v ∈ G.1) : +private theorem adj_double_one {G : Graph} {v : ℕ} (hv : v ∈ G.vertices) : (dominationGraph G).toSimpleGraph.Adj (2 * v) 1 := Graph.toSimpleGraph_adj.2 ⟨by omega, Or.inr (mem_dominationGraph_snd.2 (Or.inr (Or.inl ⟨rfl, v, hv, rfl⟩)))⟩ @@ -385,13 +388,14 @@ private theorem exists_cover_of_mem_DominatingSet {G : Graph} {k : ℕ} · rcases of_adj_edgeVertex hadj with rfl | rfl · exact Or.inl (hdouble _ he1 hs) · exact Or.inr (hdouble _ he2 hs) - refine ⟨(((D.erase 1).erase 3).image decodeVertex).filter (· ∈ G.1), + refine ⟨(((D.erase 1).erase 3).image decodeVertex).filter (· ∈ G.vertices), fun v hv => (Finset.mem_filter.1 hv).2, fun u hu v hv huv => ?_, ?_⟩ · obtain ⟨hne, he | he⟩ := Graph.toSimpleGraph_adj.1 huv · exact hedge _ (mem_properEdges.2 ⟨he, hu, hv, hne⟩) · exact (hedge _ (mem_properEdges.2 ⟨he, hv, hu, hne.symm⟩)).symm · -- The pendant `3` is dominated by itself or by the hub, so `D` loses a vertex. - have h3 : (3 : ℕ) ∈ (dominationGraph G).1 := mem_dominationGraph_fst.2 (Or.inr (Or.inl rfl)) + have h3 : (3 : ℕ) ∈ (dominationGraph G).vertices := + mem_dominationGraph_fst.2 (Or.inr (Or.inl rfl)) have hD' : ((D.erase 1).erase 3).card + 1 ≤ D.card := by rcases hD 3 h3 with h3D | ⟨s, hs, hadj⟩ · have h3D' : 3 ∈ D.erase 1 := Finset.mem_erase.2 ⟨by decide, h3D⟩ @@ -402,7 +406,8 @@ private theorem exists_cover_of_mem_DominatingSet {G : Graph} {k : ℕ} have := Finset.card_erase_add_one hs have := Finset.card_erase_le (s := D.erase 1) (a := 3) omega - have := (Finset.card_filter_le (((D.erase 1).erase 3).image decodeVertex) (· ∈ G.1)).trans + have := (Finset.card_filter_le + (((D.erase 1).erase 3).image decodeVertex) (· ∈ G.vertices)).trans (Finset.card_image_le (s := (D.erase 1).erase 3) (f := decodeVertex)) omega @@ -443,8 +448,9 @@ separately: `fun_prop` alone times out on the whole graph. -/ @[fun_prop] theorem computableInPolyTime_dominationGraph : ComputableInPolyTime dominationGraph encGraph encGraph := by - have hfst : ComputableInPolyTime (fun G : Graph => G.1) encGraph (encList encNat) := fst _ _ - have hV : ComputableInPolyTime (fun G : Graph => 1 :: 3 :: G.1.map (2 * ·)) encGraph + have hfst : ComputableInPolyTime (fun G : Graph => G.vertices) encGraph (encList encNat) := by + fun_prop + have hV : ComputableInPolyTime (fun G : Graph => 1 :: 3 :: G.vertices.map (2 * ·)) encGraph (encList encNat) := (show ComputableInPolyTime (fun V : List ℕ => 1 :: 3 :: V.map (2 * ·)) (encList encNat) (encList encNat) by fun_prop).comp hfst @@ -453,7 +459,7 @@ theorem computableInPolyTime_dominationGraph : (show ComputableInPolyTime (fun E : List (ℕ × ℕ) => E.map edgeVertex) (encList (encPair encNat encNat)) (encList encNat) by fun_prop).comp computableInPolyTime_properEdges - have hEV : ComputableInPolyTime (fun G : Graph => (1, 3) :: G.1.map fun v => (1, 2 * v)) + have hEV : ComputableInPolyTime (fun G : Graph => (1, 3) :: G.vertices.map fun v => (1, 2 * v)) encGraph (encList (encPair encNat encNat)) := (show ComputableInPolyTime (fun V : List ℕ => (1, 3) :: V.map fun v => (1, 2 * v)) (encList encNat) (encList (encPair encNat encNat)) by fun_prop).comp hfst @@ -464,7 +470,9 @@ theorem computableInPolyTime_dominationGraph : E.flatMap fun e => [(edgeVertex e, 2 * e.1), (edgeVertex e, 2 * e.2)]) (encList (encPair encNat encNat)) (encList (encPair encNat encNat)) by fun_prop).comp computableInPolyTime_properEdges - exact (of_tidy (ComputableTidilyInPolyTime.listAppend' hV.tidy hW.tidy)).prod + unfold dominationGraph + exact encGraph_mk_polyTime + (of_tidy (ComputableTidilyInPolyTime.listAppend' hV.tidy hW.tidy)) (of_tidy (ComputableTidilyInPolyTime.listAppend' hEV.tidy hEW.tidy)) /-- The reduction `toDominatingSet` is polynomial-time: the binary target is compared with the @@ -472,13 +480,15 @@ number of distinct declared vertices, computed in unary. -/ @[fun_prop] theorem computableInPolyTime_toDominatingSet : ComputableInPolyTime toDominatingSet (encPair encGraph encNat) encDominatingSet := by - have hle : ComputableInPolyTime (fun x : Graph × ℕ => decide (x.2 ≤ x.1.1.toFinset.card)) + have hle : ComputableInPolyTime + (fun x : Graph × ℕ => decide (x.2 ≤ x.1.vertices.toFinset.card)) (encPair encGraph encNat) encBool := by simp_rw [← Graph.length_distinctVertices] fun_prop have heq : toDominatingSet = fun x : Graph × ℕ => (⟨dominationGraph x.1, - if decide (x.2 ≤ x.1.1.toFinset.card) then x.2 + 1 else 0⟩ : DominatingSetInstance) := by + if decide (x.2 ≤ x.1.vertices.toFinset.card) then x.2 + 1 else 0⟩ : + DominatingSetInstance) := by funext ⟨G, k⟩ simp only [toDominatingSet, decide_eq_true_eq] rw [heq] diff --git a/CslibFrontier/Computability/Complexity/Problems/FeedbackArcSet.lean b/CslibFrontier/Computability/Complexity/Problems/FeedbackArcSet.lean index 340ccfa93f..73614ccc81 100644 --- a/CslibFrontier/Computability/Complexity/Problems/FeedbackArcSet.lean +++ b/CslibFrontier/Computability/Complexity/Problems/FeedbackArcSet.lean @@ -102,12 +102,12 @@ directed graphs with a number `k` such that removing some set of at most `k` arc directed cycle. -/ @[expose] def FeedbackArcSet : Set FeedbackArcSetInstance := - {x | ∃ S : Finset (ℕ × ℕ), (∀ e ∈ S, e ∈ x.graph.2) ∧ S.card ≤ x.bound ∧ + {x | ∃ S : Finset (ℕ × ℕ), (∀ e ∈ S, e ∈ x.graph.edges) ∧ S.card ≤ x.bound ∧ (x.graph.deleteArcs S).IsDirAcyclic} theorem mem_FeedbackArcSet_iff {G : Graph} {k : ℕ} : (⟨G, k⟩ : FeedbackArcSetInstance) ∈ FeedbackArcSet ↔ - ∃ S : Finset (ℕ × ℕ), (∀ e ∈ S, e ∈ G.2) ∧ S.card ≤ k ∧ (G.deleteArcs S).IsDirAcyclic := + ∃ S : Finset (ℕ × ℕ), (∀ e ∈ S, e ∈ G.edges) ∧ S.card ≤ k ∧ (G.deleteArcs S).IsDirAcyclic := Iff.rfl namespace FeedbackArcSet @@ -117,11 +117,13 @@ namespace FeedbackArcSet /-- The arcs between declared vertices that the listed numbering `ρ` does not increase. -/ @[expose] def backArcs (G : Graph) (ρ : List ℕ) : List (ℕ × ℕ) := - G.2.filter fun e => - decide (e.1 ∈ G.1) && decide (e.2 ∈ G.1) && decide (colorOf G ρ e.2 ≤ colorOf G ρ e.1) + G.edges.filter fun e => + decide (e.1 ∈ G.vertices) && decide (e.2 ∈ G.vertices) && + decide (colorOf G ρ e.2 ≤ colorOf G ρ e.1) theorem mem_backArcs {G : Graph} {ρ : List ℕ} {e : ℕ × ℕ} : - e ∈ backArcs G ρ ↔ e ∈ G.2 ∧ e.1 ∈ G.1 ∧ e.2 ∈ G.1 ∧ colorOf G ρ e.2 ≤ colorOf G ρ e.1 := by + e ∈ backArcs G ρ ↔ e ∈ G.edges ∧ e.1 ∈ G.vertices ∧ e.2 ∈ G.vertices ∧ + colorOf G ρ e.2 ≤ colorOf G ρ e.1 := by simp only [backArcs, List.mem_filter, Bool.and_eq_true, decide_eq_true_eq, and_assoc] /-- The check of a listed numbering: at most `k` distinct arcs are back arcs. -/ @@ -133,8 +135,8 @@ private theorem computableInPolyTime_backArcs : ComputableInPolyTime (fun x : FeedbackArcSetInstance × List ℕ => backArcs x.1.graph x.2) (encPair encFeedbackArcSet (encList encNat)) (encList (encPair encNat encNat)) := by unfold backArcs - exact filterWithInput (Graph.computableInPolyTime_snd (by fun_prop)) - (by fun_prop [Graph.computableInPolyTime_fst]) + exact filterWithInput (Graph.computableInPolyTime_edges (by fun_prop)) + (by fun_prop [Graph.computableInPolyTime_vertices]) private theorem computableInPolyTime_check : ComputableInPolyTime check (encPair encFeedbackArcSet (encList encNat)) encBool := by @@ -174,7 +176,8 @@ private theorem mem_of_certifies {G : Graph} {k : ℕ} {ρ : List ℕ} (h : Cert vertex list, whose back arcs lie in a feedback arc set. -/ private theorem exists_certifies {G : Graph} {k : ℕ} (h : (⟨G, k⟩ : FeedbackArcSetInstance) ∈ FeedbackArcSet) : - ∃ g : ℕ → ℕ, (∀ v ∈ G.1, g v ≤ G.1.toFinset.card) ∧ Certifies ⟨G, k⟩ (G.1.map g) := by + ∃ g : ℕ → ℕ, (∀ v ∈ G.vertices, g v ≤ G.vertices.toFinset.card) ∧ + Certifies ⟨G, k⟩ (G.vertices.map g) := by classical obtain ⟨S, -, hcard, hacyc⟩ := h obtain ⟨f, hflt, hf⟩ := exists_rank_of_isDirAcyclic hacyc @@ -194,7 +197,8 @@ theorem mem_NP : FeedbackArcSet ∈ NP encFeedbackArcSet := by exact G.length_vertices_le_encGraph.trans (length_le_encPair_left _ _ (G, k))) (fun ⟨G, k⟩ ρ h => mem_of_certifies ((check_iff (⟨G, k⟩, ρ)).1 h)) fun ⟨G, k⟩ h => ?_ obtain ⟨g, hgle, hg⟩ := exists_certifies h - exact ⟨g, fun v hv => (hgle v hv).trans G.1.toFinset_card_le, (check_iff (⟨G, k⟩, _)).2 hg⟩ + exact ⟨g, fun v hv => (hgle v hv).trans G.vertices.toFinset_card_le, + (check_iff (⟨G, k⟩, _)).2 hg⟩ end FeedbackArcSet @@ -206,14 +210,14 @@ namespace FeedbackVertexSet becomes the arc `2u + 1 → 2v`. -/ @[expose] def splitVertices (G : Graph) : Graph := - (G.1.map (2 * ·) ++ G.1.map (2 * · + 1), - G.1.map (fun v => (2 * v, 2 * v + 1)) ++ G.2.map fun e => (2 * e.1 + 1, 2 * e.2)) + ⟨G.vertices.map (2 * ·) ++ G.vertices.map (2 * · + 1), + G.vertices.map (fun v => (2 * v, 2 * v + 1)) ++ G.edges.map fun e => (2 * e.1 + 1, 2 * e.2)⟩ /-- The arcs of the split graph: the arc `2v → 2v + 1` of a declared vertex `v`, and the arc `2u + 1 → 2v` of an arc `u → v` between declared vertices. -/ theorem dirAdj_splitVertices {G : Graph} {a b : ℕ} : (splitVertices G).DirAdj a b ↔ - (∃ v ∈ G.1, a = 2 * v ∧ b = 2 * v + 1) ∨ + (∃ v ∈ G.vertices, a = 2 * v ∧ b = 2 * v + 1) ∨ ∃ u v, G.DirAdj u v ∧ a = 2 * u + 1 ∧ b = 2 * v := by simp only [DirAdj, splitVertices, List.mem_append, List.mem_map, Prod.mk.injEq, Prod.exists] constructor @@ -267,7 +271,8 @@ theorem toFeedbackArcSet_mem_iff (x : FeedbackVertexSetInstance) : · rintro ⟨S, -, hcard, hacyc⟩ obtain ⟨g, -, hg⟩ := exists_rank_of_isDirAcyclic hacyc -- Remove the vertices whose arcs `2v → 2v + 1` or `2v + 1 → 2w` were removed. - refine ⟨(S.image fun e => e.1 / 2).filter (· ∈ G.1), fun v hv => (Finset.mem_filter.1 hv).2, + refine ⟨(S.image fun e => e.1 / 2).filter (· ∈ G.vertices), + fun v hv => (Finset.mem_filter.1 hv).2, (Finset.card_filter_le _ _).trans (Finset.card_image_le.trans hcard), isDirAcyclic_of_rank (f := fun v => g (2 * v + 1)) fun u v huv => ?_⟩ rw [dirAdj_deleteVerts] at huv @@ -284,7 +289,7 @@ theorem toFeedbackArcSet_mem_iff (x : FeedbackVertexSetInstance) : -- Remove the arcs `2v → 2v + 1` of the removed vertices. refine ⟨R.image fun v => (2 * v, 2 * v + 1), fun e he => ?_, Finset.card_image_le.trans hcard, isDirAcyclic_of_rank - (f := splitRank R f (G.deleteVerts R).1.toFinset.card) fun a b hab => ?_⟩ + (f := splitRank R f (G.deleteVerts R).vertices.toFinset.card) fun a b hab => ?_⟩ · obtain ⟨v, hv, rfl⟩ := Finset.mem_image.1 he exact List.mem_append_left _ (List.mem_map.2 ⟨v, hRV v hv, rfl⟩) · rw [dirAdj_deleteArcs, dirAdj_splitVertices] at hab @@ -296,7 +301,7 @@ theorem toFeedbackArcSet_mem_iff (x : FeedbackVertexSetInstance) : by_cases huR : u ∈ R · simp only [huR, ↓reduceIte] split <;> omega - · have hu := hflt u (mem_deleteVerts_fst.2 ⟨huv.1, huR⟩) + · have hu := hflt u (mem_deleteVerts_vertices.2 ⟨huv.1, huR⟩) by_cases hvR : v ∈ R · simp only [huR, hvR, ↓reduceIte] omega @@ -310,7 +315,7 @@ theorem computableInPolyTime_toFeedbackArcSet : ComputableInPolyTime toFeedbackArcSet encFeedbackVertexSet encFeedbackArcSet := by have hs : ComputableInPolyTime (fun x : FeedbackVertexSetInstance => splitVertices x.graph) encFeedbackVertexSet encGraph := by - unfold splitVertices encGraph + unfold splitVertices fun_prop unfold toFeedbackArcSet fun_prop diff --git a/CslibFrontier/Computability/Complexity/Problems/FeedbackVertexSet.lean b/CslibFrontier/Computability/Complexity/Problems/FeedbackVertexSet.lean index 2298272b57..6371ec2b62 100644 --- a/CslibFrontier/Computability/Complexity/Problems/FeedbackVertexSet.lean +++ b/CslibFrontier/Computability/Complexity/Problems/FeedbackVertexSet.lean @@ -96,19 +96,19 @@ directed graphs with a number `k` such that removing some set of at most `k` dec leaves no directed cycle. -/ @[expose] def FeedbackVertexSet : Set FeedbackVertexSetInstance := - {x | ∃ R : Finset ℕ, (∀ v ∈ R, v ∈ x.graph.1) ∧ R.card ≤ x.bound ∧ + {x | ∃ R : Finset ℕ, (∀ v ∈ R, v ∈ x.graph.vertices) ∧ R.card ≤ x.bound ∧ (x.graph.deleteVerts R).IsDirAcyclic} theorem mem_FeedbackVertexSet_iff {G : Graph} {k : ℕ} : (⟨G, k⟩ : FeedbackVertexSetInstance) ∈ FeedbackVertexSet ↔ - ∃ R : Finset ℕ, (∀ v ∈ R, v ∈ G.1) ∧ R.card ≤ k ∧ (G.deleteVerts R).IsDirAcyclic := + ∃ R : Finset ℕ, (∀ v ∈ R, v ∈ G.vertices) ∧ R.card ≤ k ∧ (G.deleteVerts R).IsDirAcyclic := Iff.rfl /-- Membership in `FeedbackVertexSet` through cycles: at most `k` declared vertices meet every directed cycle. -/ theorem mem_FeedbackVertexSet_iff_forall_isDirCycle {G : Graph} {k : ℕ} : (⟨G, k⟩ : FeedbackVertexSetInstance) ∈ FeedbackVertexSet ↔ - ∃ R : Finset ℕ, (∀ v ∈ R, v ∈ G.1) ∧ R.card ≤ k ∧ + ∃ R : Finset ℕ, (∀ v ∈ R, v ∈ G.vertices) ∧ R.card ≤ k ∧ ∀ c, G.IsDirCycle c → ∃ v ∈ c, v ∈ R := by simp only [mem_FeedbackVertexSet_iff, isDirAcyclic_deleteVerts_iff] @@ -123,21 +123,21 @@ private def zeroCount (x : FeedbackVertexSetInstance × List ℕ) : ℕ := /-- The check of one arc under a listed numbering: unless both ends are declared vertices with nonzero numbers, the arc increases the number. -/ private def arcOK (x : FeedbackVertexSetInstance × List ℕ) (e : ℕ × ℕ) : Bool := - !(decide (e.1 ∈ x.1.graph.1) && decide (e.2 ∈ x.1.graph.1) && + !(decide (e.1 ∈ x.1.graph.vertices) && decide (e.2 ∈ x.1.graph.vertices) && decide (colorOf x.1.graph x.2 e.1 ≠ 0) && decide (colorOf x.1.graph x.2 e.2 ≠ 0)) || decide (colorOf x.1.graph x.2 e.1 < colorOf x.1.graph x.2 e.2) /-- The check of a listed numbering: at most `k` distinct declared vertices are numbered `0`, and every arc between declared vertices with nonzero numbers increases the number. -/ private def check (x : FeedbackVertexSetInstance × List ℕ) : Bool := - decide (zeroCount x ≤ x.1.bound) && x.1.graph.2.all (arcOK x) + decide (zeroCount x ≤ x.1.bound) && x.1.graph.edges.all (arcOK x) private theorem computableInPolyTime_arcOK : ComputableInPolyTime (fun p : (FeedbackVertexSetInstance × List ℕ) × (ℕ × ℕ) => arcOK p.1 p.2) (encPair (encPair encFeedbackVertexSet (encList encNat)) (encPair encNat encNat)) encBool := by unfold arcOK - fun_prop [Graph.computableInPolyTime_fst] + fun_prop [Graph.computableInPolyTime_vertices] private theorem computableInPolyTime_check : ComputableInPolyTime check (encPair encFeedbackVertexSet (encList encNat)) encBool := by @@ -146,12 +146,13 @@ private theorem computableInPolyTime_check : unfold zeroCount exact countPWithInput (by fun_prop) (by fun_prop) exact bool_and.comp ((binary_le' (unary_to_binary.comp hz) (by fun_prop)).prod - (allWithInput (Graph.computableInPolyTime_snd (by fun_prop)) computableInPolyTime_arcOK)) + (allWithInput (Graph.computableInPolyTime_edges (by fun_prop)) computableInPolyTime_arcOK)) /-- The distinct declared vertices numbered `0` are counted by `zeroCount`. -/ private theorem zeroCount_eq (x : FeedbackVertexSetInstance × List ℕ) : - zeroCount x = (x.1.graph.1.toFinset.filter fun v => colorOf x.1.graph x.2 v = 0).card := by - have hset : x.1.graph.distinctVertices.toFinset = x.1.graph.1.toFinset := by + zeroCount x = + (x.1.graph.vertices.toFinset.filter fun v => colorOf x.1.graph x.2 v = 0).card := by + have hset : x.1.graph.distinctVertices.toFinset = x.1.graph.vertices.toFinset := by ext v simp only [List.mem_toFinset, mem_distinctVertices] rw [zeroCount, List.countP_eq_length_filter, @@ -160,14 +161,14 @@ private theorem zeroCount_eq (x : FeedbackVertexSetInstance × List ℕ) : private theorem check_iff (x : FeedbackVertexSetInstance × List ℕ) : check x = true ↔ - (x.1.graph.1.toFinset.filter fun v => colorOf x.1.graph x.2 v = 0).card ≤ x.1.bound ∧ - ∀ e ∈ x.1.graph.2, e.1 ∈ x.1.graph.1 → e.2 ∈ x.1.graph.1 → + (x.1.graph.vertices.toFinset.filter fun v => colorOf x.1.graph x.2 v = 0).card ≤ x.1.bound ∧ + ∀ e ∈ x.1.graph.edges, e.1 ∈ x.1.graph.vertices → e.2 ∈ x.1.graph.vertices → colorOf x.1.graph x.2 e.1 ≠ 0 → colorOf x.1.graph x.2 e.2 ≠ 0 → colorOf x.1.graph x.2 e.1 < colorOf x.1.graph x.2 e.2 := by simp only [check, arcOK, zeroCount_eq, Bool.and_eq_true, decide_eq_true_eq, List.all_eq_true, Bool.or_eq_true, Bool.not_eq_true', Bool.and_eq_false_iff, decide_eq_false_iff_not, not_not] refine and_congr_right fun _ => forall₂_congr fun e _ => ?_ - by_cases h1 : e.1 ∈ x.1.graph.1 <;> by_cases h2 : e.2 ∈ x.1.graph.1 <;> + by_cases h1 : e.1 ∈ x.1.graph.vertices <;> by_cases h2 : e.2 ∈ x.1.graph.vertices <;> by_cases h3 : colorOf x.1.graph x.2 e.1 = 0 <;> by_cases h4 : colorOf x.1.graph x.2 e.2 = 0 <;> simp only [h1, h2, h3, h4, not_true, not_false_eq_true, true_or, or_true, false_or, or_false, @@ -176,15 +177,16 @@ private theorem check_iff (x : FeedbackVertexSetInstance × List ℕ) : /-- The witness relation: the number of distinct declared vertices numbered `0` is at most `k`, and every arc between declared vertices with nonzero numbers increases the number. -/ private def Certifies (x : FeedbackVertexSetInstance) (ρ : List ℕ) : Prop := - (x.graph.1.toFinset.filter fun v => colorOf x.graph ρ v = 0).card ≤ x.bound ∧ - ∀ e ∈ x.graph.2, e.1 ∈ x.graph.1 → e.2 ∈ x.graph.1 → colorOf x.graph ρ e.1 ≠ 0 → + (x.graph.vertices.toFinset.filter fun v => colorOf x.graph ρ v = 0).card ≤ x.bound ∧ + ∀ e ∈ x.graph.edges, e.1 ∈ x.graph.vertices → e.2 ∈ x.graph.vertices → + colorOf x.graph ρ e.1 ≠ 0 → colorOf x.graph ρ e.2 ≠ 0 → colorOf x.graph ρ e.1 < colorOf x.graph ρ e.2 /-- A numbering removing the vertices numbered `0` shows membership. -/ private theorem mem_of_certifies {G : Graph} {k : ℕ} {ρ : List ℕ} (h : Certifies ⟨G, k⟩ ρ) : (⟨G, k⟩ : FeedbackVertexSetInstance) ∈ FeedbackVertexSet := by obtain ⟨hcard, hρ⟩ := h - refine ⟨G.1.toFinset.filter fun v => colorOf G ρ v = 0, fun v hv => ?_, hcard, + refine ⟨G.vertices.toFinset.filter fun v => colorOf G ρ v = 0, fun v hv => ?_, hcard, isDirAcyclic_of_rank (f := colorOf G ρ) fun u v huv => ?_⟩ · exact List.mem_toFinset.1 (Finset.mem_filter.1 hv).1 · rw [dirAdj_deleteVerts] at huv @@ -196,19 +198,20 @@ private theorem mem_of_certifies {G : Graph} {k : ℕ} {ρ : List ℕ} (h : Cert vertex list. -/ private theorem exists_certifies {G : Graph} {k : ℕ} (h : (⟨G, k⟩ : FeedbackVertexSetInstance) ∈ FeedbackVertexSet) : - ∃ g : ℕ → ℕ, (∀ v ∈ G.1, g v ≤ G.1.toFinset.card) ∧ Certifies ⟨G, k⟩ (G.1.map g) := by + ∃ g : ℕ → ℕ, (∀ v ∈ G.vertices, g v ≤ G.vertices.toFinset.card) ∧ + Certifies ⟨G, k⟩ (G.vertices.map g) := by classical obtain ⟨R, hRV, hcard, hacyc⟩ := h obtain ⟨f, hflt, hf⟩ := exists_rank_of_isDirAcyclic hacyc - have hsub : (G.deleteVerts R).1.toFinset.card ≤ G.1.toFinset.card := + have hsub : (G.deleteVerts R).vertices.toFinset.card ≤ G.vertices.toFinset.card := Finset.card_le_card fun v hv => List.mem_toFinset.2 - (mem_deleteVerts_fst.1 (List.mem_toFinset.1 hv)).1 + (mem_deleteVerts_vertices.1 (List.mem_toFinset.1 hv)).1 refine ⟨fun v => if v ∈ R then 0 else f v + 1, fun v hv => ?_, ?_, fun e he h1 h2 h3 h4 => ?_⟩ · dsimp only split · exact Nat.zero_le _ - · exact (hflt v (mem_deleteVerts_fst.2 ⟨hv, ‹_›⟩)).trans_le hsub - · have hfilter : (G.1.toFinset.filter fun v => colorOf G (G.1.map fun v => + · exact (hflt v (mem_deleteVerts_vertices.2 ⟨hv, ‹_›⟩)).trans_le hsub + · have hfilter : (G.vertices.toFinset.filter fun v => colorOf G (G.vertices.map fun v => if v ∈ R then 0 else f v + 1) v = 0) = R := by ext v simp only [Finset.mem_filter, List.mem_toFinset] @@ -242,7 +245,8 @@ theorem mem_NP : FeedbackVertexSet ∈ NP encFeedbackVertexSet := by exact G.length_vertices_le_encGraph.trans (length_le_encPair_left _ _ (G, k))) (fun ⟨G, k⟩ ρ h => mem_of_certifies ((check_iff (⟨G, k⟩, ρ)).1 h)) fun ⟨G, k⟩ h => ?_ obtain ⟨g, hgle, hg⟩ := exists_certifies h - exact ⟨g, fun v hv => (hgle v hv).trans G.1.toFinset_card_le, (check_iff (⟨G, k⟩, _)).2 hg⟩ + exact ⟨g, fun v hv => (hgle v hv).trans G.vertices.toFinset_card_le, + (check_iff (⟨G, k⟩, _)).2 hg⟩ end FeedbackVertexSet @@ -254,12 +258,12 @@ namespace VertexCover two are the edges. -/ @[expose] def bidirect (G : Graph) : Graph := - (G.1, G.2.filter (fun e => decide (e.1 ≠ e.2)) ++ - (G.2.filter fun e => decide (e.1 ≠ e.2)).map fun e => (e.2, e.1)) + ⟨G.vertices, G.edges.filter (fun e => decide (e.1 ≠ e.2)) ++ + (G.edges.filter fun e => decide (e.1 ≠ e.2)).map fun e => (e.2, e.1)⟩ /-- The arcs of `bidirect G` join the adjacent declared vertices of `G`. -/ theorem dirAdj_bidirect {G : Graph} {u v : ℕ} : - (bidirect G).DirAdj u v ↔ u ∈ G.1 ∧ v ∈ G.1 ∧ G.toSimpleGraph.Adj u v := by + (bidirect G).DirAdj u v ↔ u ∈ G.vertices ∧ v ∈ G.vertices ∧ G.toSimpleGraph.Adj u v := by simp only [DirAdj, bidirect, List.mem_append, List.mem_filter, List.mem_map, decide_eq_true_eq, Prod.exists, Prod.mk.injEq, toSimpleGraph_adj] constructor @@ -275,7 +279,7 @@ arcs, or produce a self-loop with target `0` when the target exceeds the number declared vertices. -/ @[expose] def toFeedbackVertexSet (x : Graph × ℕ) : FeedbackVertexSetInstance := - if x.2 ≤ x.1.1.toFinset.card then ⟨bidirect x.1, x.2⟩ else ⟨([0], [(0, 0)]), 0⟩ + if x.2 ≤ x.1.vertices.toFinset.card then ⟨bidirect x.1, x.2⟩ else ⟨⟨[0], [(0, 0)]⟩, 0⟩ /-- **Vertex covers are feedback vertex sets of the bidirected graph.** -/ theorem toFeedbackVertexSet_mem_iff (x : Graph × ℕ) : @@ -317,17 +321,17 @@ unary and compared with the binary target. -/ @[fun_prop] theorem computableInPolyTime_toFeedbackVertexSet : ComputableInPolyTime toFeedbackVertexSet (encPair encGraph encNat) encFeedbackVertexSet := by - have hn : ComputableInPolyTime (fun x : Graph × ℕ => x.1.1.toFinset.card) + have hn : ComputableInPolyTime (fun x : Graph × ℕ => x.1.vertices.toFinset.card) (encPair encGraph encNat) encUnary := by simp_rw [← Graph.length_distinctVertices] exact (length_list encNat).comp (Graph.computableInPolyTime_distinctVertices.comp (fst _ _)) have hb : ComputableInPolyTime (fun x : Graph × ℕ => bidirect x.1) (encPair encGraph encNat) encGraph := by - unfold bidirect encGraph + unfold bidirect fun_prop have heq : toFeedbackVertexSet = fun x : Graph × ℕ => - if decide (x.2 ≤ x.1.1.toFinset.card) then ⟨bidirect x.1, x.2⟩ - else ⟨([0], [(0, 0)]), 0⟩ := by + if decide (x.2 ≤ x.1.vertices.toFinset.card) then ⟨bidirect x.1, x.2⟩ + else ⟨⟨[0], [(0, 0)]⟩, 0⟩ := by funext x simp only [toFeedbackVertexSet, decide_eq_true_eq] rw [heq] diff --git a/CslibFrontier/Computability/Complexity/Problems/GeneralizedGeography.lean b/CslibFrontier/Computability/Complexity/Problems/GeneralizedGeography.lean index fa5804317e..f8cc9694c0 100644 --- a/CslibFrontier/Computability/Complexity/Problems/GeneralizedGeography.lean +++ b/CslibFrontier/Computability/Complexity/Problems/GeneralizedGeography.lean @@ -64,8 +64,10 @@ namespace Cslib.Complexity namespace Graph /-- A move to an unvisited declared vertex leaves fewer unvisited declared vertices. -/ -theorem card_sdiff_cons_lt {G : Graph} {vis : List ℕ} {w : ℕ} (hw : w ∈ G.1) (hvis : w ∉ vis) : - (G.1.toFinset \ (w :: vis).toFinset).card < (G.1.toFinset \ vis.toFinset).card := by +theorem card_sdiff_cons_lt {G : Graph} {vis : List ℕ} {w : ℕ} (hw : w ∈ G.vertices) + (hvis : w ∉ vis) : + (G.vertices.toFinset \ (w :: vis).toFinset).card < + (G.vertices.toFinset \ vis.toFinset).card := by rw [List.toFinset_cons, Finset.sdiff_insert] exact Finset.card_erase_lt_of_mem (Finset.mem_sdiff.2 ⟨List.mem_toFinset.2 hw, fun h => hvis (List.mem_toFinset.1 h)⟩) @@ -75,14 +77,14 @@ to move wins when some arc from `v` leads to an unvisited declared vertex `w` fr player, to move with `w` visited, does not win. -/ @[expose] def GeographyWins (G : Graph) (vis : List ℕ) (v : ℕ) : Prop := - ∃ w, ∃ _ : (v, w) ∈ G.2 ∧ w ∈ G.1 ∧ w ∉ vis, ¬G.GeographyWins (w :: vis) w -termination_by (G.1.toFinset \ vis.toFinset).card + ∃ w, ∃ _ : (v, w) ∈ G.edges ∧ w ∈ G.vertices ∧ w ∉ vis, ¬G.GeographyWins (w :: vis) w +termination_by (G.vertices.toFinset \ vis.toFinset).card decreasing_by rename_i h; exact card_sdiff_cons_lt h.2.1 h.2.2 /-- **The backward induction of Generalized Geography.** -/ theorem geographyWins_iff {G : Graph} {vis : List ℕ} {v : ℕ} : G.GeographyWins vis v ↔ - ∃ w, (v, w) ∈ G.2 ∧ w ∈ G.1 ∧ w ∉ vis ∧ ¬G.GeographyWins (w :: vis) w := by + ∃ w, (v, w) ∈ G.edges ∧ w ∈ G.vertices ∧ w ∉ vis ∧ ¬G.GeographyWins (w :: vis) w := by rw [GeographyWins] simp only [exists_prop, and_assoc] @@ -90,14 +92,14 @@ theorem geographyWins_iff {G : Graph} {vis : List ℕ} {v : ℕ} : player. -/ theorem not_geographyWins_iff {G : Graph} {vis : List ℕ} {v : ℕ} : ¬G.GeographyWins vis v ↔ - ∀ w, (v, w) ∈ G.2 → w ∈ G.1 → w ∉ vis → G.GeographyWins (w :: vis) w := by + ∀ w, (v, w) ∈ G.edges → w ∈ G.vertices → w ∉ vis → G.GeographyWins (w :: vis) w := by rw [geographyWins_iff] simp only [not_exists, not_and, not_not] /-- **Only the set of visited vertices matters.** -/ theorem geographyWins_congr {G : Graph} {vis vis' : List ℕ} (h : ∀ u, u ∈ vis ↔ u ∈ vis') (v : ℕ) : G.GeographyWins vis v ↔ G.GeographyWins vis' v := by - suffices ∀ n (vis vis' : List ℕ), (G.1.toFinset \ vis.toFinset).card < n → + suffices ∀ n (vis vis' : List ℕ), (G.vertices.toFinset \ vis.toFinset).card < n → (∀ u, u ∈ vis ↔ u ∈ vis') → ∀ v, G.GeographyWins vis v → G.GeographyWins vis' v from ⟨this _ vis vis' (Nat.lt_succ_self _) h v, this _ vis' vis (Nat.lt_succ_self _) (fun u => (h u).symm) v⟩ @@ -111,7 +113,8 @@ theorem geographyWins_congr {G : Graph} {vis vis' : List ℕ} (h : ∀ u, u ∈ have hlt := card_sdiff_cons_lt hw hwvis refine ih (w :: vis') (w :: vis) ?_ (fun u => ?_) w hwin' · have hcard : - (G.1.toFinset \ (w :: vis').toFinset) = (G.1.toFinset \ (w :: vis).toFinset) := by + (G.vertices.toFinset \ (w :: vis').toFinset) = + (G.vertices.toFinset \ (w :: vis).toFinset) := by ext u simp only [Finset.mem_sdiff, List.mem_toFinset, List.mem_cons, h] rw [hcard] @@ -150,7 +153,7 @@ theorem mem_GeneralizedGeography {x : GeneralizedGeographyInstance} : /-- On a graph without arcs the first player cannot move and loses. -/ theorem notMem_GeneralizedGeography_of_nil {V : List ℕ} {s : ℕ} : - (⟨(V, []), s⟩ : GeneralizedGeographyInstance) ∉ GeneralizedGeography := by + (⟨⟨V, []⟩, s⟩ : GeneralizedGeographyInstance) ∉ GeneralizedGeography := by rw [mem_GeneralizedGeography, Graph.geographyWins_iff] simp diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph.lean b/CslibFrontier/Computability/Complexity/Problems/Graph.lean index 66a404c9c2..f94768321f 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph.lean @@ -8,6 +8,7 @@ module -- upstream: candidate public import CslibFrontier.Computability.Complexity.Classes.NP.Witness +public import CslibFrontier.Computability.Machines.Turing.MultiTape.Encodings.Structure public import Mathlib.Combinatorics.SimpleGraph.Basic public import Mathlib.Combinatorics.SimpleGraph.Maps public import Mathlib.Data.Fintype.Sets @@ -93,30 +94,39 @@ namespace Cslib.Complexity open Turing.MultiTapeTM ComputableInPolyTime /-- A graph on natural numbers, given by its vertices and its edges. -/ -abbrev Graph : Type := List ℕ × List (ℕ × ℕ) +@[ext] +structure Graph where + /-- The declared vertices of the graph, possibly with repetitions. -/ + vertices : List ℕ + /-- The declared edges of the graph as pairs of vertex identifiers. -/ + edges : List (ℕ × ℕ) + deriving DecidableEq, Repr, Inhabited /-- Graphs encoded by framed lists of binary vertex labels and binary edge pairs. -/ -def encGraph : Graph ↪ List Bool := - encPair (encList encNat) (encList (encPair encNat encNat)) +enc_structure encGraph : Graph where + vertices : (encList encNat), + edges : (encList (encPair encNat encNat)) + +attribute [fun_prop] encGraph_vertices_polyTime encGraph_edges_polyTime encGraph_mk_polyTime /-- The simple graph of an edge list: two distinct vertices are adjacent when an edge joins them, in either direction. -/ -def Graph.toSimpleGraph (G : Graph) : SimpleGraph ℕ := SimpleGraph.fromRel fun u v => (u, v) ∈ G.2 - +def Graph.toSimpleGraph (G : Graph) : SimpleGraph ℕ := + SimpleGraph.fromRel fun u v => (u, v) ∈ G.edges /-- Adjacency in the simple graph of an edge list: two distinct vertices joined by an edge in either direction. It is not a `simp` lemma, so that the adjacency stays folded until a proof unfolds it. -/ theorem Graph.toSimpleGraph_adj {G : Graph} {u v : ℕ} : - G.toSimpleGraph.Adj u v ↔ u ≠ v ∧ ((u, v) ∈ G.2 ∨ (v, u) ∈ G.2) := + G.toSimpleGraph.Adj u v ↔ u ≠ v ∧ ((u, v) ∈ G.edges ∨ (v, u) ∈ G.edges) := SimpleGraph.fromRel_adj _ _ _ /-- The simple graph induced by `G` on its declared vertices. -/ -def Graph.induced (G : Graph) : SimpleGraph {v // v ∈ G.1} := - G.toSimpleGraph.induce {v | v ∈ G.1} +def Graph.induced (G : Graph) : SimpleGraph {v // v ∈ G.vertices} := + G.toSimpleGraph.induce {v | v ∈ G.vertices} @[simp] -theorem Graph.induced_adj {G : Graph} {u v : {v // v ∈ G.1}} : +theorem Graph.induced_adj {G : Graph} {u v : {v // v ∈ G.vertices}} : G.induced.Adj u v ↔ G.toSimpleGraph.Adj u v := Iff.rfl @@ -125,40 +135,42 @@ namespace Graph /-! ### Declared vertices -/ /-- A graph has no more declared vertices than the length of its encoding. -/ -theorem length_vertices_le_encGraph (G : Graph) : G.1.length ≤ (encGraph G).length := by - calc G.1.length ≤ (encList encNat G.1).length := length_le_length_encList encNat G.1 - _ ≤ (encGraph G).length := by enc_len [encGraph] +theorem length_vertices_le_encGraph (G : Graph) : G.vertices.length ≤ (encGraph G).length := by + calc G.vertices.length ≤ (encList encNat G.vertices).length := + length_le_length_encList encNat G.vertices + _ ≤ (encGraph G).length := by enc_len [encGraph_apply] /-- A graph lists no more vertices and edges than the length of its encoding. -/ theorem length_vertices_add_length_edges_le_encGraph (G : Graph) : - G.1.length + G.2.length ≤ (encGraph G).length := by - have h₁ := length_le_length_encList encNat G.1 - have h₂ := length_le_length_encList (encPair encNat encNat) G.2 - rw [encGraph, length_encPair] - omega + G.vertices.length + G.edges.length ≤ (encGraph G).length := by + have h₁ := length_le_length_encList encNat G.vertices + have h₂ := length_le_length_encList (encPair encNat encNat) G.edges + enc_len [encGraph_apply] /-- The declared vertices of `G` without repetitions, in the order of their first occurrences. -/ -def distinctVertices (G : Graph) : List ℕ := selectDistinct G.1 (List.replicate G.1.length true) +def distinctVertices (G : Graph) : List ℕ := + selectDistinct G.vertices (List.replicate G.vertices.length true) /-- The distinct declared vertices are the declared vertices. -/ -theorem mem_distinctVertices {G : Graph} {v : ℕ} : v ∈ G.distinctVertices ↔ v ∈ G.1 := +theorem mem_distinctVertices {G : Graph} {v : ℕ} : v ∈ G.distinctVertices ↔ v ∈ G.vertices := mem_selectDistinct_replicate /-- The distinct declared vertices are listed without repetitions. -/ theorem nodup_distinctVertices (G : Graph) : G.distinctVertices.Nodup := nodup_selectDistinct _ _ /-- The distinct declared vertices are as many as the elements of the vertex list. -/ -theorem length_distinctVertices (G : Graph) : G.distinctVertices.length = G.1.toFinset.card := - length_selectDistinct_replicate G.1 +theorem length_distinctVertices (G : Graph) : + G.distinctVertices.length = G.vertices.toFinset.card := + length_selectDistinct_replicate G.vertices /-- A graph has no more distinct vertices than the length of its encoding. -/ theorem length_distinctVertices_le (G : Graph) : G.distinctVertices.length ≤ (encGraph G).length := by rw [length_distinctVertices] - exact G.1.toFinset_card_le.trans G.length_vertices_le_encGraph + exact G.vertices.toFinset_card_le.trans G.length_vertices_le_encGraph /-- Positions in `G.distinctVertices` enumerate the declared vertices. -/ -def vertexEquiv (G : Graph) : Fin G.distinctVertices.length ≃ {v // v ∈ G.1} := +def vertexEquiv (G : Graph) : Fin G.distinctVertices.length ≃ {v // v ∈ G.vertices} := (G.nodup_distinctVertices.getEquiv _).trans (Equiv.subtypeEquivRight fun _ => mem_distinctVertices) @@ -168,19 +180,19 @@ theorem vertexEquiv_apply (G : Graph) (i : Fin G.distinctVertices.length) : @[simp] theorem card_vertexSubtype (G : Graph) : - Fintype.card {v // v ∈ G.1} = G.1.toFinset.card := by + Fintype.card {v // v ∈ G.vertices} = G.vertices.toFinset.card := by rw [← Fintype.card_of_bijective G.vertexEquiv.bijective, Fintype.card_fin, length_distinctVertices] theorem natCard_vertexSubtype (G : Graph) : - Nat.card {v // v ∈ G.1} = G.1.toFinset.card := by + Nat.card {v // v ∈ G.vertices} = G.vertices.toFinset.card := by rw [Nat.card_eq_fintype_card, card_vertexSubtype] /-- Listing the distinct vertices is polynomial-time. -/ @[fun_prop] theorem computableInPolyTime_distinctVertices : ComputableInPolyTime distinctVertices encGraph (encList encNat) := by - unfold distinctVertices encGraph + unfold distinctVertices fun_prop /-! ### Subset verification on encoded graphs -/ @@ -189,7 +201,7 @@ theorem computableInPolyTime_distinctVertices : joins them in either direction. -/ def adjBool (G : Graph) (u v : ℕ) : Bool := decide (u ≠ v) && - (G.2.any (fun e => decide (e = (u, v))) || G.2.any (fun e => decide (e = (v, u)))) + (G.edges.any (fun e => decide (e = (u, v))) || G.edges.any (fun e => decide (e = (v, u)))) @[simp] theorem adjBool_eq_true_iff (G : Graph) (u v : ℕ) : @@ -206,7 +218,7 @@ theorem adjBool_eq_false_iff (G : Graph) (u v : ℕ) : theorem computableInPolyTime_adjBool : ComputableInPolyTime (fun p : (Graph × ℕ) × ℕ => p.1.1.adjBool p.1.2 p.2) (encPair (encPair encGraph encNat) encNat) encBool := by - unfold adjBool encGraph + unfold adjBool fun_prop /-- Testing adjacency between computed vertices of a computed graph is polynomial-time. This is @@ -220,18 +232,18 @@ theorem computableInPolyTime_adjBool_comp {γ : Type} {enc : γ ↪ List Bool} computableInPolyTime_adjBool.comp ((hG.prod hu).prod hv) /-- Verify a cardinality-`m` vertex subset problem with pairwise relation `rel`: select distinct -declared vertices from `G.1` by the certificate mask, compare the selected count with `m` in +declared vertices from `G.vertices` by the certificate mask, compare the selected count with `m` in binary, and test `rel` on all distinct selected pairs. -/ def verifySubset (rel : Graph → ℕ → ℕ → Bool) : (Graph × ℕ) × List Bool → Bool := - subsetVerifier (fun x => x.1.1) fun y => - decide (y.2.length = y.1.2) && y.2.all fun u => y.2.all fun v => decide (u = v) || rel y.1.1 u v + subsetVerifier (fun (G, _) => G.vertices) fun ((G, k), vs) => + decide (vs.length = k) && vs.all fun u => vs.all fun v => decide (u = v) || rel G u v /-- A finite subset of declared vertices with a pairwise property corresponds to a finite subset of -`ℕ` contained in `G.1` with the same pairwise property and cardinality. -/ +`ℕ` contained in `G.vertices` with the same pairwise property and cardinality. -/ theorem exists_subtype_pairwise_iff {G : Graph} {P : ℕ → ℕ → Prop} {m : ℕ} : - (∃ S : Finset {v // v ∈ G.1}, - (S : Set {v // v ∈ G.1}).Pairwise (fun u v => P u v) ∧ S.card = m) ↔ - ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.1) ∧ (S : Set ℕ).Pairwise P ∧ S.card = m := by + (∃ S : Finset {v // v ∈ G.vertices}, + (S : Set {v // v ∈ G.vertices}).Pairwise (fun u v => P u v) ∧ S.card = m) ↔ + ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.vertices) ∧ (S : Set ℕ).Pairwise P ∧ S.card = m := by constructor · rintro ⟨S, hS, rfl⟩ refine ⟨S.map (.subtype _), @@ -243,9 +255,9 @@ theorem exists_subtype_pairwise_iff {G : Graph} {P : ℕ → ℕ → Prop} {m : obtain ⟨v, hv, rfl⟩ := hv exact hS hu hv (Subtype.coe_ne_coe.1 huv) · rintro ⟨S, hSV, hS, rfl⟩ - have hmap : (S.subtype (· ∈ G.1)).map (.subtype _) = S := + have hmap : (S.subtype (· ∈ G.vertices)).map (.subtype _) = S := (Finset.subtype_map _).trans (Finset.filter_eq_self.mpr hSV) - refine ⟨S.subtype (· ∈ G.1), ?_, by rw [← Finset.card_map (.subtype _), hmap]⟩ + refine ⟨S.subtype (· ∈ G.vertices), ?_, by rw [← Finset.card_map (.subtype _), hmap]⟩ intro u hu v hv huv rw [Finset.mem_coe, Finset.mem_subtype] at hu hv exact hS hu hv (Subtype.ext_iff.not.1 huv) @@ -255,13 +267,13 @@ relation agrees with `rel` on distinct vertices. -/ theorem isVerifier_verifySubset {rel : Graph → ℕ → ℕ → Bool} {P : Graph → ℕ → ℕ → Prop} (hrel : ∀ G u v, u ≠ v → (rel G u v = true ↔ P G u v)) : IsVerifier (encPair encGraph encNat) Polynomial.X (verifySubset rel) - {x | ∃ S : Finset {v // v ∈ x.1.1}, - (S : Set {v // v ∈ x.1.1}).Pairwise (fun u v => P x.1 u v) ∧ S.card = x.2} := by + {x | ∃ S : Finset {v // v ∈ x.1.vertices}, + (S : Set {v // v ∈ x.1.vertices}).Pairwise (fun u v => P x.1 u v) ∧ S.card = x.2} := by have hset : - {x : Graph × ℕ | ∃ S : Finset {v // v ∈ x.1.1}, - (S : Set {v // v ∈ x.1.1}).Pairwise (fun u v => P x.1 u v) ∧ S.card = x.2} = + {x : Graph × ℕ | ∃ S : Finset {v // v ∈ x.1.vertices}, + (S : Set {v // v ∈ x.1.vertices}).Pairwise (fun u v => P x.1 u v) ∧ S.card = x.2} = {x : Graph × ℕ | ∃ S : Finset ℕ, - (∀ v ∈ S, v ∈ x.1.1) ∧ (S : Set ℕ).Pairwise (P x.1) ∧ S.card = x.2} := + (∀ v ∈ S, v ∈ x.1.vertices) ∧ (S : Set ℕ).Pairwise (P x.1) ∧ S.card = x.2} := Set.ext fun _ => exists_subtype_pairwise_iff rw [hset] refine isVerifier_subsetVerifier @@ -286,7 +298,7 @@ theorem computableInPolyTime_verifySubset {rel : Graph → ℕ → ℕ → Bool} ComputableInPolyTime (verifySubset rel) (encPair (encPair encGraph encNat) encBits) encBool := by refine computableInPolyTime_subsetVerifier (encγ := encNat) - (by unfold encGraph; fun_prop) (bool_and' ?_ ?_) + (by fun_prop) (bool_and' ?_ ?_) · exact ((length_list_binary encNat).comp (snd _ _)).decide_eq' (by fun_prop) · apply allWithInput (snd _ _) apply allWithInput ((snd _ _).comp (fst _ _)) diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph/DartConnectivity.lean b/CslibFrontier/Computability/Complexity/Problems/Graph/DartConnectivity.lean index b169662345..81fdb44944 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph/DartConnectivity.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph/DartConnectivity.lean @@ -28,11 +28,11 @@ theorem exists_of_adj {G : Graph} {u v : ℕ} (h : G.toSimpleGraph.Adj u v) : ∃ d : Dart G, d.vertex = u ∧ d.reverse.vertex = v := by obtain ⟨-, h | h⟩ := toSimpleGraph_adj.mp h · obtain ⟨i, hi⟩ := List.mem_iff_get.mp h - change G.2[i.val] = (u, v) at hi + change G.edges[i.val] = (u, v) at hi refine ⟨(i, false), ?_, ?_⟩ <;> simp [vertex, reverse, edgeEndpoint, hi] · obtain ⟨i, hi⟩ := List.mem_iff_get.mp h - change G.2[i.val] = (v, u) at hi + change G.edges[i.val] = (v, u) at hi refine ⟨(i, true), ?_, ?_⟩ <;> simp [vertex, reverse, edgeEndpoint, hi] @@ -44,7 +44,7 @@ theorem eq_of_sameComponent {G : Graph} {β : Sort*} (f : Dart G → β) (ha : ∀ d, f d = f d.reverse) {d e : Dart G} (h : RotationCertificate.SameComponent G d.vertex e.vertex) : f d = f e := by obtain ⟨hd, he, ⟨p⟩⟩ := h - have hw {u v : {v // v ∈ G.1}} (p : G.induced.Walk u v) : + have hw {u v : {v // v ∈ G.vertices}} (p : G.induced.Walk u v) : ∀ d e : Dart G, d.vertex = u.val → e.vertex = v.val → f d = f e := by induction p with | nil => diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph/DirectedCycle.lean b/CslibFrontier/Computability/Complexity/Problems/Graph/DirectedCycle.lean index d5020aea26..087fa71689 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph/DirectedCycle.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph/DirectedCycle.lean @@ -14,7 +14,7 @@ public import CslibFrontier.Foundations.Relation.Acyclic # Directed cycles A graph, given by a list of vertices and a list of edges, is read as a directed graph through its -arcs `(u, v) ∈ G.2` between declared vertices (`Cslib.Complexity.Graph.DirAdj`), as for the +arcs `(u, v) ∈ G.edges` between declared vertices (`Cslib.Complexity.Graph.DirAdj`), as for the directed Hamiltonian problems. A *directed cycle* (`Cslib.Complexity.Graph.IsDirCycle`) is a nonempty list of distinct declared vertices whose consecutive vertices, read cyclically (`Cycle.Chain`), are joined by arcs; a vertex with a self-loop is a cycle of length one, and two @@ -62,49 +62,52 @@ open Turing.MultiTapeTM namespace Graph -/-- The arc relation of a directed graph: an arc `(u, v) ∈ G.2` between declared vertices. -/ -def DirAdj (G : Graph) (u v : ℕ) : Prop := u ∈ G.1 ∧ v ∈ G.1 ∧ (u, v) ∈ G.2 +/-- The arc relation of a directed graph: an arc `(u, v) ∈ G.edges` between declared vertices. -/ +def DirAdj (G : Graph) (u v : ℕ) : Prop := u ∈ G.vertices ∧ v ∈ G.vertices ∧ (u, v) ∈ G.edges /-- `c` is a directed cycle of `G`: a nonempty list of distinct declared vertices whose consecutive vertices, read cyclically, are joined by arcs. A vertex with a self-loop is a cycle of length one. -/ def IsDirCycle (G : Graph) (c : List ℕ) : Prop := - c ≠ [] ∧ c.Nodup ∧ (∀ v ∈ c, v ∈ G.1) ∧ (c : Cycle ℕ).Chain fun u v => (u, v) ∈ G.2 + c ≠ [] ∧ c.Nodup ∧ (∀ v ∈ c, v ∈ G.vertices) ∧ (c : Cycle ℕ).Chain fun u v => (u, v) ∈ G.edges /-- `G` is acyclic: it has no directed cycle. -/ def IsDirAcyclic (G : Graph) : Prop := ∀ c, ¬G.IsDirCycle c /-- Remove the vertices of `R`: the remaining vertices are those not in `R`, and the arcs are kept, so that arcs at removed vertices join no declared vertices. -/ -def deleteVerts (G : Graph) (R : Finset ℕ) : Graph := (G.1.filter fun v => v ∉ R, G.2) +def deleteVerts (G : Graph) (R : Finset ℕ) : Graph := + ⟨G.vertices.filter fun v => v ∉ R, G.edges⟩ /-- Remove the arcs of `S`, keeping the vertices. -/ -def deleteArcs (G : Graph) (S : Finset (ℕ × ℕ)) : Graph := (G.1, G.2.filter fun e => e ∉ S) +def deleteArcs (G : Graph) (S : Finset (ℕ × ℕ)) : Graph := + ⟨G.vertices, G.edges.filter fun e => e ∉ S⟩ @[simp] -theorem mem_deleteVerts_fst {G : Graph} {R : Finset ℕ} {v : ℕ} : - v ∈ (G.deleteVerts R).1 ↔ v ∈ G.1 ∧ v ∉ R := by +theorem mem_deleteVerts_vertices {G : Graph} {R : Finset ℕ} {v : ℕ} : + v ∈ (G.deleteVerts R).vertices ↔ v ∈ G.vertices ∧ v ∉ R := by simp [deleteVerts] @[simp] -theorem deleteVerts_snd (G : Graph) (R : Finset ℕ) : (G.deleteVerts R).2 = G.2 := rfl +theorem deleteVerts_edges (G : Graph) (R : Finset ℕ) : (G.deleteVerts R).edges = G.edges := rfl @[simp] -theorem deleteArcs_fst (G : Graph) (S : Finset (ℕ × ℕ)) : (G.deleteArcs S).1 = G.1 := rfl +theorem deleteArcs_vertices (G : Graph) (S : Finset (ℕ × ℕ)) : + (G.deleteArcs S).vertices = G.vertices := rfl @[simp] -theorem mem_deleteArcs_snd {G : Graph} {S : Finset (ℕ × ℕ)} {e : ℕ × ℕ} : - e ∈ (G.deleteArcs S).2 ↔ e ∈ G.2 ∧ e ∉ S := by +theorem mem_deleteArcs_edges {G : Graph} {S : Finset (ℕ × ℕ)} {e : ℕ × ℕ} : + e ∈ (G.deleteArcs S).edges ↔ e ∈ G.edges ∧ e ∉ S := by simp [deleteArcs] theorem dirAdj_deleteVerts {G : Graph} {R : Finset ℕ} {u v : ℕ} : (G.deleteVerts R).DirAdj u v ↔ G.DirAdj u v ∧ u ∉ R ∧ v ∉ R := by - simp only [DirAdj, mem_deleteVerts_fst, deleteVerts_snd] + simp only [DirAdj, mem_deleteVerts_vertices, deleteVerts_edges] tauto theorem dirAdj_deleteArcs {G : Graph} {S : Finset (ℕ × ℕ)} {u v : ℕ} : (G.deleteArcs S).DirAdj u v ↔ G.DirAdj u v ∧ (u, v) ∉ S := by - simp only [DirAdj, deleteArcs_fst, mem_deleteArcs_snd] + simp only [DirAdj, deleteArcs_vertices, mem_deleteArcs_edges] tauto /-- Directed cycles are the nonempty lists without repetitions that are closed chains of the arc @@ -138,8 +141,9 @@ theorem isDirAcyclic_iff_acyclic {G : Graph} : G.IsDirAcyclic ↔ Relation.Acycl number of distinct declared vertices, and every arc between declared vertices increases the number. -/ theorem exists_rank_of_isDirAcyclic {G : Graph} (h : G.IsDirAcyclic) : - ∃ f : ℕ → ℕ, (∀ v ∈ G.1, f v < G.1.toFinset.card) ∧ ∀ u v, G.DirAdj u v → f u < f v := by - obtain ⟨f, hlt, hf⟩ := (isDirAcyclic_iff_acyclic.1 h).exists_rank G.1.toFinset + ∃ f : ℕ → ℕ, (∀ v ∈ G.vertices, f v < G.vertices.toFinset.card) ∧ + ∀ u v, G.DirAdj u v → f u < f v := by + obtain ⟨f, hlt, hf⟩ := (isDirAcyclic_iff_acyclic.1 h).exists_rank G.vertices.toFinset (fun _ _ h => ⟨List.mem_toFinset.2 h.1, List.mem_toFinset.2 h.2.1⟩) exact ⟨f, fun v hv => hlt v (List.mem_toFinset.2 hv), hf⟩ @@ -152,7 +156,8 @@ theorem isDirAcyclic_of_rank {G : Graph} {f : ℕ → ℕ} (hf : ∀ u v, G.DirA /-- **Acyclicity through topological numberings.** -/ theorem isDirAcyclic_iff_exists_rank {G : Graph} : G.IsDirAcyclic ↔ - ∃ f : ℕ → ℕ, (∀ v ∈ G.1, f v < G.1.toFinset.card) ∧ ∀ u v, G.DirAdj u v → f u < f v := + ∃ f : ℕ → ℕ, (∀ v ∈ G.vertices, f v < G.vertices.toFinset.card) ∧ + ∀ u v, G.DirAdj u v → f u < f v := ⟨exists_rank_of_isDirAcyclic, fun ⟨_, _, hf⟩ => isDirAcyclic_of_rank hf⟩ /-- **Feedback vertex sets meet every cycle**: removing the vertices of `R` leaves an acyclic @@ -164,10 +169,10 @@ theorem isDirAcyclic_deleteVerts_iff {G : Graph} {R : Finset ℕ} : by_contra hR push Not at hR obtain ⟨hne, hnd, hV, hch⟩ := hc - exact h c ⟨hne, hnd, fun v hv => mem_deleteVerts_fst.2 ⟨hV v hv, hR v hv⟩, hch⟩ + exact h c ⟨hne, hnd, fun v hv => mem_deleteVerts_vertices.2 ⟨hV v hv, hR v hv⟩, hch⟩ · rintro h c ⟨hne, hnd, hV, hch⟩ - obtain ⟨v, hv, hvR⟩ := h c ⟨hne, hnd, fun v hv => (mem_deleteVerts_fst.1 (hV v hv)).1, hch⟩ - exact (mem_deleteVerts_fst.1 (hV v hv)).2 hvR + obtain ⟨v, hv, hvR⟩ := h c ⟨hne, hnd, fun v hv => (mem_deleteVerts_vertices.1 (hV v hv)).1, hch⟩ + exact (mem_deleteVerts_vertices.1 (hV v hv)).2 hvR end Graph diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph/OrthogonalDrawing.lean b/CslibFrontier/Computability/Complexity/Problems/Graph/OrthogonalDrawing.lean index bd32ccfd3b..a923a089ee 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph/OrthogonalDrawing.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph/OrthogonalDrawing.lean @@ -49,43 +49,43 @@ abbrev OrthogonalDrawing : Type := List Point × List Route /-- Displayed position of a declared vertex, using its first position in the vertex list. -/ def drawingPosition (G : Graph) (d : OrthogonalDrawing) (v : ℕ) : Point := - d.1.getD (G.1.idxOf v) (0, 0) + d.1.getD (G.vertices.idxOf v) (0, 0) /-- A supplied planar orthogonal embedding of the loopless listed graph. Parallel listed edges have distinct simple routes; all contacts are tested in the real plane. -/ structure IsPlanarOrthogonalDrawing (G : Graph) (d : OrthogonalDrawing) : Prop where /-- Exactly one position per declared vertex. -/ - vertexCount : d.1.length = G.1.length + vertexCount : d.1.length = G.vertices.length /-- Exactly one route per listed edge occurrence. -/ - edgeCount : d.2.length = G.2.length + edgeCount : d.2.length = G.edges.length /-- Labels describe distinct vertices. -/ - labelsNodup : G.1.Nodup + labelsNodup : G.vertices.Nodup /-- Distinct declared vertices have distinct positions. -/ positionsNodup : d.1.Nodup /-- Both endpoints of every edge are declared. -/ - endpointsDeclared : ∀ e ∈ G.2, e.1 ∈ G.1 ∧ e.2 ∈ G.1 + endpointsDeclared : ∀ e ∈ G.edges, e.1 ∈ G.vertices ∧ e.2 ∈ G.vertices /-- This interface concerns loopless graphs. -/ - loopless : ∀ e ∈ G.2, e.1 ≠ e.2 + loopless : ∀ e ∈ G.edges, e.1 ≠ e.2 /-- Route endpoints are the incident vertices, in either order. -/ - endpoints : ∀ i (hi : i < G.2.length), + endpoints : ∀ i (hi : i < G.edges.length), let r := d.2.getD i [] - let a := drawingPosition G d G.2[i].1 - let b := drawingPosition G d G.2[i].2 + let a := drawingPosition G d G.edges[i].1 + let b := drawingPosition G d G.edges[i].2 (r.head? = some a ∧ r.getLast? = some b) ∨ (r.head? = some b ∧ r.getLast? = some a) /-- Every segment is nondegenerate and axis aligned. -/ - orthogonal : ∀ i < G.2.length, IsOrthogonal (d.2.getD i []) + orthogonal : ∀ i < G.edges.length, IsOrthogonal (d.2.getD i []) /-- Every route has no retracing, repeated points, or self-crossing. -/ - simple : ∀ i < G.2.length, IsSimple (d.2.getD i []) + simple : ∀ i < G.edges.length, IsSimple (d.2.getD i []) /-- A route visits only its incident graph vertices. -/ - avoidsVertices : ∀ i (hi : i < G.2.length), ∀ v ∈ G.1, + avoidsVertices : ∀ i (hi : i < G.edges.length), ∀ v ∈ G.vertices, realPoint (drawingPosition G d v) ∈ support (d.2.getD i []) → - v = G.2[i].1 ∨ v = G.2[i].2 + v = G.edges[i].1 ∨ v = G.edges[i].2 /-- Distinct routes intersect only at common graph endpoints. -/ - contacts : ∀ i (hi : i < G.2.length), ∀ j (hj : j < G.2.length), i ≠ j → + contacts : ∀ i (hi : i < G.edges.length), ∀ j (hj : j < G.edges.length), i ≠ j → ∀ p ∈ support (d.2.getD i []), p ∈ support (d.2.getD j []) → - ∃ v ∈ G.1, (v = G.2[i].1 ∨ v = G.2[i].2) ∧ - (v = G.2[j].1 ∨ v = G.2[j].2) ∧ p = realPoint (drawingPosition G d v) + ∃ v ∈ G.vertices, (v = G.edges[i].1 ∨ v = G.edges[i].2) ∧ + (v = G.edges[j].1 ∨ v = G.edges[j].2) ∧ p = realPoint (drawingPosition G d v) /-- Every listed position and route point lies in the nonnegative integer rectangle. -/ def drawingFits (d : OrthogonalDrawing) (width height : ℕ) : Prop := diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph/OrthogonalLayout.lean b/CslibFrontier/Computability/Complexity/Problems/Graph/OrthogonalLayout.lean index 15d2e32b8f..59019bf0e2 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph/OrthogonalLayout.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph/OrthogonalLayout.lean @@ -14,7 +14,8 @@ import Mathlib.Data.List.GetD /-! # Finite orthogonal routes indexed by the existing graph edge list -No new graph representation is introduced. Positions follow `G.1` and routes follow `G.2`, so +No new graph representation is introduced. +Positions follow `G.vertices` and routes follow `G.edges`, so every edge occurrence keeps its original index, including parallel edge occurrences. The input port table is a finite list assigning a pair of three-way ports to each edge occurrence. @@ -38,54 +39,56 @@ open Orthogonal /-- Vertex positions indexed by the existing declared vertex list. -/ def orthogonalPositions (G : Graph) : List Point := - (List.range G.1.length).map Layout.center + (List.range G.vertices.length).map Layout.center /-- The finite route list, in exactly the order of the graph's edge occurrences. A missing port-table entry defaults to the left port at both ends. Contact separation requires that the supplied table assigns distinct ports to different incidences. -/ def orthogonalRoutes (G : Graph) (ports : List (Fin 3 × Fin 3)) : List Route := - G.2.zipIdx.map fun (edge, i) => - Layout.route G.1.length i (G.1.idxOf edge.1) (G.1.idxOf edge.2) + G.edges.zipIdx.map fun (edge, i) => + Layout.route G.vertices.length i (G.vertices.idxOf edge.1) (G.vertices.idxOf edge.2) (ports.getD i (0, 0)).1 (ports.getD i (0, 0)).2 /-- The output lists have the original vertex and edge counts. -/ @[simp] theorem orthogonalPositions_length (G : Graph) : - G.orthogonalPositions.length = G.1.length := by simp [orthogonalPositions] + G.orthogonalPositions.length = G.vertices.length := by simp [orthogonalPositions] /-- The output lists have the original vertex and edge counts. -/ @[simp] theorem orthogonalRoutes_length (G : Graph) (ports : List (Fin 3 × Fin 3)) : - (G.orthogonalRoutes ports).length = G.2.length := by simp [orthogonalRoutes] + (G.orthogonalRoutes ports).length = G.edges.length := by simp [orthogonalRoutes] /-- The route at index `i` is the route for the edge at the same index. -/ theorem getElem_orthogonalRoutes (G : Graph) (ports : List (Fin 3 × Fin 3)) - {i : ℕ} (hi : i < G.2.length) : + {i : ℕ} (hi : i < G.edges.length) : (G.orthogonalRoutes ports)[i]'(by simpa using hi) = - Layout.route G.1.length i (G.1.idxOf G.2[i].1) (G.1.idxOf G.2[i].2) + Layout.route G.vertices.length i (G.vertices.idxOf G.edges[i].1) + (G.vertices.idxOf G.edges[i].2) (ports.getD i (0, 0)).1 (ports.getD i (0, 0)).2 := by simp [orthogonalRoutes, List.getElem_zipIdx] /-- A side-indexed version of the exact route formula. -/ theorem getElem_orthogonalRoutes_endpoint (G : Graph) (ports : List (Fin 3 × Fin 3)) - {i : ℕ} (hi : i < G.2.length) : + {i : ℕ} (hi : i < G.edges.length) : (G.orthogonalRoutes ports)[i]'(by simpa using hi) = - Layout.route G.1.length i (G.1.idxOf (G.edgeEndpoint i false)) - (G.1.idxOf (G.edgeEndpoint i true)) (portAt ports i false) (portAt ports i true) := by + Layout.route G.vertices.length i (G.vertices.idxOf (G.edgeEndpoint i false)) + (G.vertices.idxOf (G.edgeEndpoint i true)) (portAt ports i false) + (portAt ports i true) := by rw [getElem_orthogonalRoutes G ports hi] simp only [edgeEndpoint, portAt, Bool.false_eq_true, ↓reduceIte] rw [List.getD_eq_getElem _ _ hi] /-- Every route has the actual indexed graph edge's endpoints, regardless of the port table. -/ theorem orthogonalRoutes_endpoints (G : Graph) (ports : List (Fin 3 × Fin 3)) - {i : ℕ} (hi : i < G.2.length) : + {i : ℕ} (hi : i < G.edges.length) : let r := (G.orthogonalRoutes ports)[i]'(by simpa using hi) - r.head? = some (Layout.center (G.1.idxOf G.2[i].1)) ∧ - r.getLast? = some (Layout.center (G.1.idxOf G.2[i].2)) := by + r.head? = some (Layout.center (G.vertices.idxOf G.edges[i].1)) ∧ + r.getLast? = some (Layout.center (G.vertices.idxOf G.edges[i].2)) := by simp [getElem_orthogonalRoutes G ports hi] /-- Declared, distinct edge endpoints give a proper orthogonal route. -/ theorem orthogonalRoutes_orthogonal (G : Graph) (ports : List (Fin 3 × Fin 3)) - {i : ℕ} (hi : i < G.2.length) (hu : G.2[i].1 ∈ G.1) (hv : G.2[i].2 ∈ G.1) - (hne : G.2[i].1 ≠ G.2[i].2) : + {i : ℕ} (hi : i < G.edges.length) (hu : G.edges[i].1 ∈ G.vertices) + (hv : G.edges[i].2 ∈ G.vertices) (hne : G.edges[i].1 ≠ G.edges[i].2) : IsOrthogonal ((G.orthogonalRoutes ports)[i]'(by simpa using hi)) := by rw [getElem_orthogonalRoutes G ports hi] exact Layout.route_orthogonal (List.idxOf_lt_length_iff.2 hu) @@ -93,8 +96,8 @@ theorem orthogonalRoutes_orthogonal (G : Graph) (ports : List (Fin 3 × Fin 3)) /-- Declared, distinct edge endpoints give a continuously simple route. -/ theorem orthogonalRoutes_simple (G : Graph) (ports : List (Fin 3 × Fin 3)) - {i : ℕ} (hi : i < G.2.length) (hu : G.2[i].1 ∈ G.1) (hv : G.2[i].2 ∈ G.1) - (hne : G.2[i].1 ≠ G.2[i].2) : + {i : ℕ} (hi : i < G.edges.length) (hu : G.edges[i].1 ∈ G.vertices) + (hv : G.edges[i].2 ∈ G.vertices) (hne : G.edges[i].1 ≠ G.edges[i].2) : IsSimple ((G.orthogonalRoutes ports)[i]'(by simpa using hi)) := by rw [getElem_orthogonalRoutes G ports hi] exact Layout.route_simple (List.idxOf_lt_length_iff.2 hu) @@ -103,24 +106,24 @@ theorem orthogonalRoutes_simple (G : Graph) (ports : List (Fin 3 × Fin 3)) /-- At most `2m²` candidate grid sites suffice for every bridge/stem crossing. Candidates may repeat or be unused; this explicit list is a cover, not a claim that every candidate is crossed. -/ def crossingCandidates (G : Graph) (ports : List (Fin 3 × Fin 3)) : List Point := - (List.range G.2.length).flatMap fun i => - (List.range G.2.length).flatMap fun j => - [(Layout.column (G.1.idxOf (G.edgeEndpoint j false)) (portAt ports j false), - Layout.track G.1.length i), - (Layout.column (G.1.idxOf (G.edgeEndpoint j true)) (portAt ports j true), - Layout.track G.1.length i)] + (List.range G.edges.length).flatMap fun i => + (List.range G.edges.length).flatMap fun j => + [(Layout.column (G.vertices.idxOf (G.edgeEndpoint j false)) (portAt ports j false), + Layout.track G.vertices.length i), + (Layout.column (G.vertices.idxOf (G.edgeEndpoint j true)) (portAt ports j true), + Layout.track G.vertices.length i)] /-- A quadratic bound on the explicit crossing-site cover, counting repeated candidates. -/ @[simp] theorem crossingCandidates_length (G : Graph) (ports : List (Fin 3 × Fin 3)) : - (G.crossingCandidates ports).length = 2 * G.2.length ^ 2 := by + (G.crossingCandidates ports).length = 2 * G.edges.length ^ 2 := by simp [crossingCandidates, List.length_flatMap, List.map_const', pow_two, Nat.mul_comm, Nat.mul_assoc] /-- Every indexed bridge/port intersection point occurs in the finite candidate cover. -/ theorem mem_crossingCandidates (G : Graph) (ports : List (Fin 3 × Fin 3)) - {i j : ℕ} (hi : i < G.2.length) (hj : j < G.2.length) (side : Bool) : - (Layout.column (G.1.idxOf (G.edgeEndpoint j side)) (portAt ports j side), - Layout.track G.1.length i) ∈ G.crossingCandidates ports := by + {i j : ℕ} (hi : i < G.edges.length) (hj : j < G.edges.length) (side : Bool) : + (Layout.column (G.vertices.idxOf (G.edgeEndpoint j side)) (portAt ports j side), + Layout.track G.vertices.length i) ∈ G.crossingCandidates ports := by apply List.mem_flatMap.2 refine ⟨i, List.mem_range.2 hi, List.mem_flatMap.2 ⟨j, List.mem_range.2 hj, ?_⟩⟩ cases side <;> simp @@ -129,15 +132,15 @@ theorem mem_crossingCandidates (G : Graph) (ports : List (Fin 3 × Fin 3)) `2m²` candidate crossing sites. Strict transversality is supplied by `Layout.route_contact`; this finite cover additionally bounds the number of distinct crossing sites. -/ theorem orthogonalRoutes_contact_cover (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) (hdeg : ∀ v, G.incidenceVertices.count v ≤ 3) {i j : ℕ} - (hi : i < G.2.length) (hj : j < G.2.length) (hij : i ≠ j) {p : ℝ × ℝ} + (hi : i < G.edges.length) (hj : j < G.edges.length) (hij : i ≠ j) {p : ℝ × ℝ} (hp : p ∈ support ((G.orthogonalRoutes G.canonicalPorts)[i]'(by simpa using hi))) (hq : p ∈ support ((G.orthogonalRoutes G.canonicalPorts)[j]'(by simpa using hj))) : - (∃ w < G.1.length, p = realPoint (Layout.center w)) ∨ + (∃ w < G.vertices.length, p = realPoint (Layout.center w)) ∨ ∃ z ∈ G.crossingCandidates G.canonicalPorts, p = realPoint z := by - have hindex (k : ℕ) (hk : k < G.2.length) (side : Bool) : - G.1.idxOf (G.edgeEndpoint k side) < G.1.length := + have hindex (k : ℕ) (hk : k < G.edges.length) (side : Bool) : + G.vertices.idxOf (G.edgeEndpoint k side) < G.vertices.length := List.idxOf_lt_length_iff.2 (edgeEndpoint_mem G hG hk side) rw [getElem_orthogonalRoutes_endpoint G G.canonicalPorts hi] at hp rw [getElem_orthogonalRoutes_endpoint G G.canonicalPorts hj] at hq @@ -163,27 +166,30 @@ theorem orthogonalRoutes_contact_cover (G : Graph) /-- Exactly five segments are emitted for every edge occurrence. -/ theorem orthogonalRoutes_segments (G : Graph) (ports : List (Fin 3 × Fin 3)) : - ((G.orthogonalRoutes ports).map fun r => r.consecutivePairs.length).sum = 5 * G.2.length := by + ((G.orthogonalRoutes ports).map fun r => r.consecutivePairs.length).sum = + 5 * G.edges.length := by simp [orthogonalRoutes, Layout.route, List.map_map, Function.comp_def, Nat.mul_comm] /-- The total wire length is at most `90 (n+m)²`, for `n` declared vertices and `m` edge occurrences. Endpoint membership is the only graph hypothesis needed for this resource bound. -/ theorem orthogonalRoutes_wireLength_le (G : Graph) (ports : List (Fin 3 × Fin 3)) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) : - ((G.orthogonalRoutes ports).map wireLength).sum ≤ 90 * (G.1.length + G.2.length) ^ 2 := by + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) : + ((G.orthogonalRoutes ports).map wireLength).sum ≤ + 90 * (G.vertices.length + G.edges.length) ^ 2 := by have hbound : ∀ r ∈ G.orthogonalRoutes ports, - wireLength r ≤ 80 * G.1.length + 10 * G.2.length := by + wireLength r ≤ 80 * G.vertices.length + 10 * G.edges.length := by intro r hr obtain ⟨⟨edge, i⟩, he, rfl⟩ := List.mem_map.1 hr obtain ⟨hi, heq⟩ := List.getElem?_eq_some_iff.1 (List.mem_zipIdx_iff_getElem?.1 he) - change G.2[i] = edge at heq - have hm : edge ∈ G.2 := by grind + change G.edges[i] = edge at heq + have hm : edge ∈ G.edges := by grind exact Layout.route_wireLength_le hi (List.idxOf_lt_length_iff.2 (hG edge hm).1) (List.idxOf_lt_length_iff.2 (hG edge hm).2) _ _ calc - _ ≤ ((G.orthogonalRoutes ports).map fun _ => 80 * G.1.length + 10 * G.2.length).sum := + _ ≤ ((G.orthogonalRoutes ports).map fun _ => + 80 * G.vertices.length + 10 * G.edges.length).sum := List.sum_le_sum hbound - _ = G.2.length * (80 * G.1.length + 10 * G.2.length) := by simp - _ ≤ 90 * (G.1.length + G.2.length) ^ 2 := by nlinarith + _ = G.edges.length * (80 * G.vertices.length + 10 * G.edges.length) := by simp + _ ≤ 90 * (G.vertices.length + G.edges.length) ^ 2 := by nlinarith end Cslib.Complexity.Graph diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph/OrthogonalPath.lean b/CslibFrontier/Computability/Complexity/Problems/Graph/OrthogonalPath.lean index 128e70ff58..a458757eee 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph/OrthogonalPath.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph/OrthogonalPath.lean @@ -47,29 +47,29 @@ open Orthogonal /-- Canonical unit paths, with the checked linear per-edge wire budget. -/ def unitPaths (G : Graph) : List Route := (G.orthogonalRoutes G.canonicalPorts).map - (subdivideWithin (80 * G.1.length + 10 * G.2.length)) + (subdivideWithin (80 * G.vertices.length + 10 * G.edges.length)) /-- Access one finite canonical unit path, defaulting to the empty path outside the edge range. -/ def unitPath (G : Graph) (i : ℕ) : Route := G.unitPaths.getD i [] /-- One unit path is emitted for every listed edge occurrence. -/ -@[simp] theorem unitPaths_length (G : Graph) : G.unitPaths.length = G.2.length := by +@[simp] theorem unitPaths_length (G : Graph) : G.unitPaths.length = G.edges.length := by simp [unitPaths] private theorem canonical_orthogonal (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.2.length) : + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.edges.length) : IsOrthogonal ((G.orthogonalRoutes G.canonicalPorts)[i]'(by simpa using hi)) := orthogonalRoutes_orthogonal G G.canonicalPorts hi (hG _ (List.getElem_mem hi)).1 (hG _ (List.getElem_mem hi)).2 (hloop _ (List.getElem_mem hi)) /-- The budgeted compiler produces the full subdivision at every existing edge. -/ theorem unitPath_eq_subdivide (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.2.length) : + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.edges.length) : G.unitPath i = subdivide ((G.orthogonalRoutes G.canonicalPorts)[i]'(by simpa using hi)) := by have hwire : wireLength ((G.orthogonalRoutes G.canonicalPorts)[i]'(by simpa using hi)) ≤ - 80 * G.1.length + 10 * G.2.length := by + 80 * G.vertices.length + 10 * G.edges.length := by rw [getElem_orthogonalRoutes] exact Layout.route_wireLength_le hi (List.idxOf_lt_length_iff.2 (hG _ (List.getElem_mem hi)).1) @@ -81,10 +81,11 @@ theorem unitPath_eq_subdivide (G : Graph) /-- Canonical unit paths preserve both original indexed edge endpoints. -/ theorem unitPath_endpoints (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.2.length) : - (G.unitPath i).head? = some (Layout.center (G.1.idxOf (G.edgeEndpoint i false))) ∧ - (G.unitPath i).getLast? = some (Layout.center (G.1.idxOf (G.edgeEndpoint i true))) := by + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.edges.length) : + (G.unitPath i).head? = some (Layout.center (G.vertices.idxOf (G.edgeEndpoint i false))) ∧ + (G.unitPath i).getLast? = + some (Layout.center (G.vertices.idxOf (G.edgeEndpoint i true))) := by rw [unitPath_eq_subdivide G hG hloop hi, subdivide_head, subdivide_last (canonical_orthogonal G hG hloop hi), getElem_orthogonalRoutes_endpoint G G.canonicalPorts hi] @@ -92,8 +93,8 @@ theorem unitPath_endpoints (G : Graph) /-- Canonical unit paths never repeat a listed lattice point. -/ theorem unitPath_nodup (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.2.length) : + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.edges.length) : (G.unitPath i).Nodup := by rw [unitPath_eq_subdivide G hG hloop hi] exact subdivide_nodup (canonical_orthogonal G hG hloop hi) @@ -102,52 +103,53 @@ theorem unitPath_nodup (G : Graph) /-- The first unit neighbor is the source center plus its actual canonical port direction. -/ theorem unitPath_first_neighbor (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.2.length) + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.edges.length) (d : Point) : - (G.unitPath i).getD 1 d = Layout.center (G.1.idxOf (G.edgeEndpoint i false)) + + (G.unitPath i).getD 1 d = Layout.center (G.vertices.idxOf (G.edgeEndpoint i false)) + Layout.portStep (portAt G.canonicalPorts i false) := by - have hf : segmentWalk (Layout.center (G.1.idxOf (G.edgeEndpoint i false))) - (Layout.elbow (G.1.idxOf (G.edgeEndpoint i false)) + have hf : segmentWalk (Layout.center (G.vertices.idxOf (G.edgeEndpoint i false))) + (Layout.elbow (G.vertices.idxOf (G.edgeEndpoint i false)) (portAt G.canonicalPorts i false)) <:+: G.unitPath i := by rw [unitPath_eq_subdivide G hG hloop hi] apply segmentWalk_infix_subdivide (canonical_orthogonal G hG hloop hi) - (e := (Layout.center (G.1.idxOf (G.edgeEndpoint i false)), - Layout.elbow (G.1.idxOf (G.edgeEndpoint i false)) (portAt G.canonicalPorts i false))) + (e := (Layout.center (G.vertices.idxOf (G.edgeEndpoint i false)), + Layout.elbow (G.vertices.idxOf (G.edgeEndpoint i false)) (portAt G.canonicalPorts i false))) rw [getElem_orthogonalRoutes_endpoint G G.canonicalPorts hi] simp [Layout.route, List.consecutivePairs] rw [Layout.fanWalk] at hf - have hp : [_ , _] <:+: [Layout.center (G.1.idxOf (G.edgeEndpoint i false)), - Layout.center (G.1.idxOf (G.edgeEndpoint i false)) + + have hp : [_ , _] <:+: [Layout.center (G.vertices.idxOf (G.edgeEndpoint i false)), + Layout.center (G.vertices.idxOf (G.edgeEndpoint i false)) + Layout.portStep (portAt G.canonicalPorts i false), - Layout.elbow (G.1.idxOf (G.edgeEndpoint i false)) + Layout.elbow (G.vertices.idxOf (G.edgeEndpoint i false)) (portAt G.canonicalPorts i false)] := ⟨[], [_], rfl⟩ exact (infix_head_pair_getD (unitPath_nodup G hG hloop hi) (hp.trans hf) (unitPath_endpoints G hG hloop hi).1 d).2 /-- The penultimate unit point is the target center plus its canonical port direction. -/ theorem unitPath_last_neighbor (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.2.length) + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.edges.length) (d : Point) : (G.unitPath i).getD ((G.unitPath i).length - 2) d = - Layout.center (G.1.idxOf (G.edgeEndpoint i true)) + + Layout.center (G.vertices.idxOf (G.edgeEndpoint i true)) + Layout.portStep (portAt G.canonicalPorts i true) := by - have hf : segmentWalk (Layout.elbow (G.1.idxOf (G.edgeEndpoint i true)) + have hf : segmentWalk (Layout.elbow (G.vertices.idxOf (G.edgeEndpoint i true)) (portAt G.canonicalPorts i true)) - (Layout.center (G.1.idxOf (G.edgeEndpoint i true))) <:+: G.unitPath i := by + (Layout.center (G.vertices.idxOf (G.edgeEndpoint i true))) <:+: G.unitPath i := by rw [unitPath_eq_subdivide G hG hloop hi] apply segmentWalk_infix_subdivide (canonical_orthogonal G hG hloop hi) - (e := (Layout.elbow (G.1.idxOf (G.edgeEndpoint i true)) (portAt G.canonicalPorts i true), - Layout.center (G.1.idxOf (G.edgeEndpoint i true)))) + (e := (Layout.elbow (G.vertices.idxOf (G.edgeEndpoint i true)) + (portAt G.canonicalPorts i true), + Layout.center (G.vertices.idxOf (G.edgeEndpoint i true)))) rw [getElem_orthogonalRoutes_endpoint G G.canonicalPorts hi] simp [Layout.route, List.consecutivePairs] rw [Layout.returnFanWalk] at hf - have hp : [_ , _] <:+: [Layout.elbow (G.1.idxOf (G.edgeEndpoint i true)) + have hp : [_ , _] <:+: [Layout.elbow (G.vertices.idxOf (G.edgeEndpoint i true)) (portAt G.canonicalPorts i true), - Layout.center (G.1.idxOf (G.edgeEndpoint i true)) + + Layout.center (G.vertices.idxOf (G.edgeEndpoint i true)) + Layout.portStep (portAt G.canonicalPorts i true), - Layout.center (G.1.idxOf (G.edgeEndpoint i true))] := ⟨[_], [], rfl⟩ + Layout.center (G.vertices.idxOf (G.edgeEndpoint i true))] := ⟨[_], [], rfl⟩ exact (infix_last_pair_getD (unitPath_nodup G hG hloop hi) (hp.trans hf) (unitPath_endpoints G hG hloop hi).2 d).2 @@ -158,10 +160,10 @@ def endpointNeighbor (G : Graph) (i : ℕ) (side : Bool) : Point := /-- Side-indexed exact endpoint-neighbor formula. -/ theorem endpointNeighbor_eq (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.2.length) + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.edges.length) (side : Bool) : - G.endpointNeighbor i side = Layout.center (G.1.idxOf (G.edgeEndpoint i side)) + + G.endpointNeighbor i side = Layout.center (G.vertices.idxOf (G.edgeEndpoint i side)) + Layout.portStep (portAt G.canonicalPorts i side) := by cases side · exact unitPath_first_neighbor G hG hloop hi (0, 0) @@ -169,11 +171,11 @@ theorem endpointNeighbor_eq (G : Graph) /-- Different edge occurrences at the same source center use different unit neighbors. -/ theorem endpointNeighbor_ne (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) (hdeg : ∀ v, G.incidenceVertices.count v ≤ 3) {i j : ℕ} - (hi : i < G.2.length) (hj : j < G.2.length) (hij : i ≠ j) (left right : Bool) - (hc : G.1.idxOf (G.edgeEndpoint i left) = G.1.idxOf (G.edgeEndpoint j right)) : + (hi : i < G.edges.length) (hj : j < G.edges.length) (hij : i ≠ j) (left right : Bool) + (hc : G.vertices.idxOf (G.edgeEndpoint i left) = G.vertices.idxOf (G.edgeEndpoint j right)) : G.endpointNeighbor i left ≠ G.endpointNeighbor j right := by intro h rw [endpointNeighbor_eq G hG hloop hi, endpointNeighbor_eq G hG hloop hj, hc] at h @@ -182,16 +184,16 @@ theorem endpointNeighbor_ne (G : Graph) /-- Every consecutive pair in a canonical unit path is exactly one Manhattan unit apart. -/ theorem unitPath_unit (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.2.length) + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.edges.length) {e : Point × Point} (he : e ∈ (G.unitPath i).consecutivePairs) : length e.1 e.2 = 1 := by rw [unitPath_eq_subdivide G hG hloop hi] at he exact subdivide_unit (canonical_orthogonal G hG hloop hi) he /-- Unit paths retain the whole continuous real support of their original polygonal routes. -/ theorem unitPath_support (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.2.length) : + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.edges.length) : support (G.unitPath i) = support ((G.orthogonalRoutes G.canonicalPorts)[i]'(by simpa using hi)) := by rw [unitPath_eq_subdivide G hG hloop hi, @@ -199,8 +201,8 @@ theorem unitPath_support (G : Graph) /-- An integer point lies on a unit path exactly when it lies on the original real route. -/ theorem mem_unitPath_iff_support (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.2.length) (p : Point) : + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.edges.length) (p : Point) : p ∈ G.unitPath i ↔ realPoint p ∈ support ((G.orthogonalRoutes G.canonicalPorts)[i]'(by simpa using hi)) := by rw [mem_iff_realPoint_mem_support (fun _ he => unitPath_unit G hG hloop hi he), @@ -208,11 +210,11 @@ theorem mem_unitPath_iff_support (G : Graph) /-- Every listed unit point lies in the original explicit linear rectangle. -/ theorem unitPath_bounds (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.2.length) + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.edges.length) {p : Point} (hp : p ∈ G.unitPath i) : - 0 ≤ p.1 ∧ p.1 ≤ 8 * (G.1.length : ℤ) ∧ - 0 ≤ p.2 ∧ p.2 ≤ 8 * (G.1.length : ℤ) + 2 * G.2.length := by + 0 ≤ p.1 ∧ p.1 ≤ 8 * (G.vertices.length : ℤ) ∧ + 0 ≤ p.2 ∧ p.2 ≤ 8 * (G.vertices.length : ℤ) + 2 * G.edges.length := by have hp' := (mem_unitPath_iff_support G hG hloop hi p).1 hp apply point_bounds_of_mem_support (canonical_orthogonal G hG hloop hi) _ hp' intro q hq @@ -222,8 +224,8 @@ theorem unitPath_bounds (G : Graph) /-- Every canonical edge path is nonempty. -/ theorem unitPath_ne_nil (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.2.length) : + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.edges.length) : G.unitPath i ≠ [] := by intro h have he := (unitPath_endpoints G hG hloop hi).1 @@ -233,13 +235,14 @@ theorem unitPath_ne_nil (G : Graph) /-- A source grid center lies on a canonical unit path exactly when its position index is one of that graph edge's two endpoint indices. -/ theorem unitPath_center_iff (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i w : ℕ} - (hi : i < G.2.length) (hw : w < G.1.length) : - Layout.center w ∈ G.unitPath i ↔ w = G.1.idxOf G.2[i].1 ∨ w = G.1.idxOf G.2[i].2 := by + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i w : ℕ} + (hi : i < G.edges.length) (hw : w < G.vertices.length) : + Layout.center w ∈ G.unitPath i ↔ + w = G.vertices.idxOf G.edges[i].1 ∨ w = G.vertices.idxOf G.edges[i].2 := by have hu := (hG _ (List.getElem_mem hi)).1 have hv := (hG _ (List.getElem_mem hi)).2 - have hne : G.1.idxOf G.2[i].1 ≠ G.1.idxOf G.2[i].2 := + have hne : G.vertices.idxOf G.edges[i].1 ≠ G.vertices.idxOf G.edges[i].2 := fun h => hloop _ (List.getElem_mem hi) ((List.idxOf_inj hu).1 h) rw [mem_unitPath_iff_support G hG hloop hi, getElem_orthogonalRoutes G G.canonicalPorts hi] exact Layout.center_mem_support_iff (List.idxOf_lt_length_iff.2 hu) @@ -247,20 +250,20 @@ theorem unitPath_center_iff (G : Graph) /-- No source vertex center lies on a nonincident path, stated using the graph's actual labels. -/ theorem unitPath_declared_center_iff (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i v : ℕ} - (hi : i < G.2.length) (hv : v ∈ G.1) : - Layout.center (G.1.idxOf v) ∈ G.unitPath i ↔ v = G.2[i].1 ∨ v = G.2[i].2 := by + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i v : ℕ} + (hi : i < G.edges.length) (hv : v ∈ G.vertices) : + Layout.center (G.vertices.idxOf v) ∈ G.unitPath i ↔ v = G.edges[i].1 ∨ v = G.edges[i].2 := by rw [unitPath_center_iff G hG hloop hi (List.idxOf_lt_length_iff.2 hv)] simp only [List.idxOf_inj hv] /-- Nonvertex occupied cells are neither end of a canonical path. -/ theorem unitPath_not_endpoints (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.2.length) + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i : ℕ} (hi : i < G.edges.length) {p : Point} (hcenter : p ∉ G.orthogonalPositions) : (G.unitPath i).head? ≠ some p ∧ (G.unitPath i).getLast? ≠ some p := by - have hcenters (side : Bool) : Layout.center (G.1.idxOf (G.edgeEndpoint i side)) ∈ + have hcenters (side : Bool) : Layout.center (G.vertices.idxOf (G.edgeEndpoint i side)) ∈ G.orthogonalPositions := List.mem_map.2 ⟨_, List.mem_range.2 (List.idxOf_lt_length_iff.2 (edgeEndpoint_mem G hG hi side)), rfl⟩ @@ -278,8 +281,8 @@ theorem unitPath_not_endpoints (G : Graph) /-- An occupied point away from source centers has a genuine predecessor and successor at every occurrence index. Together with `unitPath_nodup`, that occurrence index is unique. -/ theorem unitPath_interior_index (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i k : ℕ} (hi : i < G.2.length) + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i k : ℕ} (hi : i < G.edges.length) (hk : k < (G.unitPath i).length) {p : Point} (heq : (G.unitPath i)[k] = p) (hcenter : p ∉ G.orthogonalPositions) : 0 < k ∧ k + 1 < (G.unitPath i).length := by have hne := unitPath_not_endpoints G hG hloop hi hcenter @@ -301,8 +304,8 @@ private theorem pair_mem_at {r : Route} {k : ℕ} (hk : k + 1 < r.length) : /-- The two actual predecessor/successor lookups at a nonvertex cell are unit neighbors. -/ theorem unitPath_neighbor_lengths (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i k : ℕ} (hi : i < G.2.length) + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i k : ℕ} (hi : i < G.edges.length) (hk : k < (G.unitPath i).length) {p : Point} (heq : (G.unitPath i)[k] = p) (hcenter : p ∉ G.orthogonalPositions) : length ((G.unitPath i).getD (k - 1) p) p = 1 ∧ @@ -319,8 +322,8 @@ theorem unitPath_neighbor_lengths (G : Graph) /-- A nonvertex cell's predecessor and successor are distinct, so its two ports differ. -/ theorem unitPath_neighbors_distinct (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i k : ℕ} (hi : i < G.2.length) + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i k : ℕ} (hi : i < G.edges.length) (hk : k < (G.unitPath i).length) {p : Point} (heq : (G.unitPath i)[k] = p) (hcenter : p ∉ G.orthogonalPositions) : (G.unitPath i).getD (k - 1) p ≠ (G.unitPath i).getD (k + 1) p := by @@ -333,20 +336,20 @@ theorem unitPath_neighbors_distinct (G : Graph) /-- The vertical column assigned to one canonical graph incidence. -/ def pathColumn (G : Graph) (i : ℕ) (side : Bool) : ℤ := - Layout.column (G.1.idxOf (G.edgeEndpoint i side)) (portAt G.canonicalPorts i side) + Layout.column (G.vertices.idxOf (G.edgeEndpoint i side)) (portAt G.canonicalPorts i side) /-- A genuine strict transverse crossing of edge `i`'s bridge with one stem of edge `j`. The geometry is the canonical construction, not an assumed drawing witness. -/ def BridgeStemCrossing (G : Graph) (i j : ℕ) (side : Bool) (p : Point) : Prop := - Transverse (G.pathColumn i false, Layout.track G.1.length i) - (G.pathColumn i true, Layout.track G.1.length i) - (Layout.elbow (G.1.idxOf (G.edgeEndpoint j side)) (portAt G.canonicalPorts j side)) - (G.pathColumn j side, Layout.track G.1.length j) (realPoint p) + Transverse (G.pathColumn i false, Layout.track G.vertices.length i) + (G.pathColumn i true, Layout.track G.vertices.length i) + (Layout.elbow (G.vertices.idxOf (G.edgeEndpoint j side)) (portAt G.canonicalPorts j side)) + (G.pathColumn j side, Layout.track G.vertices.length j) (realPoint p) /-- A canonical crossing lies at its exact integer stem column and bridge row. -/ theorem BridgeStemCrossing.point {G : Graph} {i j : ℕ} {side : Bool} {p : Point} (hc : G.BridgeStemCrossing i j side p) : - p = (G.pathColumn j side, Layout.track G.1.length i) := + p = (G.pathColumn j side, Layout.track G.vertices.length i) := realPoint_injective (Layout.bridge_stem_crossing_point hc) /-- The crossing lies strictly between the bridge's two port columns. -/ @@ -362,17 +365,18 @@ theorem BridgeStemCrossing.horizontal {G : Graph} {i j : ℕ} {side : Bool} {p : /-- The crossing lies strictly above the stem's elbow and below its top endpoint. -/ theorem BridgeStemCrossing.vertical {G : Graph} - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) {i j : ℕ} - (hj : j < G.2.length) {side : Bool} {p : Point} (hc : G.BridgeStemCrossing i j side p) : - (Layout.elbow (G.1.idxOf (G.edgeEndpoint j side)) (portAt G.canonicalPorts j side)).2 < p.2 ∧ - p.2 < Layout.track G.1.length j := by + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) {i j : ℕ} + (hj : j < G.edges.length) {side : Bool} {p : Point} (hc : G.BridgeStemCrossing i j side p) : + (Layout.elbow (G.vertices.idxOf (G.edgeEndpoint j side)) + (portAt G.canonicalPorts j side)).2 < p.2 ∧ + p.2 < Layout.track G.vertices.length j := by have hp := hc.point have htop := Layout.elbow_below_track (e := j) (List.idxOf_lt_length_iff.2 (edgeEndpoint_mem G hG hj side)) (portAt G.canonicalPorts j side) rcases hc with ⟨_, _, _, _, _, hlo, hhi⟩ | ⟨_, _, _, hlo, hhi, _⟩ · rw [hp] - change _ < Layout.track G.1.length i ∧ _ + change _ < Layout.track G.vertices.length i ∧ _ omega · simp only [pathColumn, Layout.elbow, min_self, max_self] at hlo hhi omega @@ -380,16 +384,16 @@ theorem BridgeStemCrossing.vertical {G : Graph} /-- Actual contact of two distinct canonical unit paths is a vertex center or a strict bridge/stem crossing, in one of the two orientations. -/ theorem unitPath_contact (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) (hdeg : ∀ v, G.incidenceVertices.count v ≤ 3) {i j : ℕ} - (hi : i < G.2.length) (hj : j < G.2.length) (hij : i ≠ j) {p : Point} + (hi : i < G.edges.length) (hj : j < G.edges.length) (hij : i ≠ j) {p : Point} (hp : p ∈ G.unitPath i) (hq : p ∈ G.unitPath j) : p ∈ G.orthogonalPositions ∨ (∃ side, G.BridgeStemCrossing i j side p) ∨ ∃ side, G.BridgeStemCrossing j i side p := by - have hindex (k : ℕ) (hk : k < G.2.length) (side : Bool) : - G.1.idxOf (G.edgeEndpoint k side) < G.1.length := + have hindex (k : ℕ) (hk : k < G.edges.length) (side : Bool) : + G.vertices.idxOf (G.edgeEndpoint k side) < G.vertices.length := List.idxOf_lt_length_iff.2 (edgeEndpoint_mem G hG hk side) have hp' := (mem_unitPath_iff_support G hG hloop hi p).1 hp have hq' := (mem_unitPath_iff_support G hG hloop hj p).1 hq @@ -403,7 +407,7 @@ theorem unitPath_contact (G : Graph) (canonical_index_port_ne G hG hdeg hi hj hij true true) hp' hq' rcases hc with ⟨w, hw, _, heq⟩ | h | h | h | h · apply Or.inl - have hw' : w < G.1.length := by + have hw' : w < G.vertices.length := by rcases hw with rfl | rfl · exact hindex i hi false · exact hindex i hi true @@ -416,26 +420,27 @@ theorem unitPath_contact (G : Graph) /-- Unique canonical tracks and incidence columns admit at most the named two paths through their intersection, independently of whether that intersection is actually occupied. -/ theorem unitPath_on_column_track (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) (hdeg : ∀ v, G.incidenceVertices.count v ≤ 3) {i j k : ℕ} - (hj : j < G.2.length) (hk : k < G.2.length) (side : Bool) : - (G.pathColumn j side, Layout.track G.1.length i) ∈ G.unitPath k → k = i ∨ k = j := by + (hj : j < G.edges.length) (hk : k < G.edges.length) (side : Bool) : + (G.pathColumn j side, Layout.track G.vertices.length i) ∈ G.unitPath k → k = i ∨ k = j := by intro hp have hp' := (mem_unitPath_iff_support G hG hloop hk _).1 hp rw [getElem_orthogonalRoutes_endpoint G G.canonicalPorts hk, Layout.support_route] at hp' - have hfan (b : Bool) (hf : realPoint (G.pathColumn j side, Layout.track G.1.length i) ∈ - Layout.fan (G.1.idxOf (G.edgeEndpoint k b)) (portAt G.canonicalPorts k b)) : False := by + have hfan (b : Bool) (hf : realPoint (G.pathColumn j side, Layout.track G.vertices.length i) ∈ + Layout.fan (G.vertices.idxOf (G.edgeEndpoint k b)) + (portAt G.canonicalPorts k b)) : False := by have hb := (Layout.fan_bounds hf).2.2.2 - have hw : (G.1.idxOf (G.edgeEndpoint k b) : ℝ) + 1 ≤ G.1.length := by + have hw : (G.vertices.idxOf (G.edgeEndpoint k b) : ℝ) + 1 ≤ G.vertices.length := by exact_mod_cast List.idxOf_lt_length_iff.2 (edgeEndpoint_mem G hG hk b) have hi0 : (0 : ℝ) ≤ i := by positivity - change (Layout.track G.1.length i : ℝ) ≤ _ at hb + change (Layout.track G.vertices.length i : ℝ) ≤ _ at hb simp only [Layout.track] at hb push_cast at hb linarith - have hstem (b : Bool) (hs : realPoint (G.pathColumn j side, Layout.track G.1.length i) ∈ - Layout.stem G.1.length k (G.1.idxOf (G.edgeEndpoint k b)) + have hstem (b : Bool) (hs : realPoint (G.pathColumn j side, Layout.track G.vertices.length i) ∈ + Layout.stem G.vertices.length k (G.vertices.idxOf (G.edgeEndpoint k b)) (portAt G.canonicalPorts k b)) : k = i ∨ k = j := by by_cases hkj : k = j · exact Or.inr hkj @@ -451,8 +456,9 @@ theorem unitPath_on_column_track (G : Graph) · exact False.elim (hfan false hp') · exact hstem false hp' · have hy := (Layout.mem_bridge.1 hp').1 - change (Layout.track G.1.length i : ℝ) = (Layout.track G.1.length k : ℝ) at hy - have hy' : Layout.track G.1.length i = Layout.track G.1.length k := by exact_mod_cast hy + change (Layout.track G.vertices.length i : ℝ) = (Layout.track G.vertices.length k : ℝ) at hy + have hy' : Layout.track G.vertices.length i = Layout.track G.vertices.length k := + by exact_mod_cast hy simp only [Layout.track] at hy' exact Or.inl (by omega) · exact hstem true hp' @@ -460,10 +466,10 @@ theorem unitPath_on_column_track (G : Graph) /-- Outside declared vertex centers, any two distinct occupied paths exclude every third path. -/ theorem unitPath_atMostTwo (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) (hdeg : ∀ v, G.incidenceVertices.count v ≤ 3) {i j k : ℕ} - (hi : i < G.2.length) (hj : j < G.2.length) (hk : k < G.2.length) (hij : i ≠ j) + (hi : i < G.edges.length) (hj : j < G.edges.length) (hk : k < G.edges.length) (hij : i ≠ j) {p : Point} (hcenter : p ∉ G.orthogonalPositions) (hp : p ∈ G.unitPath i) (hq : p ∈ G.unitPath j) (hr : p ∈ G.unitPath k) : k = i ∨ k = j := by rcases unitPath_contact G hG hloop hdeg hi hj hij hp hq with h | ⟨side, hc⟩ | ⟨side, hc⟩ @@ -475,10 +481,10 @@ theorem unitPath_atMostTwo (G : Graph) /-- Every nonvertex shared point belongs to the original finite quadratic candidate cover. -/ theorem unitPath_shared_mem_crossingCandidates (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) (hdeg : ∀ v, G.incidenceVertices.count v ≤ 3) {i j : ℕ} - (hi : i < G.2.length) (hj : j < G.2.length) (hij : i ≠ j) {p : Point} + (hi : i < G.edges.length) (hj : j < G.edges.length) (hij : i ≠ j) {p : Point} (hcenter : p ∉ G.orthogonalPositions) (hp : p ∈ G.unitPath i) (hq : p ∈ G.unitPath j) : p ∈ G.crossingCandidates G.canonicalPorts := by rcases unitPath_contact G hG hloop hdeg hi hj hij hp hq with h | ⟨side, hc⟩ | ⟨side, hc⟩ @@ -518,15 +524,15 @@ private theorem listed_on_upper_track {n e f u v : ℕ} (hu : u < n) (hv : v < n /-- A crossing is not any listed point of either original polygonal route, including its elbows and upper endpoints. Thus no crossing occurs at a bend. -/ theorem BridgeStemCrossing.not_listed {G : Graph} - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) {i j : ℕ} - (hi : i < G.2.length) (hj : j < G.2.length) {side : Bool} {p : Point} + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) {i j : ℕ} + (hi : i < G.edges.length) (hj : j < G.edges.length) {side : Bool} {p : Point} (hc : G.BridgeStemCrossing i j side p) : p ∉ (G.orthogonalRoutes G.canonicalPorts)[i]'(by simpa using hi) ∧ p ∉ (G.orthogonalRoutes G.canonicalPorts)[j]'(by simpa using hj) := by - have hindex (k : ℕ) (hk : k < G.2.length) (b : Bool) : - G.1.idxOf (G.edgeEndpoint k b) < G.1.length := + have hindex (k : ℕ) (hk : k < G.edges.length) (b : Bool) : + G.vertices.idxOf (G.edgeEndpoint k b) < G.vertices.length := List.idxOf_lt_length_iff.2 (edgeEndpoint_mem G hG hk b) - have hrow : p.2 = Layout.track G.1.length i := congrArg Prod.snd hc.point + have hrow : p.2 = Layout.track G.vertices.length i := congrArg Prod.snd hc.point have hx := hc.horizontal have hy := hc.vertical hG hj constructor @@ -542,14 +548,14 @@ theorem BridgeStemCrossing.not_listed {G : Graph} rw [getElem_orthogonalRoutes_endpoint G G.canonicalPorts hj] at hp have htop := listed_on_upper_track (hindex j hj false) (hindex j hj true) _ _ hp hrow rcases htop with heq | heq <;> - have heq' : p.2 = Layout.track G.1.length j := congrArg Prod.snd heq <;> omega + have heq' : p.2 = Layout.track G.vertices.length j := congrArg Prod.snd heq <;> omega /-- A strict crossing produces actual consecutive opposite horizontal/vertical unit neighbors on its two paths. Both the positions and their traversal order come from the canonical data. -/ theorem BridgeStemCrossing.neighbors {G : Graph} - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) {i j : ℕ} - (hi : i < G.2.length) (hj : j < G.2.length) {side : Bool} {p : Point} + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) {i j : ℕ} + (hi : i < G.edges.length) (hj : j < G.edges.length) {side : Bool} {p : Point} (hc : G.BridgeStemCrossing i j side p) : StraightThrough (G.unitPath i) p (1, 0) ∧ StraightThrough (G.unitPath j) p (0, 1) := by have hri := canonical_orthogonal G hG hloop hi @@ -558,15 +564,15 @@ theorem BridgeStemCrossing.neighbors {G : Graph} rw [getElem_orthogonalRoutes_endpoint G G.canonicalPorts hj] at hrj have hp := hc.point have hpx : p.1 = G.pathColumn j side := congrArg Prod.fst hp - have hpy : p.2 = Layout.track G.1.length i := congrArg Prod.snd hp + have hpy : p.2 = Layout.track G.vertices.length i := congrArg Prod.snd hp have hx := hc.horizontal have hy := hc.vertical hG hj constructor · rw [unitPath_eq_subdivide G hG hloop hi, getElem_orthogonalRoutes_endpoint G G.canonicalPorts hi] apply subdivide_horizontal hri - (e := ((G.pathColumn i false, Layout.track G.1.length i), - (G.pathColumn i true, Layout.track G.1.length i))) + (e := ((G.pathColumn i false, Layout.track G.vertices.length i), + (G.pathColumn i true, Layout.track G.vertices.length i))) · simp [Layout.route, List.consecutivePairs, pathColumn] · rfl · exact hpy @@ -575,35 +581,36 @@ theorem BridgeStemCrossing.neighbors {G : Graph} getElem_orthogonalRoutes_endpoint G G.canonicalPorts hj] cases side · apply subdivide_vertical hrj - (e := (Layout.elbow (G.1.idxOf (G.edgeEndpoint j false)) - (portAt G.canonicalPorts j false), (G.pathColumn j false, Layout.track G.1.length j))) + (e := (Layout.elbow (G.vertices.idxOf (G.edgeEndpoint j false)) + (portAt G.canonicalPorts j false), + (G.pathColumn j false, Layout.track G.vertices.length j))) · simp [Layout.route, List.consecutivePairs, pathColumn] · rfl · exact hpx - · change min (Layout.elbow (G.1.idxOf (G.edgeEndpoint j false)) - (portAt G.canonicalPorts j false)).2 (Layout.track G.1.length j) < p.2 ∧ - p.2 < max (Layout.elbow (G.1.idxOf (G.edgeEndpoint j false)) - (portAt G.canonicalPorts j false)).2 (Layout.track G.1.length j) + · change min (Layout.elbow (G.vertices.idxOf (G.edgeEndpoint j false)) + (portAt G.canonicalPorts j false)).2 (Layout.track G.vertices.length j) < p.2 ∧ + p.2 < max (Layout.elbow (G.vertices.idxOf (G.edgeEndpoint j false)) + (portAt G.canonicalPorts j false)).2 (Layout.track G.vertices.length j) omega · apply subdivide_vertical hrj - (e := ((G.pathColumn j true, Layout.track G.1.length j), - Layout.elbow (G.1.idxOf (G.edgeEndpoint j true)) (portAt G.canonicalPorts j true))) + (e := ((G.pathColumn j true, Layout.track G.vertices.length j), + Layout.elbow (G.vertices.idxOf (G.edgeEndpoint j true)) (portAt G.canonicalPorts j true))) · simp [Layout.route, List.consecutivePairs, pathColumn] · rfl · exact hpx - · change min (Layout.track G.1.length j) (Layout.elbow - (G.1.idxOf (G.edgeEndpoint j true)) (portAt G.canonicalPorts j true)).2 < p.2 ∧ - p.2 < max (Layout.track G.1.length j) (Layout.elbow - (G.1.idxOf (G.edgeEndpoint j true)) (portAt G.canonicalPorts j true)).2 + · change min (Layout.track G.vertices.length j) (Layout.elbow + (G.vertices.idxOf (G.edgeEndpoint j true)) (portAt G.canonicalPorts j true)).2 < p.2 ∧ + p.2 < max (Layout.track G.vertices.length j) (Layout.elbow + (G.vertices.idxOf (G.edgeEndpoint j true)) (portAt G.canonicalPorts j true)).2 omega /-- Every actual nonvertex contact has one horizontally straight path and one vertically straight path, with opposite immediate unit neighbors. -/ theorem unitPath_crossing_neighbors (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) - (hloop : ∀ edge ∈ G.2, edge.1 ≠ edge.2) + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) + (hloop : ∀ edge ∈ G.edges, edge.1 ≠ edge.2) (hdeg : ∀ v, G.incidenceVertices.count v ≤ 3) {i j : ℕ} - (hi : i < G.2.length) (hj : j < G.2.length) (hij : i ≠ j) {p : Point} + (hi : i < G.edges.length) (hj : j < G.edges.length) (hij : i ≠ j) {p : Point} (hcenter : p ∉ G.orthogonalPositions) (hp : p ∈ G.unitPath i) (hq : p ∈ G.unitPath j) : (StraightThrough (G.unitPath i) p (1, 0) ∧ StraightThrough (G.unitPath j) p (0, 1)) ∨ (StraightThrough (G.unitPath i) p (0, 1) ∧ StraightThrough (G.unitPath j) p (1, 0)) := by diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph/PerfectMatching.lean b/CslibFrontier/Computability/Complexity/Problems/Graph/PerfectMatching.lean index 802d701b24..4a681a76ad 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph/PerfectMatching.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph/PerfectMatching.lean @@ -56,7 +56,8 @@ open Cslib.Logic.PL /-- The edges of `G.induced`, each listed once: the edges of `G` between distinct declared vertices, with repetitions in either orientation erased. -/ @[expose] def edgeList (G : Graph) : List (ℕ × ℕ) := - (G.2.filter fun e => decide (e.1 ≠ e.2) && decide (e.1 ∈ G.1) && decide (e.2 ∈ G.1)).eraseDupsBy + (G.edges.filter fun e => + decide (e.1 ≠ e.2) && decide (e.1 ∈ G.vertices) && decide (e.2 ∈ G.vertices)).eraseDupsBy fun a b => decide (a = b) || decide (a = (b.2, b.1)) /-- Whether the vertex `v` is an end of the edge `e`. -/ @@ -75,20 +76,21 @@ variable {G : Graph} /-- An entry of the edge list joins two distinct declared vertices by an edge of `G`. -/ theorem of_mem_edgeList {e : ℕ × ℕ} (he : e ∈ G.edgeList) : - e ∈ G.2 ∧ e.1 ≠ e.2 ∧ e.1 ∈ G.1 ∧ e.2 ∈ G.1 := by + e ∈ G.edges ∧ e.1 ≠ e.2 ∧ e.1 ∈ G.vertices ∧ e.2 ∈ G.vertices := by have := List.mem_filter.mp ((List.eraseDupsBy_sublist _).subset he) simp only [Bool.and_eq_true, decide_eq_true_eq] at this exact ⟨this.1, this.2.1.1, this.2.1.2, this.2.2⟩ /-- Each edge of `G.induced` is in the edge list in one of its orientations. -/ -theorem exists_mem_edgeList {u v : {v // v ∈ G.1}} (h : G.induced.Adj u v) : +theorem exists_mem_edgeList {u v : {v // v ∈ G.vertices}} (h : G.induced.Adj u v) : (u.1, v.1) ∈ G.edgeList ∨ (v.1, u.1) ∈ G.edgeList := by rw [induced_adj, toSimpleGraph_adj] at h obtain ⟨hne, huv⟩ := h - have key : ∀ e ∈ G.2, e.1 ≠ e.2 → e.1 ∈ G.1 → e.2 ∈ G.1 → e ∈ G.edgeList ∨ e.swap ∈ G.edgeList := + have key : ∀ e ∈ G.edges, e.1 ≠ e.2 → e.1 ∈ G.vertices → e.2 ∈ G.vertices → + e ∈ G.edgeList ∨ e.swap ∈ G.edgeList := fun e he h1 h2 h3 => by - have hmem : e ∈ G.2.filter fun e => - decide (e.1 ≠ e.2) && decide (e.1 ∈ G.1) && decide (e.2 ∈ G.1) := + have hmem : e ∈ G.edges.filter fun e => + decide (e.1 ≠ e.2) && decide (e.1 ∈ G.vertices) && decide (e.2 ∈ G.vertices) := List.mem_filter.mpr ⟨he, by simp [h1, h2, h3]⟩ obtain ⟨b, hb, rfl | hab⟩ := List.exists_mem_eraseDupsBy (r := fun a b => decide (a = b) || decide (a = (b.2, b.1))) hmem @@ -152,7 +154,7 @@ theorem mem_vars_perfectMatchingInstance {x : ℕ} : declared vertex is an end of exactly one edge at a position in `S`. -/ private theorem forall_mem_perfectMatchingInstance_iff {S : Finset ℕ} : (∀ c ∈ G.perfectMatchingInstance, (c.2.filter fun v => v ∈ S).length = 1) ↔ - ∀ v ∈ G.1, ∃! i, i < G.edgeList.length ∧ incident (G.edgeList.getD i (0, 0)) v = true ∧ + ∀ v ∈ G.vertices, ∃! i, i < G.edgeList.length ∧ incident (G.edgeList.getD i (0, 0)) v = true ∧ i ∈ S := by simp only [perfectMatchingInstance, List.forall_mem_map, mem_distinctVertices, List.filter_filter, List.length_filter_range_eq_one_iff, Bool.and_eq_true, decide_eq_true_eq] @@ -160,7 +162,7 @@ private theorem forall_mem_perfectMatchingInstance_iff {S : Finset ℕ} : /-- The adjacency of a subgraph of `G.induced`, read on natural numbers. -/ private def AdjNat (M : G.induced.Subgraph) (u w : ℕ) : Prop := - ∃ (hu : u ∈ G.1) (hw : w ∈ G.1), M.Adj ⟨u, hu⟩ ⟨w, hw⟩ + ∃ (hu : u ∈ G.vertices) (hw : w ∈ G.vertices), M.Adj ⟨u, hu⟩ ⟨w, hw⟩ /-- The positions of the edges of a subgraph. -/ private noncomputable def positions (M : G.induced.Subgraph) : Finset ℕ := by @@ -192,7 +194,7 @@ private theorem exists_other_end {e : ℕ × ℕ} {v : ℕ} (hv : incident e v = · exact ⟨e.1, Or.inr rfl⟩ private theorem isPerfectMatching_ofPositions {S : Finset ℕ} (hS : ∀ i ∈ S, i < G.edgeList.length) - (hcov : ∀ v ∈ G.1, ∃! i, i < G.edgeList.length ∧ + (hcov : ∀ v ∈ G.vertices, ∃! i, i < G.edgeList.length ∧ incident (G.edgeList.getD i (0, 0)) v = true ∧ i ∈ S) : (ofPositions G S).IsPerfectMatching := by refine SimpleGraph.Subgraph.isPerfectMatching_iff.mpr fun u => ?_ @@ -200,7 +202,7 @@ private theorem isPerfectMatching_ofPositions {S : Finset ℕ} (hS : ∀ i ∈ S have hmem := getD_edgeList_mem hi obtain ⟨hG, hne, h1, h2⟩ := of_mem_edgeList hmem obtain ⟨w, hw⟩ := exists_other_end hinc - have hw' : w ∈ G.1 := by rcases hw with hw | hw <;> rw [hw] at h1 h2 <;> assumption + have hw' : w ∈ G.vertices := by rcases hw with hw | hw <;> rw [hw] at h1 h2 <;> assumption have hadj : G.induced.Adj u ⟨w, hw'⟩ := by rw [induced_adj, toSimpleGraph_adj] rcases hw with hw | hw <;> rw [hw] at hG hne @@ -217,7 +219,7 @@ private theorem isPerfectMatching_ofPositions {S : Finset ℕ} (hS : ∀ i ∈ S simp only [Prod.mk.injEq] at h hne <;> omega private theorem cover_positions {M : G.induced.Subgraph} (hM : M.IsPerfectMatching) : - ∀ v ∈ G.1, ∃! i, i < G.edgeList.length ∧ + ∀ v ∈ G.vertices, ∃! i, i < G.edgeList.length ∧ incident (G.edgeList.getD i (0, 0)) v = true ∧ i ∈ positions M := by intro v hv obtain ⟨w, hvw, hwuniq⟩ := SimpleGraph.Subgraph.isPerfectMatching_iff.mp hM ⟨v, hv⟩ @@ -302,7 +304,7 @@ theorem partitionFunction_perfectMatchingInstance (G : Graph) : /-- The constraints of the perfect-matching instance: one for each distinct declared vertex. -/ theorem length_perfectMatchingInstance (G : Graph) : - G.perfectMatchingInstance.length = G.1.toFinset.card := by + G.perfectMatchingInstance.length = G.vertices.toFinset.card := by rw [perfectMatchingInstance, List.length_map, length_distinctVertices] /-- **Every variable occurs at most twice**: in the constraints of the two ends of its edge. -/ @@ -357,7 +359,7 @@ theorem length_le_of_mem_perfectMatchingInstance {d : ℕ} rw [Finset.mem_range] at hi obtain ⟨hG, hne, h1, h2⟩ := of_mem_edgeList (getD_edgeList_mem hi) simp only [incident, Bool.or_eq_true, decide_eq_true_eq] at hinc - have hother : ∃ h : other i ∈ G.1, G.induced.Adj ⟨v, hv⟩ ⟨other i, h⟩ := by + have hother : ∃ h : other i ∈ G.vertices, G.induced.Adj ⟨v, hv⟩ ⟨other i, h⟩ := by simp only [other] split_ifs with h · refine ⟨h2, ?_⟩ @@ -387,7 +389,7 @@ theorem length_le_of_mem_perfectMatchingInstance {d : ℕ} /-- **A vertex has at most as many neighbours as edge-list entries at it**, so a degree bound can be checked on the edge list. -/ -theorem card_neighborSet_le (v : {v // v ∈ G.1}) : +theorem card_neighborSet_le (v : {v // v ∈ G.vertices}) : Nat.card (G.induced.neighborSet v) ≤ (G.edgeList.filter fun e => incident e v).length := by classical have hnd : (G.edgeList.filter fun e => incident e v).Nodup := diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph/Ports.lean b/CslibFrontier/Computability/Complexity/Problems/Graph/Ports.lean index 98b161eea7..a0110b04d5 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph/Ports.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph/Ports.lean @@ -36,16 +36,16 @@ Parallel listed edges remain distinct; a loop contributes two incidences. namespace Cslib.Complexity.Graph /-- Endpoints of all listed edges, counting edge occurrences rather than simple-graph degree. -/ -def incidenceVertices (G : Graph) : List ℕ := G.2.flatMap fun e => [e.1, e.2] +def incidenceVertices (G : Graph) : List ℕ := G.edges.flatMap fun e => [e.1, e.2] /-- Number of endpoint occurrences is twice the edge-list length. -/ @[simp] theorem incidenceVertices_length (G : Graph) : - G.incidenceVertices.length = 2 * G.2.length := by + G.incidenceVertices.length = 2 * G.edges.length := by simp [incidenceVertices, List.length_flatMap, List.map_const', Nat.mul_comm] /-- The even endpoint occurrence is the first endpoint of the corresponding listed edge. -/ theorem getD_incidenceVertices_even (G : Graph) (i : ℕ) : - G.incidenceVertices.getD (2 * i) 0 = (G.2.getD i (0, 0)).1 := by + G.incidenceVertices.getD (2 * i) 0 = (G.edges.getD i (0, 0)).1 := by rcases G with ⟨V, E⟩ induction E generalizing i with | nil => simp [incidenceVertices] @@ -58,7 +58,7 @@ theorem getD_incidenceVertices_even (G : Graph) (i : ℕ) : /-- The odd endpoint occurrence is the second endpoint of the corresponding listed edge. -/ theorem getD_incidenceVertices_odd (G : Graph) (i : ℕ) : - G.incidenceVertices.getD (2 * i + 1) 0 = (G.2.getD i (0, 0)).2 := by + G.incidenceVertices.getD (2 * i + 1) 0 = (G.edges.getD i (0, 0)).2 := by rcases G with ⟨V, E⟩ induction E generalizing i with | nil => simp [incidenceVertices] @@ -76,14 +76,14 @@ def incidencePort (G : Graph) (k : ℕ) : Fin 3 := /-- The edge-indexed finite table of canonical port pairs. -/ def canonicalPorts (G : Graph) : List (Fin 3 × Fin 3) := - (List.range G.2.length).map fun i => (G.incidencePort (2 * i), G.incidencePort (2 * i + 1)) + (List.range G.edges.length).map fun i => (G.incidencePort (2 * i), G.incidencePort (2 * i + 1)) /-- One canonical port pair per listed edge. -/ -@[simp] theorem canonicalPorts_length (G : Graph) : G.canonicalPorts.length = G.2.length := by +@[simp] theorem canonicalPorts_length (G : Graph) : G.canonicalPorts.length = G.edges.length := by simp [canonicalPorts] /-- Reading the canonical table at an existing edge returns its two incidence ports. -/ -theorem getD_canonicalPorts (G : Graph) {i : ℕ} (hi : i < G.2.length) : +theorem getD_canonicalPorts (G : Graph) {i : ℕ} (hi : i < G.edges.length) : G.canonicalPorts.getD i (0, 0) = (G.incidencePort (2 * i), G.incidencePort (2 * i + 1)) := by rw [List.getD_eq_getElem _ _ (by simpa using hi)] simp [canonicalPorts] @@ -120,7 +120,7 @@ theorem incidencePort_injective (G : Graph) /-- Endpoint label of a listed edge occurrence; `false` means first, `true` second. -/ def edgeEndpoint (G : Graph) (i : ℕ) (side : Bool) : ℕ := - if side then (G.2.getD i (0, 0)).2 else (G.2.getD i (0, 0)).1 + if side then (G.edges.getD i (0, 0)).2 else (G.edges.getD i (0, 0)).1 /-- The endpoint occurrence index agrees with the finite flattened endpoint list. -/ theorem getD_incidenceVertices_endpoint (G : Graph) (i : ℕ) (side : Bool) : @@ -130,7 +130,7 @@ theorem getD_incidenceVertices_endpoint (G : Graph) (i : ℕ) (side : Bool) : · exact getD_incidenceVertices_odd G i /-- A listed endpoint occurrence lies in the endpoint list's index range. -/ -theorem endpoint_index_lt (G : Graph) {i : ℕ} (hi : i < G.2.length) (side : Bool) : +theorem endpoint_index_lt (G : Graph) {i : ℕ} (hi : i < G.edges.length) (side : Bool) : 2 * i + side.toNat < G.incidenceVertices.length := by rw [incidenceVertices_length] cases side <;> simp only [Bool.toNat_false, Bool.toNat_true] <;> omega @@ -138,7 +138,7 @@ theorem endpoint_index_lt (G : Graph) {i : ℕ} (hi : i < G.2.length) (side : Bo /-- Vertex and canonical port determine both the edge occurrence and which end it is. -/ theorem endpoint_port_injective (G : Graph) (hdeg : ∀ v, G.incidenceVertices.count v ≤ 3) {i j : ℕ} - (hi : i < G.2.length) (hj : j < G.2.length) (left right : Bool) + (hi : i < G.edges.length) (hj : j < G.edges.length) (left right : Bool) (hv : G.edgeEndpoint i left = G.edgeEndpoint j right) (hp : G.incidencePort (2 * i + left.toNat) = G.incidencePort (2 * j + right.toNat)) : i = j ∧ left = right := by @@ -158,27 +158,27 @@ def portAt (ports : List (Fin 3 × Fin 3)) (i : ℕ) (side : Bool) : Fin 3 := if side then (ports.getD i (0, 0)).2 else (ports.getD i (0, 0)).1 /-- The canonical table reads the corresponding incidence's occurrence-rank port. -/ -theorem portAt_canonicalPorts (G : Graph) {i : ℕ} (hi : i < G.2.length) (side : Bool) : +theorem portAt_canonicalPorts (G : Graph) {i : ℕ} (hi : i < G.edges.length) (side : Bool) : portAt G.canonicalPorts i side = G.incidencePort (2 * i + side.toNat) := by rw [portAt, getD_canonicalPorts G hi] cases side <;> rfl /-- Every indexed endpoint of a graph with declared endpoints is declared. -/ theorem edgeEndpoint_mem (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) {i : ℕ} - (hi : i < G.2.length) (side : Bool) : G.edgeEndpoint i side ∈ G.1 := by - have hmem : G.2.getD i (0, 0) ∈ G.2 := by grind + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) {i : ℕ} + (hi : i < G.edges.length) (side : Bool) : G.edgeEndpoint i side ∈ G.vertices := by + have hmem : G.edges.getD i (0, 0) ∈ G.edges := by grind cases side · exact (hG _ hmem).1 · exact (hG _ hmem).2 /-- Canonical vertex-position/port pairs are distinct at every pair of different listed edges. -/ theorem canonical_index_port_ne (G : Graph) - (hG : ∀ edge ∈ G.2, edge.1 ∈ G.1 ∧ edge.2 ∈ G.1) + (hG : ∀ edge ∈ G.edges, edge.1 ∈ G.vertices ∧ edge.2 ∈ G.vertices) (hdeg : ∀ v, G.incidenceVertices.count v ≤ 3) {i j : ℕ} - (hi : i < G.2.length) (hj : j < G.2.length) (hij : i ≠ j) (left right : Bool) : - (G.1.idxOf (G.edgeEndpoint i left), portAt G.canonicalPorts i left) ≠ - (G.1.idxOf (G.edgeEndpoint j right), portAt G.canonicalPorts j right) := by + (hi : i < G.edges.length) (hj : j < G.edges.length) (hij : i ≠ j) (left right : Bool) : + (G.vertices.idxOf (G.edgeEndpoint i left), portAt G.canonicalPorts i left) ≠ + (G.vertices.idxOf (G.edgeEndpoint j right), portAt G.canonicalPorts j right) := by intro h have hv := (List.idxOf_inj (edgeEndpoint_mem G hG hi left)).1 (congrArg Prod.fst h) have hp := congrArg Prod.snd h diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem.lean b/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem.lean index e5aa1128e3..0ff9837e95 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem.lean @@ -36,7 +36,7 @@ public section namespace Cslib.Complexity.Graph /-- A listed edge occurrence together with a choice of endpoint. -/ -abbrev Dart (G : Graph) := Fin G.2.length × Bool +abbrev Dart (G : Graph) := Fin G.edges.length × Bool namespace Dart @@ -65,7 +65,7 @@ theorem reverse_ne {G : Graph} (d : Dart G) : d.reverse ≠ d := by right_inv := reverse_reverse /-- There are two darts per listed edge, counting loops and parallel occurrences. -/ -theorem card (G : Graph) : Fintype.card (Dart G) = 2 * G.2.length := by +theorem card (G : Graph) : Fintype.card (Dart G) = 2 * G.edges.length := by simp [Dart, Nat.mul_comm] end Dart diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Certificate.lean b/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Certificate.lean index f2c7736cf5..14c8347b34 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Certificate.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Certificate.lean @@ -68,7 +68,7 @@ namespace Dart @[expose] def index {G : Graph} (d : Dart G) : ℕ := 2 * d.1.val + d.2.toNat /-- Indexed darts are exactly the positions of the flattened endpoint list. -/ -@[expose] def indexEquiv (G : Graph) : Dart G ≃ Fin (2 * G.2.length) where +@[expose] def indexEquiv (G : Graph) : Dart G ≃ Fin (2 * G.edges.length) where toFun d := ⟨d.index, by have := endpoint_index_lt G d.1.isLt d.2 simpa only [incidenceVertices_length, index] using this⟩ @@ -114,28 +114,28 @@ namespace RotationCertificate /-- All declared endpoints, exact table length, permutation, and exact endpoint fibers. -/ @[expose] def verifyRotation (G : Graph) (c : RotationCertificate) : Bool := - (G.incidenceVertices.all fun v => G.1.any fun u => decide (u = v)) && - decide (c.successor.length = 2 * G.2.length) && - enumeratesBool (List.range (2 * G.2.length)) c.successor && - (List.range (2 * G.2.length)).all fun i => - (List.range (2 * G.2.length)).all fun j => - decide (orbitBool (2 * G.2.length) c.successor i j = + (G.incidenceVertices.all fun v => G.vertices.any fun u => decide (u = v)) && + decide (c.successor.length = 2 * G.edges.length) && + enumeratesBool (List.range (2 * G.edges.length)) c.successor && + (List.range (2 * G.edges.length)).all fun i => + (List.range (2 * G.edges.length)).all fun j => + decide (orbitBool (2 * G.edges.length) c.successor i j = decide (G.incidenceVertices.getD i 0 = G.incidenceVertices.getD j 0)) private theorem verifyRotation_parts {G : Graph} {c : RotationCertificate} (h : verifyRotation G c = true) : - (∀ v ∈ G.incidenceVertices, v ∈ G.1) ∧ - c.successor.length = 2 * G.2.length ∧ c.successor.Nodup ∧ - (∀ v, v ∈ c.successor ↔ v < 2 * G.2.length) ∧ - ∀ i < 2 * G.2.length, ∀ j < 2 * G.2.length, - orbitBool (2 * G.2.length) c.successor i j = + (∀ v ∈ G.incidenceVertices, v ∈ G.vertices) ∧ + c.successor.length = 2 * G.edges.length ∧ c.successor.Nodup ∧ + (∀ v, v ∈ c.successor ↔ v < 2 * G.edges.length) ∧ + ∀ i < 2 * G.edges.length, ∀ j < 2 * G.edges.length, + orbitBool (2 * G.edges.length) c.successor i j = decide (G.incidenceVertices.getD i 0 = G.incidenceVertices.getD j 0) := by simpa only [verifyRotation, Bool.and_eq_true, List.all_eq_true, List.any_eq_true, decide_eq_true_eq, enumeratesBool_eq_true_iff, List.mem_range, exists_eq_right, and_assoc] using h private def tableMap (G : Graph) (c : RotationCertificate) (h : verifyRotation G c = true) - (i : Fin (2 * G.2.length)) : Fin (2 * G.2.length) := + (i : Fin (2 * G.edges.length)) : Fin (2 * G.edges.length) := ⟨c.successor.getD i 0, (verifyRotation_parts h).2.2.2.1 _ |>.mp (by rw [List.getD_eq_getElem _ _ (by rw [(verifyRotation_parts h).2.1]; exact i.isLt)] exact List.getElem_mem _)⟩ @@ -153,7 +153,7 @@ private theorem tableMap_injective {G : Graph} {c : RotationCertificate} exact congrArg (fun k : Fin c.successor.length => k.val) heq' private noncomputable def tablePerm (G : Graph) (c : RotationCertificate) - (h : verifyRotation G c = true) : Equiv.Perm (Fin (2 * G.2.length)) := + (h : verifyRotation G c = true) : Equiv.Perm (Fin (2 * G.edges.length)) := Equiv.ofBijective (tableMap G c h) ⟨tableMap_injective h, Finite.surjective_of_injective (tableMap_injective h)⟩ @@ -176,7 +176,7 @@ private theorem sameCycle_iff_bounded {α : Type} [Fintype α] that permutation. The clock is the cardinality, rather than the permutation's order. -/ theorem orbitBool_eq_true_iff {G : Graph} (f : Equiv.Perm (Dart G)) (table : List ℕ) (ht : ∀ d, table.getD d.index 0 = (f d).index) (d e : Dart G) : - orbitBool (2 * G.2.length) table d.index e.index = true ↔ f.SameCycle d e := by + orbitBool (2 * G.edges.length) table d.index e.index = true ↔ f.SameCycle d e := by have hw (k : ℕ) (d : Dart G) : walk table k d.index = (f^[k] d).index := by induction k with | zero => rfl @@ -217,11 +217,11 @@ theorem index_decode_rotate {G : Graph} {c : RotationCertificate} /-- A table for an abstract rotation, in the canonical edge/end order. -/ @[expose] def ofRotation {G : Graph} (r : RotationSystem G) : RotationCertificate := - ⟨List.ofFn fun i : Fin (2 * G.2.length) => (r.rotate ((Dart.indexEquiv G).symm i)).index⟩ + ⟨List.ofFn fun i : Fin (2 * G.edges.length) => (r.rotate ((Dart.indexEquiv G).symm i)).index⟩ /-- The canonical table has exactly two entries per listed edge. -/ @[simp] theorem length_ofRotation {G : Graph} (r : RotationSystem G) : - (ofRotation r).successor.length = 2 * G.2.length := by simp [ofRotation] + (ofRotation r).successor.length = 2 * G.edges.length := by simp [ofRotation] /-- Reading the canonical table recovers the rotation. -/ theorem getD_ofRotation {G : Graph} (r : RotationSystem G) (d : Dart G) : @@ -232,14 +232,14 @@ theorem getD_ofRotation {G : Graph} (r : RotationSystem G) (d : Dart G) : /-- Every existing rotation has an accepted finite certificate when its endpoints are declared. No rotation is assumed to be geometrically planar. -/ theorem verifyRotation_ofRotation {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) : verifyRotation G (ofRotation r) = true := by - have hinj : Function.Injective (fun i : Fin (2 * G.2.length) => + have hinj : Function.Injective (fun i : Fin (2 * G.edges.length) => (r.rotate ((Dart.indexEquiv G).symm i)).index) := by intro i j h exact (Dart.indexEquiv G).symm.injective (r.rotate.injective ((Dart.indexEquiv G).injective (Fin.ext h))) - have hmem (v : ℕ) : v ∈ (ofRotation r).successor ↔ v < 2 * G.2.length := by + have hmem (v : ℕ) : v ∈ (ofRotation r).successor ↔ v < 2 * G.edges.length := by simp only [ofRotation, List.mem_ofFn] constructor · rintro ⟨i, rfl⟩ @@ -265,7 +265,7 @@ theorem verifyRotation_ofRotation {G : Graph} (r : RotationSystem G) /-- Encoding and then decoding preserves the actual successor permutation. -/ theorem decode_ofRotation_rotate {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) : (decode G (ofRotation r) (verifyRotation_ofRotation r hG)).rotate = r.rotate := by apply Equiv.ext intro d @@ -277,7 +277,7 @@ theorem decode_ofRotation_rotate {G : Graph} (r : RotationSystem G) /-- Canonical table encoding and checked decoding recover the existing rotation structure. -/ theorem decode_ofRotation {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) : decode G (ofRotation r) (verifyRotation_ofRotation r hG) = r := by have hrot := decode_ofRotation_rotate r hG generalize decode G (ofRotation r) (verifyRotation_ofRotation r hG) = s at hrot ⊢ @@ -290,11 +290,11 @@ theorem decode_ofRotation {G : Graph} (r : RotationSystem G) The bound depends on the encoded graph length, even for sparse, very large vertex labels. -/ theorem length_enc_ofRotation_le {G : Graph} (r : RotationSystem G) : (encRotationCertificate (ofRotation r)).length ≤ 10 * ((encGraph G).length + 1) ^ 2 := by - have hm : G.2.length ≤ (encGraph G).length := by - calc G.2.length ≤ (encList (encPair encNat encNat) G.2).length := + have hm : G.edges.length ≤ (encGraph G).length := by + calc G.edges.length ≤ (encList (encPair encNat encNat) G.edges).length := length_le_length_encList _ _ - _ ≤ (encGraph G).length := by enc_len [encGraph] - have hb : ∀ v ∈ (ofRotation r).successor, (encNat v).length ≤ 2 * G.2.length := by + _ ≤ (encGraph G).length := by enc_len [encGraph_apply] + have hb : ∀ v ∈ (ofRotation r).successor, (encNat v).length ≤ 2 * G.edges.length := by intro v hv obtain ⟨i, rfl⟩ := List.mem_ofFn.mp hv have hi := (Dart.indexEquiv G (r.rotate ((Dart.indexEquiv G).symm i))).isLt @@ -308,11 +308,11 @@ theorem length_enc_ofRotation_le {G : Graph} (r : RotationSystem G) : /-- One breadth-first expansion, retaining the old list. Repetitions are harmless: the result is used only for membership, never as a vertex count. -/ @[expose] def componentStep (G : Graph) (seen : List ℕ) : List ℕ := - seen ++ G.1.filter fun v => seen.any fun u => G.adjBool u v + seen ++ G.vertices.filter fun v => seen.any fun u => G.adjBool u v /-- Finite reachability from a declared root, with one iteration per declared vertex record. -/ @[expose] def componentVertices (G : Graph) (root : ℕ) : List ℕ := - (componentStep G)^[G.1.length] [root] + (componentStep G)^[G.vertices.length] [root] /-- Native component membership; no range up to a binary vertex label is constructed. -/ @[expose] def componentBool (G : Graph) (root v : ℕ) : Bool := @@ -320,10 +320,10 @@ is used only for membership, never as a vertex count. -/ /-- Membership in the same connected component of the existing graph on declared vertices. -/ @[expose] def SameComponent (G : Graph) (u v : ℕ) : Prop := - ∃ hu : u ∈ G.1, ∃ hv : v ∈ G.1, G.induced.Reachable ⟨u, hu⟩ ⟨v, hv⟩ + ∃ hu : u ∈ G.vertices, ∃ hv : v ∈ G.vertices, G.induced.Reachable ⟨u, hu⟩ ⟨v, hv⟩ /-- A declared vertex is in its own component, including an isolated vertex. -/ -theorem SameComponent.refl {G : Graph} {v : ℕ} (hv : v ∈ G.1) : SameComponent G v v := +theorem SameComponent.refl {G : Graph} {v : ℕ} (hv : v ∈ G.vertices) : SameComponent G v v := ⟨hv, hv, .rfl⟩ /-- Component membership is symmetric. -/ @@ -341,7 +341,7 @@ theorem SameComponent.trans {G : Graph} {u v w : ℕ} private theorem mem_componentStep {G : Graph} {seen : List ℕ} {v : ℕ} : v ∈ componentStep G seen ↔ - v ∈ seen ∨ v ∈ G.1 ∧ ∃ u ∈ seen, G.toSimpleGraph.Adj u v := by + v ∈ seen ∨ v ∈ G.vertices ∧ ∃ u ∈ seen, G.toSimpleGraph.Adj u v := by simp [componentStep, List.any_eq_true] private theorem mem_iterate_componentStep {G : Graph} {seen : List ℕ} {v : ℕ} @@ -353,7 +353,7 @@ private theorem mem_iterate_componentStep {G : Graph} {seen : List ℕ} {v : ℕ exact mem_componentStep.mpr (Or.inl ih) private theorem walk_mem_componentStep {G : Graph} - {u v : {v // v ∈ G.1}} (p : G.induced.Walk u v) {seen : List ℕ} + {u v : {v // v ∈ G.vertices}} (p : G.induced.Walk u v) {seen : List ℕ} (hu : u.val ∈ seen) : v.val ∈ (componentStep G)^[p.length] seen := by induction p generalizing seen with | nil => exact hu @@ -364,7 +364,7 @@ private theorem walk_mem_componentStep {G : Graph} /-- The finite expansion agrees exactly with connectedness in the existing induced graph. The path bound is the number of declared vertices, including malformed duplicate records. -/ -theorem componentBool_eq_true_iff {G : Graph} {root v : ℕ} (hr : root ∈ G.1) : +theorem componentBool_eq_true_iff {G : Graph} {root v : ℕ} (hr : root ∈ G.vertices) : componentBool G root v = true ↔ SameComponent G root v := by simp only [componentBool, List.any_eq_true, decide_eq_true_eq, exists_eq_right] constructor @@ -386,13 +386,13 @@ theorem componentBool_eq_true_iff {G : Graph} {root v : ℕ} (hr : root ∈ G.1) exact hs _ _ hv · rintro ⟨hr, hv, hconn⟩ obtain ⟨p, hp⟩ := hconn.exists_isPath - have hc : Fintype.card {v // v ∈ G.1} = G.1.toFinset.card := card_vertexSubtype G - have hlen' : p.length ≤ G.1.length := by + have hc : Fintype.card {v // v ∈ G.vertices} = G.vertices.toFinset.card := card_vertexSubtype G + have hlen' : p.length ≤ G.vertices.length := by have := hp.length_lt rw [hc] at this - exact this.le.trans G.1.toFinset_card_le + exact this.le.trans G.vertices.toFinset_card_le have hm := walk_mem_componentStep p (seen := [root]) (by simp) - have := mem_iterate_componentStep (G := G) hm (G.1.length - p.length) + have := mem_iterate_componentStep (G := G) hm (G.vertices.length - p.length) rwa [← Function.iterate_add_apply, Nat.sub_add_cancel hlen'] at this /-- Reverse a dart's endpoint bit without inspecting vertex labels. -/ @@ -406,7 +406,7 @@ theorem reverseIndex_index {G : Graph} (d : Dart G) : reverseIndex d.index = d.r /-- The face successor table: reverse the edge, then look up the inverse rotation. `idxOf` returns the table length on failure, making this total on malformed certificates. -/ @[expose] def faceTable (G : Graph) (c : RotationCertificate) : List ℕ := - (List.range (2 * G.2.length)).map fun i => c.successor.idxOf (reverseIndex i) + (List.range (2 * G.edges.length)).map fun i => c.successor.idxOf (reverseIndex i) /-- Test that this dart is the least indexed member of its face orbit, including fixed points. -/ @[expose] def faceRepBool (n : ℕ) (table : List ℕ) (i : ℕ) : Bool := @@ -429,7 +429,7 @@ private theorem idxOf_decode {G : Graph} {c : RotationCertificate} theorem getD_faceTable {G : Graph} {c : RotationCertificate} (h : verifyRotation G c = true) (d : Dart G) : (faceTable G c).getD d.index 0 = ((decode G c h).faceNext d).index := by - have hd : d.index < 2 * G.2.length := (Dart.indexEquiv G d).isLt + have hd : d.index < 2 * G.edges.length := (Dart.indexEquiv G d).isLt rw [List.getD_eq_getElem _ _ (by simpa only [faceTable, List.length_map, List.length_range] using hd)] simp only [faceTable, List.getElem_map, List.getElem_range, reverseIndex_index, @@ -440,13 +440,13 @@ theorem getD_faceTable {G : Graph} {c : RotationCertificate} incident dart, use the point-component convention `V=1,E=0,F=1`, not a dart orbit. For an edge component, count distinct vertices, listed edge positions, and every face orbit. -/ @[expose] def verifyEuler (G : Graph) (c : RotationCertificate) : Bool := - G.1.all fun root => + G.vertices.all fun root => if G.incidenceVertices.any (fun v => decide (v = root)) then decide (G.distinctVertices.countP (componentBool G root) + - ((List.range (2 * G.2.length)).countP fun i => + ((List.range (2 * G.edges.length)).countP fun i => componentBool G root (G.incidenceVertices.getD i 0) && - faceRepBool (2 * G.2.length) (faceTable G c) i) = - G.2.countP (fun e => componentBool G root e.1) + 2) + faceRepBool (2 * G.edges.length) (faceTable G c) i) = + G.edges.countP (fun e => componentBool G root e.1) + 2) else decide (1 + 1 = 0 + 2) /-- Full supplied-certificate checker: rotation validity and componentwise numerical Euler. -/ @@ -459,7 +459,7 @@ namespace RotationSystem /-- Every dart endpoint is declared when all listed edge endpoints are declared. -/ theorem vertex_mem {G : Graph} - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) : d.vertex ∈ G.1 := by + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) : d.vertex ∈ G.vertices := by apply hG rw [← Dart.vertex_index, List.getD_eq_getElem _ _ (show d.index < G.incidenceVertices.length from endpoint_index_lt G d.1.isLt d.2)] @@ -468,7 +468,7 @@ theorem vertex_mem {G : Graph} /-- The two indexed ends of an edge belong to the same declared-vertex component, including when the edge is a loop. All listed endpoints must be declared. -/ theorem sameComponent_reverse {G : Graph} - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) : RotationCertificate.SameComponent G d.vertex d.reverse.vertex := by have hd := vertex_mem hG d have he := vertex_mem hG d.reverse @@ -479,7 +479,7 @@ theorem sameComponent_reverse {G : Graph} change G.toSimpleGraph.Adj d.vertex d.reverse.vertex rw [toSimpleGraph_adj] refine ⟨h, ?_⟩ - have hm : G.2.getD d.1.val (0, 0) ∈ G.2 := by + have hm : G.edges.getD d.1.val (0, 0) ∈ G.edges := by rw [List.getD_eq_getElem _ _ d.1.isLt] exact List.getElem_mem _ rcases d with ⟨i, b⟩ @@ -489,7 +489,7 @@ theorem sameComponent_reverse {G : Graph} /-- A face successor stays in the same edge component. -/ theorem sameComponent_faceNext {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) : RotationCertificate.SameComponent G d.vertex (r.faceNext d).vertex := by have hv : (r.faceNext d).vertex = d.reverse.vertex := by change (r.rotate.symm d.reverse).vertex = d.reverse.vertex @@ -501,7 +501,7 @@ theorem sameComponent_faceNext {G : Graph} (r : RotationSystem G) /-- All darts of each face orbit belong to one edge component. Thus componentwise face counts cannot split an orbit or transfer a face between disconnected edge components. -/ theorem sameComponent_of_sameFace {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) {d e : Dart G} + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) {d e : Dart G} (h : r.faceNext.SameCycle d e) : RotationCertificate.SameComponent G d.vertex e.vertex := by obtain ⟨k, -, rfl⟩ := (RotationCertificate.sameCycle_iff_bounded r.faceNext d e).mp h @@ -523,14 +523,14 @@ point component with one conventional face and no dart. This is a numerical pred existing rotations, not an equivalence to geometric planarity. In particular, geometric outer faces of disconnected drawings have not been identified with these componentwise faces. -/ @[expose] noncomputable def NumericalGenusZero {G : Graph} (r : RotationSystem G) : Prop := - ∀ root ∈ G.1, + ∀ root ∈ G.vertices, if ∃ d : Dart G, d.vertex = root then G.distinctVertices.countP (fun v => decide (RotationCertificate.SameComponent G root v)) + - ((List.finRange (2 * G.2.length)).countP fun i => + ((List.finRange (2 * G.edges.length)).countP fun i => decide (RotationCertificate.SameComponent G root ((Dart.indexEquiv G).symm i).vertex ∧ r.IsFaceRepresentative ((Dart.indexEquiv G).symm i))) = - G.2.countP (fun e => decide (RotationCertificate.SameComponent G root e.1)) + 2 + G.edges.countP (fun e => decide (RotationCertificate.SameComponent G root e.1)) + 2 else 1 + 1 = 0 + 2 /-- Each face orbit has exactly one representative, including a one-dart fixed-point orbit. -/ @@ -560,7 +560,7 @@ private theorem incidentBool_eq_true_iff (G : Graph) (root : ℕ) : constructor · intro hr obtain ⟨i, hi⟩ := List.mem_iff_get.mp hr - let j : Fin (2 * G.2.length) := ⟨i.val, i.isLt.trans_eq (incidenceVertices_length G)⟩ + let j : Fin (2 * G.edges.length) := ⟨i.val, i.isLt.trans_eq (incidenceVertices_length G)⟩ let d := (Dart.indexEquiv G).symm j refine ⟨d, ?_⟩ have hd : d.index = i.val := congrArg Fin.val ((Dart.indexEquiv G).apply_symm_apply j) @@ -574,7 +574,7 @@ private theorem incidentBool_eq_true_iff (G : Graph) (root : ℕ) : /-- The face representative test counts exactly the existing face orbits. -/ theorem faceRepBool_eq_true_iff {G : Graph} {c : RotationCertificate} (h : verifyRotation G c = true) (d : Dart G) : - faceRepBool (2 * G.2.length) (faceTable G c) d.index = true ↔ + faceRepBool (2 * G.edges.length) (faceTable G c) d.index = true ↔ (decode G c h).IsFaceRepresentative d := by simp only [faceRepBool, List.all_eq_true, List.mem_range, Bool.or_eq_true, Bool.not_eq_true', decide_eq_true_eq, RotationSystem.IsFaceRepresentative] @@ -586,7 +586,7 @@ theorem faceRepBool_eq_true_iff {G : Graph} {c : RotationCertificate} · intro hr i hi let e := (Dart.indexEquiv G).symm ⟨i, hi⟩ have he : e.index = i := congrArg Fin.val ((Dart.indexEquiv G).apply_symm_apply ⟨i, hi⟩) - by_cases ho : orbitBool (2 * G.2.length) (faceTable G c) d.index i = true + by_cases ho : orbitBool (2 * G.edges.length) (faceTable G c) d.index i = true · right rw [← he] at ho ⊢ exact hr e ((orbitBool_eq_true_iff _ _ (getD_faceTable h) d e).mp ho) @@ -607,10 +607,10 @@ theorem verifyEuler_eq_true_iff {G : Graph} {c : RotationCertificate} apply Bool.eq_iff_iff.mpr rw [decide_eq_true_eq] exact componentBool_eq_true_iff hr - have hfaces : ((List.range (2 * G.2.length)).countP fun i => + have hfaces : ((List.range (2 * G.edges.length)).countP fun i => componentBool G root (G.incidenceVertices.getD i 0) && - faceRepBool (2 * G.2.length) (faceTable G c) i) = - ((List.finRange (2 * G.2.length)).countP fun i => + faceRepBool (2 * G.edges.length) (faceTable G c) i) = + ((List.finRange (2 * G.edges.length)).countP fun i => decide (SameComponent G root ((Dart.indexEquiv G).symm i).vertex ∧ (decode G c h).IsFaceRepresentative ((Dart.indexEquiv G).symm i))) := by rw [← List.map_coe_finRange_eq_range, List.countP_map] @@ -643,7 +643,7 @@ theorem verifyGenusZero_eq_true_iff (G : Graph) (c : RotationCertificate) : /-- Every numerical genus-zero rotation has a certificate within the explicit quadratic bit bound, and every accepted such certificate supplies an actual numerical genus-zero rotation. -/ theorem exists_bounded_certificate_iff (G : Graph) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) : (∃ r : RotationSystem G, r.NumericalGenusZero) ↔ ∃ c : RotationCertificate, (encRotationCertificate c).length ≤ 10 * ((encGraph G).length + 1) ^ 2 ∧ diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/ComponentCounts.lean b/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/ComponentCounts.lean index dd94c599bc..bb5f7db0ca 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/ComponentCounts.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/ComponentCounts.lean @@ -32,17 +32,17 @@ namespace Cslib.Complexity.Graph open Classical in /-- Distinct declared vertex names in the existing component of `root`. -/ @[expose] noncomputable def componentVertexSet (G : Graph) (root : ℕ) : Finset ℕ := - G.1.toFinset.filter fun v => RotationCertificate.SameComponent G root v + G.vertices.toFinset.filter fun v => RotationCertificate.SameComponent G root v open Classical in /-- Listed edge positions whose first endpoint belongs to `root`'s existing component. -/ -@[expose] noncomputable def componentEdgeSet (G : Graph) (root : ℕ) : Finset (Fin G.2.length) := +@[expose] noncomputable def componentEdgeSet (G : Graph) (root : ℕ) : Finset (Fin G.edges.length) := Finset.univ.filter fun i => RotationCertificate.SameComponent G root (Dart.vertex (G := G) (i, false)) /-- Membership for an actual dart reduces to the existing component relation. -/ theorem mem_componentVertexSet_dart {G : Graph} (root : ℕ) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) : d.vertex ∈ G.componentVertexSet root ↔ RotationCertificate.SameComponent G root d.vertex := by simp only [componentVertexSet, Finset.mem_filter, List.mem_toFinset, @@ -50,7 +50,7 @@ theorem mem_componentVertexSet_dart {G : Graph} (root : ℕ) /-- Either indexed endpoint identifies the same set of listed component edges. -/ theorem mem_componentEdgeSet_iff {G : Graph} (root : ℕ) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (i : Fin G.2.length) (b : Bool) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (i : Fin G.edges.length) (b : Bool) : i ∈ G.componentEdgeSet root ↔ (Dart.vertex (G := G) (i, b)) ∈ G.componentVertexSet root := by rw [mem_componentVertexSet_dart root hG] simp only [componentEdgeSet, Finset.mem_filter, Finset.mem_univ, true_and] @@ -65,7 +65,7 @@ theorem card_componentVertexSet (G : Graph) (root : ℕ) : (G.componentVertexSet root).card = G.distinctVertices.countP (fun v => decide (RotationCertificate.SameComponent G root v)) := by classical - have he : G.distinctVertices.toFinset = G.1.toFinset := by + have he : G.distinctVertices.toFinset = G.vertices.toFinset := by ext v simp only [List.mem_toFinset, mem_distinctVertices] rw [componentVertexSet, ← he] @@ -74,15 +74,15 @@ theorem card_componentVertexSet (G : Graph) (root : ℕ) : open Classical in /-- Counting indexed component edges agrees with the existing edge-list occurrence count. -/ theorem card_componentEdgeSet (G : Graph) (root : ℕ) : - (G.componentEdgeSet root).card = G.2.countP + (G.componentEdgeSet root).card = G.edges.countP (fun edge => decide (RotationCertificate.SameComponent G root edge.1)) := by classical - have h := (List.nodup_finRange G.2.length).card_eq_countP + have h := (List.nodup_finRange G.edges.length).card_eq_countP (P := fun i => RotationCertificate.SameComponent G root (Dart.vertex (G := G) (i, false))) rw [List.toFinset_finRange] at h rw [componentEdgeSet, h] - conv_rhs => rw [← List.map_get_finRange G.2] + conv_rhs => rw [← List.map_get_finRange G.edges] simp only [List.countP_map, Function.comp_def, Dart.vertex, edgeEndpoint, Bool.false_eq_true, ite_false] apply List.countP_congr @@ -95,7 +95,7 @@ namespace RotationSystem open Classical in /-- Exactly one indexed dart per actual face in the chosen edge component. -/ @[expose] noncomputable def componentFaceIndices {G : Graph} (r : RotationSystem G) - (root : ℕ) : Finset (Fin (2 * G.2.length)) := + (root : ℕ) : Finset (Fin (2 * G.edges.length)) := Finset.univ.filter fun i => RotationCertificate.SameComponent G root ((Dart.indexEquiv G).symm i).vertex ∧ r.IsFaceRepresentative ((Dart.indexEquiv G).symm i) @@ -104,12 +104,12 @@ open Classical in /-- The finite face set is precisely the face count in `NumericalGenusZero`. -/ theorem card_componentFaceIndices {G : Graph} (r : RotationSystem G) (root : ℕ) : (r.componentFaceIndices root).card = - ((List.finRange (2 * G.2.length)).countP fun i => + ((List.finRange (2 * G.edges.length)).countP fun i => decide (RotationCertificate.SameComponent G root ((Dart.indexEquiv G).symm i).vertex ∧ r.IsFaceRepresentative ((Dart.indexEquiv G).symm i))) := by classical simpa only [componentFaceIndices, List.toFinset_finRange] using - (List.nodup_finRange (2 * G.2.length)).card_eq_countP + (List.nodup_finRange (2 * G.edges.length)).card_eq_countP (P := fun i => RotationCertificate.SameComponent G root ((Dart.indexEquiv G).symm i).vertex ∧ r.IsFaceRepresentative ((Dart.indexEquiv G).symm i)) @@ -117,15 +117,15 @@ theorem card_componentFaceIndices {G : Graph} (r : RotationSystem G) (root : ℕ open Classical in /-- Source face representatives and canonical dual vertex names agree in each edge component. -/ noncomputable def componentFaceDualVertexEquiv {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) : ↥(r.componentFaceIndices d.vertex) ≃ ↥(r.dualGraph.componentVertexSet (r.dualDartEquiv d).vertex) := - Equiv.ofBijective (fun i => ⟨(i : Fin (2 * G.2.length)).val, by + Equiv.ofBijective (fun i => ⟨(i : Fin (2 * G.edges.length)).val, by let a := (Dart.indexEquiv G).symm i.val obtain ⟨hc, hr⟩ := (Finset.mem_filter.mp i.property).2 have h := (mem_componentVertexSet_dart _ r.dualGraph_endpoints_declared (r.dualDartEquiv a)).mpr ((r.sameComponent_dual_iff hG d a).mpr hc) - have ha : a.index = (i : Fin (2 * G.2.length)).val := + have ha : a.index = (i : Fin (2 * G.edges.length)).val := congrArg Fin.val ((Dart.indexEquiv G).apply_symm_apply i.val) rwa [r.vertex_dualDartEquiv a, (r.faceId_eq_index_iff a).mpr hr, ha] at h⟩) ⟨by intro i j h @@ -150,7 +150,7 @@ noncomputable def componentFaceDualVertexEquiv {G : Graph} (r : RotationSystem G /-- Componentwise face counts equal componentwise declared dual vertex counts. -/ theorem card_component_faces_eq_dual_vertices {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) : (r.componentFaceIndices d.vertex).card = (r.dualGraph.componentVertexSet (r.dualDartEquiv d).vertex).card := by simpa only [Fintype.card_coe] using @@ -158,7 +158,7 @@ theorem card_component_faces_eq_dual_vertices {G : Graph} (r : RotationSystem G) /-- Edge positions in corresponding source and dual components are identified explicitly. -/ @[expose] noncomputable def componentEdgeDualEquiv {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) : ↥(G.componentEdgeSet d.vertex) ≃ ↥(r.dualGraph.componentEdgeSet (r.dualDartEquiv d).vertex) := (finCongr r.dualGraph_length.symm).subtypeEquiv (by @@ -168,7 +168,7 @@ theorem card_component_faces_eq_dual_vertices {G : Graph} (r : RotationSystem G) /-- Corresponding components retain every listed edge occurrence. -/ theorem card_component_edges_eq_dual {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) : (G.componentEdgeSet d.vertex).card = (r.dualGraph.componentEdgeSet (r.dualDartEquiv d).vertex).card := by simpa only [Fintype.card_coe] using Fintype.card_congr (r.componentEdgeDualEquiv hG d) @@ -177,7 +177,7 @@ open Classical in /-- In each edge component, every dual face is exactly one nonempty source vertex fiber. Declared isolated source points are absent from this dart-indexed correspondence. -/ noncomputable def componentDualFaceVertexEquiv {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) : ↥(r.dualRotation.componentFaceIndices (r.dualDartEquiv d).vertex) ≃ ↥(G.componentVertexSet d.vertex) := Equiv.ofBijective (fun i => @@ -227,7 +227,7 @@ noncomputable def componentDualFaceVertexEquiv {G : Graph} (r : RotationSystem G /-- The dual face count in an edge component is its number of distinct source vertices. -/ theorem card_component_dual_faces_eq_vertices {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) : (r.dualRotation.componentFaceIndices (r.dualDartEquiv d).vertex).card = (G.componentVertexSet d.vertex).card := by simpa only [Fintype.card_coe] using @@ -236,7 +236,7 @@ theorem card_component_dual_faces_eq_vertices {G : Graph} (r : RotationSystem G) /-- The existing componentwise numerical predicate is exactly Euler on these finite sets. Point components retain their original conventional face and impose no dart-index condition. -/ theorem numericalGenusZero_iff_component_cards {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) : r.NumericalGenusZero ↔ ∀ d : Dart G, (G.componentVertexSet d.vertex).card + (r.componentFaceIndices d.vertex).card = (G.componentEdgeSet d.vertex).card + 2 := by @@ -258,7 +258,7 @@ theorem numericalGenusZero_iff_component_cards {G : Graph} (r : RotationSystem G predicate. Source point components are dropped, and their conventional Euler equations are tautologies on the source side. The empty graph is included. No geometric premise is used. -/ theorem numericalGenusZero_dual_iff {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) : r.dualRotation.NumericalGenusZero ↔ r.NumericalGenusZero := by rw [r.dualRotation.numericalGenusZero_iff_component_cards r.dualGraph_endpoints_declared, r.numericalGenusZero_iff_component_cards hG] diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Components.lean b/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Components.lean index f3d3c17395..0a8622ed16 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Components.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Components.lean @@ -33,7 +33,7 @@ namespace RotationSystem /-- Dual and source edge components agree on the same indexed darts. Isolated source points are excluded automatically, since this correspondence is stated at darts. -/ theorem sameComponent_dual_iff {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d e : Dart G) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d e : Dart G) : RotationCertificate.SameComponent r.dualGraph (r.dualDartEquiv d).vertex (r.dualDartEquiv e).vertex ↔ RotationCertificate.SameComponent G d.vertex e.vertex := by diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Differences.lean b/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Differences.lean index a7d52e2b50..aa499bb9fd 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Differences.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Differences.lean @@ -34,7 +34,7 @@ public section namespace Cslib.Complexity.Graph /-- Boolean spin difference at each listed edge occurrence, including loops and parallels. -/ -@[expose] def edgeGradient (G : Graph) (spin : ℕ → Bool) : Fin G.2.length → Bool := +@[expose] def edgeGradient (G : Graph) (spin : ℕ → Bool) : Fin G.edges.length → Bool := fun i => spin (Dart.vertex (G := G) (i, false)) ^^ spin (Dart.vertex (G := G) (i, true)) @@ -59,16 +59,16 @@ variable {G : Graph} (r : RotationSystem G) open Classical in /-- Parity of the edge labels on all darts of one actual face. The two ends of an edge are separate summands, so repeated edge traversals have their proper multiplicity. -/ -@[expose] noncomputable def faceParity (x : Fin G.2.length → Bool) (f : r.Face) : Bool := +@[expose] noncomputable def faceParity (x : Fin G.edges.length → Bool) (f : r.Face) : Bool := parityOn (Finset.univ.filter fun d : Dart G => (Quotient.mk _ d : r.Face) = f) (fun d => x d.1) /-- Every actual face orbit has even Boolean edge parity, including singleton orbits. -/ -@[expose] def FaceEven (x : Fin G.2.length → Bool) : Prop := +@[expose] def FaceEven (x : Fin G.edges.length → Bool) : Prop := ∀ f : r.Face, r.faceParity x f = false /-- Reading face parity from a dart uses exactly its permutation orbit. -/ -theorem faceParity_mk (x : Fin G.2.length → Bool) (d : Dart G) : +theorem faceParity_mk (x : Fin G.edges.length → Bool) (d : Dart G) : r.faceParity x (Quotient.mk _ d) = parityOn (Finset.univ.filter fun e : Dart G => r.faceNext.SameCycle d e) (fun e => x e.1) := by @@ -80,7 +80,7 @@ theorem faceParity_mk (x : Fin G.2.length → Bool) (d : Dart G) : Equiv.Perm.SameCycle.setoid, Equiv.Perm.sameCycle_comm] /-- Quantifying over dart representatives covers every actual face, including fixed points. -/ -theorem faceEven_iff_dart (x : Fin G.2.length → Bool) : +theorem faceEven_iff_dart (x : Fin G.edges.length → Bool) : r.FaceEven x ↔ ∀ d : Dart G, r.faceParity x (Quotient.mk _ d) = false := by constructor · exact fun h d => h _ @@ -88,7 +88,7 @@ theorem faceEven_iff_dart (x : Fin G.2.length → Bool) : induction f using Quotient.inductionOn with | _ d => exact h d /-- Canonical face IDs compute parity on the same actual orbit, with dart multiplicity. -/ -theorem faceParity_eq_sum_faceId (x : Fin G.2.length → Bool) (d : Dart G) : +theorem faceParity_eq_sum_faceId (x : Fin G.edges.length → Bool) (d : Dart G) : r.faceParity x (Quotient.mk _ d) = ∑ e : Dart G, if r.faceId e = r.faceId d then x e.1 else 0 := by classical @@ -98,7 +98,7 @@ theorem faceParity_eq_sum_faceId (x : Fin G.2.length → Bool) (d : Dart G) : simp only [r.faceId_eq_iff, Equiv.Perm.sameCycle_comm] /-- Face parity is additive in edge labels. -/ -theorem faceParity_add (x y : Fin G.2.length → Bool) (f : r.Face) : +theorem faceParity_add (x y : Fin G.edges.length → Bool) (f : r.Face) : r.faceParity (x + y) f = (r.faceParity x f ^^ r.faceParity y f) := by classical change r.faceParity (x + y) f = r.faceParity x f + r.faceParity y f @@ -122,7 +122,7 @@ theorem faceEven_edgeGradient (spin : ℕ → Bool) : r.FaceEven (G.edgeGradient exact BooleanRing.add_self _ /-- Face-even assignments are closed under exclusive-or addition. -/ -theorem FaceEven.add {x y : Fin G.2.length → Bool} +theorem FaceEven.add {x y : Fin G.edges.length → Bool} (hx : r.FaceEven x) (hy : r.FaceEven y) : r.FaceEven (x + y) := by intro f rw [faceParity_add, hx f, hy f] diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Dual.lean b/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Dual.lean index f4a786ce39..88df11b466 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Dual.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Dual.lean @@ -36,7 +36,7 @@ namespace Cslib.Complexity.Graph.RotationSystem variable {G : Graph} (r : RotationSystem G) /-- Finite dart indices in an actual face orbit, including its least index. -/ -@[expose] def faceDartIndices (d : Dart G) : Finset (Fin (2 * G.2.length)) := +@[expose] def faceDartIndices (d : Dart G) : Finset (Fin (2 * G.edges.length)) := Finset.univ.filter fun i => r.faceNext.SameCycle d ((Dart.indexEquiv G).symm i) /-- Every dart belongs to its own face orbit. -/ @@ -97,16 +97,16 @@ theorem faceId_eq_index_iff (d : Dart G) : exact he.symm.trans (congrArg Dart.index hed) /-- The canonical face name is bounded by the number of darts, even for huge vertex labels. -/ -theorem faceId_lt (d : Dart G) : r.faceId d < 2 * G.2.length := +theorem faceId_lt (d : Dart G) : r.faceId d < 2 * G.edges.length := ((r.faceDartIndices d).min' (r.faceDartIndices_nonempty d)).isLt /-- Canonical dual vertex equality is exactly the existing executable face-table orbit test. This links the abstract construction to `faceTable (ofRotation r)` without another face enumeration or an existentially chosen relabeling. -/ theorem faceId_eq_iff_faceTable - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d e : Dart G) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d e : Dart G) : r.faceId d = r.faceId e ↔ - RotationCertificate.orbitBool (2 * G.2.length) + RotationCertificate.orbitBool (2 * G.edges.length) (RotationCertificate.faceTable G (RotationCertificate.ofRotation r)) d.index e.index = true := by rw [r.faceId_eq_iff] @@ -121,12 +121,12 @@ theorem faceId_eq_iff_faceTable /-- The listed dual has one vertex per actual face and one edge per source edge occurrence. The first/second ends of edge `i` are the faces of darts `(i,false)` and `(i,true)`. -/ @[expose] def dualGraph : Graph := - (((List.finRange (2 * G.2.length)).map fun i => + ⟨((List.finRange (2 * G.edges.length)).map fun i => r.faceId ((Dart.indexEquiv G).symm i)).dedup, - List.ofFn fun i : Fin G.2.length => (r.faceId (i, false), r.faceId (i, true))) + List.ofFn fun i : Fin G.edges.length => (r.faceId (i, false), r.faceId (i, true))⟩ /-- Dualization preserves the number of listed edges, including loops and parallel edges. -/ -@[simp] theorem dualGraph_length : r.dualGraph.2.length = G.2.length := by +@[simp] theorem dualGraph_length : r.dualGraph.edges.length = G.edges.length := by simp [dualGraph] /-- Identification of source and dual darts preserves edge positions and endpoint bits. -/ @@ -144,23 +144,23 @@ The first/second ends of edge `i` are the faces of darts `(i,false)` and `(i,tru @[simp] theorem vertex_dualDartEquiv (d : Dart G) : (r.dualDartEquiv d).vertex = r.faceId d := by rcases d with ⟨i, b⟩ - change (if b then (r.dualGraph.2.getD i.val (0, 0)).2 - else (r.dualGraph.2.getD i.val (0, 0)).1) = r.faceId (i, b) - have he : r.dualGraph.2.getD i.val (0, 0) = + change (if b then (r.dualGraph.edges.getD i.val (0, 0)).2 + else (r.dualGraph.edges.getD i.val (0, 0)).1) = r.faceId (i, b) + have he : r.dualGraph.edges.getD i.val (0, 0) = (r.faceId (i, false), r.faceId (i, true)) := by rw [List.getD_eq_getElem _ _ (by simpa only [dualGraph_length] using i.isLt)] simp [dualGraph] cases b <;> simp only [he, Bool.false_eq_true, ite_false, ite_true] /-- Every dual dart endpoint is declared, with no assumption on source endpoint declarations. -/ -theorem dual_vertex_mem (d : Dart r.dualGraph) : d.vertex ∈ r.dualGraph.1 := by +theorem dual_vertex_mem (d : Dart r.dualGraph) : d.vertex ∈ r.dualGraph.vertices := by obtain ⟨e, rfl⟩ := r.dualDartEquiv.surjective d rw [vertex_dualDartEquiv] apply List.mem_dedup.mpr exact List.mem_map.mpr ⟨Dart.indexEquiv G e, List.mem_finRange _, by simp⟩ /-- Each declared dual vertex is incident to a dual dart. Source isolated vertices are omitted. -/ -theorem dualGraph_no_isolates (v : ℕ) (hv : v ∈ r.dualGraph.1) : +theorem dualGraph_no_isolates (v : ℕ) (hv : v ∈ r.dualGraph.vertices) : ∃ d : Dart r.dualGraph, d.vertex = v := by obtain ⟨i, -, hi⟩ := List.mem_map.mp (List.mem_dedup.mp hv) exact ⟨r.dualDartEquiv ((Dart.indexEquiv G).symm i), by @@ -168,10 +168,10 @@ theorem dualGraph_no_isolates (v : ℕ) (hv : v ∈ r.dualGraph.1) : /-- Every endpoint in the dual's flattened incidence list is declared. -/ theorem dualGraph_endpoints_declared : - ∀ v ∈ r.dualGraph.incidenceVertices, v ∈ r.dualGraph.1 := by + ∀ v ∈ r.dualGraph.incidenceVertices, v ∈ r.dualGraph.vertices := by intro v hv obtain ⟨i, hi⟩ := List.mem_iff_get.mp hv - let j : Fin (2 * r.dualGraph.2.length) := + let j : Fin (2 * r.dualGraph.edges.length) := ⟨i.val, i.isLt.trans_eq (incidenceVertices_length _)⟩ have h := r.dual_vertex_mem ((Dart.indexEquiv r.dualGraph).symm j) have hj : ((Dart.indexEquiv r.dualGraph).symm j).index = i.val := @@ -265,7 +265,7 @@ noncomputable def dualFaceVertexEquiv : r.dualRotation.Face ≃ IncidentVertex G theorem card_dual_faces : Nat.card r.dualRotation.Face = Nat.card (IncidentVertex G) := Nat.card_congr r.dualFaceVertexEquiv -private def faceDualVertex : r.Face → {v // v ∈ r.dualGraph.1} := +private def faceDualVertex : r.Face → {v // v ∈ r.dualGraph.vertices} := Quotient.lift (fun d => ⟨r.faceId d, by simpa only [vertex_dualDartEquiv] using r.dual_vertex_mem (r.dualDartEquiv d)⟩) @@ -274,7 +274,7 @@ private def faceDualVertex : r.Face → {v // v ∈ r.dualGraph.1} := exact Subtype.ext ((r.faceId_eq_iff d e).mpr h)) /-- Canonical names identify every source face with exactly one declared dual vertex. -/ -noncomputable def faceDualVertexEquiv : r.Face ≃ {v // v ∈ r.dualGraph.1} := +noncomputable def faceDualVertexEquiv : r.Face ≃ {v // v ∈ r.dualGraph.vertices} := Equiv.ofBijective (faceDualVertex r) ⟨by intro f g induction f using Quotient.inductionOn with | _ d => diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/FaceCounts.lean b/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/FaceCounts.lean index 496329ac9b..611adcc9ad 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/FaceCounts.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/FaceCounts.lean @@ -42,7 +42,7 @@ abbrev ComponentFace {G : Graph} (r : RotationSystem G) (root : ℕ) := /-- A face represented by a dart belongs precisely to that dart's component. -/ theorem componentFace_mk_iff {G : Graph} (r : Graph.RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (root : ℕ) (d : Graph.Dart G) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (root : ℕ) (d : Graph.Dart G) : (∃ e : Graph.Dart G, SameComponent G root e.vertex ∧ (Quotient.mk _ e : r.Face) = Quotient.mk _ d) ↔ SameComponent G root d.vertex := by constructor @@ -52,7 +52,7 @@ theorem componentFace_mk_iff {G : Graph} (r : Graph.RotationSystem G) /-- Exactly one representative in a component is counted for each actual face. -/ theorem card_componentFaceRepresentatives {G : Graph} (r : Graph.RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (root : ℕ) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (root : ℕ) : Nat.card {d : Graph.Dart G // SameComponent G root d.vertex ∧ r.IsFaceRepresentative d} = Nat.card (ComponentFace r root) := by let f : {d : Graph.Dart G // SameComponent G root d.vertex ∧ r.IsFaceRepresentative d} → @@ -73,8 +73,8 @@ theorem card_componentFaceRepresentatives {G : Graph} (r : Graph.RotationSystem open Classical in /-- The numerical face count equals the cardinality of the full component face quotient. -/ theorem count_faceRepresentatives_eq_card_componentFace {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (root : ℕ) : - ((List.finRange (2 * G.2.length)).countP fun i => + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (root : ℕ) : + ((List.finRange (2 * G.edges.length)).countP fun i => decide (SameComponent G root ((Dart.indexEquiv G).symm i).vertex ∧ r.IsFaceRepresentative ((Dart.indexEquiv G).symm i))) = Nat.card (ComponentFace r root) := by @@ -93,7 +93,7 @@ end RotationSystem open Classical in /-- Declared dense vertex names can be counted directly as a subtype of `Fin n`. -/ -theorem countP_vertices_range (G : Graph) (n : ℕ) (hG : G.1 = List.range n) (root : ℕ) : +theorem countP_vertices_range (G : Graph) (n : ℕ) (hG : G.vertices = List.range n) (root : ℕ) : G.distinctVertices.countP (fun v => decide (SameComponent G root v)) = Nat.card {v : Fin n // SameComponent G root v.val} := by rw [← card_componentVertexSet, ← Nat.card_eq_finsetCard] diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Lifting.lean b/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Lifting.lean index dbff7fb454..1ec335bc3b 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Lifting.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph/RotationSystem/Lifting.lean @@ -42,7 +42,7 @@ private abbrev Vertex (G : Graph) (root : ℕ) := ↥(G.componentVertexSet root) private abbrev Edge (G : Graph) (root : ℕ) := ↥(G.componentEdgeSet root) private noncomputable def endpoint (G : Graph) (root : ℕ) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (i : Edge G root) (b : Bool) : Vertex G root := + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (i : Edge G root) (b : Bool) : Vertex G root := ⟨Dart.vertex (G := G) (i.val, b), (mem_componentEdgeSet_iff root hG i.val b).mp i.property⟩ @@ -51,24 +51,24 @@ private noncomputable def extend (G : Graph) (root : ℕ) (s : Vertex G root → if h : v ∈ G.componentVertexSet root then s ⟨v, h⟩ else 0 private theorem extend_endpoint (G : Graph) (root : ℕ) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (s : Vertex G root → ZMod 2) + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (s : Vertex G root → ZMod 2) (i : Edge G root) (b : Bool) : extend G root s (Dart.vertex (G := G) (i.val, b)) = s (endpoint G root hG i b) := by simp only [extend, dite_eq_left ((mem_componentEdgeSet_iff root hG i.val b).mp i.property), endpoint] private noncomputable def rootVertex (G : Graph) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) : Vertex G d.vertex := + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) : Vertex G d.vertex := ⟨d.vertex, (mem_componentVertexSet_dart _ hG d).mpr (.refl (vertex_mem hG d))⟩ private theorem component_constant (G : Graph) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) (s : Vertex G d.vertex → ZMod 2) (hs : ∀ i : Edge G d.vertex, s (endpoint G d.vertex hG i false) = s (endpoint G d.vertex hG i true)) : ∀ a b, s a = s b := by classical - have hedge (i : Fin G.2.length) : + have hedge (i : Fin G.edges.length) : extend G d.vertex s (Dart.vertex (G := G) (i, false)) = extend G d.vertex s (Dart.vertex (G := G) (i, true)) := by by_cases hi : i ∈ G.componentEdgeSet d.vertex @@ -95,39 +95,39 @@ private theorem component_constant (G : Graph) simpa only [hp, hq, extend, dite_eq_left a.property, dite_eq_left b.property] using heq private noncomputable def primalMatrix (G : Graph) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) : Matrix (Vertex G d.vertex) (Edge G d.vertex) (ZMod 2) := Matrix.endpointIncidence (fun i => endpoint G d.vertex hG i false) (fun i => endpoint G d.vertex hG i true) private theorem rank_primalMatrix (G : Graph) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) : (primalMatrix G hG d).rank + 1 = Fintype.card (Vertex G d.vertex) := Matrix.rank_endpointIncidence_add_one _ _ (rootVertex G hG d) (component_constant G hG d) private noncomputable def dualEndpoint {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) (i : Edge G d.vertex) (b : Bool) : Vertex r.dualGraph (r.dualDartEquiv d).vertex := endpoint r.dualGraph (r.dualDartEquiv d).vertex r.dualGraph_endpoints_declared (r.componentEdgeDualEquiv hG d i) b private theorem dualEndpoint_val {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) (i : Edge G d.vertex) (b : Bool) : (dualEndpoint r hG d i b).val = r.faceId (i.val, b) := by change (r.dualDartEquiv (i.val, b)).vertex = _ exact r.vertex_dualDartEquiv _ private noncomputable def dualMatrix {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) : Matrix (Vertex r.dualGraph (r.dualDartEquiv d).vertex) (Edge G d.vertex) (ZMod 2) := Matrix.endpointIncidence (fun i => dualEndpoint r hG d i false) (fun i => dualEndpoint r hG d i true) private theorem rank_dualMatrix {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) : (dualMatrix r hG d).rank + 1 = Fintype.card (Vertex r.dualGraph (r.dualDartEquiv d).vertex) := by apply Matrix.rank_endpointIncidence_add_one _ _ @@ -139,12 +139,12 @@ private theorem rank_dualMatrix {G : Graph} (r : RotationSystem G) exact hs i private theorem dualMatrix_mulVec_sum {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) - (x : Fin G.2.length → ZMod 2) + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) + (x : Fin G.edges.length → ZMod 2) (v : Vertex r.dualGraph (r.dualDartEquiv d).vertex) : (dualMatrix r hG d).mulVec (fun i => x i.val) v = ∑ a : Dart G, if r.faceId a = v.val then x a.1 else 0 := by - have hmem (i : Fin G.2.length) (b : Bool) (h : r.faceId (i, b) = v.val) : + have hmem (i : Fin G.edges.length) (b : Bool) (h : r.faceId (i, b) = v.val) : i ∈ G.componentEdgeSet d.vertex := by apply (mem_componentEdgeSet_iff d.vertex hG i b).mpr apply (mem_componentVertexSet_dart _ hG _).mpr @@ -155,7 +155,7 @@ private theorem dualMatrix_mulVec_sum {G : Graph} (r : RotationSystem G) have hsum (b : Bool) : (∑ i ∈ G.componentEdgeSet d.vertex, if r.faceId (i, b) = v.val then x i else 0) = - ∑ i : Fin G.2.length, if r.faceId (i, b) = v.val then x i else 0 := by + ∑ i : Fin G.edges.length, if r.faceId (i, b) = v.val then x i else 0 := by apply Finset.sum_subset (Finset.subset_univ _) intro i _ hi have hn : r.faceId (i, b) ≠ v.val := fun h => hi (hmem i b h) @@ -178,16 +178,16 @@ private theorem dualMatrix_mulVec_sum {G : Graph} (r : RotationSystem G) (fun i => if r.faceId (i, false) = v.val then x i else 0)) (Finset.sum_coe_sort (G.componentEdgeSet d.vertex) (fun i => if r.faceId (i, true) = v.val then x i else 0)) - _ = (∑ i : Fin G.2.length, if r.faceId (i, false) = v.val then x i else 0) + - ∑ i : Fin G.2.length, if r.faceId (i, true) = v.val then x i else 0 := by + _ = (∑ i : Fin G.edges.length, if r.faceId (i, false) = v.val then x i else 0) + + ∑ i : Fin G.edges.length, if r.faceId (i, true) = v.val then x i else 0 := by rw [hsum false, hsum true] _ = ∑ a : Dart G, if r.faceId a = v.val then x a.1 else 0 := by simp only [Dart, Fintype.sum_prod_type, Fintype.sum_bool, Finset.sum_add_distrib] exact add_comm _ _ private theorem dualMatrix_mulVec_bit {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) - (x : Fin G.2.length → Bool) + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) + (x : Fin G.edges.length → Bool) (v : Vertex r.dualGraph (r.dualDartEquiv d).vertex) (a : Dart G) (ha : r.faceId a = v.val) : (dualMatrix r hG d).mulVec (fun i => BitString.bitEquiv.symm (x i.val)) v = @@ -197,7 +197,7 @@ private theorem dualMatrix_mulVec_bit {G : Graph} (r : RotationSystem G) simp only [apply_ite, map_zero, ha] private theorem primalMatrix_gradient (G : Graph) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) (s : Vertex G d.vertex → ZMod 2) (i : Edge G d.vertex) : (primalMatrix G hG d).transpose.mulVec s i = BitString.bitEquiv.symm @@ -208,7 +208,7 @@ private theorem primalMatrix_gradient (G : Graph) rw [extend_endpoint G d.vertex hG s i false, extend_endpoint G d.vertex hG s i true] private theorem matrix_product_zero {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (d : Dart G) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (d : Dart G) : dualMatrix r hG d * (primalMatrix G hG d).transpose = 0 := by classical apply Matrix.mulVec_injective @@ -226,8 +226,8 @@ private theorem matrix_product_zero {G : Graph} (r : RotationSystem G) /-- Every face-even assignment lifts on each edge component, with all rank and dimension ingredients discharged internally from existing reachability and componentwise Euler. -/ theorem exists_vertexSpins_on_component {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (hgenus : r.NumericalGenusZero) - (x : Fin G.2.length → Bool) (hx : r.FaceEven x) (d : Dart G) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (hgenus : r.NumericalGenusZero) + (x : Fin G.edges.length → Bool) (hx : r.FaceEven x) (d : Dart G) : ∃ spin : ℕ → Bool, ∀ i ∈ G.componentEdgeSet d.vertex, G.edgeGradient spin i = x i := by classical have heuler := (r.numericalGenusZero_iff_component_cards hG).mp hgenus d @@ -254,7 +254,7 @@ theorem exists_vertexSpins_on_component {G : Graph} (r : RotationSystem G) exact (primalMatrix_gradient G hG d s ⟨i, hi⟩).symm.trans (congrFun hs ⟨i, hi⟩) private noncomputable def chosenVertex (G : Graph) (c : G.induced.ConnectedComponent) : - {v // v ∈ G.1} := + {v // v ∈ G.vertices} := (Quot.exists_rep c).choose private theorem chosenVertex_spec (G : Graph) (c : G.induced.ConnectedComponent) : @@ -262,18 +262,18 @@ private theorem chosenVertex_spec (G : Graph) (c : G.induced.ConnectedComponent) (Quot.exists_rep c).choose_spec private noncomputable def componentSpin {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (hgenus : r.NumericalGenusZero) - (x : Fin G.2.length → Bool) (hx : r.FaceEven x) (c : G.induced.ConnectedComponent) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (hgenus : r.NumericalGenusZero) + (x : Fin G.edges.length → Bool) (hx : r.FaceEven x) (c : G.induced.ConnectedComponent) : ℕ → Bool := if h : ∃ d : Dart G, d.vertex = (chosenVertex G c).val then (r.exists_vertexSpins_on_component hG hgenus x hx h.choose).choose else fun _ => false private theorem componentSpin_correct {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (hgenus : r.NumericalGenusZero) - (x : Fin G.2.length → Bool) (hx : r.FaceEven x) (c : G.induced.ConnectedComponent) + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (hgenus : r.NumericalGenusZero) + (x : Fin G.edges.length → Bool) (hx : r.FaceEven x) (c : G.induced.ConnectedComponent) (h : ∃ d : Dart G, d.vertex = (chosenVertex G c).val) - (i : Fin G.2.length) (hi : i ∈ G.componentEdgeSet (chosenVertex G c).val) : + (i : Fin G.edges.length) (hi : i ∈ G.componentEdgeSet (chosenVertex G c).val) : G.edgeGradient (componentSpin r hG hgenus x hx c) i = x i := by rw [componentSpin, dite_eq_left h] apply (r.exists_vertexSpins_on_component hG hgenus x hx h.choose).choose_spec @@ -283,18 +283,18 @@ private theorem componentSpin_correct {G : Graph} (r : RotationSystem G) lifts to spins on the original natural-number names. Components are solved independently; isolated declared points and names outside the graph may be assigned `false`. -/ theorem exists_vertexSpins_of_faceEven {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (hgenus : r.NumericalGenusZero) - (x : Fin G.2.length → Bool) (hx : r.FaceEven x) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (hgenus : r.NumericalGenusZero) + (x : Fin G.edges.length → Bool) (hx : r.FaceEven x) : ∃ spin : ℕ → Bool, G.edgeGradient spin = x := by let spin (v : ℕ) : Bool := - if hv : v ∈ G.1 then + if hv : v ∈ G.vertices then componentSpin r hG hgenus x hx (G.induced.connectedComponentMk ⟨v, hv⟩) v else false refine ⟨spin, funext fun i => ?_⟩ let u := Dart.vertex (G := G) (i, false) let v := Dart.vertex (G := G) (i, true) - have hu : u ∈ G.1 := vertex_mem hG (i, false) - have hv : v ∈ G.1 := vertex_mem hG (i, true) + have hu : u ∈ G.vertices := vertex_mem hG (i, false) + have hv : v ∈ G.vertices := vertex_mem hG (i, true) let c := G.induced.connectedComponentMk ⟨u, hu⟩ have hcomp : RotationCertificate.SameComponent G u v := sameComponent_reverse hG (i, false) have hcc : c = G.induced.connectedComponentMk ⟨v, hv⟩ := @@ -322,8 +322,8 @@ is explicit. Loops, parallel occurrences, disconnected components, points, dupli declarations, and sparse natural-number names are all retained. The converse needs no genus assumption; see `faceEven_edgeGradient`. -/ theorem faceEven_iff_exists_vertexSpins {G : Graph} (r : RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) (hgenus : r.NumericalGenusZero) - (x : Fin G.2.length → Bool) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) (hgenus : r.NumericalGenusZero) + (x : Fin G.edges.length → Bool) : r.FaceEven x ↔ ∃ spin : ℕ → Bool, G.edgeGradient spin = x := by constructor · exact r.exists_vertexSpins_of_faceEven hG hgenus x diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph/SpanningTrees.lean b/CslibFrontier/Computability/Complexity/Problems/Graph/SpanningTrees.lean index 266ad3360a..b193efbce2 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph/SpanningTrees.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph/SpanningTrees.lean @@ -79,7 +79,7 @@ instance instDecidableRelInducedAdj (G : Graph) : DecidableRel G.induced.Adj := /-- The degree of a declared vertex in `G.induced` is its number of neighbours among the distinct declared vertices. -/ -private theorem degree_induced (G : Graph) (u : {v // v ∈ G.1}) : +private theorem degree_induced (G : Graph) (u : {v // v ∈ G.vertices}) : G.induced.degree u = (G.distinctVertices.filter fun w => G.adjBool u w).length := by rw [← SimpleGraph.card_neighborFinset_eq_degree, ← List.toFinset_card_of_nodup (G.nodup_distinctVertices.filter _)] @@ -108,10 +108,10 @@ private theorem rowEntry_reducedLaplacianRows (G : Graph) {i j : ℕ} Kirchhoff's matrix-tree theorem. -/ theorem spanningTreeCount_eq_natCard (G : Graph) : G.spanningTreeCount = - Nat.card {T : SimpleGraph {v // v ∈ G.1} // T ≤ G.induced ∧ T.IsTree} := by + Nat.card {T : SimpleGraph {v // v ∈ G.vertices} // T ≤ G.induced ∧ T.IsTree} := by unfold spanningTreeCount split_ifs with h - · have : IsEmpty {v // v ∈ G.1} := ⟨fun v => by + · have : IsEmpty {v // v ∈ G.vertices} := ⟨fun v => by have := mem_distinctVertices.2 v.2 rw [h] at this exact List.not_mem_nil this⟩ @@ -123,13 +123,13 @@ theorem spanningTreeCount_eq_natCard (G : Graph) : have hnd := G.nodup_distinctVertices rw [hvs, List.nodup_cons] at hnd have htail : G.distinctVertices.tail = rest := by rw [hvs, List.tail_cons] - have haG : a ∈ G.1 := mem_distinctVertices.1 (by rw [hvs]; exact List.mem_cons_self ..) - have hmem : ∀ {w}, w ∈ rest → w ∈ G.1 := fun hw => + have haG : a ∈ G.vertices := mem_distinctVertices.1 (by rw [hvs]; exact List.mem_cons_self ..) + have hmem : ∀ {w}, w ∈ rest → w ∈ G.vertices := fun hw => mem_distinctVertices.1 (by rw [hvs]; exact List.mem_cons_of_mem _ hw) - set r : {v // v ∈ G.1} := ⟨a, haG⟩ + set r : {v // v ∈ G.vertices} := ⟨a, haG⟩ have hlen : G.reducedLaplacianRows.length = rest.length := by rw [reducedLaplacianRows, List.length_map, htail] - let f : Fin G.reducedLaplacianRows.length → {v : {v // v ∈ G.1} // v ≠ r} := fun i => + let f : Fin G.reducedLaplacianRows.length → {v : {v // v ∈ G.vertices} // v ≠ r} := fun i => ⟨⟨rest[i.1]'(hlen ▸ i.2), hmem (List.getElem_mem _)⟩, fun hr => hnd.1 (by have h : rest[i.1]'(hlen ▸ i.2) = a := congrArg Subtype.val hr rw [← h] @@ -164,7 +164,7 @@ theorem spanningTreeCount_eq_natCard (G : Graph) : · subst hij simp [degree_induced] · have hne : rest[i.1] ≠ rest[j.1] := fun h => hij (Fin.ext ((hnd.2.getElem_inj_iff).1 h)) - have hne' : (⟨rest[i.1], hmem (List.getElem_mem _)⟩ : {v // v ∈ G.1}) ≠ + have hne' : (⟨rest[i.1], hmem (List.getElem_mem _)⟩ : {v // v ∈ G.vertices}) ≠ ⟨rest[j.1], hmem (List.getElem_mem _)⟩ := fun h => hne (congrArg Subtype.val h) by_cases hadj : G.toSimpleGraph.Adj rest[i.1] rest[j.1] <;> simp [hne, hne', hadj] @@ -218,9 +218,11 @@ theorem computableInPolyTime_spanningTreeCount : binary natural numbers. -/ theorem computableInPolyTime_natCard_isTree : ComputableInPolyTime - (fun G : Graph => Nat.card {T : SimpleGraph {v // v ∈ G.1} // T ≤ G.induced ∧ T.IsTree}) + (fun G : Graph => + Nat.card {T : SimpleGraph {v // v ∈ G.vertices} // T ≤ G.induced ∧ T.IsTree}) encGraph encNat := by - have h : (fun G : Graph => Nat.card {T : SimpleGraph {v // v ∈ G.1} // T ≤ G.induced ∧ T.IsTree}) + have h : (fun G : Graph => + Nat.card {T : SimpleGraph {v // v ∈ G.vertices} // T ≤ G.induced ∧ T.IsTree}) = fun G => G.spanningTreeCount.toNat := funext fun G => by rw [spanningTreeCount_eq_natCard, Int.toNat_natCast] rw [h] diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph/VertexColors.lean b/CslibFrontier/Computability/Complexity/Problems/Graph/VertexColors.lean index 2e1c2b70b0..ca30aed156 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph/VertexColors.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph/VertexColors.lean @@ -29,15 +29,15 @@ witnesses read them in this form (`Cslib.Complexity.mem_NP_of_colorOf`). ## Main results -* `Cslib.Complexity.Graph.computableInPolyTime_fst`, - `Cslib.Complexity.Graph.computableInPolyTime_snd`: projecting the declared vertex list or edge +* `Cslib.Complexity.Graph.computableInPolyTime_vertices`, + `Cslib.Complexity.Graph.computableInPolyTime_edges`: projecting the declared vertex list or edge list from `encGraph` is polynomial-time. * `Cslib.Complexity.colorOf_map`: listing the colors of the declared vertices gives every declared vertex its color. * `Cslib.Complexity.computableInPolyTime_colorOf`, `Cslib.Complexity.computableInPolyTime_colorOf_comp`: reading a color is polynomial-time. * `Cslib.Complexity.Graph.length_encList_map_le`: quadratic encoded-length bound for a bounded - vertex labeling listed along `G.1`. + vertex labeling listed along `G.vertices`. * `Cslib.Complexity.mem_NP_of_colorOf`: `NP` membership for graph problems verified from a bounded vertex labeling. -/ @@ -49,23 +49,21 @@ namespace Cslib.Complexity open Turing.MultiTapeTM /-- Projecting the declared vertex list of a computed graph is polynomial-time. -/ -theorem Graph.computableInPolyTime_fst {γ : Type} {enc : γ ↪ List Bool} {G : γ → Graph} +theorem Graph.computableInPolyTime_vertices {γ : Type} {enc : γ ↪ List Bool} {G : γ → Graph} (hG : ComputableInPolyTime G enc encGraph) : - ComputableInPolyTime (fun x => (G x).1) enc (encList encNat) := by - unfold encGraph at hG - exact (ComputableInPolyTime.fst _ _).comp hG + ComputableInPolyTime (fun x => (G x).vertices) enc (encList encNat) := by + fun_prop /-- Projecting the edge list of a computed graph is polynomial-time. -/ -theorem Graph.computableInPolyTime_snd {γ : Type} {enc : γ ↪ List Bool} {G : γ → Graph} +theorem Graph.computableInPolyTime_edges {γ : Type} {enc : γ ↪ List Bool} {G : γ → Graph} (hG : ComputableInPolyTime G enc encGraph) : - ComputableInPolyTime (fun x => (G x).2) enc (encList (encPair encNat encNat)) := by - unfold encGraph at hG - exact (ComputableInPolyTime.snd _ _).comp hG + ComputableInPolyTime (fun x => (G x).edges) enc (encList (encPair encNat encNat)) := by + fun_prop /-- The color of the vertex `v` under a list of colors `cs` parallel to the vertex list of `G`: the entry of `cs` at the first occurrence of `v`, and `0` when there is none. -/ @[expose] -def colorOf (G : Graph) (cs : List ℕ) (v : ℕ) : ℕ := cs.getD (G.1.idxOf v) 0 +def colorOf (G : Graph) (cs : List ℕ) (v : ℕ) : ℕ := cs.getD (G.vertices.idxOf v) 0 /-- Graphs with vertex colors: the graph (`encGraph`), framed, then the list of colors parallel to its vertex list (`Cslib.Complexity.colorOf`), each color framed and in binary. -/ @@ -74,9 +72,9 @@ abbrev encColoredGraph : Graph × List ℕ ↪ List Bool := encPair encGraph (en /-- The list of the colors of the declared vertices under `c` gives every declared vertex its color. -/ @[simp] -theorem colorOf_map {G : Graph} {c : ℕ → ℕ} {v : ℕ} (hv : v ∈ G.1) : - colorOf G (G.1.map c) v = c v := by - have hlt : G.1.idxOf v < G.1.length := List.idxOf_lt_length_iff.2 hv +theorem colorOf_map {G : Graph} {c : ℕ → ℕ} {v : ℕ} (hv : v ∈ G.vertices) : + colorOf G (G.vertices.map c) v = c v := by + have hlt : G.vertices.idxOf v < G.vertices.length := List.idxOf_lt_length_iff.2 hv rw [colorOf, List.getD_eq_getElem _ _ (by simpa using hlt), List.getElem_map, List.getElem_idxOf hlt] @@ -86,7 +84,7 @@ theorem computableInPolyTime_colorOf : ComputableInPolyTime (fun p : (Graph × List ℕ) × ℕ => colorOf p.1.1 p.1.2 p.2) (encPair encColoredGraph encNat) encNat := by unfold colorOf - fun_prop [Graph.computableInPolyTime_fst] + fun_prop /-- Reading computed colors of computed vertices is polynomial-time; the compositional form of `Cslib.Complexity.computableInPolyTime_colorOf` used by `fun_prop`. -/ @@ -99,9 +97,10 @@ theorem computableInPolyTime_colorOf_comp {γ : Type} {enc : γ ↪ List Bool} { /-- A labeling listed along the vertex list of a graph with at most `n` listed vertices, whose values on the declared vertices are at most `n`, has an encoding of length at most `2 n ^ 2 + n`. -/ -theorem Graph.length_encList_map_le {G : Graph} {f : ℕ → ℕ} {n : ℕ} (hG : G.1.length ≤ n) - (hf : ∀ v ∈ G.1, f v ≤ n) : (encList encNat (G.1.map f)).length ≤ 2 * n ^ 2 + n := by - have hentry : ∀ a ∈ G.1.map f, (encNat a).length ≤ n := by +theorem Graph.length_encList_map_le {G : Graph} {f : ℕ → ℕ} {n : ℕ} (hG : G.vertices.length ≤ n) + (hf : ∀ v ∈ G.vertices, f v ≤ n) : + (encList encNat (G.vertices.map f)).length ≤ 2 * n ^ 2 + n := by + have hentry : ∀ a ∈ G.vertices.map f, (encNat a).length ≤ n := by intro a ha obtain ⟨v, hv, rfl⟩ := List.mem_map.1 ha exact (length_encNat_le_self _).trans (hf v hv) @@ -112,28 +111,28 @@ theorem Graph.length_encList_map_le {G : Graph} {f : ℕ → ℕ} {n : ℕ} (hG /-- **NP membership from a bounded vertex labeling.** Let `check` be polynomial-time on instances `x` paired with a list of vertex labels (`Cslib.Complexity.colorOf`). If every accepted list certifies membership in `S`, and every member `x ∈ S` has a labeling `g` of the declared vertices -`(graph x).1` bounded by `(graph x).1.length` whose list `(graph x).1.map g` is accepted by -`check`, then `S ∈ NP enc`. -/ +`(graph x).vertices` bounded by `(graph x).vertices.length` whose list +`(graph x).vertices.map g` is accepted by `check`, then `S ∈ NP enc`. -/ theorem mem_NP_of_colorOf {α : Type} {enc : α ↪ List Bool} (graph : α → Graph) {S : Set α} {check : α × List ℕ → Bool} (hcheck : ComputableInPolyTime check (encPair enc (encList encNat)) encBool) - (hlen : ∀ x, (graph x).1.length ≤ (enc x).length) + (hlen : ∀ x, (graph x).vertices.length ≤ (enc x).length) (hsound : ∀ x cs, check (x, cs) = true → x ∈ S) (hcomplete : ∀ x, x ∈ S → - ∃ g : ℕ → ℕ, (∀ v ∈ (graph x).1, g v ≤ (graph x).1.length) ∧ - check (x, (graph x).1.map g) = true) : + ∃ g : ℕ → ℕ, (∀ v ∈ (graph x).vertices, g v ≤ (graph x).vertices.length) ∧ + check (x, (graph x).vertices.map g) = true) : S ∈ NP enc := by have hset : S = {x | ∃ cs : List ℕ, check (x, cs) = true} := by ext x - exact ⟨fun h => let ⟨g, _, hg⟩ := hcomplete x h; ⟨(graph x).1.map g, hg⟩, + exact ⟨fun h => let ⟨g, _, hg⟩ := hcomplete x h; ⟨(graph x).vertices.map g, hg⟩, fun ⟨cs, hcs⟩ => hsound x cs hcs⟩ rw [hset] refine mem_NP_of_witness (p := 2 * Polynomial.X ^ 2 + Polynomial.X) (.list .nat) hcheck (fun _ _ => Iff.rfl) ?_ rintro x cs hcs obtain ⟨g, hgle, hg⟩ := hcomplete x (hsound x cs hcs) - have hN : (graph x).1.length ≤ (enc x).length := hlen x - refine ⟨(graph x).1.map g, hg, ?_⟩ + have hN : (graph x).vertices.length ≤ (enc x).length := hlen x + refine ⟨(graph x).vertices.map g, hg, ?_⟩ have h := Graph.length_encList_map_le hN fun v hv => (hgle v hv).trans hN simpa only [Polynomial.eval_add, Polynomial.eval_mul, Polynomial.eval_pow, Polynomial.eval_X, Polynomial.eval_ofNat] using h diff --git a/CslibFrontier/Computability/Complexity/Problems/GraphIsomorphism.lean b/CslibFrontier/Computability/Complexity/Problems/GraphIsomorphism.lean index 0f0e094520..5697914d50 100644 --- a/CslibFrontier/Computability/Complexity/Problems/GraphIsomorphism.lean +++ b/CslibFrontier/Computability/Complexity/Problems/GraphIsomorphism.lean @@ -162,7 +162,7 @@ namespace GraphIsomorphism many vertices as the first graph and preserves adjacency. -/ @[expose] def verifier : GraphIsomorphismInstance × List Bool → Bool := - Graph.verifyOrdering (fun x => x.second.1) fun x p => + Graph.verifyOrdering (fun x => x.second.vertices) fun x p => decide (p.length = x.first.distinctVertices.length) && (List.range x.first.distinctVertices.length).all fun i => (List.range x.first.distinctVertices.length).all fun j => @@ -176,8 +176,7 @@ theorem computableInPolyTime_verifier : (fun x : GraphIsomorphismInstance × List ℕ => x.1.first.distinctVertices.length) (encPair encGraphIsomorphism (encList encNat)) encUnary := (length_list encNat).comp (by fun_prop) - refine Graph.computableInPolyTime_verifyOrdering - ((fst (encList encNat) (encList (encPair encNat encNat))).comp (by fun_prop)) + refine Graph.computableInPolyTime_verifyOrdering (by fun_prop) (bool_and' (((length_list encNat).comp (snd _ _)).decide_eq' hlen) ?_) refine allWithInput (range.comp hlen) (allWithInput (range.comp (hlen.comp (fst _ _))) ?_) @@ -189,7 +188,7 @@ of the length of the encoded pair of graphs. -/ theorem isVerifier : IsVerifier encGraphIsomorphism (Polynomial.X ^ 2) verifier GraphIsomorphism := by have hset : GraphIsomorphism = - {x : GraphIsomorphismInstance | ∃ p : List ℕ, p.Nodup ∧ (∀ v, v ∈ p ↔ v ∈ x.second.1) ∧ + {x : GraphIsomorphismInstance | ∃ p : List ℕ, p.Nodup ∧ (∀ v, v ∈ p ↔ v ∈ x.second.vertices) ∧ p.length = x.first.distinctVertices.length ∧ ∀ i < x.first.distinctVertices.length, ∀ j < x.first.distinctVertices.length, x.second.adjBool (p.getD i 0) (p.getD j 0) = x.first.adjIndex i j} := by @@ -212,7 +211,7 @@ theorem isVerifier : ← Graph.induced_adj_vertexEquiv G ⟨i, hi⟩ ⟨j, hj⟩] exact φ.map_adj_iff · rintro ⟨p, hnd, hcov, hlen, hadj⟩ - let e : Fin G.distinctVertices.length ≃ {v // v ∈ H.1} := + let e : Fin G.distinctVertices.length ≃ {v // v ∈ H.vertices} := (finCongr hlen.symm).trans ((hnd.getEquiv _).trans (Equiv.subtypeEquivRight hcov)) refine ⟨⟨G.vertexEquiv.symm.trans e, fun {u v} => ?_⟩⟩ obtain ⟨i, rfl⟩ := G.vertexEquiv.surjective u diff --git a/CslibFrontier/Computability/Complexity/Problems/Hamiltonian.lean b/CslibFrontier/Computability/Complexity/Problems/Hamiltonian.lean index aeb582e293..f45b557fb5 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Hamiltonian.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Hamiltonian.lean @@ -21,7 +21,7 @@ import Mathlib.Data.List.GetD # Hamiltonian paths and cycles A graph, given by a list of vertices and a list of edges, is read as a directed graph through its -arcs `(u, v) ∈ G.2`, or as an undirected graph through `Cslib.Complexity.Graph.toSimpleGraph` +arcs `(u, v) ∈ G.edges`, or as an undirected graph through `Cslib.Complexity.Graph.toSimpleGraph` restricted to the declared vertices (`SimpleGraph.induce`). In both readings the vertices are the declared ones: repeated vertex entries count once, and edges with an undeclared endpoint are never used. @@ -90,13 +90,13 @@ namespace Graph /-- `p` is a Hamiltonian path of the directed graph `G` from `s` to `t`: it lists every declared vertex exactly once, starts at `s`, ends at `t`, and consecutive vertices are joined by arcs. -/ def IsDirHamPath (G : Graph) (s t : ℕ) (p : List ℕ) : Prop := - p.Nodup ∧ (∀ v, v ∈ p ↔ v ∈ G.1) ∧ p.head? = some s ∧ p.getLast? = some t ∧ - p.IsChain fun u v => (u, v) ∈ G.2 + p.Nodup ∧ (∀ v, v ∈ p ↔ v ∈ G.vertices) ∧ p.head? = some s ∧ p.getLast? = some t ∧ + p.IsChain fun u v => (u, v) ∈ G.edges /-- `p` is a Hamiltonian cycle of the directed graph `G`: a nonempty list of every declared vertex exactly once whose consecutive vertices, read cyclically, are joined by arcs. -/ def IsDirHamCycle (G : Graph) (p : List ℕ) : Prop := - p.Nodup ∧ (∀ v, v ∈ p ↔ v ∈ G.1) ∧ p ≠ [] ∧ (p : Cycle ℕ).Chain fun u v => (u, v) ∈ G.2 + p.Nodup ∧ (∀ v, v ∈ p ↔ v ∈ G.vertices) ∧ p ≠ [] ∧ (p : Cycle ℕ).Chain fun u v => (u, v) ∈ G.edges end Graph @@ -110,13 +110,13 @@ def DirHamCycle : Set Graph := {G | ∃ p, G.IsDirHamCycle p} /-- The graphs with a Hamiltonian path from `s` to `t` through their declared vertices [Sipser, Theorem 7.55][Sipser2013]. -/ def HamPath : Set (Graph × ℕ × ℕ) := - {x | ∃ (hs : x.2.1 ∈ x.1.1) (ht : x.2.2 ∈ x.1.1) - (p : (x.1.toSimpleGraph.induce {v | v ∈ x.1.1}).Walk ⟨x.2.1, hs⟩ ⟨x.2.2, ht⟩), + {x | ∃ (hs : x.2.1 ∈ x.1.vertices) (ht : x.2.2 ∈ x.1.vertices) + (p : (x.1.toSimpleGraph.induce {v | v ∈ x.1.vertices}).Walk ⟨x.2.1, hs⟩ ⟨x.2.2, ht⟩), p.IsHamiltonian} /-- The graphs with a Hamiltonian cycle through their declared vertices [Karp1972]. -/ def HamCycle : Set Graph := - {G | ∃ (a : {v | v ∈ G.1}) (p : (G.toSimpleGraph.induce {v | v ∈ G.1}).Walk a a), + {G | ∃ (a : {v | v ∈ G.vertices}) (p : (G.toSimpleGraph.induce {v | v ∈ G.vertices}).Walk a a), p.IsHamiltonianCycle} /-! ### Lists of vertices -/ @@ -129,33 +129,33 @@ theorem _root_.Cycle.chain_coe_iff_isChain_append_take_one {α : Type*} {R : α | cons a l => rw [Cycle.chain_coe_cons]; simp /-- Lift a list of declared vertices to the vertex type of the induced graph. -/ -private def liftVertices (G : Graph) (p : List ℕ) (h : ∀ v ∈ p, v ∈ G.1) : - List {v | v ∈ G.1} := +private def liftVertices (G : Graph) (p : List ℕ) (h : ∀ v ∈ p, v ∈ G.vertices) : + List {v | v ∈ G.vertices} := p.pmap Subtype.mk h -private theorem map_val_liftVertices (G : Graph) (p : List ℕ) (h : ∀ v ∈ p, v ∈ G.1) : +private theorem map_val_liftVertices (G : Graph) (p : List ℕ) (h : ∀ v ∈ p, v ∈ G.vertices) : (liftVertices G p h).map Subtype.val = p := by simp [liftVertices, List.map_pmap] -private theorem isChain_liftVertices {G : Graph} {p : List ℕ} (h : ∀ v ∈ p, v ∈ G.1) +private theorem isChain_liftVertices {G : Graph} {p : List ℕ} (h : ∀ v ∈ p, v ∈ G.vertices) (hp : p.IsChain G.toSimpleGraph.Adj) : - (liftVertices G p h).IsChain (G.toSimpleGraph.induce {v | v ∈ G.1}).Adj := - List.isChain_pmap_of_isChain (S := (G.toSimpleGraph.induce {v | v ∈ G.1}).Adj) - (f := fun a ha => (⟨a, ha⟩ : {v | v ∈ G.1})) (fun _ _ _ _ hab => hab) hp h + (liftVertices G p h).IsChain (G.toSimpleGraph.induce {v | v ∈ G.vertices}).Adj := + List.isChain_pmap_of_isChain (S := (G.toSimpleGraph.induce {v | v ∈ G.vertices}).Adj) + (f := fun a ha => (⟨a, ha⟩ : {v | v ∈ G.vertices})) (fun _ _ _ _ hab => hab) hp h -private theorem nodup_liftVertices {G : Graph} {p : List ℕ} (h : ∀ v ∈ p, v ∈ G.1) +private theorem nodup_liftVertices {G : Graph} {p : List ℕ} (h : ∀ v ∈ p, v ∈ G.vertices) (hp : p.Nodup) : (liftVertices G p h).Nodup := hp.pmap fun _ _ _ _ hab => congrArg Subtype.val hab -private theorem mem_liftVertices {G : Graph} {p : List ℕ} (h : ∀ v ∈ p, v ∈ G.1) - (v : {v | v ∈ G.1}) : v ∈ liftVertices G p h ↔ v.1 ∈ p := by +private theorem mem_liftVertices {G : Graph} {p : List ℕ} (h : ∀ v ∈ p, v ∈ G.vertices) + (v : {v | v ∈ G.vertices}) : v ∈ liftVertices G p h ↔ v.1 ∈ p := by refine ⟨fun hv => ?_, fun hv => List.mem_pmap.2 ⟨v.1, hv, rfl⟩⟩ have := List.mem_map_of_mem (f := Subtype.val) hv rwa [map_val_liftVertices] at this /-- Undirected Hamiltonian paths as lists of vertices. -/ theorem mem_HamPath_iff {G : Graph} {s t : ℕ} : - (G, s, t) ∈ HamPath ↔ ∃ p : List ℕ, p.Nodup ∧ (∀ v, v ∈ p ↔ v ∈ G.1) ∧ + (G, s, t) ∈ HamPath ↔ ∃ p : List ℕ, p.Nodup ∧ (∀ v, v ∈ p ↔ v ∈ G.vertices) ∧ p.head? = some s ∧ p.getLast? = some t ∧ p.IsChain G.toSimpleGraph.Adj := by constructor · rintro ⟨hs, ht, w, hw⟩ @@ -172,7 +172,7 @@ theorem mem_HamPath_iff {G : Graph} {s t : ℕ} : · exact List.isChain_map_of_isChain (S := G.toSimpleGraph.Adj) Subtype.val (fun _ _ hab => hab) w.isChain_adj_support · rintro ⟨p, hnd, hcov, hs, ht, hch⟩ - have hV : ∀ v ∈ p, v ∈ G.1 := fun v hv => (hcov v).1 hv + have hV : ∀ v ∈ p, v ∈ G.vertices := fun v hv => (hcov v).1 hv have hne : p ≠ [] := by rintro rfl; simp at hs have hne' : liftVertices G p hV ≠ [] := by simpa [liftVertices] using hne have hhead : (liftVertices G p hV).head hne' = ⟨s, (hcov s).1 (List.mem_of_head? hs)⟩ := by @@ -193,7 +193,7 @@ theorem mem_HamPath_iff {G : Graph} {s t : ℕ} : /-- Undirected Hamiltonian cycles as lists of vertices. -/ theorem mem_HamCycle_iff {G : Graph} : - G ∈ HamCycle ↔ ∃ p : List ℕ, p.Nodup ∧ (∀ v, v ∈ p ↔ v ∈ G.1) ∧ 3 ≤ p.length ∧ + G ∈ HamCycle ↔ ∃ p : List ℕ, p.Nodup ∧ (∀ v, v ∈ p ↔ v ∈ G.vertices) ∧ 3 ≤ p.length ∧ (p : Cycle ℕ).Chain G.toSimpleGraph.Adj := by simp_rw [Cycle.chain_coe_iff_isChain_append_take_one] constructor @@ -227,7 +227,7 @@ theorem mem_HamCycle_iff {G : Graph} : exact List.isChain_map_of_isChain (S := G.toSimpleGraph.Adj) Subtype.val (fun _ _ hab => hab) (SimpleGraph.Walk.cons h q).isChain_adj_support · rintro ⟨p, hnd, hcov, hlen, hch⟩ - have hV : ∀ v ∈ p, v ∈ G.1 := fun v hv => (hcov v).1 hv + have hV : ∀ v ∈ p, v ∈ G.vertices := fun v hv => (hcov v).1 hv have hne : p ≠ [] := by rintro rfl; simp at hlen set L := liftVertices G p hV with hL have hLne : L ≠ [] := by simpa [L, liftVertices] using hne @@ -240,7 +240,7 @@ theorem mem_HamCycle_iff {G : Graph} : rw [List.map_append, map_val_liftVertices, List.map_singleton, hav, List.take_one, List.head?_eq_some_head hne] rfl - have hchain : (L ++ [a]).IsChain (G.toSimpleGraph.induce {v | v ∈ G.1}).Adj := by + have hchain : (L ++ [a]).IsChain (G.toSimpleGraph.induce {v | v ∈ G.vertices}).Adj := by have := (List.isChain_map (R := G.toSimpleGraph.Adj) Subtype.val).1 (hmap ▸ hch) exact this have hne' : L ++ [a] ≠ [] := by simp @@ -290,8 +290,9 @@ private theorem exists_rotated_cons {q : List ℕ} {s : ℕ} (hs : s ∈ q) : /-- Closing a Hamiltonian path with an arc back to its start. If no arc of `G` enters the declared vertex `s`, then adding the arc `(t, s)` creates a Hamiltonian cycle exactly when `G` has a Hamiltonian path from `s` to `t`. -/ -theorem mem_DirHamCycle_cons_iff {G : Graph} {s t : ℕ} (hs : s ∈ G.1) - (hin : ∀ u, (u, s) ∉ G.2) : (G.1, (t, s) :: G.2) ∈ DirHamCycle ↔ (G, s, t) ∈ DirHamPath := by +theorem mem_DirHamCycle_cons_iff {G : Graph} {s t : ℕ} (hs : s ∈ G.vertices) + (hin : ∀ u, (u, s) ∉ G.edges) : + ⟨G.vertices, (t, s) :: G.edges⟩ ∈ DirHamCycle ↔ (G, s, t) ∈ DirHamPath := by constructor · rintro ⟨q, hnd, hcov, -, hch⟩ obtain ⟨rest, hq, hnd', hmem'⟩ := exists_rotated_cons ((hcov s).2 hs) @@ -342,11 +343,11 @@ abbrev encPathInstance : Graph × ℕ × ℕ ↪ List Bool := encPair encGraph ( namespace Graph /-- Executable arc test of a directed graph: `(u, v)` is an edge of `G`. -/ -def arcBool (G : Graph) (u v : ℕ) : Bool := G.2.any fun e => decide (e = (u, v)) +def arcBool (G : Graph) (u v : ℕ) : Bool := G.edges.any fun e => decide (e = (u, v)) /-- `arcBool` decides whether `(u, v)` is an edge of the list. -/ @[simp] -theorem arcBool_eq_true_iff (G : Graph) (u v : ℕ) : G.arcBool u v = true ↔ (u, v) ∈ G.2 := by +theorem arcBool_eq_true_iff (G : Graph) (u v : ℕ) : G.arcBool u v = true ↔ (u, v) ∈ G.edges := by simp [arcBool] /-- Testing for an arc in an encoded graph is polynomial-time. -/ @@ -457,7 +458,7 @@ end Graph /-! ### Membership in `NP` -/ private theorem length_vertices_le_encPath (x : Graph × ℕ × ℕ) : - x.1.1.length ≤ (encPair encGraph (encPair encNat encNat) x).length := by + x.1.vertices.length ≤ (encPair encGraph (encPair encNat encNat) x).length := by refine x.1.length_vertices_le_encGraph.trans ?_ simp only [length_encPair] omega @@ -517,7 +518,7 @@ namespace DirHamPath (`Graph.decodeVertexList`) and check that it lists every declared vertex exactly once, starts at `s`, ends at `t`, and that consecutive vertices are joined by arcs. -/ def verifier : (Graph × ℕ × ℕ) × List Bool → Bool := - Graph.verifyOrdering (fun x => x.1.1) fun x p => + Graph.verifyOrdering (fun x => x.1.vertices) fun x p => decide (p.head? = some x.2.1) && decide (p.getLast? = some x.2.2) && Graph.chainBool x.1.arcBool p @@ -527,7 +528,7 @@ theorem isVerifier : IsVerifier encPathInstance (Polynomial.X ^ 2) verifier DirHamPath := Graph.isVerifier_verifyOrdering (P := fun (x : Graph × ℕ × ℕ) p => p.head? = some x.2.1 ∧ p.getLast? = some x.2.2 ∧ - p.IsChain fun u v => (u, v) ∈ x.1.2) + p.IsChain fun u v => (u, v) ∈ x.1.edges) length_vertices_le_encPath fun x p => by simp only [Bool.and_eq_true, decide_eq_true_eq, Graph.chainBool_eq_true_iff (Graph.arcBool_eq_true_iff x.1), and_assoc] @@ -551,14 +552,14 @@ namespace DirHamCycle (`Graph.decodeVertexList`) and check that it is nonempty, lists every declared vertex exactly once, and that cyclically consecutive vertices are joined by arcs. -/ def verifier : Graph × List Bool → Bool := - Graph.verifyOrdering (fun G => G.1) fun G p => + Graph.verifyOrdering (fun G => G.vertices) fun G p => decide (p ≠ []) && Graph.cycleChainBool G.arcBool p /-- `DirHamCycle.verifier` verifies `DirHamCycle` with certificates of length `n ^ 2` for graphs of encoded length `n`. -/ theorem isVerifier : IsVerifier encGraph (Polynomial.X ^ 2) verifier DirHamCycle := Graph.isVerifier_verifyOrdering - (P := fun (G : Graph) p => p ≠ [] ∧ (p : Cycle ℕ).Chain fun u v => (u, v) ∈ G.2) + (P := fun (G : Graph) p => p ≠ [] ∧ (p : Cycle ℕ).Chain fun u v => (u, v) ∈ G.edges) Graph.length_vertices_le_encGraph fun G p => by simp only [Bool.and_eq_true, decide_eq_true_eq, Graph.cycleChainBool_eq_true_iff (Graph.arcBool_eq_true_iff G)] @@ -584,7 +585,7 @@ namespace HamPath (`Graph.decodeVertexList`) and check that it lists every declared vertex exactly once, starts at `s`, ends at `t`, and that consecutive vertices are adjacent. -/ def verifier : (Graph × ℕ × ℕ) × List Bool → Bool := - Graph.verifyOrdering (fun x => x.1.1) fun x p => + Graph.verifyOrdering (fun x => x.1.vertices) fun x p => decide (p.head? = some x.2.1) && decide (p.getLast? = some x.2.2) && Graph.chainBool x.1.adjBool p @@ -620,7 +621,7 @@ namespace HamCycle (`Graph.decodeVertexList`) and check that it has at least three vertices, lists every declared vertex exactly once, and that cyclically consecutive vertices are adjacent. -/ def verifier : Graph × List Bool → Bool := - Graph.verifyOrdering (fun G => G.1) fun G p => + Graph.verifyOrdering (fun G => G.vertices) fun G p => decide (3 ≤ p.length) && Graph.cycleChainBool G.adjBool p /-- `HamCycle.verifier` verifies `HamCycle` with certificates of length `n ^ 2` for graphs of diff --git a/CslibFrontier/Computability/Complexity/Problems/Hamiltonian/Diamond/Defs.lean b/CslibFrontier/Computability/Complexity/Problems/Hamiltonian/Diamond/Defs.lean index 09dd744a74..c514a1a72e 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Hamiltonian/Diamond/Defs.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Hamiltonian/Diamond/Defs.lean @@ -95,12 +95,12 @@ def vertices (N : ℕ) (slots : ℕ → ℕ) (M : ℕ) : List ℕ := /-- The diamond graph with `N` diamonds whose rows have `slots i` slots, `M` clause nodes and the detours `D`. -/ def graph (N : ℕ) (slots : ℕ → ℕ) (M : ℕ) (D : List Detour) : Graph := - (vertices N slots M, arcs N slots D) + ⟨vertices N slots M, arcs N slots D⟩ /-- The diamond graph closed by an arc from the bottom of the last diamond to the top of the first. -/ def cycleGraph (N : ℕ) (slots : ℕ → ℕ) (M : ℕ) (D : List Detour) : Graph := - (vertices N slots M, (topNode N, topNode 0) :: arcs N slots D) + ⟨vertices N slots M, (topNode N, topNode 0) :: arcs N slots D⟩ /-- The detours go through declared nodes, and the detours across each slot all belong to one clause. -/ diff --git a/CslibFrontier/Computability/Complexity/Problems/Hamiltonian/Undirected.lean b/CslibFrontier/Computability/Complexity/Problems/Hamiltonian/Undirected.lean index 8d5ab7017c..69f137feb6 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Hamiltonian/Undirected.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Hamiltonian/Undirected.lean @@ -73,28 +73,28 @@ namespace Graph /-- Vertex `r` of the gadget of `v` in `Graph.split G`: `r = 0` is `in(v)`, `r = 1` is `mid(v)` and `r = 2` is `out(v)`. Vertices are numbered by their first occurrence in `G.1`, so an undeclared vertex gets the number `G.1.length`, which no declared vertex has. -/ -def splitVertex (G : Graph) (v r : ℕ) : ℕ := Nat.pair (G.1.idxOf v) r +def splitVertex (G : Graph) (v r : ℕ) : ℕ := Nat.pair (G.vertices.idxOf v) r /-- The vertex-splitting graph of a directed graph `G`: every declared vertex `v` becomes a path `in(v) - mid(v) - out(v)`, and every arc `(u, v)` becomes an edge `out(u) - in(v)`. -/ def split (G : Graph) : Graph := - (G.1.flatMap fun v => [G.splitVertex v 0, G.splitVertex v 1, G.splitVertex v 2], - G.1.flatMap (fun v => [(G.splitVertex v 0, G.splitVertex v 1), + ⟨G.vertices.flatMap fun v => [G.splitVertex v 0, G.splitVertex v 1, G.splitVertex v 2], + G.vertices.flatMap (fun v => [(G.splitVertex v 0, G.splitVertex v 1), (G.splitVertex v 1, G.splitVertex v 2)]) ++ - G.2.map fun e => (G.splitVertex e.1 2, G.splitVertex e.2 0)) + G.edges.map fun e => (G.splitVertex e.1 2, G.splitVertex e.2 0)⟩ /-- Gadget vertices are equal exactly when their vertices have the same first occurrence and their positions in the gadget agree. -/ theorem splitVertex_eq_splitVertex_iff {G : Graph} {u v r r' : ℕ} : - G.splitVertex u r = G.splitVertex v r' ↔ G.1.idxOf u = G.1.idxOf v ∧ r = r' := + G.splitVertex u r = G.splitVertex v r' ↔ G.vertices.idxOf u = G.vertices.idxOf v ∧ r = r' := Nat.pair_eq_pair -private theorem eq_of_idxOf_eq {G : Graph} {u v : ℕ} (hv : v ∈ G.1) - (h : G.1.idxOf u = G.1.idxOf v) : u = v := +private theorem eq_of_idxOf_eq {G : Graph} {u v : ℕ} (hv : v ∈ G.vertices) + (h : G.vertices.idxOf u = G.vertices.idxOf v) : u = v := ((List.idxOf_inj hv).1 h.symm).symm /-- Gadget vertices of a declared vertex determine the vertex and the position in the gadget. -/ -theorem splitVertex_eq_splitVertex_iff_of_mem {G : Graph} {u v r r' : ℕ} (hv : v ∈ G.1) : +theorem splitVertex_eq_splitVertex_iff_of_mem {G : Graph} {u v r r' : ℕ} (hv : v ∈ G.vertices) : G.splitVertex u r = G.splitVertex v r' ↔ u = v ∧ r = r' := by rw [splitVertex_eq_splitVertex_iff] exact ⟨fun h => ⟨eq_of_idxOf_eq hv h.1, h.2⟩, fun h => ⟨h.1 ▸ rfl, h.2⟩⟩ @@ -111,22 +111,23 @@ private theorem mem_triple {G : Graph} {v x : ℕ} : exacts [Or.inl rfl, Or.inr (Or.inl rfl), Or.inr (Or.inr rfl)] /-- The split graph lists three vertices for each declared vertex. -/ -theorem length_split_fst (G : Graph) : G.split.1.length = 3 * G.1.length := by +theorem length_split_fst (G : Graph) : G.split.vertices.length = 3 * G.vertices.length := by simp [split, List.length_flatMap, mul_comm] /-- The split graph lists two edges for each declared vertex and one for each arc. -/ -theorem length_split_snd (G : Graph) : G.split.2.length = 2 * G.1.length + G.2.length := by +theorem length_split_snd (G : Graph) : + G.split.edges.length = 2 * G.vertices.length + G.edges.length := by simp [split, List.length_flatMap, mul_comm] /-- The declared vertices of the split graph are the gadget vertices of declared vertices. -/ theorem mem_split_fst_iff {G : Graph} {x : ℕ} : - x ∈ G.split.1 ↔ ∃ v ∈ G.1, ∃ r < 3, x = G.splitVertex v r := by + x ∈ G.split.vertices ↔ ∃ v ∈ G.vertices, ∃ r < 3, x = G.splitVertex v r := by simp only [split, List.mem_flatMap, mem_triple] /-- A gadget vertex is a declared vertex of the split graph exactly when its vertex is declared and its position is `0`, `1` or `2`. -/ theorem splitVertex_mem_split_fst_iff {G : Graph} {v r : ℕ} : - G.splitVertex v r ∈ G.split.1 ↔ v ∈ G.1 ∧ r < 3 := by + G.splitVertex v r ∈ G.split.vertices ↔ v ∈ G.vertices ∧ r < 3 := by rw [mem_split_fst_iff] constructor · rintro ⟨w, hw, r', hr', h⟩ @@ -138,33 +139,33 @@ theorem splitVertex_mem_split_fst_iff {G : Graph} {v r : ℕ} : /-! ### Adjacency in the split graph -/ private theorem mem_split_snd_iff {G : Graph} {a b : ℕ} : - (a, b) ∈ G.split.2 ↔ (∃ v ∈ G.1, (a = G.splitVertex v 0 ∧ b = G.splitVertex v 1) ∨ + (a, b) ∈ G.split.edges ↔ (∃ v ∈ G.vertices, (a = G.splitVertex v 0 ∧ b = G.splitVertex v 1) ∨ (a = G.splitVertex v 1 ∧ b = G.splitVertex v 2)) ∨ - ∃ e ∈ G.2, a = G.splitVertex e.1 2 ∧ b = G.splitVertex e.2 0 := by + ∃ e ∈ G.edges, a = G.splitVertex e.1 2 ∧ b = G.splitVertex e.2 0 := by simp only [split, List.mem_append, List.mem_flatMap, List.mem_cons, List.not_mem_nil, or_false, List.mem_map, Prod.mk.injEq] refine or_congr Iff.rfl (exists_congr fun e => and_congr_right fun _ => ?_) exact ⟨fun ⟨h₁, h₂⟩ => ⟨h₁.symm, h₂.symm⟩, fun ⟨h₁, h₂⟩ => ⟨h₁.symm, h₂.symm⟩⟩ private theorem adj_split_iff {G : Graph} {a b : ℕ} : - G.split.toSimpleGraph.Adj a b ↔ a ≠ b ∧ ((a, b) ∈ G.split.2 ∨ (b, a) ∈ G.split.2) := + G.split.toSimpleGraph.Adj a b ↔ a ≠ b ∧ ((a, b) ∈ G.split.edges ∨ (b, a) ∈ G.split.edges) := toSimpleGraph_adj -private theorem adj_in_mid {G : Graph} {v : ℕ} (hv : v ∈ G.1) : +private theorem adj_in_mid {G : Graph} {v : ℕ} (hv : v ∈ G.vertices) : G.split.toSimpleGraph.Adj (G.splitVertex v 0) (G.splitVertex v 1) := by refine adj_split_iff.2 ⟨fun h => ?_, Or.inl (mem_split_snd_iff.2 (Or.inl ⟨v, hv, ?_⟩))⟩ · simp [splitVertex_eq_splitVertex_iff] at h · exact Or.inl ⟨rfl, rfl⟩ -private theorem adj_mid_out {G : Graph} {v : ℕ} (hv : v ∈ G.1) : +private theorem adj_mid_out {G : Graph} {v : ℕ} (hv : v ∈ G.vertices) : G.split.toSimpleGraph.Adj (G.splitVertex v 1) (G.splitVertex v 2) := by refine adj_split_iff.2 ⟨fun h => ?_, Or.inl (mem_split_snd_iff.2 (Or.inl ⟨v, hv, ?_⟩))⟩ · simp [splitVertex_eq_splitVertex_iff] at h · exact Or.inr ⟨rfl, rfl⟩ /-- Between declared vertices, an exit is adjacent to an entry exactly along an arc. -/ -private theorem adj_out_in_iff {G : Graph} {u v : ℕ} (hu : u ∈ G.1) (hv : v ∈ G.1) : - G.split.toSimpleGraph.Adj (G.splitVertex u 2) (G.splitVertex v 0) ↔ (u, v) ∈ G.2 := by +private theorem adj_out_in_iff {G : Graph} {u v : ℕ} (hu : u ∈ G.vertices) (hv : v ∈ G.vertices) : + G.split.toSimpleGraph.Adj (G.splitVertex u 2) (G.splitVertex v 0) ↔ (u, v) ∈ G.edges := by rw [adj_split_iff, mem_split_snd_iff, mem_split_snd_iff] simp only [splitVertex_eq_splitVertex_iff] constructor @@ -199,9 +200,9 @@ private theorem eq_of_adj_mid {G : Graph} {v x : ℕ} /-- An exit vertex of a declared vertex is adjacent only to its middle vertex and to entries of its out-neighbours. -/ -private theorem eq_of_adj_out {G : Graph} {v x : ℕ} (hv : v ∈ G.1) +private theorem eq_of_adj_out {G : Graph} {v x : ℕ} (hv : v ∈ G.vertices) (h : G.split.toSimpleGraph.Adj (G.splitVertex v 2) x) : - x = G.splitVertex v 1 ∨ ∃ w, (v, w) ∈ G.2 ∧ x = G.splitVertex w 0 := by + x = G.splitVertex v 1 ∨ ∃ w, (v, w) ∈ G.edges ∧ x = G.splitVertex w 0 := by rw [adj_split_iff, mem_split_snd_iff, mem_split_snd_iff] at h rcases h with ⟨-, (⟨w, -, ⟨h₁, rfl⟩ | ⟨h₁, rfl⟩⟩ | ⟨e, he, h₁, rfl⟩) | (⟨w, -, ⟨rfl, h₁⟩ | ⟨rfl, h₁⟩⟩ | ⟨e, -, rfl, h₁⟩)⟩ <;> @@ -221,7 +222,7 @@ private theorem eq_of_adj_out {G : Graph} {v x : ℕ} (hv : v ∈ G.1) private def gadgets (G : Graph) (p : List ℕ) : List ℕ := p.flatMap fun v => [G.splitVertex v 0, G.splitVertex v 1, G.splitVertex v 2] -private theorem split_fst (G : Graph) : G.split.1 = gadgets G G.1 := rfl +private theorem split_fst (G : Graph) : G.split.vertices = gadgets G G.vertices := rfl private theorem gadgets_nil (G : Graph) : gadgets G [] = [] := rfl @@ -257,7 +258,7 @@ private theorem getLast?_gadgets (G : Graph) (p : List ℕ) : simp /-- The gadgets of distinct declared vertices are disjoint. -/ -private theorem nodup_gadgets_iff {G : Graph} {p : List ℕ} (hp : ∀ v ∈ p, v ∈ G.1) : +private theorem nodup_gadgets_iff {G : Graph} {p : List ℕ} (hp : ∀ v ∈ p, v ∈ G.vertices) : (gadgets G p).Nodup ↔ p.Nodup := by rw [gadgets, List.nodup_flatMap] constructor @@ -274,12 +275,12 @@ private theorem nodup_gadgets_iff {G : Graph} {p : List ℕ} (hp : ∀ v ∈ p, exact ((splitVertex_eq_splitVertex_iff_of_mem (hp b hb)).1 h).1 private theorem mem_gadgets_iff_of_forall_mem_iff {G : Graph} {p : List ℕ} - (hp : ∀ v, v ∈ p ↔ v ∈ G.1) (x : ℕ) : x ∈ gadgets G p ↔ x ∈ G.split.1 := by + (hp : ∀ v, v ∈ p ↔ v ∈ G.vertices) (x : ℕ) : x ∈ gadgets G p ↔ x ∈ G.split.vertices := by simp only [split_fst, mem_gadgets, hp] private theorem forall_mem_iff_of_mem_gadgets_iff {G : Graph} {p : List ℕ} - (hp : ∀ v ∈ p, v ∈ G.1) (hcov : ∀ x, x ∈ gadgets G p ↔ x ∈ G.split.1) (v : ℕ) : - v ∈ p ↔ v ∈ G.1 := by + (hp : ∀ v ∈ p, v ∈ G.vertices) (hcov : ∀ x, x ∈ gadgets G p ↔ x ∈ G.split.vertices) (v : ℕ) : + v ∈ p ↔ v ∈ G.vertices := by refine ⟨hp v, fun hv => ?_⟩ obtain ⟨w, hw, r, -, h⟩ := mem_gadgets.1 ((hcov _).2 (splitVertex_mem_split_fst_iff.2 ⟨hv, (by omega : 1 < 3)⟩)) @@ -287,19 +288,19 @@ private theorem forall_mem_iff_of_mem_gadgets_iff {G : Graph} {p : List ℕ} exact hw /-- Along declared vertices, a chain of gadgets is a directed walk. -/ -private theorem isChain_gadgets_iff {G : Graph} {p : List ℕ} (hp : ∀ v ∈ p, v ∈ G.1) : - (gadgets G p).IsChain G.split.toSimpleGraph.Adj ↔ p.IsChain fun u v => (u, v) ∈ G.2 := by +private theorem isChain_gadgets_iff {G : Graph} {p : List ℕ} (hp : ∀ v ∈ p, v ∈ G.vertices) : + (gadgets G p).IsChain G.split.toSimpleGraph.Adj ↔ p.IsChain fun u v => (u, v) ∈ G.edges := by induction p with | nil => simp [gadgets_nil] | cons a p ih => have ha := hp a (List.mem_cons_self ..) - have hp' : ∀ v ∈ p, v ∈ G.1 := fun v hv => hp v (List.mem_cons_of_mem _ hv) + have hp' : ∀ v ∈ p, v ∈ G.vertices := fun v hv => hp v (List.mem_cons_of_mem _ hv) have h₃ : [G.splitVertex a 0, G.splitVertex a 1, G.splitVertex a 2].IsChain G.split.toSimpleGraph.Adj := List.isChain_cons_cons.2 ⟨adj_in_mid ha, List.isChain_pair.2 (adj_mid_out ha)⟩ have hhead : (∀ x ∈ [G.splitVertex a 0, G.splitVertex a 1, G.splitVertex a 2].getLast?, ∀ y ∈ p.head?.map (G.splitVertex · 0), G.split.toSimpleGraph.Adj x y) ↔ - ∀ y ∈ p.head?, (a, y) ∈ G.2 := by + ∀ y ∈ p.head?, (a, y) ∈ G.edges := by cases p with | nil => simp | cons b p => simp [adj_out_in_iff ha (hp' b (List.mem_cons_self ..))] @@ -310,14 +311,14 @@ private theorem isChain_gadgets_iff {G : Graph} {p : List ℕ} (hp : ∀ v ∈ p exact ⟨fun ⟨_, h₁, h₂⟩ => ⟨h₂, h₁⟩, fun ⟨h₁, h₂⟩ => ⟨h₃, h₂, h₁⟩⟩ /-- Along declared vertices, a closed chain of gadgets is a closed directed walk. -/ -private theorem chain_gadgets_iff {G : Graph} {p : List ℕ} (hp : ∀ v ∈ p, v ∈ G.1) : +private theorem chain_gadgets_iff {G : Graph} {p : List ℕ} (hp : ∀ v ∈ p, v ∈ G.vertices) : (gadgets G p : Cycle ℕ).Chain G.split.toSimpleGraph.Adj ↔ - (p : Cycle ℕ).Chain fun u v => (u, v) ∈ G.2 := by + (p : Cycle ℕ).Chain fun u v => (u, v) ∈ G.edges := by cases p with | nil => rfl | cons a p => have ha := hp a (List.mem_cons_self ..) - have hp' : ∀ v ∈ a :: (p ++ [a]), v ∈ G.1 := by grind + have hp' : ∀ v ∈ a :: (p ++ [a]), v ∈ G.vertices := by grind have e : gadgets G (a :: (p ++ [a])) = (G.splitVertex a 0 :: G.splitVertex a 1 :: G.splitVertex a 2 :: (gadgets G p ++ [G.splitVertex a 0])) ++ [G.splitVertex a 1, G.splitVertex a 2] := by @@ -333,10 +334,10 @@ private theorem chain_gadgets_iff {G : Graph} {p : List ℕ} (hp : ∀ v ∈ p, entry, does not end at a middle vertex, and contains the middle vertex of each entry it contains, is a sequence of gadgets. -/ private theorem exists_eq_gadgets {G : Graph} (n : ℕ) : ∀ (l : List ℕ) (v : ℕ), l.length ≤ n → - l.IsChain G.split.toSimpleGraph.Adj → l.Nodup → (∀ x ∈ l, x ∈ G.split.1) → + l.IsChain G.split.toSimpleGraph.Adj → l.Nodup → (∀ x ∈ l, x ∈ G.split.vertices) → l.head? = some (G.splitVertex v 0) → (∀ u, l.getLast? ≠ some (G.splitVertex u 1)) → - (∀ u ∈ G.1, G.splitVertex u 0 ∈ l → G.splitVertex u 1 ∈ l) → - ∃ p, (∀ u ∈ p, u ∈ G.1) ∧ l = gadgets G p := by + (∀ u ∈ G.vertices, G.splitVertex u 0 ∈ l → G.splitVertex u 1 ∈ l) → + ∃ p, (∀ u ∈ p, u ∈ G.vertices) ∧ l = gadgets G p := by induction n with | zero => intro l v hlen _ _ _ hhead @@ -345,7 +346,8 @@ private theorem exists_eq_gadgets {G : Graph} (n : ℕ) : ∀ (l : List ℕ) (v | succ n ih => intro l v hlen hch hnd hdecl hhead hlast hmid obtain ⟨l₁, rfl⟩ := List.head?_eq_some_iff.1 hhead - have hv : v ∈ G.1 := (splitVertex_mem_split_fst_iff.1 (hdecl _ (List.mem_cons_self ..))).1 + have hv : v ∈ G.vertices := + (splitVertex_mem_split_fst_iff.1 (hdecl _ (List.mem_cons_self ..))).1 have hnd₀ : G.splitVertex v 0 ∉ l₁ := (List.nodup_cons.1 hnd).1 have hnd₁ : l₁.Nodup := (List.nodup_cons.1 hnd).2 have hch₁ : l₁.IsChain G.split.toSimpleGraph.Adj := hch.tail @@ -381,7 +383,7 @@ private theorem exists_eq_gadgets {G : Graph} (n : ℕ) : ∀ (l : List ℕ) (v -- The vertex after `out(v)` is the entry of an out-neighbour `w` of `v`. rcases eq_of_adj_out hv (List.isChain_cons_cons.1 hch₁.tail).1 with rfl | ⟨w, -, rfl⟩ · simp at hnd₁ - have hmid' : ∀ u ∈ G.1, G.splitVertex u 0 ∈ G.splitVertex w 0 :: l₃ → + have hmid' : ∀ u ∈ G.vertices, G.splitVertex u 0 ∈ G.splitVertex w 0 :: l₃ → G.splitVertex u 1 ∈ G.splitVertex w 0 :: l₃ := by intro u hu hu0 rcases List.mem_append.1 (show G.splitVertex u 1 ∈ @@ -437,7 +439,7 @@ theorem split_mem_HamPath_iff (G : Graph) (s t : ℕ) : rw [hb, ((splitVertex_eq_splitVertex_iff_of_mem (hp b (List.mem_of_getLast? hb))).1 h.symm).1] · rintro ⟨p, hnd, hcov, hs, ht, hch⟩ - have hp : ∀ v ∈ p, v ∈ G.1 := fun v hv => (hcov v).1 hv + have hp : ∀ v ∈ p, v ∈ G.vertices := fun v hv => (hcov v).1 hv exact ⟨gadgets G p, (nodup_gadgets_iff hp).2 hnd, mem_gadgets_iff_of_forall_mem_iff hcov, by rw [head?_gadgets, hs]; rfl, by rw [getLast?_gadgets, ht]; rfl, (isChain_gadgets_iff hp).2 hch⟩ @@ -455,7 +457,7 @@ theorem split_mem_HamCycle_iff (G : Graph) : G.split ∈ HamCycle ↔ G ∈ DirH -- Rotations and reversals preserve the hypotheses. have key : ∀ L : List ℕ, L ~r (A ++ G.splitVertex u 0 :: B) ∨ L ~r (A ++ G.splitVertex u 0 :: B).reverse → - L.Nodup ∧ (∀ x, x ∈ L ↔ x ∈ G.split.1) ∧ 3 ≤ L.length ∧ + L.Nodup ∧ (∀ x, x ∈ L ↔ x ∈ G.split.vertices) ∧ 3 ≤ L.length ∧ (L : Cycle ℕ).Chain G.split.toSimpleGraph.Adj := by rintro L (h | h) · exact ⟨h.nodup_iff.2 hnd, fun x => h.mem_iff.trans (hcov x), h.perm.length_eq ▸ hlen, @@ -515,7 +517,7 @@ theorem split_mem_HamCycle_iff (G : Graph) : G.split ∈ HamCycle ↔ G ∈ DirH rintro rfl simp [gadgets_nil] at hL · rintro ⟨p, hnd, hcov, hne, hch⟩ - have hp : ∀ v ∈ p, v ∈ G.1 := fun v hv => (hcov v).1 hv + have hp : ∀ v ∈ p, v ∈ G.vertices := fun v hv => (hcov v).1 hv refine ⟨gadgets G p, (nodup_gadgets_iff hp).2 hnd, mem_gadgets_iff_of_forall_mem_iff hcov, ?_, (chain_gadgets_iff hp).2 hch⟩ rw [length_gadgets] @@ -529,7 +531,7 @@ paired with the unary position in the gadget, and converted to binary. -/ private theorem computableInPolyTime_splitVertex : ComputableInPolyTime (fun p : Graph × ℕ × ℕ => p.1.splitVertex p.2.1 p.2.2) (encPair encGraph (encPair encNat encUnary)) encNat := by - unfold splitVertex encGraph + unfold splitVertex exact unary_to_binary.comp (by fun_prop) /-- Splitting the vertices of a graph is polynomial-time. -/ @@ -537,8 +539,7 @@ private theorem computableInPolyTime_splitVertex : theorem computableInPolyTime_split : ComputableInPolyTime split encGraph encGraph := by have hsv := computableInPolyTime_splitVertex unfold split - unfold encGraph at hsv ⊢ - fun_prop + exact encGraph_mk_polyTime (by fun_prop) (by fun_prop) end Graph diff --git a/CslibFrontier/Computability/Complexity/Problems/IndependentSet/Sparse.lean b/CslibFrontier/Computability/Complexity/Problems/IndependentSet/Sparse.lean index 2b3dacb40c..f7691cc302 100644 --- a/CslibFrontier/Computability/Complexity/Problems/IndependentSet/Sparse.lean +++ b/CslibFrontier/Computability/Complexity/Problems/IndependentSet/Sparse.lean @@ -108,7 +108,7 @@ open SparseIndependentSet in Theorem 3.3][GareyJohnson1979]: the graph of `φ` with the target `n + m`, for `n` distinct variables and `m` clauses. -/ def toSparseIndependentSet (φ : CNF ℕ) : Graph × ℕ := - ((vertices φ, edges φ), φ.distinctVars.length + φ.length) + (⟨vertices φ, edges φ⟩, φ.distinctVars.length + φ.length) namespace SparseIndependentSet @@ -413,7 +413,7 @@ open SparseIndependentSet in of `toSparseIndependentSet` has at most `(k ^ 2 + 5 k) m` vertices and edges in total, counted as listed. -/ theorem length_toSparseIndependentSet_le {k : ℕ} {φ : CNF ℕ} (hφ : ∀ c ∈ φ, c.length ≤ k) : - (toSparseIndependentSet φ).1.1.length + (toSparseIndependentSet φ).1.2.length ≤ + (toSparseIndependentSet φ).1.vertices.length + (toSparseIndependentSet φ).1.edges.length ≤ (k * k + 5 * k) * φ.length := by have hzip : ∀ c ∈ φ.zipIdx, c.1.length ≤ k := fun c hc => by obtain ⟨cl, i⟩ := c @@ -486,7 +486,10 @@ theorem computableInPolyTime_toSparseIndependentSet : (encList (encPair encNat encNat)) := by unfold edges fun_prop - exact (hV.prod hE).prod hk + have hG : ComputableInPolyTime (fun φ => (⟨vertices φ, edges φ⟩ : Graph)) + (encList (encList (encPair encNat encBool))) encGraph := by + fun_prop + exact hG.prod hk end SAT diff --git a/CslibFrontier/Computability/Complexity/Problems/LongPath.lean b/CslibFrontier/Computability/Complexity/Problems/LongPath.lean index ad9edf600f..1335d2fb12 100644 --- a/CslibFrontier/Computability/Complexity/Problems/LongPath.lean +++ b/CslibFrontier/Computability/Complexity/Problems/LongPath.lean @@ -48,7 +48,7 @@ namespace Graph vertices are adjacent. -/ @[expose] def IsVertexPath (G : Graph) (p : List ℕ) : Prop := - p.Nodup ∧ (∀ v ∈ p, v ∈ G.1) ∧ p.IsChain G.toSimpleGraph.Adj + p.Nodup ∧ (∀ v ∈ p, v ∈ G.vertices) ∧ p.IsChain G.toSimpleGraph.Adj /-- A path has at most as many vertices as the distinct declared vertices. -/ theorem IsVertexPath.length_le {G : Graph} {p : List ℕ} (hp : G.IsVertexPath p) : @@ -84,14 +84,14 @@ theorem not_mem_LongPath_of_lt {G : Graph} {k : ℕ} (hk : G.distinctVertices.le induced graph. -/ theorem mem_LongPath_succ_iff {G : Graph} {k : ℕ} : (G, k + 1) ∈ LongPath ↔ - ∃ (u v : {v // v ∈ G.1}) (w : G.induced.Walk u v), w.IsPath ∧ w.length = k := by + ∃ (u v : {v // v ∈ G.vertices}) (w : G.induced.Walk u v), w.IsPath ∧ w.length = k := by rw [mem_LongPath_iff] constructor · rintro ⟨p, ⟨hnd, hV, hch⟩, hlen⟩ - have hLne : p.pmap (fun a ha => (⟨a, ha⟩ : {v // v ∈ G.1})) hV ≠ [] := by + have hLne : p.pmap (fun a ha => (⟨a, ha⟩ : {v // v ∈ G.vertices})) hV ≠ [] := by grind [List.pmap_eq_nil_iff] have hLch := List.isChain_pmap_of_isChain (S := G.induced.Adj) - (f := fun a ha => (⟨a, ha⟩ : {v // v ∈ G.1})) (fun _ _ _ _ hab => hab) hch hV + (f := fun a ha => (⟨a, ha⟩ : {v // v ∈ G.vertices})) (fun _ _ _ _ hab => hab) hch hV refine ⟨_, _, SimpleGraph.Walk.ofSupport _ hLne hLch, ?_, ?_⟩ · rw [SimpleGraph.Walk.isPath_def, SimpleGraph.Walk.support_ofSupport] exact hnd.pmap fun _ _ _ _ hab => congrArg Subtype.val hab diff --git a/CslibFrontier/Computability/Complexity/Problems/LongPath/Hardness.lean b/CslibFrontier/Computability/Complexity/Problems/LongPath/Hardness.lean index 2de83be385..a8cf753b47 100644 --- a/CslibFrontier/Computability/Complexity/Problems/LongPath/Hardness.lean +++ b/CslibFrontier/Computability/Complexity/Problems/LongPath/Hardness.lean @@ -79,13 +79,13 @@ namespace LongPath (`Graph.decodeVertexList`), check that it lists every declared vertex exactly once, and check that its first `k` entries are `k` vertices with consecutive ones adjacent. -/ def verifier : (Graph × ℕ) × List Bool → Bool := - Graph.verifyOrdering (fun (G, _) => G.1) fun (G, k) p => + Graph.verifyOrdering (fun (G, _) => G.vertices) fun (G, k) p => decide ((p.take k).length = k) && Graph.chainBool G.adjBool (p.take k) /-- A path on `k` vertices extends to an enumeration of the declared vertices. -/ private theorem exists_enum_of_isVertexPath {G : Graph} {q : List ℕ} (hq : G.IsVertexPath q) : - ∃ p : List ℕ, p.Nodup ∧ (∀ v, v ∈ p ↔ v ∈ G.1) ∧ p.take q.length = q := by - refine ⟨q ++ (G.1.dedup.filter fun v => v ∉ q), ?_, fun v => ?_, List.take_left⟩ + ∃ p : List ℕ, p.Nodup ∧ (∀ v, v ∈ p ↔ v ∈ G.vertices) ∧ p.take q.length = q := by + refine ⟨q ++ (G.vertices.dedup.filter fun v => v ∉ q), ?_, fun v => ?_, List.take_left⟩ · refine List.nodup_append.2 ⟨hq.1, (List.nodup_dedup _).filter _, fun a ha b hb hab => ?_⟩ subst hab exact of_decide_eq_true (List.mem_filter.1 hb).2 ha @@ -106,7 +106,7 @@ are adjacent. -/ theorem isVerifier : IsVerifier (encPair encGraph encNat) (Polynomial.X ^ 2) verifier LongPath := by have h := Graph.isVerifier_verifyOrdering (enc := encPair encGraph encNat) - (V := fun x : Graph × ℕ => x.1.1) + (V := fun x : Graph × ℕ => x.1.vertices) (check := fun x p => decide ((p.take x.2).length = x.2) && Graph.chainBool x.1.adjBool (p.take x.2)) (P := fun x p => (p.take x.2).length = x.2 ∧ (p.take x.2).IsChain x.1.toSimpleGraph.Adj) @@ -131,7 +131,7 @@ first `min (length) k` entries, a count in unary. -/ @[fun_prop] theorem computableInPolyTime_verifier : ComputableInPolyTime verifier (encPair (encPair encGraph encNat) encBits) encBool := by - refine Graph.computableInPolyTime_verifyOrdering (by unfold encGraph; fun_prop) ?_ + refine Graph.computableInPolyTime_verifyOrdering (by fun_prop) ?_ have htake : ComputableInPolyTime (fun q : (Graph × ℕ) × List ℕ => q.2.take q.1.2) (encPair (encPair encGraph encNat) (encList encNat)) (encList encNat) := by have hmin : ComputableInPolyTime @@ -155,14 +155,15 @@ namespace HamPath /-- The label of the vertex `v` of the graph `G` in copy `i`: the pair of `i` and two more than the position of the first occurrence of `v` in the vertex list of `G`. -/ -def copyLabel (G : Graph) (i v : ℕ) : ℕ := Nat.pair i (G.1.idxOf v + 2) +def copyLabel (G : Graph) (i v : ℕ) : ℕ := Nat.pair i (G.vertices.idxOf v + 2) -/-- Copy `i` of the instance `x = (G, s, t)`: the vertices of `G`, relabeled by `copyLabel`, with +/-- Copy `i` of the instance `x = ⟨G, s, t⟩`: the vertices of `G`, relabeled by `copyLabel`, with two pendant vertices `Nat.pair i 0` and `Nat.pair i 1` attached to `s` and to `t`. -/ def pendantCopy (x : Graph × ℕ × ℕ) (i : ℕ) : Graph := - (Nat.pair i 0 :: Nat.pair i 1 :: x.1.1.map (copyLabel x.1 i), - (Nat.pair i 0, copyLabel x.1 i x.2.1) :: (Nat.pair i 1, copyLabel x.1 i x.2.2) :: - x.1.2.map fun e => (copyLabel x.1 i e.1, copyLabel x.1 i e.2)) + ⟨Nat.pair i 0 :: Nat.pair i 1 :: x.1.vertices.map (copyLabel x.1 i), + (Nat.pair i 0, copyLabel x.1 i x.2.1) :: + (Nat.pair i 1, copyLabel x.1 i x.2.2) :: + x.1.edges.map fun e => (copyLabel x.1 i e.1, copyLabel x.1 i e.2)⟩ /-! ### Polynomial time -/ @@ -172,10 +173,9 @@ private theorem computableInPolyTime_copyLabel {γ : Type} {enc : γ ↪ List Bo ComputableInPolyTime (fun c => copyLabel (G c) (i c) (v c)) enc encNat := by have hpair : ComputableInPolyTime (fun p : ℕ × ℕ => Nat.pair p.1 p.2) (encPair encUnary encUnary) encUnary := by fun_prop - have hV : ComputableInPolyTime (fun c => (G c).1) enc (encList encNat) := by - unfold encGraph at hG - exact hG.fst' - have hidx : ComputableInPolyTime (fun c => (G c).1.idxOf (v c)) enc encUnary := + have hV : ComputableInPolyTime (fun c => (G c).vertices) enc (encList encNat) := by + fun_prop + have hidx : ComputableInPolyTime (fun c => (G c).vertices.idxOf (v c)) enc encUnary := idxOfWithInput hV hv exact unary_to_binary.comp (hpair.comp (hi.prod (by fun_prop))) @@ -195,27 +195,29 @@ theorem computableInPolyTime_pendantCopy : have h1 : ComputableInPolyTime (fun p : (Graph × ℕ × ℕ) × ℕ => Nat.pair p.2 1) (encPair encPathInstance encUnary) encNat := unary_to_binary.comp (hpair.comp (hi.prod (const 1))) - have hs : ComputableInPolyTime (fun p : (Graph × ℕ × ℕ) × ℕ => copyLabel p.1.1 p.2 p.1.2.1) + have hs : ComputableInPolyTime + (fun p : (Graph × ℕ × ℕ) × ℕ => copyLabel p.1.1 p.2 p.1.2.1) (encPair encPathInstance encUnary) encNat := computableInPolyTime_copyLabel hG hi (by fun_prop) - have ht : ComputableInPolyTime (fun p : (Graph × ℕ × ℕ) × ℕ => copyLabel p.1.1 p.2 p.1.2.2) + have ht : ComputableInPolyTime + (fun p : (Graph × ℕ × ℕ) × ℕ => copyLabel p.1.1 p.2 p.1.2.2) (encPair encPathInstance encUnary) encNat := computableInPolyTime_copyLabel hG hi (by fun_prop) have hV : ComputableInPolyTime - (fun p : (Graph × ℕ × ℕ) × ℕ => p.1.1.1.map (copyLabel p.1.1 p.2)) + (fun p : (Graph × ℕ × ℕ) × ℕ => p.1.1.vertices.map (copyLabel p.1.1 p.2)) (encPair encPathInstance encUnary) (encList encNat) := by - refine mapWithInput (by unfold encGraph at hG; exact hG.fst') ?_ + refine mapWithInput (by fun_prop) ?_ exact computableInPolyTime_copyLabel (hG.comp (fst _ _)) (hi.comp (fst _ _)) (snd _ _) have hE : ComputableInPolyTime (fun p : (Graph × ℕ × ℕ) × ℕ => - p.1.1.2.map fun e => (copyLabel p.1.1 p.2 e.1, copyLabel p.1.1 p.2 e.2)) + p.1.1.edges.map fun e => (copyLabel p.1.1 p.2 e.1, copyLabel p.1.1 p.2 e.2)) (encPair encPathInstance encUnary) (encList (encPair encNat encNat)) := by - refine mapWithInput (by unfold encGraph at hG; exact hG.snd') ?_ + refine mapWithInput (by fun_prop) ?_ exact (computableInPolyTime_copyLabel (hG.comp (fst _ _)) (hi.comp (fst _ _)) (snd _ _).fst').prod (computableInPolyTime_copyLabel (hG.comp (fst _ _)) (hi.comp (fst _ _)) (snd _ _).snd') - unfold pendantCopy encGraph - exact (cons' h0 (cons' h1 hV)).prod (cons' (h0.prod hs) (cons' (h1.prod ht) hE)) + unfold pendantCopy + exact encGraph_mk_polyTime (cons' h0 (cons' h1 hV)) (cons' (h0.prod hs) (cons' (h1.prod ht) hE)) /-! ### Labels -/ @@ -229,7 +231,7 @@ private theorem copyLabel_ne_pair {G : Graph} {i v i' j : ℕ} (hj : j < 2) : rw [copyLabel, Nat.pair_eq_pair] at h omega -private theorem copyLabel_inj {G : Graph} {i u v : ℕ} (hu : u ∈ G.1) : +private theorem copyLabel_inj {G : Graph} {i u v : ℕ} (hu : u ∈ G.vertices) : copyLabel G i u = copyLabel G i v ↔ u = v := by rw [copyLabel, copyLabel, Nat.pair_eq_pair, ← List.idxOf_inj hu] omega @@ -242,19 +244,19 @@ private theorem pair_zero_ne_pair_one (i i' : ℕ) : Nat.pair i 0 ≠ Nat.pair i /-! ### One copy -/ private theorem mem_pendantCopy_fst {x : Graph × ℕ × ℕ} {i w : ℕ} : - w ∈ (pendantCopy x i).1 ↔ - w = Nat.pair i 0 ∨ w = Nat.pair i 1 ∨ ∃ v ∈ x.1.1, copyLabel x.1 i v = w := by + w ∈ (pendantCopy x i).vertices ↔ + w = Nat.pair i 0 ∨ w = Nat.pair i 1 ∨ ∃ v ∈ x.1.vertices, copyLabel x.1 i v = w := by simp only [pendantCopy, List.mem_cons, List.mem_map] private theorem mem_pendantCopy_snd {x : Graph × ℕ × ℕ} {i : ℕ} {e : ℕ × ℕ} : - e ∈ (pendantCopy x i).2 ↔ e = (Nat.pair i 0, copyLabel x.1 i x.2.1) ∨ + e ∈ (pendantCopy x i).edges ↔ e = (Nat.pair i 0, copyLabel x.1 i x.2.1) ∨ e = (Nat.pair i 1, copyLabel x.1 i x.2.2) ∨ - ∃ d ∈ x.1.2, (copyLabel x.1 i d.1, copyLabel x.1 i d.2) = e := by + ∃ d ∈ x.1.edges, (copyLabel x.1 i d.1, copyLabel x.1 i d.2) = e := by simp only [pendantCopy, List.mem_cons, List.mem_map] /-- Every vertex of copy `i` is a pair with first component `i`. -/ theorem unpair_of_mem_pendantCopy_fst {x : Graph × ℕ × ℕ} {i w : ℕ} - (h : w ∈ (pendantCopy x i).1) : (Nat.unpair w).1 = i := by + (h : w ∈ (pendantCopy x i).vertices) : (Nat.unpair w).1 = i := by rcases mem_pendantCopy_fst.1 h with rfl | rfl | ⟨v, -, rfl⟩ · simp only [Nat.unpair_pair] · simp only [Nat.unpair_pair] @@ -262,7 +264,7 @@ theorem unpair_of_mem_pendantCopy_fst {x : Graph × ℕ × ℕ} {i w : ℕ} /-- Both ends of every edge of copy `i` are pairs with first component `i`. -/ theorem unpair_of_mem_pendantCopy_snd {x : Graph × ℕ × ℕ} {i : ℕ} {e : ℕ × ℕ} - (h : e ∈ (pendantCopy x i).2) : (Nat.unpair e.1).1 = i ∧ (Nat.unpair e.2).1 = i := by + (h : e ∈ (pendantCopy x i).edges) : (Nat.unpair e.1).1 = i ∧ (Nat.unpair e.2).1 = i := by rcases mem_pendantCopy_snd.1 h with rfl | rfl | ⟨d, -, rfl⟩ · exact ⟨by simp only [Nat.unpair_pair], unpair_copyLabel_fst _ _ _⟩ · exact ⟨by simp only [Nat.unpair_pair], unpair_copyLabel_fst _ _ _⟩ @@ -276,7 +278,7 @@ variable {G : Graph} {s t i : ℕ} /-- The only neighbor of the pendant vertex `Nat.pair i 0` is the copy of `s`. -/ private theorem eq_of_adj_pair_zero {w : ℕ} - (h : (pendantCopy (G, s, t) i).toSimpleGraph.Adj (Nat.pair i 0) w) : + (h : (pendantCopy ⟨G, s, t⟩ i).toSimpleGraph.Adj (Nat.pair i 0) w) : w = copyLabel G i s := by rw [Graph.toSimpleGraph_adj] at h rcases h.2 with h | h @@ -291,7 +293,7 @@ private theorem eq_of_adj_pair_zero {w : ℕ} /-- The only neighbor of the pendant vertex `Nat.pair i 1` is the copy of `t`. -/ private theorem eq_of_adj_pair_one {w : ℕ} - (h : (pendantCopy (G, s, t) i).toSimpleGraph.Adj (Nat.pair i 1) w) : + (h : (pendantCopy ⟨G, s, t⟩ i).toSimpleGraph.Adj (Nat.pair i 1) w) : w = copyLabel G i t := by rw [Graph.toSimpleGraph_adj] at h rcases h.2 with h | h @@ -305,11 +307,12 @@ private theorem eq_of_adj_pair_one {w : ℕ} · exact absurd (Prod.mk.inj h).2 (copyLabel_ne_pair (by omega)) /-- On copies of declared vertices, adjacency of the copy is adjacency of `G`. -/ -private theorem adj_copyLabel_iff {u v : ℕ} (hu : u ∈ G.1) (hv : v ∈ G.1) : - (pendantCopy (G, s, t) i).toSimpleGraph.Adj (copyLabel G i u) (copyLabel G i v) ↔ +private theorem adj_copyLabel_iff {u v : ℕ} (hu : u ∈ G.vertices) (hv : v ∈ G.vertices) : + (pendantCopy ⟨G, s, t⟩ i).toSimpleGraph.Adj (copyLabel G i u) (copyLabel G i v) ↔ G.toSimpleGraph.Adj u v := by - have key : ∀ {u v : ℕ}, u ∈ G.1 → v ∈ G.1 → - ((copyLabel G i u, copyLabel G i v) ∈ (pendantCopy (G, s, t) i).2 ↔ (u, v) ∈ G.2) := by + have key : ∀ {u v : ℕ}, u ∈ G.vertices → v ∈ G.vertices → + ((copyLabel G i u, copyLabel G i v) ∈ (pendantCopy ⟨G, s, t⟩ i).edges ↔ + (u, v) ∈ G.edges) := by intro u v hu hv rw [mem_pendantCopy_snd] constructor @@ -325,26 +328,27 @@ private theorem adj_copyLabel_iff {u v : ℕ} (hu : u ∈ G.1) (hv : v ∈ G.1) rw [Graph.toSimpleGraph_adj, Graph.toSimpleGraph_adj, key hu hv, key hv hu, Ne, copyLabel_inj hu] -private theorem pair_zero_mem_pendantCopy : Nat.pair i 0 ∈ (pendantCopy (G, s, t) i).1 := +private theorem pair_zero_mem_pendantCopy : Nat.pair i 0 ∈ (pendantCopy ⟨G, s, t⟩ i).vertices := mem_pendantCopy_fst.2 (Or.inl rfl) -private theorem pair_one_mem_pendantCopy : Nat.pair i 1 ∈ (pendantCopy (G, s, t) i).1 := +private theorem pair_one_mem_pendantCopy : Nat.pair i 1 ∈ (pendantCopy ⟨G, s, t⟩ i).vertices := mem_pendantCopy_fst.2 (Or.inr (Or.inl rfl)) /-- A copy has two more distinct vertices than `G`. -/ private theorem card_pendantCopy : - (pendantCopy (G, s, t) i).1.toFinset.card = G.distinctVertices.length + 2 := by - have h0 : Nat.pair i 0 ∉ insert (Nat.pair i 1) (G.1.toFinset.image (copyLabel G i)) := by + (pendantCopy ⟨G, s, t⟩ i).vertices.toFinset.card = G.distinctVertices.length + 2 := by + have h0 : Nat.pair i 0 ∉ insert (Nat.pair i 1) (G.vertices.toFinset.image (copyLabel G i)) := by simp only [Finset.mem_insert, Finset.mem_image, not_or, not_exists, not_and] exact ⟨pair_zero_ne_pair_one i i, fun v _ h => copyLabel_ne_pair (by omega) h⟩ - have h1 : Nat.pair i 1 ∉ G.1.toFinset.image (copyLabel G i) := by + have h1 : Nat.pair i 1 ∉ G.vertices.toFinset.image (copyLabel G i) := by simp only [Finset.mem_image, not_exists, not_and] exact fun v _ h => copyLabel_ne_pair (by omega) h - have hinj : Set.InjOn (copyLabel G i) G.1.toFinset := fun u hu v _ h => + have hinj : Set.InjOn (copyLabel G i) G.vertices.toFinset := fun u hu v _ h => (copyLabel_inj (List.mem_toFinset.1 (Finset.mem_coe.1 hu))).1 h - have hV : (pendantCopy (G, s, t) i).1 = - Nat.pair i 0 :: Nat.pair i 1 :: G.1.map (copyLabel G i) := rfl - have hmap : (G.1.map (copyLabel G i)).toFinset = G.1.toFinset.image (copyLabel G i) := by + have hV : (pendantCopy ⟨G, s, t⟩ i).vertices = + Nat.pair i 0 :: Nat.pair i 1 :: G.vertices.map (copyLabel G i) := rfl + have hmap : (G.vertices.map (copyLabel G i)).toFinset = + G.vertices.toFinset.image (copyLabel G i) := by ext w simp rw [hV, List.toFinset_cons, List.toFinset_cons, hmap, @@ -375,9 +379,9 @@ private theorem head_or_last_of_forall_adj {R : ℕ → ℕ → Prop} {q : List /-- A path of a copy from `Nat.pair i 0` to `Nat.pair i 1` through the copies of all declared vertices gives a Hamiltonian path of `G` from `s` to `t`. -/ private theorem mem_HamPath_of_path {q : List ℕ} (hnd : q.Nodup) - (hV : ∀ w ∈ q, w ∈ (pendantCopy (G, s, t) i).1) - (hch : q.IsChain (pendantCopy (G, s, t) i).toSimpleGraph.Adj) - (hcov : ∀ v ∈ G.1, copyLabel G i v ∈ q) (ha : q.head? = some (Nat.pair i 0)) + (hV : ∀ w ∈ q, w ∈ (pendantCopy ⟨G, s, t⟩ i).vertices) + (hch : q.IsChain (pendantCopy ⟨G, s, t⟩ i).toSimpleGraph.Adj) + (hcov : ∀ v ∈ G.vertices, copyLabel G i v ∈ q) (ha : q.head? = some (Nat.pair i 0)) (hb : q.getLast? = some (Nat.pair i 1)) : (G, s, t) ∈ HamPath := by obtain ⟨l, rfl⟩ := List.getLast?_eq_some_iff.1 hb obtain ⟨m, rfl⟩ : ∃ m, l = Nat.pair i 0 :: m := by @@ -389,7 +393,7 @@ private theorem mem_HamPath_of_path {q : List ℕ} (hnd : q.Nodup) have hnd' := List.nodup_cons.1 hnd have hb' : Nat.pair i 1 ∉ m := fun h => (List.nodup_append.1 hnd'.2).2.2 _ h _ (List.mem_singleton_self _) rfl - have hm : ∀ w ∈ m, ∃ v ∈ G.1, copyLabel G i v = w := by + have hm : ∀ w ∈ m, ∃ v ∈ G.vertices, copyLabel G i v = w := by intro w hw rcases mem_pendantCopy_fst.1 (hV w (List.mem_cons_of_mem _ (List.mem_append_left _ hw))) with rfl | rfl | h @@ -397,7 +401,7 @@ private theorem mem_HamPath_of_path {q : List ℕ} (hnd : q.Nodup) · exact absurd hw hb' · exact h choose! dec hdec using hm - obtain ⟨p, hpV, rfl⟩ : ∃ p : List ℕ, (∀ v ∈ p, v ∈ G.1) ∧ p.map (copyLabel G i) = m := by + obtain ⟨p, hpV, rfl⟩ : ∃ p : List ℕ, (∀ v ∈ p, v ∈ G.vertices) ∧ p.map (copyLabel G i) = m := by refine ⟨m.map dec, fun v hv => ?_, ?_⟩ · obtain ⟨w, hw, rfl⟩ := List.mem_map.1 hv exact (hdec w hw).1 @@ -442,14 +446,14 @@ private theorem mem_HamPath_of_mem_LongPath (h : (pendantCopy (G, s, t) i, G.distinctVertices.length + 2) ∈ LongPath) : (G, s, t) ∈ HamPath := by obtain ⟨q, ⟨hnd, hV, hch⟩, hlen⟩ := mem_LongPath_iff.1 h - have hfin : q.toFinset = (pendantCopy (G, s, t) i).1.toFinset := by + have hfin : q.toFinset = (pendantCopy ⟨G, s, t⟩ i).vertices.toFinset := by refine Finset.eq_of_subset_of_card_le (fun w hw => List.mem_toFinset.2 (hV w (List.mem_toFinset.1 hw))) ?_ rw [card_pendantCopy, List.toFinset_card_of_nodup hnd, hlen] - have hmem : ∀ w ∈ (pendantCopy (G, s, t) i).1, w ∈ q := fun w hw => by + have hmem : ∀ w ∈ (pendantCopy ⟨G, s, t⟩ i).vertices, w ∈ q := fun w hw => by rw [← List.mem_toFinset, hfin, List.mem_toFinset] exact hw - have hcov : ∀ v ∈ G.1, copyLabel G i v ∈ q := fun v hv => + have hcov : ∀ v ∈ G.vertices, copyLabel G i v ∈ q := fun v hv => hmem _ (mem_pendantCopy_fst.2 (Or.inr (Or.inr ⟨v, hv, rfl⟩))) have hend0 := head_or_last_of_forall_adj hnd hch (hmem _ pair_zero_mem_pendantCopy) (fun hw => eq_of_adj_pair_zero (hw.elim (fun h => h.symm) id)) @@ -474,7 +478,7 @@ the copy on two more vertices than `G` has. -/ private theorem mem_LongPath_of_mem_HamPath (h : (G, s, t) ∈ HamPath) : (pendantCopy (G, s, t) i, G.distinctVertices.length + 2) ∈ LongPath := by obtain ⟨p, hnd, hcov, hs, ht, hch⟩ := mem_HamPath_iff.1 h - have hpV : ∀ v ∈ p, v ∈ G.1 := fun v hv => (hcov v).1 hv + have hpV : ∀ v ∈ p, v ∈ G.vertices := fun v hv => (hcov v).1 hv have hlen : p.length = G.distinctVertices.length := by rw [Graph.length_distinctVertices, ← List.toFinset_card_of_nodup hnd] congr 1 @@ -517,7 +521,7 @@ private theorem mem_LongPath_of_mem_HamPath (h : (G, s, t) ∈ HamPath) : Or.inr (mem_pendantCopy_snd.2 (Or.inr (Or.inl rfl)))⟩ · simp only [List.length_cons, List.length_append, List.length_map, List.length_nil, hlen] -/-- **One copy**: the copy of `(G, s, t)` has a path on two more vertices than `G` has exactly +/-- **One copy**: the copy of `⟨G, s, t⟩` has a path on two more vertices than `G` has exactly when `G` has a Hamiltonian path from `s` to `t`. -/ theorem pendantCopy_mem_LongPath_iff : (pendantCopy (G, s, t) i, G.distinctVertices.length + 2) ∈ LongPath ↔ @@ -532,15 +536,18 @@ end HamPath Hamiltonian path from `s` to `t` exactly when its pendant copy has a path on two more vertices than the graph has. -/ theorem LongPath_npHard : NPHard (encPair encGraph encNat) LongPath := by - have hk : ComputableInPolyTime (fun x : Graph × ℕ × ℕ => x.1.distinctVertices.length + 2) + have hk : ComputableInPolyTime + (fun x : Graph × ℕ × ℕ => x.1.distinctVertices.length + 2) encPathInstance encNat := by - have hn : ComputableInPolyTime (fun x : Graph × ℕ × ℕ => x.1.distinctVertices.length) + have hn : ComputableInPolyTime + (fun x : Graph × ℕ × ℕ => x.1.distinctVertices.length) encPathInstance encUnary := (length_list encNat).comp (Graph.computableInPolyTime_distinctVertices.comp (by fun_prop)) exact unary_to_binary.comp (by fun_prop) - refine HamPath_npHard.trans ⟨fun x => (HamPath.pendantCopy x 0, x.1.distinctVertices.length + 2), - (HamPath.computableInPolyTime_pendantCopy.comp (id'.prod (const 0))).prod hk, fun x => ?_⟩ - exact HamPath.pendantCopy_mem_LongPath_iff.symm + refine HamPath_npHard.trans + ⟨fun x => (HamPath.pendantCopy x 0, x.1.distinctVertices.length + 2), + (HamPath.computableInPolyTime_pendantCopy.comp (id'.prod (const 0))).prod hk, + fun ⟨G, s, t⟩ => HamPath.pendantCopy_mem_LongPath_iff.symm⟩ /-- **k-Path is `NP`-complete**, with the target `k` in binary. -/ theorem LongPath_npComplete : NPComplete (encPair encGraph encNat) LongPath := diff --git a/CslibFrontier/Computability/Complexity/Problems/SAT/ExactOne/DrawingSource.lean b/CslibFrontier/Computability/Complexity/Problems/SAT/ExactOne/DrawingSource.lean index bb7257925b..f7233cdd26 100644 --- a/CslibFrontier/Computability/Complexity/Problems/SAT/ExactOne/DrawingSource.lean +++ b/CslibFrontier/Computability/Complexity/Problems/SAT/ExactOne/DrawingSource.lean @@ -46,11 +46,11 @@ open Orthogonal /-- The clause endpoint of a listed incidence, with a fixed fallback for invalid indices. -/ def sourceStart (G : Graph) (d : Graph.OrthogonalDrawing) (i : ℕ) : Point := - Graph.drawingPosition G d (G.2.getD i (0, 0)).2 + Graph.drawingPosition G d (G.edges.getD i (0, 0)).2 /-- The variable endpoint of a listed incidence, with a fixed fallback for invalid indices. -/ def sourceFinish (G : Graph) (d : Graph.OrthogonalDrawing) (i : ℕ) : Point := - Graph.drawingPosition G d (G.2.getD i (0, 0)).1 + Graph.drawingPosition G d (G.edges.getD i (0, 0)).1 /-- Normalize the supplied incidence route to begin at its clause endpoint. -/ def sourceRoute (G : Graph) (d : Graph.OrthogonalDrawing) (i : ℕ) : Route := @@ -59,12 +59,12 @@ def sourceRoute (G : Graph) (d : Graph.OrthogonalDrawing) (i : ℕ) : Route := /-- Declared vertices with equal positions are equal in a valid drawing. -/ theorem drawingPosition_injOn {G : Graph} {d : Graph.OrthogonalDrawing} - (hd : Graph.IsPlanarOrthogonalDrawing G d) {v w : ℕ} (hv : v ∈ G.1) (hw : w ∈ G.1) + (hd : Graph.IsPlanarOrthogonalDrawing G d) {v w : ℕ} (hv : v ∈ G.vertices) (hw : w ∈ G.vertices) (he : Graph.drawingPosition G d v = Graph.drawingPosition G d w) : v = w := by have hi := List.idxOf_lt_length_of_mem hv have hj := List.idxOf_lt_length_of_mem hw - have hi' : G.1.idxOf v < d.1.length := by rw [hd.vertexCount]; exact hi - have hj' : G.1.idxOf w < d.1.length := by rw [hd.vertexCount]; exact hj + have hi' : G.vertices.idxOf v < d.1.length := by rw [hd.vertexCount]; exact hi + have hj' : G.vertices.idxOf w < d.1.length := by rw [hd.vertexCount]; exact hj simp only [Graph.drawingPosition, List.getD_eq_getElem _ _ hi', List.getD_eq_getElem _ _ hj'] at he have he' := hd.positionsNodup.getElem_inj_iff.mp he @@ -103,14 +103,14 @@ theorem wireLength_sourceRoute (G : Graph) (d : Graph.OrthogonalDrawing) (i : /-- The normalized route begins at the clause and ends at the variable. -/ theorem sourceRoute_endpoints {G : Graph} {d : Graph.OrthogonalDrawing} - (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.2.length) : + (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.edges.length) : (sourceRoute G d i).head? = some (sourceStart G d i) ∧ (sourceRoute G d i).getLast? = some (sourceFinish G d i) := by have he := hd.endpoints i hi dsimp only at he - have hs : sourceStart G d i = Graph.drawingPosition G d G.2[i].2 := by + have hs : sourceStart G d i = Graph.drawingPosition G d G.edges[i].2 := by rw [sourceStart, List.getD_eq_getElem _ _ hi] - have hf : sourceFinish G d i = Graph.drawingPosition G d G.2[i].1 := by + have hf : sourceFinish G d i = Graph.drawingPosition G d G.edges[i].1 := by rw [sourceFinish, List.getD_eq_getElem _ _ hi] rw [← hs, ← hf] at he unfold sourceRoute @@ -125,7 +125,7 @@ theorem sourceRoute_endpoints {G : Graph} {d : Graph.OrthogonalDrawing} /-- The clause and variable endpoints occupy different points. -/ theorem sourceStart_ne_finish {G : Graph} {d : Graph.OrthogonalDrawing} - (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.2.length) : + (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.edges.length) : sourceStart G d i ≠ sourceFinish G d i := by have hm := hd.endpointsDeclared _ (List.getElem_mem hi) have hn := hd.loopless _ (List.getElem_mem hi) @@ -135,7 +135,7 @@ theorem sourceStart_ne_finish {G : Graph} {d : Graph.OrthogonalDrawing} /-- Every normalized valid incidence has at least two listed points. -/ theorem sourceRoute_length_ge_two {G : Graph} {d : Graph.OrthogonalDrawing} - (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.2.length) : + (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.edges.length) : 2 ≤ (sourceRoute G d i).length := by have he := sourceRoute_endpoints hd hi have hn := sourceStart_ne_finish hd hi @@ -148,7 +148,7 @@ theorem sourceRoute_length_ge_two {G : Graph} {d : Graph.OrthogonalDrawing} /-- Normalization preserves proper orthogonality and continuous simplicity. -/ theorem sourceRoute_regular {G : Graph} {d : Graph.OrthogonalDrawing} - (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.2.length) : + (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.edges.length) : IsOrthogonal (sourceRoute G d i) ∧ IsSimple (sourceRoute G d i) := by unfold sourceRoute split @@ -158,7 +158,7 @@ theorem sourceRoute_regular {G : Graph} {d : Graph.OrthogonalDrawing} /-- A normalized incidence starts with its actual clause vertex and a proper next point. -/ theorem sourceRoute_shape {G : Graph} {d : Graph.OrthogonalDrawing} - (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.2.length) : + (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.edges.length) : ∃ b r, sourceRoute G d i = sourceStart G d i :: b :: r := by have hh := (sourceRoute_endpoints hd hi).1 obtain ⟨r, hr⟩ := List.head?_eq_some_iff.mp hh @@ -173,8 +173,8 @@ def sourceDirection (G : Graph) (d : Graph.OrthogonalDrawing) (i : ℕ) : Point /-- Incidences at the same clause vertex leave in different cardinal directions. -/ theorem sourceDirection_ne {G : Graph} {d : Graph.OrthogonalDrawing} (hd : Graph.IsPlanarOrthogonalDrawing G d) {i j : ℕ} - (hi : i < G.2.length) (hj : j < G.2.length) (hij : i ≠ j) - (hc : G.2[i].2 = G.2[j].2) : sourceDirection G d i ≠ sourceDirection G d j := by + (hi : i < G.edges.length) (hj : j < G.edges.length) (hij : i ≠ j) + (hc : G.edges[i].2 = G.edges[j].2) : sourceDirection G d i ≠ sourceDirection G d j := by obtain ⟨b, r, hr⟩ := sourceRoute_shape hd hi obtain ⟨c, s, hs⟩ := sourceRoute_shape hd hj have he : sourceStart G d i = sourceStart G d j := by grind [sourceStart] @@ -205,7 +205,7 @@ def exteriorPort (k radius : ℕ) (G : Graph) (d : Graph.OrthogonalDrawing) (i : /-- The trimmed exterior incidence is proper and continuously simple. -/ theorem exteriorRoute_regular {G : Graph} {d : Graph.OrthogonalDrawing} - (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.2.length) + (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.edges.length) {k radius : ℕ} (hr : radius < k) : IsOrthogonal (exteriorRoute k radius G d i) ∧ IsSimple (exteriorRoute k radius G d i) := ⟨isOrthogonal_cutStart (sourceRoute_regular hd hi).1 hr, @@ -213,7 +213,7 @@ theorem exteriorRoute_regular {G : Graph} {d : Graph.OrthogonalDrawing} /-- The exterior incidence has its designated box port and the original scaled variable endpoint. -/ theorem exteriorRoute_endpoints {G : Graph} {d : Graph.OrthogonalDrawing} - (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.2.length) + (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.edges.length) (k radius : ℕ) : (exteriorRoute k radius G d i).head? = some (exteriorPort k radius G d i) ∧ (exteriorRoute k radius G d i).getLast? = some (k • sourceFinish G d i) := by @@ -231,7 +231,7 @@ theorem exteriorRoute_endpoints {G : Graph} {d : Graph.OrthogonalDrawing} /-- Cutting removes exactly the radius from the scaled Manhattan cost. -/ theorem exteriorRoute_wireLength {G : Graph} {d : Graph.OrthogonalDrawing} - (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.2.length) + (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.edges.length) {k radius : ℕ} (hr : radius < k) : wireLength (exteriorRoute k radius G d i) + radius = k * wireLength (d.2.getD i []) := by @@ -247,7 +247,7 @@ theorem exteriorRoute_segmentCount (k radius : ℕ) (G : Graph) (d : Graph.Ortho /-- The exterior route meets its own closed clause box only at the designated port. -/ theorem exteriorRoute_box_contact {G : Graph} {d : Graph.OrthogonalDrawing} - (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.2.length) + (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.edges.length) {k radius : ℕ} (hr : radius < k) {p : ℝ × ℝ} (hp : p ∈ support (exteriorRoute k radius G d i)) (hb : inPointBox (k • sourceStart G d i) radius p) : @@ -263,8 +263,8 @@ theorem exteriorRoute_box_contact {G : Graph} {d : Graph.OrthogonalDrawing} /-- A nonincident declared vertex has a closed replacement box disjoint from an exterior route. -/ theorem exteriorRoute_avoids_box {G : Graph} {d : Graph.OrthogonalDrawing} - (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.2.length) - {v : ℕ} (hv : v ∈ G.1) (hv₁ : v ≠ G.2[i].1) (hv₂ : v ≠ G.2[i].2) + (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.edges.length) + {v : ℕ} (hv : v ∈ G.vertices) (hv₁ : v ≠ G.edges[i].1) (hv₂ : v ≠ G.edges[i].2) {k radius : ℕ} (hr : radius < k) {p : ℝ × ℝ} (hp : p ∈ support (exteriorRoute k radius G d i)) : ¬ inPointBox (k • Graph.drawingPosition G d v) radius p := by @@ -298,7 +298,7 @@ theorem exteriorPort_ne_center (k radius : ℕ) (hr : 0 < radius) (G : Graph) /-- A trimmed incidence no longer contains the scaled source clause vertex. -/ theorem exteriorRoute_not_mem_center {G : Graph} {d : Graph.OrthogonalDrawing} - (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.2.length) + (hd : Graph.IsPlanarOrthogonalDrawing G d) {i : ℕ} (hi : i < G.edges.length) {k radius : ℕ} (hr₀ : 0 < radius) (hr : radius < k) : realPoint (k • sourceStart G d i) ∉ support (exteriorRoute k radius G d i) := by intro hp @@ -318,11 +318,11 @@ theorem exteriorRoute_not_mem_center {G : Graph} {d : Graph.OrthogonalDrawing} The common source clause endpoint has been removed by the positive-radius cut. -/ theorem exteriorRoute_contacts {G : Graph} {d : Graph.OrthogonalDrawing} (hd : Graph.IsPlanarOrthogonalDrawing G d) {i j : ℕ} - (hi : i < G.2.length) (hj : j < G.2.length) (hij : i ≠ j) + (hi : i < G.edges.length) (hj : j < G.edges.length) (hij : i ≠ j) {k radius : ℕ} (hr₀ : 0 < radius) (hr : radius < k) {p : ℝ × ℝ} (hp : p ∈ support (exteriorRoute k radius G d i)) (hq : p ∈ support (exteriorRoute k radius G d j)) : - G.2[i].1 = G.2[j].1 ∧ p = realPoint (k • sourceFinish G d i) := by + G.edges[i].1 = G.edges[j].1 ∧ p = realPoint (k • sourceFinish G d i) := by have hp' := support_cutStart_subset (sourceRoute_regular hd hi).1 hr hp have hq' := support_cutStart_subset (sourceRoute_regular hd hj).1 hr hq rw [support_dilateRoute] at hp' hq' @@ -336,12 +336,12 @@ theorem exteriorRoute_contacts {G : Graph} {d : Graph.OrthogonalDrawing} obtain ⟨v, _, hvi, hvj, hv⟩ := hd.contacts i hi j hj hij q hq₁ hr₁ have hpv : p = realPoint (k • Graph.drawingPosition G d v) := by rw [← he₁, hv, realPoint_nsmul] - have hvi₂ : v ≠ G.2[i].2 := by + have hvi₂ : v ≠ G.edges[i].2 := by intro he have hs : sourceStart G d i = Graph.drawingPosition G d v := by rw [sourceStart, List.getD_eq_getElem _ _ hi, ← he] exact exteriorRoute_not_mem_center hd hi hr₀ hr (by simpa only [hs, hpv] using hp) - have hvj₂ : v ≠ G.2[j].2 := by + have hvj₂ : v ≠ G.edges[j].2 := by intro he have hs : sourceStart G d j = Graph.drawingPosition G d v := by rw [sourceStart, List.getD_eq_getElem _ _ hj, ← he] @@ -353,7 +353,8 @@ theorem exteriorRoute_contacts {G : Graph} {d : Graph.OrthogonalDrawing} /-- A different source vertex center lies outside the closed local replacement box. -/ theorem sourceVertex_not_mem_box {G : Graph} {d : Graph.OrthogonalDrawing} - (hd : Graph.IsPlanarOrthogonalDrawing G d) {v w : ℕ} (hv : v ∈ G.1) (hw : w ∈ G.1) + (hd : Graph.IsPlanarOrthogonalDrawing G d) {v w : ℕ} + (hv : v ∈ G.vertices) (hw : w ∈ G.vertices) (hvw : v ≠ w) {k radius : ℕ} (hr : radius < k) : ¬ inPointBox (k • Graph.drawingPosition G d v) radius (realPoint (k • Graph.drawingPosition G d w)) := by diff --git a/CslibFrontier/Computability/Complexity/Problems/SAT/Incidence.lean b/CslibFrontier/Computability/Complexity/Problems/SAT/Incidence.lean index 100ea5872a..b8ab414682 100644 --- a/CslibFrontier/Computability/Complexity/Problems/SAT/Incidence.lean +++ b/CslibFrontier/Computability/Complexity/Problems/SAT/Incidence.lean @@ -56,23 +56,23 @@ variable {V : Type} [DecidableEq V] /-- The finite incidence graph, with one edge record for every literal occurrence. -/ def incidenceGraph (φ : CNF V) : Graph := - (List.range (φ.distinctVars.length + φ.length), + ⟨List.range (φ.distinctVars.length + φ.length), (Cslib.Logic.PL.occurrences φ).map fun o => - (φ.distinctVars.idxOf o.2.2.1, φ.distinctVars.length + o.1)) + (φ.distinctVars.idxOf o.2.2.1, φ.distinctVars.length + o.1)⟩ /-- There is one vertex per distinct variable and per clause position. -/ theorem length_vertices_incidenceGraph (φ : CNF V) : - (incidenceGraph φ).1.length = φ.numVars + φ.length := by + (incidenceGraph φ).vertices.length = φ.numVars + φ.length := by simp [incidenceGraph, CNF.length_distinctVars] /-- Every literal occurrence gives exactly one edge record. -/ theorem length_edges_incidenceGraph (φ : CNF V) : - (incidenceGraph φ).2.length = φ.vars.length := by + (incidenceGraph φ).edges.length = φ.vars.length := by simp [incidenceGraph, length_occurrences] /-- The edge records join the variable and clause parts of the declared vertex interval. -/ theorem incidenceGraph_bipartite {φ : CNF V} {e : ℕ × ℕ} - (he : e ∈ (incidenceGraph φ).2) : + (he : e ∈ (incidenceGraph φ).edges) : e.1 < φ.distinctVars.length ∧ φ.distinctVars.length ≤ e.2 ∧ e.2 < φ.distinctVars.length + φ.length := by obtain ⟨o, ho, rfl⟩ := List.mem_map.mp he @@ -85,15 +85,15 @@ theorem incidenceGraph_bipartite {φ : CNF V} {e : ℕ × ℕ} /-- Every edge endpoint occurs in the list of declared vertices. -/ theorem incidenceGraph_endpoints {φ : CNF V} {e : ℕ × ℕ} - (he : e ∈ (incidenceGraph φ).2) : - e.1 ∈ (incidenceGraph φ).1 ∧ e.2 ∈ (incidenceGraph φ).1 := by + (he : e ∈ (incidenceGraph φ).edges) : + e.1 ∈ (incidenceGraph φ).vertices ∧ e.2 ∈ (incidenceGraph φ).vertices := by obtain ⟨hu, _, hv⟩ := incidenceGraph_bipartite he simp only [incidenceGraph, List.mem_range] exact ⟨hu.trans_le (Nat.le_add_right _ _), hv⟩ /-- The variable and clause name intervals are disjoint, so incidence edges are not loops. -/ theorem incidenceGraph_loopless {φ : CNF V} {e : ℕ × ℕ} - (he : e ∈ (incidenceGraph φ).2) : e.1 ≠ e.2 := by + (he : e ∈ (incidenceGraph φ).edges) : e.1 ≠ e.2 := by obtain ⟨hu, hv, _⟩ := incidenceGraph_bipartite he omega @@ -107,19 +107,23 @@ theorem computableInPolyTime_incidenceGraph (enc : V ↪ List Bool) : have hd := CNF.computableInPolyTime_distinctVars enc have ho := Cslib.Logic.PL.computableInPolyTime_occurrences enc unfold Cslib.Logic.PL.encOccurrence at ho - have hu : ComputableInPolyTime (incidenceGraph (V := V)) + have hu : ComputableInPolyTime (fun φ : CNF V => + (List.range (φ.distinctVars.length + φ.length), + (Cslib.Logic.PL.occurrences φ).map fun o => + (φ.distinctVars.idxOf o.2.2.1, φ.distinctVars.length + o.1))) (encList (encList (encPair enc encBool))) unaryGraph := by - unfold incidenceGraph unaryGraph + dsimp only [unaryGraph] fun_prop - have hc : ComputableInPolyTime (fun g : Graph => g) unaryGraph encGraph := by - open ComputableInPolyTime in - have hp := (unary_to_binary.comp (fst encUnary encUnary)).prod - (unary_to_binary.comp (snd encUnary encUnary)) - open ComputableInPolyTime in - have h := ((map unary_to_binary).comp - (fst (encList encUnary) (encList (encPair encUnary encUnary)))).prod - ((map hp).comp (snd (encList encUnary) (encList (encPair encUnary encUnary)))) - simpa [Function.comp_def, unaryGraph, encGraph] using h - exact hc.comp hu + open ComputableInPolyTime in + have hp := (unary_to_binary.comp (fst encUnary encUnary)).prod + (unary_to_binary.comp (snd encUnary encUnary)) + open ComputableInPolyTime in + have hV := (map unary_to_binary).comp + (fst (encList encUnary) (encList (encPair encUnary encUnary))) + have hE := (map hp).comp (snd (encList encUnary) (encList (encPair encUnary encUnary))) + have hc := encGraph_mk_polyTime hV hE + have hcomp := hc.comp hu + convert hcomp using 2 with φ + ext <;> simp [incidenceGraph] end Cslib.Complexity.SAT diff --git a/CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PathAssignments.lean b/CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PathAssignments.lean index c49b247cde..47b985609b 100644 --- a/CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PathAssignments.lean +++ b/CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PathAssignments.lean @@ -93,14 +93,14 @@ theorem occurrencePath_crossing_straight {φ : CNF ℕ} StraightThrough (occurrencePath φ o) p (0, 1) := by have hsite := (mem_crossingSites φ p).1 hp let G := SAT.incidenceGraph φ - have hG : ∀ e ∈ G.2, e.1 ∈ G.1 ∧ e.2 ∈ G.1 := + have hG : ∀ e ∈ G.edges, e.1 ∈ G.vertices ∧ e.2 ∈ G.vertices := fun _ he => SAT.incidenceGraph_endpoints he - have hloop : ∀ e ∈ G.2, e.1 ≠ e.2 := fun _ he => SAT.incidenceGraph_loopless he + have hloop : ∀ e ∈ G.edges, e.1 ≠ e.2 := fun _ he => SAT.incidenceGraph_loopless he have hdeg : ∀ v, G.incidenceVertices.count v ≤ 3 := SAT.incidenceGraph_degree_le hw hv have hi := occurrence_index_lt ho obtain ⟨j, hj, hji, hpj⟩ := List.exists_other_index_of_two_le_filter_contains (paths φ) p ((Cslib.Logic.PL.occurrences φ).idxOf o) hsite.2.2 - have hj' : j < G.2.length := by + have hj' : j < G.edges.length := by simpa only [paths_eq_unitPaths, Graph.unitPaths_length] using hj have hpi' : p ∈ G.unitPath ((Cslib.Logic.PL.occurrences φ).idxOf o) := by simpa only [occurrencePath_eq_unitPath] using hpi @@ -208,11 +208,11 @@ theorem dropLast_center_eq_source {φ : CNF ℕ} {o : Occurrence ℕ} p = Layout.center (φ.distinctVars.idxOf o.2.2.1) := by obtain ⟨w, hw, rfl⟩ := List.mem_map.mp hc have hw' := List.mem_range.mp hw - have hwmem : w ∈ (SAT.incidenceGraph φ).1 := by + have hwmem : w ∈ (SAT.incidenceGraph φ).vertices := by apply List.mem_range.mpr simpa only [SAT.incidenceGraph, List.length_range] using hw' have hi := List.idxOf_lt_length_of_mem hwmem - have hidx : (SAT.incidenceGraph φ).1.idxOf w = w := by + have hidx : (SAT.incidenceGraph φ).vertices.idxOf w = w := by simpa only [SAT.incidenceGraph, List.getElem_range] using List.getElem_idxOf hi have h := (Graph.unitPath_declared_center_iff (SAT.incidenceGraph φ) (fun _ he => SAT.incidenceGraph_endpoints he) diff --git a/CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PathGeometry.lean b/CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PathGeometry.lean index 58487c983e..911d91bcde 100644 --- a/CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PathGeometry.lean +++ b/CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PathGeometry.lean @@ -83,10 +83,10 @@ private theorem horizontal_vertical_neighbors {p q : Point} have h' := congrArg Prod.fst h <;> simp at h' private theorem canonical_neighbor_unique (G : Graph) - (hG : ∀ e ∈ G.2, e.1 ∈ G.1 ∧ e.2 ∈ G.1) - (hloop : ∀ e ∈ G.2, e.1 ≠ e.2) + (hG : ∀ e ∈ G.edges, e.1 ∈ G.vertices ∧ e.2 ∈ G.vertices) + (hloop : ∀ e ∈ G.edges, e.1 ≠ e.2) (hdeg : ∀ v, G.incidenceVertices.count v ≤ 3) {i j : ℕ} - (hi : i < G.2.length) (hj : j < G.2.length) (hij : i ≠ j) {p q : Point} + (hi : i < G.edges.length) (hj : j < G.edges.length) (hij : i ≠ j) {p q : Point} (hcenter : p ∉ G.orthogonalPositions) (hiadj : (p, q) ∈ (G.unitPath i).consecutivePairs ∨ (q, p) ∈ (G.unitPath i).consecutivePairs) @@ -118,17 +118,17 @@ theorem paths_shared_step_index {φ : CNF ℕ} (hf : (p, q) ∈ ((paths φ).getD j []).consecutivePairs ∨ (q, p) ∈ ((paths φ).getD j []).consecutivePairs) : i = j := by by_contra hij - have hi' : i < (SAT.incidenceGraph φ).2.length := by + have hi' : i < (SAT.incidenceGraph φ).edges.length := by simpa only [paths_eq_unitPaths, Graph.unitPaths_length] using hi - have hj' : j < (SAT.incidenceGraph φ).2.length := by + have hj' : j < (SAT.incidenceGraph φ).edges.length := by simpa only [paths_eq_unitPaths, Graph.unitPaths_length] using hj change (p, q) ∈ ((SAT.incidenceGraph φ).unitPath i).consecutivePairs at he change (p, q) ∈ ((SAT.incidenceGraph φ).unitPath j).consecutivePairs ∨ (q, p) ∈ ((SAT.incidenceGraph φ).unitPath j).consecutivePairs at hf - have hG : ∀ e ∈ (SAT.incidenceGraph φ).2, - e.1 ∈ (SAT.incidenceGraph φ).1 ∧ e.2 ∈ (SAT.incidenceGraph φ).1 := + have hG : ∀ e ∈ (SAT.incidenceGraph φ).edges, + e.1 ∈ (SAT.incidenceGraph φ).vertices ∧ e.2 ∈ (SAT.incidenceGraph φ).vertices := fun _ => SAT.incidenceGraph_endpoints - have hloop : ∀ e ∈ (SAT.incidenceGraph φ).2, e.1 ≠ e.2 := + have hloop : ∀ e ∈ (SAT.incidenceGraph φ).edges, e.1 ≠ e.2 := fun _ => SAT.incidenceGraph_loopless have hdeg := SAT.incidenceGraph_degree_le hw hv by_cases hp : p ∈ (SAT.incidenceGraph φ).orthogonalPositions @@ -217,7 +217,7 @@ theorem pathPorts_nodup {φ : CNF ℕ} ((paths φ).flatMap fun r => r.consecutivePairs.flatMap fun e => [(e.1, side e.1 e.2), (e.2, side e.2 e.1)]).Nodup := by have hi_edge {i : ℕ} (hi : i < (paths φ).length) : - i < (SAT.incidenceGraph φ).2.length := by + i < (SAT.incidenceGraph φ).edges.length := by simpa only [paths_eq_unitPaths, Graph.unitPaths_length] using hi have hn {i : ℕ} (hi : i < (paths φ).length) : ((paths φ)[i]).Nodup := by have h := Graph.unitPath_nodup (SAT.incidenceGraph φ) @@ -272,7 +272,7 @@ theorem pathMidpoints_nodup {φ : CNF ℕ} have hu {e : Point × Point} (he : e ∈ steps) : Orthogonal.length e.1 e.2 = 1 := by obtain ⟨r, hr, he⟩ := List.mem_flatMap.mp he obtain ⟨i, hi, rfl⟩ := List.mem_iff_getElem.mp hr - have hi' : i < (SAT.incidenceGraph φ).2.length := by + have hi' : i < (SAT.incidenceGraph φ).edges.length := by simpa only [paths_eq_unitPaths, Graph.unitPaths_length] using hi apply Graph.unitPath_unit (SAT.incidenceGraph φ) (fun _ => SAT.incidenceGraph_endpoints) (fun _ => SAT.incidenceGraph_loopless) hi' diff --git a/CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PathPolyTime.lean b/CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PathPolyTime.lean index 5f58701fb3..f1615e08d8 100644 --- a/CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PathPolyTime.lean +++ b/CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PathPolyTime.lean @@ -78,7 +78,7 @@ private theorem computable_incidencePort : ((ComputableInPolyTime.unary_mod.comp (ComputableInPolyTime.id'.prod (.const 3))).of_enc_eq fun _ => rfl) have hiv : ComputableInPolyTime Graph.incidenceVertices encGraph (encList encNat) := by - unfold Graph.incidenceVertices encGraph + unfold Graph.incidenceVertices fun_prop have htake : ComputableInPolyTime (fun q : List ℕ × ℕ => q.1.take q.2) (encPair (encList encNat) encUnary) (encList encNat) := by fun_prop @@ -92,7 +92,8 @@ attribute [local fun_prop] computable_incidencePort private theorem computable_canonicalPorts : ComputableInPolyTime Graph.canonicalPorts encGraph (encList (encPair (encFinEnum (Fin 3)) (encFinEnum (Fin 3)))) := by - have hlen : ComputableInPolyTime (fun G : Graph => G.2.length) encGraph encUnary := by fun_prop + have hlen : ComputableInPolyTime (fun G : Graph => G.edges.length) encGraph encUnary := by + fun_prop unfold Graph.canonicalPorts exact ComputableInPolyTime.mapWithInput (ComputableInPolyTime.range.comp hlen) (by fun_prop) @@ -146,17 +147,17 @@ private theorem computable_orthogonalRoutes : p.1.canonicalPorts.getD p.2.2 (0, 0)) (encPair encGraph edgeEncoding) (encPair (encFinEnum (Fin 3)) (encFinEnum (Fin 3))) := by fun_prop - have hvlen : ComputableInPolyTime (fun p : Graph × ((ℕ × ℕ) × ℕ) => p.1.1.length) + have hvlen : ComputableInPolyTime (fun p : Graph × ((ℕ × ℕ) × ℕ) => p.1.vertices.length) (encPair encGraph edgeEncoding) encUnary := by fun_prop have hargs : ComputableInPolyTime (fun p : Graph × ((ℕ × ℕ) × ℕ) => - (p.1.1.length, p.2.2, p.1.1.idxOf p.2.1.1, p.1.1.idxOf p.2.1.2, + (p.1.vertices.length, p.2.2, p.1.vertices.idxOf p.2.1.1, p.1.vertices.idxOf p.2.1.2, (p.1.canonicalPorts.getD p.2.2 (0, 0)).1, (p.1.canonicalPorts.getD p.2.2 (0, 0)).2)) (encPair encGraph edgeEncoding) routeInput := by fun_prop have hbody := computable_layoutRoute.comp hargs have hzip : ComputableInPolyTime (List.zipIdx (α := ℕ × ℕ)) (encList (encPair encNat encNat)) (encList edgeEncoding) := by fun_prop - have hlist : ComputableInPolyTime (fun G : Graph => G.2.zipIdx) encGraph + have hlist : ComputableInPolyTime (fun G : Graph => G.edges.zipIdx) encGraph (encList edgeEncoding) := by fun_prop simpa only [Graph.orthogonalRoutes, Function.comp_def] using ComputableInPolyTime.mapWithInput hlist hbody @@ -167,9 +168,9 @@ attribute [local fun_prop] computable_orthogonalRoutes @[fun_prop] theorem computableInPolyTime_budget : ComputableInPolyTime budget encCNF encUnary := by have hg := SAT.computableInPolyTime_incidenceGraph encNat - have hl1 : ComputableInPolyTime (fun φ : CNF ℕ => (SAT.incidenceGraph φ).1.length) + have hl1 : ComputableInPolyTime (fun φ : CNF ℕ => (SAT.incidenceGraph φ).vertices.length) encCNF encUnary := by fun_prop - have hl2 : ComputableInPolyTime (fun φ : CNF ℕ => (SAT.incidenceGraph φ).2.length) + have hl2 : ComputableInPolyTime (fun φ : CNF ℕ => (SAT.incidenceGraph φ).edges.length) encCNF encUnary := by fun_prop unfold budget fun_prop @@ -189,9 +190,9 @@ theorem computableInPolyTime_paths : simpa only [Function.comp_def, encCNF] using ComputableInPolyTime.mapWithInput hraw hsub private def endpointColumns (p : Graph × ℕ) : Point := - (Layout.column (p.1.1.idxOf (p.1.edgeEndpoint p.2 false)) + (Layout.column (p.1.vertices.idxOf (p.1.edgeEndpoint p.2 false)) (Graph.portAt p.1.canonicalPorts p.2 false), - Layout.column (p.1.1.idxOf (p.1.edgeEndpoint p.2 true)) + Layout.column (p.1.vertices.idxOf (p.1.edgeEndpoint p.2 true)) (Graph.portAt p.1.canonicalPorts p.2 true)) private theorem computable_endpointColumns : @@ -200,17 +201,17 @@ private theorem computable_endpointColumns : have hp : ComputableInPolyTime (fun p : Graph × ℕ => p.1.canonicalPorts.getD p.2 (0, 0)) (encPair encGraph encUnary) (encPair (encFinEnum (Fin 3)) (encFinEnum (Fin 3))) := by fun_prop - have he : ComputableInPolyTime (fun p : Graph × ℕ => p.1.2.getD p.2 (0, 0)) + have he : ComputableInPolyTime (fun p : Graph × ℕ => p.1.edges.getD p.2 (0, 0)) (encPair encGraph encUnary) (encPair encNat encNat) := by fun_prop - unfold Graph.edgeEndpoint Graph.portAt encPoint encGraph at * + unfold Graph.edgeEndpoint Graph.portAt encPoint at * simp only [Bool.false_eq_true, ↓reduceIte] fun_prop attribute [local fun_prop] computable_endpointColumns private def candidatePair (p : Graph × (ℕ × ℕ)) : List Point := - [((endpointColumns (p.1, p.2.2)).1, Layout.track p.1.1.length p.2.1), - ((endpointColumns (p.1, p.2.2)).2, Layout.track p.1.1.length p.2.1)] + [((endpointColumns (p.1, p.2.2)).1, Layout.track p.1.vertices.length p.2.1), + ((endpointColumns (p.1, p.2.2)).2, Layout.track p.1.vertices.length p.2.1)] private theorem computable_candidatePair : ComputableInPolyTime candidatePair (encPair encGraph (encPair encUnary encUnary)) @@ -221,23 +222,23 @@ private theorem computable_candidatePair : have hcols := computable_endpointColumns.comp hselect have hleft := hcols.fst' have hright := hcols.snd' - have hvlen : ComputableInPolyTime (fun p : Graph × (ℕ × ℕ) => p.1.1.length) + have hvlen : ComputableInPolyTime (fun p : Graph × (ℕ × ℕ) => p.1.vertices.length) (encPair encGraph (encPair encUnary encUnary)) encUnary := by fun_prop have htrack : ComputableInPolyTime (fun p : Graph × (ℕ × ℕ) => - Layout.track p.1.1.length p.2.1) + Layout.track p.1.vertices.length p.2.1) (encPair encGraph (encPair encUnary encUnary)) encIntBin := by fun_prop unfold candidatePair encPoint at * fun_prop private def candidateRow (p : Graph × ℕ) : List Point := - (List.range p.1.2.length).flatMap fun j => candidatePair (p.1, p.2, j) + (List.range p.1.edges.length).flatMap fun j => candidatePair (p.1, p.2, j) private theorem computable_edgeRange : - ComputableInPolyTime (fun G : Graph => List.range G.2.length) encGraph + ComputableInPolyTime (fun G : Graph => List.range G.edges.length) encGraph (encList encUnary) := ComputableInPolyTime.range.comp ((ComputableInPolyTime.length_list (encPair encNat encNat)).comp - (by unfold encGraph; fun_prop)) + (by fun_prop)) private theorem computable_candidateRow : ComputableInPolyTime candidateRow (encPair encGraph encUnary) (encList encPoint) := by @@ -247,7 +248,7 @@ private theorem computable_candidateRow : (encPair encGraph (encPair encUnary encUnary)) := by fun_prop have hbody := computable_candidatePair.comp harg change ComputableInPolyTime (fun p : Graph × ℕ => - (List.range p.1.2.length).flatMap fun j => candidatePair (p.1, p.2, j)) _ _ + (List.range p.1.edges.length).flatMap fun j => candidatePair (p.1, p.2, j)) _ _ simpa only [Function.comp_def] using ComputableInPolyTime.flatMapWithInput hlist hbody private theorem computable_crossingCandidates : @@ -265,7 +266,7 @@ private theorem computable_orthogonalPositions : unfold Graph.orthogonalPositions encRoute exact (ComputableInPolyTime.map computable_layoutCenter).comp (ComputableInPolyTime.range.comp - ((ComputableInPolyTime.length_list encNat).comp (by unfold encGraph; fun_prop))) + ((ComputableInPolyTime.length_list encNat).comp (by fun_prop))) attribute [local fun_prop] computable_orthogonalPositions diff --git a/CslibFrontier/Computability/Complexity/Problems/TSP.lean b/CslibFrontier/Computability/Complexity/Problems/TSP.lean index 71214d3912..d29ce36e7b 100644 --- a/CslibFrontier/Computability/Complexity/Problems/TSP.lean +++ b/CslibFrontier/Computability/Complexity/Problems/TSP.lean @@ -308,9 +308,10 @@ distance matrix has a closed tour of length at most the number of cities. -/ theorem toTSP_mem_iff (G : Graph) : toTSP G ∈ TSP ↔ G ∈ HamCycle := by rw [mem_HamCycle_iff] have hVnd := G.nodup_distinctVertices - have hmemV : ∀ v, v ∈ G.distinctVertices ↔ v ∈ G.1 := fun _ => Graph.mem_distinctVertices + have hmemV : ∀ v, v ∈ G.distinctVertices ↔ v ∈ G.vertices := fun _ => Graph.mem_distinctVertices have hlenV : - ∀ q : List ℕ, q.Nodup → (∀ v, v ∈ q ↔ v ∈ G.1) → q.length = G.distinctVertices.length := + ∀ q : List ℕ, q.Nodup → (∀ v, v ∈ q ↔ v ∈ G.vertices) → + q.length = G.distinctVertices.length := fun q hq hcov => ((List.perm_ext_iff_of_nodup hq hVnd).2 fun v => (hcov v).trans (hmemV v).symm).length_eq unfold toTSP diff --git a/CslibFrontier/Computability/Complexity/Problems/VertexCover.lean b/CslibFrontier/Computability/Complexity/Problems/VertexCover.lean index ee4f311d07..2b676b12cf 100644 --- a/CslibFrontier/Computability/Complexity/Problems/VertexCover.lean +++ b/CslibFrontier/Computability/Complexity/Problems/VertexCover.lean @@ -85,13 +85,14 @@ open Turing.MultiTapeTM ComputableInPolyTime /-- The graphs with a vertex cover of `k` of their vertices: `k` declared vertices meeting every edge between two declared vertices [Karp1972]. -/ def VertexCover : Set (Graph × ℕ) := - {x | ∃ S : Finset {v // v ∈ x.1.1}, x.1.induced.IsVertexCover S ∧ S.card = x.2} + {x | ∃ S : Finset {v // v ∈ x.1.vertices}, x.1.induced.IsVertexCover S ∧ S.card = x.2} /-- Membership in `VertexCover` as a finite set of `k` declared vertices meeting every edge between declared vertices. -/ theorem mem_VertexCover_iff {G : Graph} {k : ℕ} : - (G, k) ∈ VertexCover ↔ ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.1) ∧ - (∀ u ∈ G.1, ∀ v ∈ G.1, G.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) ∧ S.card = k := by + (G, k) ∈ VertexCover ↔ ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.vertices) ∧ + (∀ u ∈ G.vertices, ∀ v ∈ G.vertices, G.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) ∧ + S.card = k := by constructor · rintro ⟨S, hS, hcard⟩ refine ⟨S.map (.subtype _), @@ -100,17 +101,19 @@ theorem mem_VertexCover_iff {G : Graph} {k : ℕ} : (@hS ⟨u, hu⟩ ⟨v, hv⟩ huv).imp (Finset.mem_map_of_mem _) (Finset.mem_map_of_mem _), (Finset.card_map _).trans hcard⟩ · rintro ⟨S, hSV, hS, rfl⟩ - have hmap : (S.subtype (· ∈ G.1)).map (.subtype _) = S := + have hmap : (S.subtype (· ∈ G.vertices)).map (.subtype _) = S := (Finset.subtype_map _).trans (Finset.filter_eq_self.mpr hSV) - refine ⟨S.subtype (· ∈ G.1), fun u v huv => ?_, + refine ⟨S.subtype (· ∈ G.vertices), fun u v huv => ?_, by rw [← Finset.card_map (.subtype _), hmap]⟩ simpa [Finset.mem_subtype] using hS u.1 u.2 v.1 v.2 huv /-- `VertexCover` through covers of at most `k` vertices: `(G, k) ∈ VertexCover` exactly when `k` is at most the number of distinct declared vertices and some cover has at most `k` vertices. -/ theorem mem_VertexCover_iff_exists_card_le {G : Graph} {k : ℕ} : - (G, k) ∈ VertexCover ↔ k ≤ G.1.toFinset.card ∧ ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.1) ∧ - (∀ u ∈ G.1, ∀ v ∈ G.1, G.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) ∧ S.card ≤ k := by + (G, k) ∈ VertexCover ↔ k ≤ G.vertices.toFinset.card ∧ ∃ S : Finset ℕ, + (∀ v ∈ S, v ∈ G.vertices) ∧ + (∀ u ∈ G.vertices, ∀ v ∈ G.vertices, G.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) ∧ + S.card ≤ k := by rw [mem_VertexCover_iff] constructor · rintro ⟨S, hSV, hS, rfl⟩ @@ -129,7 +132,8 @@ namespace IndependentSet `m` by `n - m`, where `n` is the number of distinct declared vertices, or by `n + 1` when `m > n`. -/ def toVertexCover (x : Graph × ℕ) : Graph × ℕ := - (x.1, if x.2 ≤ x.1.1.toFinset.card then x.1.1.toFinset.card - x.2 else x.1.1.toFinset.card + 1) + (x.1, if x.2 ≤ x.1.vertices.toFinset.card then x.1.vertices.toFinset.card - x.2 + else x.1.vertices.toFinset.card + 1) /-- **Independent sets are complements of vertex covers.** -/ theorem toVertexCover_mem_iff (x : Graph × ℕ) : @@ -137,12 +141,12 @@ theorem toVertexCover_mem_iff (x : Graph × ℕ) : classical obtain ⟨G, m⟩ := x simp only [toVertexCover, mem_VertexCover_iff, mem_IndependentSet_iff] - have hsub : ∀ {S : Finset ℕ}, (∀ v ∈ S, v ∈ G.1) → S ⊆ G.1.toFinset := + have hsub : ∀ {S : Finset ℕ}, (∀ v ∈ S, v ∈ G.vertices) → S ⊆ G.vertices.toFinset := fun hS v hv => List.mem_toFinset.2 (hS v hv) split_ifs with hm · constructor · rintro ⟨T, hTV, hT, hcard⟩ - refine ⟨G.1.toFinset \ T, fun v hv => List.mem_toFinset.1 (Finset.mem_sdiff.1 hv).1, + refine ⟨G.vertices.toFinset \ T, fun v hv => List.mem_toFinset.1 (Finset.mem_sdiff.1 hv).1, ⟨fun u hu v hv huv hadj => ?_, ?_⟩⟩ · simp only [Finset.coe_sdiff, Set.mem_sdiff, Finset.mem_coe, List.mem_toFinset] at hu hv rcases hT u hu.1 v hv.1 hadj with h | h @@ -151,7 +155,7 @@ theorem toVertexCover_mem_iff (x : Graph × ℕ) : · rw [Finset.card_sdiff_of_subset (hsub hTV), hcard] omega · rintro ⟨S, hSV, hS, hcard⟩ - refine ⟨G.1.toFinset \ S, fun v hv => List.mem_toFinset.1 (Finset.mem_sdiff.1 hv).1, + refine ⟨G.vertices.toFinset \ S, fun v hv => List.mem_toFinset.1 (Finset.mem_sdiff.1 hv).1, fun u hu v hv hadj => ?_, ?_⟩ · by_contra h simp only [Finset.mem_sdiff, List.mem_toFinset, not_or, not_and, not_not] at h @@ -170,22 +174,22 @@ the binary target is compared with it by saturating decoding. -/ theorem computableInPolyTime_toVertexCover : ComputableInPolyTime toVertexCover (encPair encGraph encNat) (encPair encGraph encNat) := by - have hn : ComputableInPolyTime (fun x : Graph × ℕ => x.1.1.toFinset.card) + have hn : ComputableInPolyTime (fun x : Graph × ℕ => x.1.vertices.toFinset.card) (encPair encGraph encNat) encUnary := by simp_rw [← Graph.length_distinctVertices] exact (length_list encNat).comp (Graph.computableInPolyTime_distinctVertices.comp (fst _ _)) -- `min n m`, decoded from the binary target under the unary cap `n`. have hmin : ComputableInPolyTime - (fun x : Graph × ℕ => Cslib.BitString.boundedBinaryValue x.1.1.toFinset.card + (fun x : Graph × ℕ => Cslib.BitString.boundedBinaryValue x.1.vertices.toFinset.card (encNat x.2).reverse) (encPair encGraph encNat) encUnary := bitsBoundedBinaryValueWithInput hn (bitsReverse.comp (encoding_comp (snd _ _))) have heq : toVertexCover = fun x : Graph × ℕ => - (x.1, if decide (Cslib.BitString.boundedBinaryValue x.1.1.toFinset.card + (x.1, if decide (Cslib.BitString.boundedBinaryValue x.1.vertices.toFinset.card (encNat x.2).reverse = x.2) - then x.1.1.toFinset.card - - Cslib.BitString.boundedBinaryValue x.1.1.toFinset.card (encNat x.2).reverse - else x.1.1.toFinset.card + 1) := by + then x.1.vertices.toFinset.card - + Cslib.BitString.boundedBinaryValue x.1.vertices.toFinset.card (encNat x.2).reverse + else x.1.vertices.toFinset.card + 1) := by funext x simp only [toVertexCover, Bool.decide_iff, encNat_apply, Cslib.BitString.boundedBinaryValue_eq, Cslib.BitString.binaryValue_reverse_bits, min_eq_right_iff] @@ -203,15 +207,15 @@ namespace VertexCover /-- Whether both endpoints of `e` are declared vertices of `G`. -/ def isDeclaredEdge (G : Graph) (e : ℕ × ℕ) : Bool := - decide (e.1 ∈ G.1) && decide (e.2 ∈ G.1) + decide (e.1 ∈ G.vertices) && decide (e.2 ∈ G.vertices) /-- The listed edges of `G` between declared vertices. -/ def declaredEdges (G : Graph) : List (ℕ × ℕ) := - G.2.filter (isDeclaredEdge G) + G.edges.filter (isDeclaredEdge G) /-- An edge is declared when it is listed and both endpoints are declared vertices. -/ theorem mem_declaredEdges {G : Graph} {e : ℕ × ℕ} : - e ∈ declaredEdges G ↔ e ∈ G.2 ∧ e.1 ∈ G.1 ∧ e.2 ∈ G.1 := by + e ∈ declaredEdges G ↔ e ∈ G.edges ∧ e.1 ∈ G.vertices ∧ e.2 ∈ G.vertices := by simp [declaredEdges, isDeclaredEdge] /-- Testing whether an edge joins declared vertices is polynomial-time. -/ @@ -226,21 +230,20 @@ private theorem computableInPolyTime_isDeclaredEdge : @[fun_prop] theorem computableInPolyTime_declaredEdges : ComputableInPolyTime declaredEdges encGraph (encList (encPair encNat encNat)) := by - have hE : ComputableInPolyTime (fun G : Graph => G.2) encGraph + have hE : ComputableInPolyTime (fun G : Graph => G.edges) encGraph (encList (encPair encNat encNat)) := by - unfold encGraph fun_prop exact filterWithInput hE computableInPolyTime_isDeclaredEdge /-- Whether the edge `e` joins two distinct declared vertices of `G` and neither endpoint is in `C`. -/ def isUncoveredEdge (G : Graph) (C : List ℕ) (e : ℕ × ℕ) : Bool := - decide (e.1 ∈ G.1) && decide (e.2 ∈ G.1) && + decide (e.1 ∈ G.vertices) && decide (e.2 ∈ G.vertices) && decide (e.1 ≠ e.2) && !decide (e.1 ∈ C) && !decide (e.2 ∈ C) /-- The edges of `G` between distinct declared vertices with neither endpoint in `C`. -/ def uncoveredEdges (G : Graph) (C : List ℕ) : List (ℕ × ℕ) := - G.2.filter (isUncoveredEdge G C) + G.edges.filter (isUncoveredEdge G C) /-- Whether `C` covers every edge of `G` between declared vertices. -/ def isCover (G : Graph) (C : List ℕ) : Bool := @@ -248,19 +251,19 @@ def isCover (G : Graph) (C : List ℕ) : Bool := theorem isUncoveredEdge_eq_true_iff {G : Graph} {C : List ℕ} {e : ℕ × ℕ} : isUncoveredEdge G C e = true ↔ - e.1 ∈ G.1 ∧ e.2 ∈ G.1 ∧ e.1 ≠ e.2 ∧ e.1 ∉ C ∧ e.2 ∉ C := by + e.1 ∈ G.vertices ∧ e.2 ∈ G.vertices ∧ e.1 ≠ e.2 ∧ e.1 ∉ C ∧ e.2 ∉ C := by simp [isUncoveredEdge, and_assoc] theorem mem_uncoveredEdges_iff {G : Graph} {C : List ℕ} {e : ℕ × ℕ} : e ∈ uncoveredEdges G C ↔ - e ∈ G.2 ∧ e.1 ∈ G.1 ∧ e.2 ∈ G.1 ∧ e.1 ≠ e.2 ∧ e.1 ∉ C ∧ e.2 ∉ C := by + e ∈ G.edges ∧ e.1 ∈ G.vertices ∧ e.2 ∈ G.vertices ∧ e.1 ≠ e.2 ∧ e.1 ∉ C ∧ e.2 ∉ C := by rw [uncoveredEdges, List.mem_filter, isUncoveredEdge_eq_true_iff] /-- `isCover G C` holds exactly when every edge between declared vertices of `G` has an endpoint in `C`. -/ theorem isCover_eq_true_iff {G : Graph} {C : List ℕ} : isCover G C = true ↔ - ∀ u ∈ G.1, ∀ v ∈ G.1, G.toSimpleGraph.Adj u v → u ∈ C ∨ v ∈ C := by + ∀ u ∈ G.vertices, ∀ v ∈ G.vertices, G.toSimpleGraph.Adj u v → u ∈ C ∨ v ∈ C := by simp only [isCover, uncoveredEdges, List.isEmpty_iff, List.filter_eq_nil_iff, isUncoveredEdge_eq_true_iff, Graph.toSimpleGraph_adj, not_and, not_not, Prod.forall] constructor @@ -280,7 +283,7 @@ private theorem computableInPolyTime_isUncoveredEdge : ComputableInPolyTime (fun p : (Graph × List ℕ) × (ℕ × ℕ) => isUncoveredEdge p.1.1 p.1.2 p.2) (encPair (encPair encGraph (encList encNat)) (encPair encNat encNat)) encBool := by - unfold isUncoveredEdge encGraph + unfold isUncoveredEdge fun_prop /-- Listing the uncovered edges is polynomial-time. -/ @@ -288,9 +291,8 @@ private theorem computableInPolyTime_isUncoveredEdge : theorem computableInPolyTime_uncoveredEdges : ComputableInPolyTime (fun p : Graph × List ℕ => uncoveredEdges p.1 p.2) (encPair encGraph (encList encNat)) (encList (encPair encNat encNat)) := by - have hE : ComputableInPolyTime (fun p : Graph × List ℕ => p.1.2) + have hE : ComputableInPolyTime (fun p : Graph × List ℕ => p.1.edges) (encPair encGraph (encList encNat)) (encList (encPair encNat encNat)) := by - unfold encGraph fun_prop exact filterWithInput hE computableInPolyTime_isUncoveredEdge @@ -306,14 +308,15 @@ theorem computableInPolyTime_isCover : distinct declared vertices by the certificate mask, check that their count equals `k`, and check that every edge between declared vertices has a selected endpoint (`isCover`). -/ def verifier : (Graph × ℕ) × List Bool → Bool := - subsetVerifier (fun x => x.1.1) fun y => decide (y.2.length = y.1.2) && isCover y.1.1 y.2 + subsetVerifier (fun x => x.1.vertices) fun y => decide (y.2.length = y.1.2) && isCover y.1.1 y.2 /-- `VertexCover.verifier` verifies `VertexCover` with certificates as long as the encoded instance. -/ theorem isVerifier : IsVerifier (encPair encGraph encNat) Polynomial.X verifier VertexCover := by - have hset : VertexCover = {x | ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ x.1.1) ∧ - (∀ u ∈ x.1.1, ∀ v ∈ x.1.1, x.1.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) ∧ S.card = x.2} := + have hset : VertexCover = {x | ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ x.1.vertices) ∧ + (∀ u ∈ x.1.vertices, ∀ v ∈ x.1.vertices, x.1.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) ∧ + S.card = x.2} := Set.ext fun _ => mem_VertexCover_iff rw [hset] refine isVerifier_subsetVerifier @@ -330,7 +333,7 @@ theorem isVerifier : theorem computableInPolyTime_verifier : ComputableInPolyTime verifier (encPair (encPair encGraph encNat) encBits) encBool := by refine computableInPolyTime_subsetVerifier (encγ := encNat) - (by unfold encGraph; fun_prop) + (by fun_prop) (bool_and' ?_ (computableInPolyTime_isCover.comp (((fst _ _).comp (fst _ _)).prod (snd _ _)))) exact ((length_list_binary encNat).comp (snd _ _)).decide_eq' (by fun_prop) @@ -384,16 +387,17 @@ theorem vertexCoverNum_le_of_mem_VertexCover {G : Graph} {k : ℕ} (h : (G, k) /-- **A vertex cover is the complement of an independent set**: the vertex cover number is the number of distinct declared vertices minus the independence number. -/ theorem vertexCoverNum_eq_card_sub_indepNum (G : Graph) : - G.vertexCoverNum = G.1.toFinset.card - G.indepNum := by + G.vertexCoverNum = G.vertices.toFinset.card - G.indepNum := by have hα := indepNum_mem_IndependentSet G have hαn := indepNum_le_card G - have hvc : (G, G.1.toFinset.card - G.indepNum) ∈ VertexCover := by + have hvc : (G, G.vertices.toFinset.card - G.indepNum) ∈ VertexCover := by have := (IndependentSet.toVertexCover_mem_iff (G, G.indepNum)).2 hα rwa [IndependentSet.toVertexCover, ite_eq_left hαn] at this refine le_antisymm (vertexCoverNum_le_of_mem_VertexCover hvc) ?_ have hτ : (G, G.vertexCoverNum) ∈ VertexCover := vertexCoverNum_mem_VertexCover G - have hτn : G.vertexCoverNum ≤ G.1.toFinset.card := (mem_VertexCover_iff_exists_card_le.1 hτ).1 - have hIS : (G, G.1.toFinset.card - G.vertexCoverNum) ∈ IndependentSet := by + have hτn : G.vertexCoverNum ≤ G.vertices.toFinset.card := + (mem_VertexCover_iff_exists_card_le.1 hτ).1 + have hIS : (G, G.vertices.toFinset.card - G.vertexCoverNum) ∈ IndependentSet := by refine (IndependentSet.toVertexCover_mem_iff _).1 ?_ rwa [IndependentSet.toVertexCover, ite_eq_left (Nat.sub_le _ _), Nat.sub_sub_self hτn] have := le_indepNum_iff.2 hIS diff --git a/CslibFrontier/Computability/Complexity/Problems/VertexCover/Approximation.lean b/CslibFrontier/Computability/Complexity/Problems/VertexCover/Approximation.lean index 85bb453d21..922d9044a5 100644 --- a/CslibFrontier/Computability/Complexity/Problems/VertexCover/Approximation.lean +++ b/CslibFrontier/Computability/Complexity/Problems/VertexCover/Approximation.lean @@ -66,13 +66,14 @@ theorem nodup_approxCover (G : Graph) : (approxCover G).Nodup := /-- The returned vertices are declared. -/ theorem mem_vertices_of_mem_approxCover {G : Graph} {v : ℕ} (hv : v ∈ approxCover G) : - v ∈ G.1 := by + v ∈ G.vertices := by obtain ⟨e, he, rfl | rfl⟩ := mem_matchingCover hv · exact (mem_declaredEdges.1 he).2.1 · exact (mem_declaredEdges.1 he).2.2 /-- Between declared vertices, the graph of the declared edges is the graph of `G`. -/ -theorem fromRel_declaredEdges_adj {G : Graph} {u v : ℕ} (hu : u ∈ G.1) (hv : v ∈ G.1) : +theorem fromRel_declaredEdges_adj {G : Graph} {u v : ℕ} (hu : u ∈ G.vertices) + (hv : v ∈ G.vertices) : (SimpleGraph.fromRel fun a b => (a, b) ∈ declaredEdges G).Adj u v ↔ G.toSimpleGraph.Adj u v := by simp only [SimpleGraph.fromRel_adj, Graph.toSimpleGraph, mem_declaredEdges] @@ -80,7 +81,8 @@ theorem fromRel_declaredEdges_adj {G : Graph} {u v : ℕ} (hu : u ∈ G.1) (hv : /-- **Feasibility.** The returned vertices meet every edge between declared vertices. -/ theorem isCover_approxCover (G : Graph) : - ∀ u ∈ G.1, ∀ v ∈ G.1, G.toSimpleGraph.Adj u v → u ∈ approxCover G ∨ v ∈ approxCover G := + ∀ u ∈ G.vertices, ∀ v ∈ G.vertices, G.toSimpleGraph.Adj u v → + u ∈ approxCover G ∨ v ∈ approxCover G := fun _ hu _ hv huv => isVertexCover_matchingCover _ ((fromRel_declaredEdges_adj hu hv).2 huv) /-- The returned list is a vertex cover of its length. -/ @@ -98,7 +100,7 @@ theorem length_approxCover_le_two_mul_vertexCoverNum (G : Graph) : obtain ⟨S, hSV, hS, hcard⟩ := mem_VertexCover_iff.1 (G.vertexCoverNum_mem_VertexCover) rw [← hcard] refine length_matchingCover_le_two_mul_card _ fun u v huv => ?_ - have hdecl : u ∈ G.1 ∧ v ∈ G.1 := by + have hdecl : u ∈ G.vertices ∧ v ∈ G.vertices := by rw [SimpleGraph.fromRel_adj] at huv rcases huv.2 with he | he · exact ⟨(mem_declaredEdges.1 he).2.1, (mem_declaredEdges.1 he).2.2⟩ @@ -121,7 +123,10 @@ private theorem length_encList_coverStep_le {G : Graph} (C : List ℕ) {e : ℕ (encList encNat (coverStep C e)).length ≤ (encList encNat C).length + (4 * (encGraph G).length + 2) := by obtain ⟨-, h1, h2⟩ := mem_declaredEdges.1 he - have hV : (encList encNat G.1).length ≤ (encGraph G).length := by grind [encGraph, length_encPair] + have hV : (encList encNat G.vertices).length ≤ (encGraph G).length := by + rw [encGraph_apply, length_encPair] + dsimp + omega have h1 := (length_le_encList_of_mem encNat h1).trans hV have h2 := (length_le_encList_of_mem encNat h2).trans hV unfold coverStep diff --git a/CslibFrontier/Computability/PCP/Approximation/KCenter.lean b/CslibFrontier/Computability/PCP/Approximation/KCenter.lean index bf4d8b02d2..4bdf976648 100644 --- a/CslibFrontier/Computability/PCP/Approximation/KCenter.lean +++ b/CslibFrontier/Computability/PCP/Approximation/KCenter.lean @@ -289,7 +289,7 @@ theorem decideDominatingSet_iff {A : List (List ℕ) × ℕ → List ℕ} {ρ : (hfinj.injOn) have hTdom : (indexGraph G).IsDominatingSet T := by refine SimpleGraph.isDominatingSet_iff.2 fun i => ?_ - have hfiV : f i ∈ G.1 := hfe i ▸ (e i).2 + have hfiV : f i ∈ G.vertices := hfe i ▸ (e i).2 rcases hS (f i) hfiV with h | ⟨s, hs, hadj⟩ · exact Or.inl (by simpa [T] using h) · obtain ⟨j, rfl⟩ := hsurj s (Graph.mem_distinctVertices.2 (hSV s hs)) diff --git a/CslibFrontier/Computability/PolyTime/Graph/RotationSystem.lean b/CslibFrontier/Computability/PolyTime/Graph/RotationSystem.lean index e88e4a89b4..fefd88da6d 100644 --- a/CslibFrontier/Computability/PolyTime/Graph/RotationSystem.lean +++ b/CslibFrontier/Computability/PolyTime/Graph/RotationSystem.lean @@ -154,9 +154,8 @@ encodings, without an assumed checker, recognizer, or well-formedness hypothesis encGraphRotationCertificate encGraph := by fun_prop have hc : ComputableInPolyTime (fun x : Graph × RotationCertificate => x.2.successor) encGraphRotationCertificate (encList encNat) := hsuccessor.comp (by fun_prop) - have hn : ComputableInPolyTime (fun x : Graph × RotationCertificate => 2 * x.1.2.length) + have hn : ComputableInPolyTime (fun x : Graph × RotationCertificate => 2 * x.1.edges.length) encGraphRotationCertificate encUnary := by - unfold encGraphRotationCertificate encGraph fun_prop apply ComputableInPolyTime.bool_and' · apply ComputableInPolyTime.bool_and' @@ -179,15 +178,14 @@ one copy of the declared vertex list per iteration, including on malformed graph theorem computableInPolyTime_componentVertices : ComputableInPolyTime (fun p : Graph × ℕ => componentVertices p.1 p.2) (encPair encGraph encNat) (encList encNat) := by - have hv : ComputableTidilyInPolyTime (fun p : Graph × ℕ => p.1.1) - (encPair encGraph encNat) (encList encNat) := by unfold encGraph; fun_prop - apply ComputableInPolyTime.of_tidy + have hv : ComputableInPolyTime (fun p : Graph × ℕ => p.1.vertices) + (encPair encGraph encNat) (encList encNat) := by fun_prop unfold componentVertices - refine ComputableTidilyInPolyTime.iterateWith_of_bounded_growth - (par := fun p : Graph × ℕ => p.1) (count := fun p => p.1.1.length) + refine ComputableInPolyTime.iterateWith_of_bounded_growth + (par := fun p : Graph × ℕ => p.1) (count := fun p => p.1.vertices.length) (init := fun p => [p.2]) (step := componentStep) - (by fun_prop) (by unfold encGraph; fun_prop) (by fun_prop) - componentStep_polyTime.tidy hv.polyBounded_length ?_ + (by fun_prop) (by fun_prop) (by fun_prop) + componentStep_polyTime hv.polyBounded_length ?_ intro p state simp only [componentStep, encList_append, List.length_append] apply Nat.add_le_add_left @@ -257,30 +255,40 @@ private theorem vertexCount_polyTime : private theorem edgeCount_polyTime : ComputableInPolyTime (fun p : Graph × ℕ => - p.1.2.countP (fun e => componentBool p.1 p.2 e.1)) + p.1.edges.countP (fun e => componentBool p.1 p.2 e.1)) (encPair encGraph encNat) encUnary := by apply ComputableInPolyTime.countPWithInput (encA := encPair encNat encNat) - (by unfold encGraph; fun_prop) + (by fun_prop) fun_prop private theorem faceCount_polyTime : ComputableInPolyTime (fun p : (Graph × RotationCertificate) × ℕ => - (List.range (2 * p.1.1.2.length)).countP fun i => + (List.range (2 * p.1.1.edges.length)).countP fun i => componentBool p.1.1 p.2 (p.1.1.incidenceVertices.getD i 0) && - faceRepBool (2 * p.1.1.2.length) (faceTable p.1.1 p.1.2) i) + faceRepBool (2 * p.1.1.edges.length) (faceTable p.1.1 p.1.2) i) + (encPair encGraphRotationCertificate encNat) encUnary := by + have hG₀ : ComputableInPolyTime + (fun p : (Graph × RotationCertificate) × ℕ => p.1.1) + (encPair encGraphRotationCertificate encNat) encGraph := by fun_prop + have hlen : ComputableInPolyTime + (fun p : (Graph × RotationCertificate) × ℕ => 2 * p.1.1.edges.length) (encPair encGraphRotationCertificate encNat) encUnary := by + exact ComputableInPolyTime.unary_mul' (ComputableInPolyTime.const 2) + ((ComputableInPolyTime.of_tidy + (ComputableTidilyInPolyTime.length (encPair encNat encNat))).comp + (encGraph_edges_polyTime hG₀)) apply ComputableInPolyTime.countPWithInput (encA := encNat) - (range_binary.comp (by unfold encGraphRotationCertificate encGraph; fun_prop)) + (range_binary.comp hlen) have hG : ComputableInPolyTime (fun p : ((Graph × RotationCertificate) × ℕ) × ℕ => p.1.1.1) (encPair (encPair encGraphRotationCertificate encNat) encNat) encGraph := by fun_prop have hn : ComputableInPolyTime - (fun p : ((Graph × RotationCertificate) × ℕ) × ℕ => 2 * p.1.1.1.2.length) + (fun p : ((Graph × RotationCertificate) × ℕ) × ℕ => 2 * p.1.1.1.edges.length) (encPair (encPair encGraphRotationCertificate encNat) encNat) encUnary := by exact ComputableInPolyTime.unary_mul' (ComputableInPolyTime.const 2) ((ComputableInPolyTime.of_tidy (ComputableTidilyInPolyTime.length (encPair encNat encNat))).comp - ((ComputableInPolyTime.snd _ _).comp hG)) + (encGraph_edges_polyTime hG)) exact (componentBool_polyTime hG (by fun_prop) (getD_polyTime 0 (incidenceVertices_polyTime.comp hG) (by fun_prop))).bool_and' (faceRepBool_polyTime' hn (faceTable_polyTime.comp diff --git a/CslibFrontierTests/CSPOccurrence.lean b/CslibFrontierTests/CSPOccurrence.lean index 01cc5df34c..4dd15c068d 100644 --- a/CslibFrontierTests/CSPOccurrence.lean +++ b/CslibFrontierTests/CSPOccurrence.lean @@ -28,7 +28,7 @@ private abbrev conflict : CSP (Fin 4) ℕ := [(1, [7]), (2, [7])] -- A repeated variable gives parallel edge records with distinct occurrence identities. example : repeated.occurrences = [(0, 0, 7), (0, 1, 7)] := by decide -example : CSP.incidenceGraph repeated = ([0, 1], [(0, 1), (0, 1)]) := by decide +example : CSP.incidenceGraph repeated = ⟨[0, 1], [(0, 1), (0, 1)]⟩ := by decide private abbrev first : repeated.Occurrence := ⟨⟨0, by decide⟩, ⟨0, by decide⟩⟩ private abbrev second : repeated.Occurrence := ⟨⟨0, by decide⟩, ⟨1, by decide⟩⟩ @@ -40,7 +40,7 @@ example : (CSP.incidenceGraph repeated).edgeEndpoint (CSP.incidenceEdgeIndex sec CSP.edgeEndpoint_incidenceEdgeIndex second true -- Identical constraints retain different vertices, even with the same relation and scope. -example : CSP.incidenceGraph identical = ([0, 1, 2], [(0, 1), (0, 2)]) := by decide +example : CSP.incidenceGraph identical = ⟨[0, 1, 2], [(0, 1), (0, 2)]⟩ := by decide example : identical.occurrences = [(0, 0, 7), (1, 0, 7)] := by decide example : identical.EdgeInstance := by unfold CSP.EdgeInstance @@ -56,7 +56,7 @@ example : ¬ CSP.EdgeInstance ([(0, [7]), (0, [7]), (0, [7])] : CSP (Fin 4) ℕ) decide -- Nullary constraints survive as isolated constraint vertices and still affect semantics. -example : CSP.incidenceGraph ([(0, [])] : CSP (Fin 4) ℕ) = ([0], []) := by decide +example : CSP.incidenceGraph ([(0, [])] : CSP (Fin 4) ℕ) = ⟨[0], []⟩ := by decide example : CSP.EdgeInstance ([(0, [])] : CSP (Fin 4) ℕ) := by unfold CSP.EdgeInstance decide @@ -71,7 +71,7 @@ example : ¬ ∃ l : CSP.OccurrenceLabeling ([(3, [])] : CSP (Fin 4) ℕ) Bool, cases hσ -- Empty instances have no occurrences; a global assignment uses the explicit outside value. -example : CSP.incidenceGraph ([] : CSP (Fin 4) ℕ) = ([], []) := by decide +example : CSP.incidenceGraph ([] : CSP (Fin 4) ℕ) = ⟨[], []⟩ := by decide example : CSP.EdgeInstance ([] : CSP (Fin 4) ℕ) := by unfold CSP.EdgeInstance decide @@ -81,7 +81,7 @@ example : CSP.reconstruct (F := ([] : CSP (Fin 4) ℕ)) (fun o => Fin.elim0 o.1) -- Huge binary source names do not generate a huge vertex interval. example : CSP.incidenceGraph ([(0, [2 ^ 100, 3])] : CSP (Fin 4) ℕ) = - ([0, 1, 2], [(0, 2), (1, 2)]) := by decide + ⟨[0, 1, 2], [(0, 2), (1, 2)]⟩ := by decide private def disagree : repeated.OccurrenceLabeling Bool := fun o => o.2.val == 0 diff --git a/CslibFrontierTests/CSPPrimalRotation.lean b/CslibFrontierTests/CSPPrimalRotation.lean index 5f21abb7ec..850ddfaeb8 100644 --- a/CslibFrontierTests/CSPPrimalRotation.lean +++ b/CslibFrontierTests/CSPPrimalRotation.lean @@ -34,7 +34,7 @@ private theorem unary_order : RespectsArgumentOrder unaryRotation := (verifyArgumentOrder_eq_true_iff (by decide)).mp (by decide) -- Sparse binary names never create unused graph vertices or ranges up to their magnitude. -example : polygonGraph unary = ([0], [(0, 0)]) := by decide +example : polygonGraph unary = ⟨[0], [(0, 0)]⟩ := by decide example : (polygonRotation unaryRotation).faceNext (polygonDart unaryOccurrence false) = polygonDart unaryOccurrence false := by rw [faceNext_polygonDart_false] @@ -61,8 +61,8 @@ private theorem repeated_order : RespectsArgumentOrder repeatedRotation := (verifyArgumentOrder_eq_true_iff (by decide)).mp (by decide) -- Three parallel incidence positions become three different listed loops. -example : (incidenceGraph repeated).2 = [(0, 1), (0, 1), (0, 1)] := by decide -example : polygonGraph repeated = ([0], [(0, 0), (0, 0), (0, 0)]) := by decide +example : (incidenceGraph repeated).edges = [(0, 1), (0, 1), (0, 1)] := by decide +example : polygonGraph repeated = ⟨[0], [(0, 0), (0, 0), (0, 0)]⟩ := by decide example : polygonDart (F := repeated) ⟨⟨0, by decide⟩, ⟨0, by decide⟩⟩ false ≠ polygonDart (F := repeated) ⟨⟨0, by decide⟩, ⟨1, by decide⟩⟩ false := by decide example : (polygonRotation repeatedRotation).NumericalGenusZero := @@ -91,7 +91,7 @@ private noncomputable def disconnectedRotation : private theorem disconnected_order : RespectsArgumentOrder disconnectedRotation := (verifyArgumentOrder_eq_true_iff (by decide)).mp (by decide) -example : polygonGraph disconnected = ([0, 1], [(0, 0), (1, 1)]) := by decide +example : polygonGraph disconnected = ⟨[0, 1], [(0, 0), (1, 1)]⟩ := by decide example : (polygonRotation disconnectedRotation).NumericalGenusZero := (numericalGenusZero_polygon_iff disconnectedRotation disconnected_order (by decide)).mpr ((verifyEuler_eq_true_iff (by decide)).mp (by decide)) @@ -105,7 +105,7 @@ private noncomputable def emptyRotation : Graph.RotationSystem (incidenceGraph e private theorem empty_order : RespectsArgumentOrder emptyRotation := (verifyArgumentOrder_eq_true_iff (by decide)).mp (by decide) -example : polygonGraph emptyBody = ([], []) := by decide +example : polygonGraph emptyBody = ⟨[], []⟩ := by decide example : (polygonRotation emptyRotation).NumericalGenusZero := (numericalGenusZero_polygon_iff emptyRotation empty_order (by decide)).mpr ((verifyEuler_eq_true_iff (by decide)).mp (by decide)) diff --git a/CslibFrontierTests/ChromaticNumber.lean b/CslibFrontierTests/ChromaticNumber.lean index 53e158d516..3f3dac84fe 100644 --- a/CslibFrontierTests/ChromaticNumber.lean +++ b/CslibFrontierTests/ChromaticNumber.lean @@ -19,7 +19,7 @@ namespace CslibFrontierTests.ChromaticNumber open Cslib.Complexity Turing.MultiTapeTM -private def triangle : Graph := ([0, 1, 2], [(0, 1), (1, 2), (2, 0)]) +private def triangle : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2), (2, 0)]⟩ /-! ### Headline results -/ @@ -53,11 +53,11 @@ example : (⟨triangle, 2 ^ 64⟩ : ChromaticNumberInstance) ∈ ChromaticNumber exact ⟨id, by decide, fun u _ v _ huv => huv.ne⟩ -- Without colors, exactly the graphs without declared vertices are colorable. -example : (⟨(([] : List ℕ), [(0, 1)]), 0⟩ : ChromaticNumberInstance) ∈ ChromaticNumber := by +example : (⟨⟨([] : List ℕ), [(0, 1)]⟩, 0⟩ : ChromaticNumberInstance) ∈ ChromaticNumber := by rw [mem_ChromaticNumber_iff, mem_KColoring_iff] exact ⟨id, by simp, by simp⟩ -example : (⟨(([5] : List ℕ), []), 0⟩ : ChromaticNumberInstance) ∉ ChromaticNumber := by +example : (⟨⟨([5] : List ℕ), []⟩, 0⟩ : ChromaticNumberInstance) ∉ ChromaticNumber := by rw [mem_ChromaticNumber_iff, mem_KColoring_iff] rintro ⟨c, hc, -⟩ exact Nat.not_lt_zero _ (hc 5 (by simp)) @@ -71,12 +71,12 @@ example : triangle.induced.chromaticNumber ≤ 3 := /-! ### Witnesses -/ -- A color is read at the first occurrence of a vertex, with `0` past the end of the list. -example : colorOf ([5, 7, 5], []) [2, 0, 1] 5 = 2 ∧ colorOf ([5, 7, 5], []) [2, 0, 1] 7 = 0 ∧ - colorOf ([5, 7], []) [2] 7 = 0 ∧ colorOf ([5, 7], []) [2, 3] 9 = 0 := +example : colorOf ⟨[5, 7, 5], []⟩ [2, 0, 1] 5 = 2 ∧ colorOf ⟨[5, 7, 5], []⟩ [2, 0, 1] 7 = 0 ∧ + colorOf ⟨[5, 7], []⟩ [2] 7 = 0 ∧ colorOf ⟨[5, 7], []⟩ [2, 3] 9 = 0 := ⟨rfl, rfl, rfl, rfl⟩ -example : colorOf ([5, 7], []) ([5, 7].map fun v => v + 1) 7 = 8 := - colorOf_map (G := ([5, 7], [])) (by simp) +example : colorOf ⟨[5, 7], []⟩ ([5, 7].map fun v => v + 1) 7 = 8 := + colorOf_map (G := ⟨[5, 7], []⟩) (by simp) -- Compositional `fun_prop` synthesis for colors read from computed lists. example : ComputableInPolyTime (fun p : (Graph × List ℕ) × ℕ => colorOf p.1.1 p.1.2 0) diff --git a/CslibFrontierTests/CliqueCover.lean b/CslibFrontierTests/CliqueCover.lean index e5e6b3cb31..82dd645f99 100644 --- a/CslibFrontierTests/CliqueCover.lean +++ b/CslibFrontierTests/CliqueCover.lean @@ -18,9 +18,9 @@ namespace CslibFrontierTests.CliqueCover open Cslib.Complexity Turing.MultiTapeTM -private def triangle : Graph := ([0, 1, 2], [(0, 1), (1, 2), (2, 0)]) +private def triangle : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2), (2, 0)]⟩ -private def path3 : Graph := ([0, 1, 2], [(0, 1), (1, 2)]) +private def path3 : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2)]⟩ /-! ### Headline results -/ @@ -43,8 +43,8 @@ example : (⟨path3, 2⟩ : CliqueCoverInstance) ∈ CliqueCover := by rw [Graph.mem_CliqueCover_iff_compl, mem_ChromaticNumber_iff, mem_KColoring_iff] refine ⟨fun v => if v = 2 then 1 else 0, by decide, fun u hu v hv huv => ?_⟩ rw [← Graph.adjBool_eq_true_iff] at huv - have hu' : u ∈ path3.1 := hu - have hv' : v ∈ path3.1 := hv + have hu' : u ∈ path3.vertices := hu + have hv' : v ∈ path3.vertices := hv simp only [path3, List.mem_cons, List.not_mem_nil, or_false] at hu' hv' rcases hu' with rfl | rfl | rfl <;> rcases hv' with rfl | rfl | rfl <;> revert huv <;> decide @@ -63,7 +63,7 @@ example : (⟨triangle.compl, 3⟩ : CliqueCoverInstance) ∈ CliqueCover := exact ⟨id, by decide, fun u _ v _ huv => huv.ne⟩) -- A graph without declared vertices is covered by no clique at all. -example : (⟨(([] : List ℕ), [(0, 1)]), 0⟩ : CliqueCoverInstance) ∈ CliqueCover := +example : (⟨⟨([] : List ℕ), [(0, 1)]⟩, 0⟩ : CliqueCoverInstance) ∈ CliqueCover := ⟨∅, by simp, by simp, fun v => absurd v.2 (by simp)⟩ /-! ### Clique covers and colorings of the complement -/ diff --git a/CslibFrontierTests/ColorCoding.lean b/CslibFrontierTests/ColorCoding.lean index b710a25a1e..108a392138 100644 --- a/CslibFrontierTests/ColorCoding.lean +++ b/CslibFrontierTests/ColorCoding.lean @@ -21,7 +21,7 @@ namespace CslibFrontierTests.ColorCoding open Cslib.Complexity Turing.MultiTapeTM /-- The path `0 - 1 - 2 - 3`. -/ -def P4 : Graph := ([0, 1, 2, 3], [(0, 1), (1, 2), (2, 3)]) +def P4 : Graph := ⟨[0, 1, 2, 3], [(0, 1), (1, 2), (2, 3)]⟩ /-! ### k-Path -/ diff --git a/CslibFrontierTests/ContractionMatching.lean b/CslibFrontierTests/ContractionMatching.lean index b06c1cc23e..a554d791de 100644 --- a/CslibFrontierTests/ContractionMatching.lean +++ b/CslibFrontierTests/ContractionMatching.lean @@ -82,11 +82,11 @@ example : ∃ count : List (List Bool) → ℕ, count ones₃ = 6 := by /-! ### Perfect matchings -/ /-- The cycle `0 - 1 - 2 - 3 - 4 - 5 - 0`. -/ -def cycle₆ : Graph := (List.range 6, [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 0)]) +def cycle₆ : Graph := ⟨List.range 6, [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 0)]⟩ /-- The complete bipartite graph `K₃,₃` with sides `{0, 1, 2}` and `{3, 4, 5}`. -/ def k₃₃ : Graph := - (List.range 6, (List.range 3).flatMap fun i => (List.range 3).map fun j => (i, j + 3)) + ⟨List.range 6, (List.range 3).flatMap fun i => (List.range 3).map fun j => (i, j + 3)⟩ -- The cycle `C₆` has two perfect matchings. example : cycle₆.perfectMatchingInstance.partitionFunction CSP.exactlyOneWeight = 2 := by @@ -102,16 +102,16 @@ example : Nat.card {M : k₃₃.induced.Subgraph // M.IsPerfectMatching} = 6 := -- The edge list drops the loop `(1, 1)` and the edge `(2, 5)` to an undeclared vertex, and keeps -- one copy of `0 - 1` among `(0, 1)`, `(1, 0)` and `(0, 1)`. -example : Graph.edgeList ([0, 1, 2, 1], [(0, 1), (1, 0), (1, 1), (1, 2), (2, 5), (0, 1)]) = +example : Graph.edgeList ⟨[0, 1, 2, 1], [(0, 1), (1, 0), (1, 1), (1, 2), (2, 5), (0, 1)]⟩ = [(0, 1), (1, 2)] := by decide -- The path `0 - 1` with the isolated vertex `2`: the constraint of `2` has no variable, the guard -- answers the zero instance, and there is no perfect matching. -example : guarded 2 (Graph.perfectMatchingInstance ([0, 1, 2], [(0, 1)])) = zeroInstance := by +example : guarded 2 (Graph.perfectMatchingInstance ⟨[0, 1, 2], [(0, 1)]⟩) = zeroInstance := by decide -example : Nat.card {M : Graph.induced ([0, 1, 2], [(0, 1)]) |>.Subgraph // M.IsPerfectMatching} = +example : Nat.card {M : Graph.induced ⟨[0, 1, 2], [(0, 1)]⟩ |>.Subgraph // M.IsPerfectMatching} = 0 := by rw [← Graph.partitionFunction_perfectMatchingInstance] decide +kernel diff --git a/CslibFrontierTests/Copositivity.lean b/CslibFrontierTests/Copositivity.lean index 312db0ceb4..73f89743e8 100644 --- a/CslibFrontierTests/Copositivity.lean +++ b/CslibFrontierTests/Copositivity.lean @@ -340,11 +340,11 @@ example : /-! ### Reduction instances (`cliqueToSymRatMatrix`) -/ -private def triangleG : Graph := ([0, 1, 2], [(0, 1), (1, 2), (2, 0)]) +private def triangleG : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2), (2, 0)]⟩ -private def path3G : Graph := ([0, 1, 2], [(0, 1), (1, 2)]) +private def path3G : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2)]⟩ -private def emptyG : Graph := ([], []) +private def emptyG : Graph := ⟨[], []⟩ example : cliqueToSymRatMatrix (triangleG, 3) ∈ NonCopositive := by rw [← cliqueToSymRatMatrix_mem_iff, mem_Clique_iff] diff --git a/CslibFrontierTests/DominatingSet.lean b/CslibFrontierTests/DominatingSet.lean index 9c1c9be080..2c3ed25542 100644 --- a/CslibFrontierTests/DominatingSet.lean +++ b/CslibFrontierTests/DominatingSet.lean @@ -20,13 +20,13 @@ namespace CslibFrontierTests.DominatingSet open Cslib.Complexity Turing.MultiTapeTM /-- The path `0 - 1 - 2`. -/ -private def path : Graph := ([0, 1, 2], [(0, 1), (1, 2)]) +private def path : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2)]⟩ /-- The edge `0 - 1` with the isolated vertex `2`. -/ -private def edgeIso : Graph := ([0, 1, 2], [(0, 1)]) +private def edgeIso : Graph := ⟨[0, 1, 2], [(0, 1)]⟩ /-- A self-loop at `0`, an edge to the undeclared vertex `5`, and a repeated vertex entry. -/ -private def junk : Graph := ([0, 0], [(0, 0), (0, 5)]) +private def junk : Graph := ⟨[0, 0], [(0, 0), (0, 5)]⟩ /-! ### Headline results -/ @@ -60,7 +60,7 @@ example : DominatingSet.isDominating edgeIso [0, 2] = true := by decide example : DominatingSet.isDominating junk [] = false := by decide example : DominatingSet.isDominating junk [0] = true := by decide -- The test reads adjacency off the edge list, so the undeclared `5` passes it. Certificates --- select declared vertices only (`mem_DominatingSet_iff` asks for `S ⊆ G.1`). +-- select declared vertices only (`mem_DominatingSet_iff` asks for `S ⊆ G.vertices`). example : DominatingSet.isDominating junk [5] = true := by decide /-! ### Dominating sets -/ @@ -96,12 +96,12 @@ example : VertexCover.properEdges path = [(0, 1), (1, 2)] := by decide example : VertexCover.properEdges junk = [] := by decide -- The graph of `junk`: hub, pendant and copies only. -example : VertexCover.dominationGraph junk = ([1, 3, 0, 0], [(1, 3), (1, 0), (1, 0)]) := by decide +example : VertexCover.dominationGraph junk = ⟨[1, 3, 0, 0], [(1, 3), (1, 0), (1, 0)]⟩ := by decide -- The edge `0 - 1` gets the vertex `2 * Nat.pair 0 1 + 5 = 7`, adjacent to the copies `0` and `2`. example : VertexCover.edgeVertex (0, 1) = 7 := by decide -example : VertexCover.dominationGraph ([0, 1], [(0, 1)]) = - ([1, 3, 0, 2, 7], [(1, 3), (1, 0), (1, 2), (7, 0), (7, 2)]) := by decide +example : VertexCover.dominationGraph ⟨[0, 1], [(0, 1)]⟩ = + ⟨[1, 3, 0, 2, 7], [(1, 3), (1, 0), (1, 2), (7, 0), (7, 2)]⟩ := by decide -- The target becomes `k + 1`, or `0` when `k` exceeds the number of distinct vertices. example : VertexCover.toDominatingSet (path, 1) = ⟨VertexCover.dominationGraph path, 2⟩ := by diff --git a/CslibFrontierTests/FeedbackArcSet.lean b/CslibFrontierTests/FeedbackArcSet.lean index 4a459944a5..31cdc19d9a 100644 --- a/CslibFrontierTests/FeedbackArcSet.lean +++ b/CslibFrontierTests/FeedbackArcSet.lean @@ -18,10 +18,10 @@ namespace CslibFrontierTests.FeedbackArcSet open Cslib.Complexity Turing.MultiTapeTM /-- The directed triangle `0 → 1 → 2 → 0`. -/ -private def tri : Graph := ([0, 1, 2], [(0, 1), (1, 2), (2, 0)]) +private def tri : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2), (2, 0)]⟩ /-- The two-cycle `0 ⇄ 1`. -/ -private def two : Graph := ([0, 1], [(0, 1), (1, 0)]) +private def two : Graph := ⟨[0, 1], [(0, 1), (1, 0)]⟩ /-! ### Headline results -/ @@ -84,7 +84,7 @@ example : (⟨two, 1⟩ : FeedbackArcSetInstance) ∈ FeedbackArcSet := by · exact absurd (Finset.mem_singleton_self _) hS -- A self-loop must be removed. -example : (⟨(([0], [(0, 0)]) : Graph), 0⟩ : FeedbackArcSetInstance) ∉ FeedbackArcSet := by +example : (⟨(⟨[0], [(0, 0)]⟩ : Graph), 0⟩ : FeedbackArcSetInstance) ∉ FeedbackArcSet := by rintro ⟨S, -, hcard, hacyc⟩ obtain rfl : S = ∅ := Finset.card_eq_zero.1 (Nat.le_zero.1 hcard) obtain ⟨f, -, hf⟩ := Graph.exists_rank_of_isDirAcyclic hacyc @@ -100,8 +100,8 @@ example : FeedbackArcSet.backArcs tri [1, 2, 3] = [(2, 0)] := by /-! ### The vertex-splitting reduction -/ -- The arc `0 → 1` between the vertices `0` and `1` becomes `0 → 1`, `2 → 3` and `1 → 2`. -example : FeedbackVertexSet.toFeedbackArcSet ⟨(([0, 1], [(0, 1)]) : Graph), 1⟩ = - ⟨(([0, 2, 1, 3], [(0, 1), (2, 3), (1, 2)]) : Graph), 1⟩ := by +example : FeedbackVertexSet.toFeedbackArcSet ⟨(⟨[0, 1], [(0, 1)]⟩ : Graph), 1⟩ = + ⟨(⟨[0, 2, 1, 3], [(0, 1), (2, 3), (1, 2)]⟩ : Graph), 1⟩ := by decide -- The split triangle has a feedback arc set of size one, the arc of the vertex `0`. diff --git a/CslibFrontierTests/FeedbackVertexSet.lean b/CslibFrontierTests/FeedbackVertexSet.lean index 64bdf773a3..aa22edb906 100644 --- a/CslibFrontierTests/FeedbackVertexSet.lean +++ b/CslibFrontierTests/FeedbackVertexSet.lean @@ -19,13 +19,13 @@ namespace CslibFrontierTests.FeedbackVertexSet open Cslib.Complexity Turing.MultiTapeTM /-- The directed triangle `0 → 1 → 2 → 0`. -/ -private def tri : Graph := ([0, 1, 2], [(0, 1), (1, 2), (2, 0)]) +private def tri : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2), (2, 0)]⟩ /-- A self-loop at `0`. -/ -private def loop : Graph := ([0], [(0, 0)]) +private def loop : Graph := ⟨[0], [(0, 0)]⟩ /-- An acyclic graph with arcs `0 → 1 → 2` and `0 → 2`. -/ -private def dag : Graph := ([0, 1, 2], [(0, 1), (1, 2), (0, 2)]) +private def dag : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2), (0, 2)]⟩ /-! ### Headline results -/ @@ -126,7 +126,7 @@ example : (⟨dag, 0⟩ : FeedbackVertexSetInstance) ∈ FeedbackVertexSet := by rcases he with ⟨rfl, rfl⟩ | ⟨rfl, rfl⟩ | ⟨rfl, rfl⟩ <;> decide -- Arcs to undeclared vertices are ignored: `0 ⇄ 1` with only `0` declared has no cycle. -example : (⟨(([0], [(0, 1), (1, 0)]) : Graph), 0⟩ : FeedbackVertexSetInstance) ∈ +example : (⟨(⟨[0], [(0, 1), (1, 0)]⟩ : Graph), 0⟩ : FeedbackVertexSetInstance) ∈ FeedbackVertexSet := by refine ⟨∅, by simp, by simp, Graph.isDirAcyclic_of_rank (f := fun _ => 0) fun u v h => ?_⟩ obtain ⟨⟨hu, hv, he⟩, -, -⟩ := Graph.dirAdj_deleteVerts.1 h @@ -137,21 +137,22 @@ example : (⟨(([0], [(0, 1), (1, 0)]) : Graph), 0⟩ : FeedbackVertexSetInstanc /-! ### Karp's reduction from `VertexCover` -/ -- The edge `0 - 1` becomes the arcs `0 → 1` and `1 → 0`. -example : VertexCover.toFeedbackVertexSet ((([0, 1], [(0, 1)]) : Graph), 1) = - ⟨(([0, 1], [(0, 1), (1, 0)]) : Graph), 1⟩ := by +example : VertexCover.toFeedbackVertexSet (((⟨[0, 1], [(0, 1)]⟩ : Graph), 1) : Graph × ℕ) = + ⟨(⟨[0, 1], [(0, 1), (1, 0)]⟩ : Graph), 1⟩ := by decide -- Self-loops of the undirected graph are dropped. -example : VertexCover.bidirect (([0], [(0, 0)]) : Graph) = (([0], []) : Graph) := by +example : VertexCover.bidirect (⟨[0], [(0, 0)]⟩ : Graph) = (⟨[0], []⟩ : Graph) := by decide -- A target above the number of distinct vertices goes to the self-loop with target `0`. -example : VertexCover.toFeedbackVertexSet ((([0, 1], [(0, 1)]) : Graph), 3) = - ⟨(([0], [(0, 0)]) : Graph), 0⟩ := by +example : VertexCover.toFeedbackVertexSet (((⟨[0, 1], [(0, 1)]⟩ : Graph), 3) : Graph × ℕ) = + ⟨(⟨[0], [(0, 0)]⟩ : Graph), 0⟩ := by decide -example : ((([0, 1], [(0, 1)]) : Graph), 1) ∈ VertexCover ↔ - VertexCover.toFeedbackVertexSet ((([0, 1], [(0, 1)]) : Graph), 1) ∈ FeedbackVertexSet := +example : (((⟨[0, 1], [(0, 1)]⟩ : Graph), 1) : Graph × ℕ) ∈ VertexCover ↔ + VertexCover.toFeedbackVertexSet + (((⟨[0, 1], [(0, 1)]⟩ : Graph), 1) : Graph × ℕ) ∈ FeedbackVertexSet := (VertexCover.toFeedbackVertexSet_mem_iff _).symm end CslibFrontierTests.FeedbackVertexSet diff --git a/CslibFrontierTests/FineGrained/Diameter.lean b/CslibFrontierTests/FineGrained/Diameter.lean index b5e24508b2..ecf85a26ac 100644 --- a/CslibFrontierTests/FineGrained/Diameter.lean +++ b/CslibFrontierTests/FineGrained/Diameter.lean @@ -29,18 +29,18 @@ private def p₂ : List (List Bool) × List (List Bool) := ([[true, true]], [[fa /-- The coordinates `0 ↦ 4` and `1 ↦ 6`, the vectors `0 ↦ 5` and `1 ↦ 7`, and the constants. -/ example : toDiameter p₁ = - ([4, 6, 5, 4, 6, 7, 0, 1, 2, 3], + ⟨[4, 6, 5, 4, 6, 7, 0, 1, 2, 3], [(0, 4), (1, 4), (4, 5), (0, 6), (1, 6), (0, 5), (0, 4), (1, 4), (0, 6), (1, 6), (6, 7), (1, 7), - (0, 1), (0, 2), (0, 3), (1, 2), (1, 3)]) := rfl + (0, 1), (0, 2), (0, 3), (1, 2), (1, 3)]⟩ := rfl /-- The instance with no vectors: the hubs and the padding vertices. -/ example : toDiameter ([], []) = - ([0, 1, 2, 3], [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3)]) := rfl + ⟨[0, 1, 2, 3], [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3)]⟩ := rfl /-- The parser emits the same vertices on the encoded instance. -/ example : parseRun Diameter.bitVertices Diameter.endVertices ((((false, false), false), 0), 0) - (encOV p₁) ++ Diameter.constVertices = (toDiameter p₁).1 := by decide + (encOV p₁) ++ Diameter.constVertices = (toDiameter p₁).vertices := by decide /-! ### The diameter -/ @@ -87,7 +87,8 @@ example : (∀ G : Graph, G.induced.ediam ≤ 2 → G ∈ {G : Graph | G.induced /-- Under `OVH`, the size form excludes deciders within time `(n + m) ^ 1.5 + L`. -/ example (h : OVH) : ¬DecidableInTimeO {G : Graph | G.induced.ediam ≤ 2} encGraph - fun G => ((G.1.length + G.2.length : ℕ) : ℝ) ^ (2 - (1 / 2 : ℝ)) + (encGraph G).length := + fun G => ((G.vertices.length + G.edges.length : ℕ) : ℝ) ^ (2 - (1 / 2 : ℝ)) + + (encGraph G).length := not_decidableInTimeO_diameter_size_of_OVH h (by norm_num) (fun _ h => h) fun G h hG => by have h₂ : G.induced.ediam ≤ 2 := hG rw [h] at h₂ diff --git a/CslibFrontierTests/FineGrained/GraphProblems.lean b/CslibFrontierTests/FineGrained/GraphProblems.lean index 9ab6a1d53c..4e60c42dfc 100644 --- a/CslibFrontierTests/FineGrained/GraphProblems.lean +++ b/CslibFrontierTests/FineGrained/GraphProblems.lean @@ -73,10 +73,11 @@ example : SAT.toSparseIndependentSet [[]] ∉ IndependentSet := fun h => by `2` tops, `3 * 8 + 3` row nodes and `8` clause nodes. Sipser's construction has a diamond for each occurrence, each with a pair of nodes for each clause: `9` tops, `8 * 27` row nodes and `8` clause nodes. -/ -example : (SAT.sparseDiamondGraph ψ).1.length = 37 ∧ (SAT.toDirHamPath ψ).1.1.length = 233 := by +example : (SAT.sparseDiamondGraph ψ).vertices.length = 37 ∧ + (SAT.toDirHamPath ψ).1.vertices.length = 233 := by decide +kernel -example : (SAT.sparseDiamondGraph φ₁).1.length + (SAT.sparseDiamondGraph φ₁).2.length ≤ +example : (SAT.sparseDiamondGraph φ₁).vertices.length + (SAT.sparseDiamondGraph φ₁).edges.length ≤ (23 * 2 + 1) * φ₁.length + 1 := SAT.length_sparseDiamondGraph_le (by decide) @@ -99,8 +100,8 @@ example : SAT.toSparseHamCycle φ₂ ∉ HamCycle := fun h => by /-- The formula with no clauses gives the graph of a single top, which a path visits without arcs; the empty clause gives a clause node that no detour reaches. -/ -example : SAT.toSparseDirHamPath [] = (([Diamond.topNode 0], []), Diamond.topNode 0, - Diamond.topNode 0) := by decide +example : SAT.toSparseDirHamPath [] = + (⟨[Diamond.topNode 0], []⟩, Diamond.topNode 0, Diamond.topNode 0) := by decide example : SAT.toSparseDirHamCycle [[]] ∉ DirHamCycle := fun h => by obtain ⟨σ, hσ⟩ := (SAT.toSparseDirHamCycle_mem_iff [[]]).1 h @@ -108,17 +109,18 @@ example : SAT.toSparseDirHamCycle [[]] ∉ DirHamCycle := fun h => by /-! ### The 3-coloring reduction -/ -example : (SAT.toThreeColoring φ₁).1.length + (SAT.toThreeColoring φ₁).2.length ≤ +example : (SAT.toThreeColoring φ₁).vertices.length + (SAT.toThreeColoring φ₁).edges.length ≤ (24 * 2 + 12) * φ₁.length + 9 := SAT.length_toThreeColoring_le (by decide) /-! ### Non-vacuity -/ /-- The subexponential time predicate holds for a trivial problem. -/ -example : DecidableInSubexpTime (∅ : Set Graph) encGraph fun G => G.1.length + G.2.length := +example : DecidableInSubexpTime (∅ : Set Graph) encGraph fun G => + G.vertices.length + G.edges.length := fun δ hδ => DecidableInExpTime.of_computableInO (ComputableInO.const false) (by simp) 1 1 fun G => by - have h₁ : (1 : ℝ) ≤ 2 ^ (δ * ((G.1.length + G.2.length : ℕ) : ℝ)) := + have h₁ : (1 : ℝ) ≤ 2 ^ (δ * ((G.vertices.length + G.edges.length : ℕ) : ℝ)) := Real.one_le_rpow (by norm_num) (by positivity) have h₀ : (0 : ℝ) ≤ (encGraph G).length := Nat.cast_nonneg _ simp only [Nat.cast_one, pow_one, one_mul] @@ -127,10 +129,10 @@ example : DecidableInSubexpTime (∅ : Set Graph) encGraph fun G => G.1.length + /-- Under `ETH`, clique is not decidable in time `2 ^ o(n)` for `n` distinct vertices either, and 3SAT is not decidable in subexponential time in the number of clauses. -/ example (h : ETH) : - ¬DecidableInSubexpTime Clique (encPair encGraph encNat) (fun x => x.1.1.toFinset.card) ∧ + ¬DecidableInSubexpTime Clique (encPair encGraph encNat) (fun x => x.1.vertices.toFinset.card) ∧ ¬DecidableInSubexpTime ThreeSAT encCNF List.length := ⟨fun h' => h.not_decidableInSubexpTime_Clique (h'.mono_measure (c := 1) fun x => by - have := x.1.1.toFinset_card_le + have := x.1.vertices.toFinset_card_le omega), h.not_decidableInSubexpTime_of_reduction (f := id) ComputableInPolyTime.id' (fun φ hφ => ⟨fun h => h.2, fun h' => ⟨hφ, h'⟩⟩) (k := 1) fun φ _ => by simp⟩ diff --git a/CslibFrontierTests/GapGraphsGeography.lean b/CslibFrontierTests/GapGraphsGeography.lean index 1d04768611..e7d38033ae 100644 --- a/CslibFrontierTests/GapGraphsGeography.lean +++ b/CslibFrontierTests/GapGraphsGeography.lean @@ -42,15 +42,15 @@ example : /-- Independence number, vertex cover number, and complement clique number on small graphs. -/ example : - 1 ≤ Graph.indepNum ([0], []) ∧ - Graph.indepNum ([], []) = 0 ∧ - Graph.vertexCoverNum ([], []) = 0 ∧ - (Graph.compl ([0], [])).cliqueNum = Graph.indepNum ([0], []) := by - have h := Graph.indepNum_le_card ([], []) + 1 ≤ Graph.indepNum ⟨[0], []⟩ ∧ + Graph.indepNum ⟨[], []⟩ = 0 ∧ + Graph.vertexCoverNum ⟨[], []⟩ = 0 ∧ + (Graph.compl ⟨[0], []⟩).cliqueNum = Graph.indepNum ⟨[0], []⟩ := by + have h := Graph.indepNum_le_card ⟨[], []⟩ simp only [List.toFinset_nil, Finset.card_empty, Nat.le_zero] at h exact ⟨Graph.le_indepNum_iff.2 ⟨{⟨0, by simp⟩}, by simp, rfl⟩, h, by rw [Graph.vertexCoverNum_eq_card_sub_indepNum, h]; rfl, - Graph.cliqueNum_compl ([0], [])⟩ + Graph.cliqueNum_compl ⟨[0], []⟩⟩ /-! ### Yes- and no-instances at the constants of the hardness theorems -/ @@ -101,7 +101,7 @@ example : /-! ### Generalized Geography -/ /-- With one arc from the start, the first player moves and the second player is stuck. -/ -example : (⟨([0, 1], [(0, 1)]), 0⟩ : GeneralizedGeographyInstance) ∈ GeneralizedGeography := by +example : (⟨⟨[0, 1], [(0, 1)]⟩, 0⟩ : GeneralizedGeographyInstance) ∈ GeneralizedGeography := by rw [mem_GeneralizedGeography, Graph.geographyWins_iff] refine ⟨1, by simp, by simp, by simp, ?_⟩ rw [Graph.geographyWins_iff] @@ -109,7 +109,7 @@ example : (⟨([0, 1], [(0, 1)]), 0⟩ : GeneralizedGeographyInstance) ∈ Gener /-- On a path of two arcs, the second player makes the last move. -/ example : - (⟨([0, 1, 2], [(0, 1), (1, 2)]), 0⟩ : GeneralizedGeographyInstance) ∉ + (⟨⟨[0, 1, 2], [(0, 1), (1, 2)]⟩, 0⟩ : GeneralizedGeographyInstance) ∉ GeneralizedGeography := by rw [mem_GeneralizedGeography, Graph.not_geographyWins_iff] intro w hw _ _ @@ -120,13 +120,13 @@ example : simp /-- An arc to an undeclared vertex is not a move. -/ -example : (⟨([0], [(0, 1)]), 0⟩ : GeneralizedGeographyInstance) ∉ GeneralizedGeography := by +example : (⟨⟨[0], [(0, 1)]⟩, 0⟩ : GeneralizedGeographyInstance) ∉ GeneralizedGeography := by rw [mem_GeneralizedGeography, Graph.geographyWins_iff] simp /-- On a cycle of two arcs, the second player cannot return to the visited start. -/ example : - (⟨([0, 1], [(0, 1), (1, 0)]), 0⟩ : GeneralizedGeographyInstance) ∈ GeneralizedGeography := by + (⟨⟨[0, 1], [(0, 1), (1, 0)]⟩, 0⟩ : GeneralizedGeographyInstance) ∈ GeneralizedGeography := by rw [mem_GeneralizedGeography, Graph.geographyWins_iff] refine ⟨1, by simp, by simp, by simp, ?_⟩ rw [Graph.geographyWins_iff] diff --git a/CslibFrontierTests/InteractiveProofs.lean b/CslibFrontierTests/InteractiveProofs.lean index f267c008a5..3a5032041b 100644 --- a/CslibFrontierTests/InteractiveProofs.lean +++ b/CslibFrontierTests/InteractiveProofs.lean @@ -19,19 +19,19 @@ open Cslib Cslib.Complexity Cslib.Probability Turing.MultiTapeTM /-! ### Concrete graphs -/ /-- A path on three vertices. -/ -private def path : Graph := ([0, 1, 2], [(0, 1), (1, 2)]) +private def path : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2)]⟩ /-- A triangle. -/ -private def triangle : Graph := ([0, 1, 2], [(0, 1), (1, 2), (2, 0)]) +private def triangle : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2), (2, 0)]⟩ /-- The path with other labels, a repeated vertex entry, and an edge to an undeclared vertex. -/ -private def path' : Graph := ([7, 5, 9, 5], [(9, 5), (5, 7), (7, 100)]) +private def path' : Graph := ⟨[7, 5, 9, 5], [(9, 5), (5, 7), (7, 100)]⟩ -- Repeated entries are dropped; the order of first occurrences is kept. example : path'.distinctVertices = [7, 5, 9] := by decide -- Duplicate vertex entries do not change the graph: one vertex is one vertex. -example : (⟨([1, 1], []), ([1], [])⟩ : GraphIsomorphismInstance) ∈ GraphIsomorphism := by +example : (⟨⟨[1, 1], []⟩, ⟨[1], []⟩⟩ : GraphIsomorphismInstance) ∈ GraphIsomorphism := by rw [mem_GraphIsomorphism_iff] decide @@ -46,7 +46,7 @@ theorem path_triangle : (⟨path, triangle⟩ : GraphIsomorphismInstance) ∈ Gr decide -- The empty graph and a single vertex are not isomorphic. -example : (⟨([], []), ([3], [])⟩ : GraphIsomorphismInstance) ∈ GraphNonIsomorphism := by +example : (⟨⟨[], []⟩, ⟨[3], []⟩⟩ : GraphIsomorphismInstance) ∈ GraphNonIsomorphism := by rw [GraphNonIsomorphism, Set.mem_compl_iff, mem_GraphIsomorphism_iff] decide @@ -65,7 +65,8 @@ example : (encPair encGraph encGraph) encGraphIsomorphism := by fun_prop --- The certificate decoding `[7, 5, 9]` (positions `0, 1, 2` of `path'.1 = [7, 5, 9, 5]` in unary +-- The certificate decoding `[7, 5, 9]` (positions `0, 1, 2` of +-- `path'.vertices = [7, 5, 9, 5]` in unary -- blocks of width `4`) maps positions `0, 1, 2` of `path` to `7, 5, 9` and is accepted. example : GraphIsomorphism.verifier (⟨path, path'⟩, [false, false, false, false, true, false, false, false, true, true, false, false]) = true := by @@ -84,7 +85,7 @@ example : GraphIsomorphism.verifier (⟨path, path'⟩, -- Graphs with different numbers of distinct vertices are rejected. example : - GraphIsomorphism.verifier (⟨([1, 2], []), ([1], [])⟩, []) = false := by + GraphIsomorphism.verifier (⟨⟨[1, 2], []⟩, ⟨[1], []⟩⟩, []) = false := by decide example : GraphIsomorphism ∈ IP encGraphIsomorphism := diff --git a/CslibFrontierTests/KCenterHardness.lean b/CslibFrontierTests/KCenterHardness.lean index d04a11a674..52f590ef0f 100644 --- a/CslibFrontierTests/KCenterHardness.lean +++ b/CslibFrontierTests/KCenterHardness.lean @@ -37,7 +37,7 @@ open Cslib.Complexity Cslib.Complexity.KCenter Cslib.Algorithms.Lean.KCenter /-- The path `10 - 20 - 30`, with `10` declared twice and an edge `30 - 40` to the undeclared vertex `40`. -/ -private def path3 : Graph := ([10, 20, 10, 30], [(10, 20), (30, 20), (30, 40)]) +private def path3 : Graph := ⟨[10, 20, 10, 30], [(10, 20), (30, 20), (30, 40)]⟩ example : path3.distinctVertices = [10, 20, 30] := by decide @@ -91,7 +91,7 @@ example : decideDominatingSet middle ⟨path3, 4⟩ = false := by decide /-- With `k = 0`, only the graph without vertices is accepted. -/ example : decideDominatingSet middle ⟨path3, 0⟩ = false ∧ - decideDominatingSet middle ⟨([], []), 0⟩ = true := by decide + decideDominatingSet middle ⟨⟨[], []⟩, 0⟩ = true := by decide /-! ### 3. The hypotheses of the hardness theorem -/ diff --git a/CslibFrontierTests/KarpReductions.lean b/CslibFrontierTests/KarpReductions.lean index 089f87b73b..9942e805a0 100644 --- a/CslibFrontierTests/KarpReductions.lean +++ b/CslibFrontierTests/KarpReductions.lean @@ -92,29 +92,35 @@ example : ThreeSAT.verifier (toThreeSAT [[(2 ^ 80, true), (2 ^ 80 + 1, false)]], /-! ### `IndependentSet` and `Clique` concrete and boundary tests -/ -- Empty graph: m = 0 is accepted, m > 0 is rejected. -example : IndependentSet.verifier ((([], []), 0), []) = true := rfl -example : IndependentSet.verifier ((([], []), 1), [true]) = false := rfl -example : Clique.verifier ((([], []), 0), []) = true := rfl -example : Clique.verifier ((([], []), 1), [true]) = false := rfl +example : IndependentSet.verifier (((⟨[], []⟩, 0) : Graph × ℕ), []) = true := rfl +example : IndependentSet.verifier (((⟨[], []⟩, 1) : Graph × ℕ), [true]) = false := rfl +example : Clique.verifier (((⟨[], []⟩, 0) : Graph × ℕ), []) = true := rfl +example : Clique.verifier (((⟨[], []⟩, 1) : Graph × ℕ), [true]) = false := rfl -- Oversized binary target `m = 2 ^ 100` is rejected in binary without unary expansion. -example : IndependentSet.verifier ((([0, 1], []), 2 ^ 100), [true, true]) = false := by decide -example : Clique.verifier ((([0, 1], [(0, 1)]), 2 ^ 100), [true, true]) = false := by decide +example : IndependentSet.verifier + (((⟨[0, 1], []⟩, 2 ^ 100) : Graph × ℕ), [true, true]) = false := by decide +example : Clique.verifier + (((⟨[0, 1], [(0, 1)]⟩, 2 ^ 100) : Graph × ℕ), [true, true]) = false := by decide --- Duplicate vertices in `G.1` are deduplicated at their first occurrence by `selectDistinct`, --- so selecting two copies of vertex `0` cannot forge an independent set or clique of size 2. +-- Duplicate vertices in `G.vertices` are deduplicated at their first occurrence by +-- `selectDistinct`, so selecting two copies of vertex `0` cannot forge an independent set or +-- clique of size 2. example : selectDistinct [0, 0, 1] [true, true, false] = [0] := rfl example : selectDistinct [0, 0, 1] [true, false, true] = [0, 1] := rfl -example : IndependentSet.verifier ((([0, 0, 1], []), 2), [true, true, false]) = false := rfl -example : IndependentSet.verifier ((([0, 0, 1], []), 2), [true, false, true]) = true := rfl +example : IndependentSet.verifier + (((⟨[0, 0, 1], []⟩, 2) : Graph × ℕ), [true, true, false]) = false := rfl +example : IndependentSet.verifier + (((⟨[0, 0, 1], []⟩, 2) : Graph × ℕ), [true, false, true]) = true := rfl --- Edges with endpoints outside `G.1` and self-loops in `G.2` do not create adjacency on `G.1`. +-- Edges with endpoints outside `G.vertices` and self-loops in `G.edges` do not create +-- adjacency on `G.vertices`. example : IndependentSet.verifier - ((([10, 20], [(10, 99), (99, 20), (10, 10)]), 2), [true, true]) = true := rfl + (((⟨[10, 20], [(10, 99), (99, 20), (10, 10)]⟩, 2) : Graph × ℕ), [true, true]) = true := rfl -- Sparse binary vertex labels in `IndependentSet`, `Graph.compl`, and `Clique`. private def sparseGraph : Graph := - ([2 ^ 80, 2 ^ 80 + 1, 2 ^ 80 + 2], [(2 ^ 80, 2 ^ 80 + 1)]) + ⟨[2 ^ 80, 2 ^ 80 + 1, 2 ^ 80 + 2], [(2 ^ 80, 2 ^ 80 + 1)]⟩ example : IndependentSet.verifier ((sparseGraph, 2), [true, false, true]) = true := by decide example : IndependentSet.verifier ((sparseGraph, 2), [true, true, false]) = false := by decide @@ -126,8 +132,8 @@ private def satFormula : CNF ℕ := [[(0, true), (1, false)], [(0, false), (1, t private def unsatFormula : CNF ℕ := [[(0, true)], [(0, false)]] example : SAT.toIndependentSet satFormula = - (([0, 1, 2, 3], - [(0, 1), (0, 2), (1, 0), (1, 3), (2, 0), (2, 3), (3, 1), (3, 2)]), 2) := rfl + (⟨[0, 1, 2, 3], + [(0, 1), (0, 2), (1, 0), (1, 3), (2, 0), (2, 3), (3, 1), (3, 2)]⟩, 2) := rfl -- Choosing the first literal `(0, true)` in clause 0 and the second literal `(1, true)` in clause 1 -- yields a valid independent set of size 2 (and a 2-clique in the complement). @@ -138,7 +144,7 @@ example : Clique.verifier [true, false, false, true]) = true := rfl -- Conflicting occurrences `[[(0, true)], [(0, false)]]` produce an edge between the two vertices. -example : SAT.toIndependentSet unsatFormula = (([0, 2], [(0, 2), (2, 0)]), 2) := rfl +example : SAT.toIndependentSet unsatFormula = (⟨[0, 2], [(0, 2), (2, 0)]⟩, 2) := rfl example : IndependentSet.verifier (SAT.toIndependentSet unsatFormula, [true, true]) = false := rfl end CslibFrontierTests.KarpReductions diff --git a/CslibFrontierTests/LongPathHardness.lean b/CslibFrontierTests/LongPathHardness.lean index 5dec5816c1..1e93ea122c 100644 --- a/CslibFrontierTests/LongPathHardness.lean +++ b/CslibFrontierTests/LongPathHardness.lean @@ -22,10 +22,10 @@ namespace CslibFrontierTests.LongPathHardness open Cslib Cslib.Complexity Turing.MultiTapeTM /-- The edge `1 — 2`. -/ -def edge : Graph := ([1, 2], [(1, 2)]) +def edge : Graph := ⟨[1, 2], [(1, 2)]⟩ /-- The vertices `1` and `2` without edges. -/ -def noEdge : Graph := ([1, 2], []) +def noEdge : Graph := ⟨[1, 2], []⟩ /-- The certificate naming the ordering `[1, 2]` of the vertices of `edge` and `noEdge`. -/ def cert : List Bool := [false, false, true, false] diff --git a/CslibFrontierTests/LongPathKernel.lean b/CslibFrontierTests/LongPathKernel.lean index 7bb23a9e4e..445b6f8c81 100644 --- a/CslibFrontierTests/LongPathKernel.lean +++ b/CslibFrontierTests/LongPathKernel.lean @@ -24,10 +24,10 @@ namespace CslibFrontierTests.LongPathKernel open Cslib Cslib.Complexity Turing.MultiTapeTM ComputableInPolyTime /-- The edge `1 — 2`. -/ -def edge : Graph := ([1, 2], [(1, 2)]) +def edge : Graph := ⟨[1, 2], [(1, 2)]⟩ /-- The vertices `1` and `2` without edges. -/ -def noEdge : Graph := ([1, 2], []) +def noEdge : Graph := ⟨[1, 2], []⟩ /-- The edge `1 — 2` has a Hamiltonian path from `1` to `2`. -/ theorem edge_mem_HamPath : (edge, 1, 2) ∈ HamPath := by @@ -113,8 +113,8 @@ example : IsPolyEquivalence encUnary fun p : ℕ × ℕ => decide (p.1 = p.2) := /-- The hypotheses of the cross-composition theorem hold for k-Path: `HamPath` is `NP`-hard and OR-cross-composes into k-Path, so the kernel lower bound assumes only `coNP ⊄ NP/poly`. -/ -example : NPHard (encPair encGraph (encPair encNat encNat)) HamPath ∧ - ORCrossComposes (encPair encGraph (encPair encNat encNat)) HamPath (encPair encGraph encNat) +example : NPHard encPathInstance HamPath ∧ + ORCrossComposes encPathInstance HamPath (encPair encGraph encNat) Prod.snd LongPath := ⟨HamPath_npHard, HamPath.orCrossComposes_LongPath⟩ diff --git a/CslibFrontierTests/NPCompleteGraphs.lean b/CslibFrontierTests/NPCompleteGraphs.lean index 4844cbae05..dfcb87dcfa 100644 --- a/CslibFrontierTests/NPCompleteGraphs.lean +++ b/CslibFrontierTests/NPCompleteGraphs.lean @@ -34,12 +34,12 @@ example : PolyTimeReducible encCNF encGraph SAT ThreeColoring) ∧ (KColoring 4 ∈ NP encGraph ∧ PolyTimeReducible encCNF encGraph SAT (KColoring 4)) ∧ - (DirHamPath ∈ NP (encPair encGraph (encPair encNat encNat)) ∧ - PolyTimeReducible encCNF (encPair encGraph (encPair encNat encNat)) SAT DirHamPath) ∧ + (DirHamPath ∈ NP encPathInstance ∧ + PolyTimeReducible encCNF encPathInstance SAT DirHamPath) ∧ (DirHamCycle ∈ NP encGraph ∧ PolyTimeReducible encCNF encGraph SAT DirHamCycle) ∧ - (HamPath ∈ NP (encPair encGraph (encPair encNat encNat)) ∧ - PolyTimeReducible encCNF (encPair encGraph (encPair encNat encNat)) SAT HamPath) ∧ + (HamPath ∈ NP encPathInstance ∧ + PolyTimeReducible encCNF encPathInstance SAT HamPath) ∧ (HamCycle ∈ NP encGraph ∧ PolyTimeReducible encCNF encGraph SAT HamCycle) := ⟨⟨VertexCover_npComplete.1, VertexCover_npComplete.2 encCNF SAT SAT.mem_NP⟩, @@ -55,7 +55,7 @@ example : example : PolyTimeReducible encCNF encGraph SAT ThreeColoring ∧ PolyTimeReducible encCNF (encPair encGraph encNat) SAT VertexCover ∧ - PolyTimeReducible encCNF (encPair encGraph (encPair encNat encNat)) SAT DirHamPath := + PolyTimeReducible encCNF encPathInstance SAT DirHamPath := ⟨ThreeSAT_npComplete.2 encCNF SAT SAT.mem_NP |>.trans polyTimeReducible_ThreeSAT_ThreeColoring, polyTimeReducible_SAT_IndependentSet.trans polyTimeReducible_IndependentSet_VertexCover, ThreeSAT_npComplete.2 encCNF SAT SAT.mem_NP |>.trans polyTimeReducible_ThreeSAT_DirHamPath⟩ @@ -70,8 +70,7 @@ example : ComputableInPolyTime (KColoring.verifier 3) (encPair encGraph encBits) encBool ∧ ComputableInPolyTime SAT.toThreeColoring encCNF encGraph ∧ ComputableInPolyTime (SAT.toKColoring 2) encCNF encGraph ∧ - ComputableInPolyTime SAT.toDirHamPath encCNF - (encPair encGraph (encPair encNat encNat)) ∧ + ComputableInPolyTime SAT.toDirHamPath encCNF encPathInstance ∧ ComputableInPolyTime SAT.toDirHamCycle encCNF encGraph ∧ ComputableInPolyTime (fun φ => IndependentSet.toVertexCover (SAT.toIndependentSet φ)) @@ -80,7 +79,7 @@ example : /-! ### `VertexCover` concrete and boundary tests -/ -private def triangle : Graph := ([0, 1, 2], [(0, 1), (1, 2), (2, 0)]) +private def triangle : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2), (2, 0)]⟩ -- A triangle has vertex covers with two vertices but none with one. example : @@ -119,10 +118,10 @@ example : (triangle, 3) ∈ VertexCover ∧ (triangle, 4) ∉ VertexCover := by -- Empty graph: `k = 0` is accepted and `k = 1` is rejected. example : - VertexCover.verifier ((([], []), 0), []) = true ∧ - VertexCover.verifier ((([], []), 1), [true]) = false ∧ - ((([], []) : Graph), 0) ∈ VertexCover ∧ - ((([], []) : Graph), 1) ∉ VertexCover := + VertexCover.verifier (((⟨[], []⟩, 0) : Graph × ℕ), []) = true ∧ + VertexCover.verifier (((⟨[], []⟩, 1) : Graph × ℕ), [true]) = false ∧ + ((⟨[], []⟩ : Graph), 0) ∈ VertexCover ∧ + ((⟨[], []⟩ : Graph), 1) ∉ VertexCover := ⟨rfl, rfl, mem_VertexCover_iff.2 ⟨∅, by simp, by simp, rfl⟩, fun h => absurd (mem_VertexCover_iff_exists_card_le.1 h).1 (by decide)⟩ @@ -130,22 +129,25 @@ example : -- oversized binary targets are rejected without unary expansion; and `toVertexCover` complements -- the target within distinct declared vertices. example : - VertexCover.verifier ((([0, 0, 1], [(0, 1)]), 2), [true, true, false]) = false ∧ - VertexCover.verifier ((([0, 0, 1], [(0, 1)]), 1), [true, false, false]) = true ∧ + VertexCover.verifier (((⟨[0, 0, 1], [(0, 1)]⟩, 2) : Graph × ℕ), [true, true, false]) = false ∧ + VertexCover.verifier (((⟨[0, 0, 1], [(0, 1)]⟩, 1) : Graph × ℕ), [true, false, false]) = true ∧ VertexCover.verifier - ((([10, 20], [(10, 99), (99, 20), (10, 10)]), 0), [false, false]) = true ∧ - VertexCover.verifier ((([0, 1], [(0, 1)]), 2 ^ 100), [true, true]) = false ∧ + (((⟨[10, 20], [(10, 99), (99, 20), (10, 10)]⟩, 0) : Graph × ℕ), [false, false]) = true ∧ + VertexCover.verifier (((⟨[0, 1], [(0, 1)]⟩, 2 ^ 100) : Graph × ℕ), [true, true]) = false ∧ IndependentSet.toVertexCover (triangle, 1) = (triangle, 2) ∧ - IndependentSet.toVertexCover (([0, 0, 1], []), 2) = (([0, 0, 1], []), 0) ∧ + IndependentSet.toVertexCover (((⟨[0, 0, 1], []⟩, 2) : Graph × ℕ)) = + (((⟨[0, 0, 1], []⟩, 0) : Graph × ℕ)) ∧ IndependentSet.toVertexCover (triangle, 4) = (triangle, 4) ∧ - IndependentSet.toVertexCover (([], []), 0) = (([], []), 0) := + IndependentSet.toVertexCover (((⟨[], []⟩, 0) : Graph × ℕ)) = + (((⟨[], []⟩, 0) : Graph × ℕ)) := ⟨rfl, rfl, rfl, by decide, by decide, by decide, by decide, by decide⟩ /-! ### `KColoring` and `ThreeColoring` concrete and boundary tests -/ -- A certificate from a coloring `c`: block `i` of `k` bits has `c` of vertex `i` set bits. private def certificate (k : ℕ) (G : Graph) (c : ℕ → ℕ) : List Bool := - (List.range (k * G.1.length)).map fun j => decide (j % k < c (G.1.getD (j / k) 0)) + (List.range (k * G.vertices.length)).map fun j => + decide (j % k < c (G.vertices.getD (j / k) 0)) -- Colors are counts of set bits in each vertex's block of `k` bits, and the triangle is -- 3-colorable but not 2-colorable. @@ -175,7 +177,7 @@ example : triangle ∉ KColoring 2 := by omega -- The complete graph on four vertices is not 3-colorable. -private def k4 : Graph := ([0, 1, 2, 3], [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]) +private def k4 : Graph := ⟨[0, 1, 2, 3], [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]⟩ example : k4 ∉ ThreeColoring := by rw [mem_KColoring_iff] @@ -195,31 +197,31 @@ example : k4 ∉ ThreeColoring := by example : KColoring.verifier 4 (k4, certificate 4 k4 id) = true := rfl -- A block with all bits set encodes the out-of-range color `k`. -example : KColoring.verifier 3 (([0], []), [true, true, true]) = false := rfl +example : KColoring.verifier 3 (⟨[0], []⟩, [true, true, true]) = false := rfl -- With `k = 0`, exactly the graphs without declared vertices are colorable. -example : KColoring.verifier 0 (([], [(0, 1)]), []) = true := rfl -example : KColoring.verifier 0 (([0], []), []) = false := rfl -example : (([], [(0, 1)]) : Graph) ∈ KColoring 0 := +example : KColoring.verifier 0 (⟨[], [(0, 1)]⟩, []) = true := rfl +example : KColoring.verifier 0 (⟨[0], []⟩, []) = false := rfl +example : (⟨[], [(0, 1)]⟩ : Graph) ∈ KColoring 0 := mem_KColoring_iff.2 ⟨fun _ => 0, by simp, by simp⟩ -example : (([0], []) : Graph) ∉ KColoring 0 := by +example : (⟨[0], []⟩ : Graph) ∉ KColoring 0 := by rw [mem_KColoring_iff] rintro ⟨c, hc, -⟩ exact Nat.not_lt_zero _ (hc 0 (by simp)) -- Duplicate vertices share the block of their first occurrence; undeclared edges are ignored. example : KColoring.verifier 2 - (([0, 1, 0], [(0, 1), (1, 7), (7, 0)]), [false, false, true, false]) = true := rfl + (⟨[0, 1, 0], [(0, 1), (1, 7), (7, 0)]⟩, [false, false, true, false]) = true := rfl /-! ### The 3-coloring gadget -/ -- The gadget graph of the empty formula is the palette triangle. -example : (SAT.toThreeColoring []).2 = [(0, 1), (1, 4), (4, 0)] := by decide +example : (SAT.toThreeColoring []).edges = [(0, 1), (1, 4), (4, 0)] := by decide example : SAT.toThreeColoring [] ∈ ThreeColoring := (SAT.toThreeColoring_mem_iff _).2 ⟨fun _ => true, rfl⟩ -- The stage `0` output of an empty clause is adjacent to all three palette vertices. -example : (SAT.toThreeColoring [[]]).2 = +example : (SAT.toThreeColoring [[]]).edges = [(0, 1), (1, 4), (4, 0), (258, 0), (258, 4), (258, 1), (258, 4)] := by decide example : SAT.toThreeColoring [[]] ∉ ThreeColoring := by rw [SAT.toThreeColoring_mem_iff] @@ -263,13 +265,13 @@ example : SAT.toKColoring 2 [[(0, true)], [(0, false)]] ∉ KColoring 5 := by -- A directed path through all vertices, and the boundary conventions for cycles: a single -- vertex needs a self-loop. -example : (([0, 1, 2], [(0, 1), (1, 2)]), 0, 2) ∈ DirHamPath := +example : ((⟨[0, 1, 2], [(0, 1), (1, 2)]⟩ : Graph), 0, 2) ∈ DirHamPath := ⟨[0, 1, 2], by decide, fun _ => Iff.rfl, rfl, rfl, by decide⟩ -example : ([0], [(0, 0)]) ∈ DirHamCycle := +example : (⟨[0], [(0, 0)]⟩ : Graph) ∈ DirHamCycle := ⟨[0], by decide, fun _ => Iff.rfl, by decide, (Cycle.chain_singleton _ _).2 (by decide)⟩ -example : ([0], []) ∉ DirHamCycle := by +example : (⟨[0], []⟩ : Graph) ∉ DirHamCycle := by rintro ⟨p, hnd, hcov, hne, hch⟩ obtain ⟨a, rest, rfl⟩ := List.exists_cons_of_ne_nil hne have ha : a = 0 := by simpa using (hcov a).1 List.mem_cons_self @@ -279,19 +281,19 @@ example : ([0], []) ∉ DirHamCycle := by simp at hch -- Closing a path into a cycle needs an arc back to a start vertex that no arc enters. -example : ([0, 1, 2], [(2, 0), (0, 1), (1, 2)]) ∈ DirHamCycle := - (Graph.mem_DirHamCycle_cons_iff (G := ([0, 1, 2], [(0, 1), (1, 2)])) (by decide) +example : (⟨[0, 1, 2], [(2, 0), (0, 1), (1, 2)]⟩ : Graph) ∈ DirHamCycle := + (Graph.mem_DirHamCycle_cons_iff (G := ⟨[0, 1, 2], [(0, 1), (1, 2)]⟩) (by decide) (fun u => by simp)).2 ⟨[0, 1, 2], by decide, fun _ => Iff.rfl, rfl, rfl, by decide⟩ -- The diamond graph of the empty formula is the single top node, and that of a unit clause has -- one diamond with a row of six nodes and one clause node. -example : SAT.toDirHamPath [] = (([0], []), 0, 0) := by decide +example : SAT.toDirHamPath [] = (⟨[0], []⟩, 0, 0) := by decide example : SAT.toDirHamPath [] ∈ DirHamPath := (SAT.toDirHamPath_mem_iff _).2 ⟨fun _ => true, rfl⟩ example : SAT.toDirHamPath [[(0, true)]] = - (([0, 1, 2, 3, 17, 82, 257, 626, 6], + (⟨[0, 1, 2, 3, 17, 82, 257, 626, 6], [(0, 2), (0, 626), (2, 1), (626, 1), (2, 3), (3, 2), (3, 17), (17, 3), (17, 82), (82, 17), - (82, 257), (257, 82), (257, 626), (626, 257), (17, 6), (6, 82)]), 0, 1) := by + (82, 257), (257, 82), (257, 626), (626, 257), (17, 6), (6, 82)]⟩, 0, 1) := by decide -- The rightward traversal with the detour through the clause node. diff --git a/CslibFrontierTests/NPCompleteGraphs/Hamiltonian.lean b/CslibFrontierTests/NPCompleteGraphs/Hamiltonian.lean index b1b65a1d36..1e1aba4ca5 100644 --- a/CslibFrontierTests/NPCompleteGraphs/Hamiltonian.lean +++ b/CslibFrontierTests/NPCompleteGraphs/Hamiltonian.lean @@ -92,21 +92,21 @@ example : Graph.cycleChainBool (fun u v => decide (u = v)) [7] = true := rfl /-! ### Directed Hamiltonian paths -/ -private def dpath : Graph := ([0, 1, 2], [(0, 1), (1, 2)]) +private def dpath : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2)]⟩ -example : DirHamPath.verifier ((dpath, 0, 2), certificate dpath.1 [0, 1, 2]) = true := rfl +example : DirHamPath.verifier ((dpath, 0, 2), certificate dpath.vertices [0, 1, 2]) = true := rfl -- Arcs are directed. -example : DirHamPath.verifier ((dpath, 2, 0), certificate dpath.1 [2, 1, 0]) = false := rfl +example : DirHamPath.verifier ((dpath, 2, 0), certificate dpath.vertices [2, 1, 0]) = false := rfl -- Wrong endpoints, a repeated vertex, and a missing arc are rejected. -example : DirHamPath.verifier ((dpath, 0, 1), certificate dpath.1 [0, 1, 2]) = false := rfl -example : DirHamPath.verifier ((dpath, 0, 2), certificate dpath.1 [0, 1, 1]) = false := rfl -example : DirHamPath.verifier ((dpath, 1, 2), certificate dpath.1 [1, 0, 2]) = false := rfl +example : DirHamPath.verifier ((dpath, 0, 1), certificate dpath.vertices [0, 1, 2]) = false := rfl +example : DirHamPath.verifier ((dpath, 0, 2), certificate dpath.vertices [0, 1, 1]) = false := rfl +example : DirHamPath.verifier ((dpath, 1, 2), certificate dpath.vertices [1, 0, 2]) = false := rfl example : (dpath, 0, 2) ∈ DirHamPath := ⟨[0, 1, 2], by decide, fun v => by simp [dpath], rfl, rfl, by simp [dpath]⟩ -- An undeclared endpoint is rejected. -example : DirHamPath.verifier ((dpath, 0, 9), certificate dpath.1 [0, 1, 2]) = false := rfl +example : DirHamPath.verifier ((dpath, 0, 9), certificate dpath.vertices [0, 1, 2]) = false := rfl example : (dpath, 0, 9) ∉ DirHamPath := by rintro ⟨p, -, hmem, -, ht, -⟩ @@ -114,13 +114,13 @@ example : (dpath, 0, 9) ∉ DirHamPath := by simp [dpath] at this -- `s = t` on a one-vertex graph is accepted. -example : DirHamPath.verifier (((([5], []), 5, 5)), [false]) = true := rfl +example : DirHamPath.verifier ((((⟨[5], []⟩ : Graph), 5, 5)), [false]) = true := rfl example : (([5], []), 5, 5) ∈ DirHamPath := ⟨[5], by decide, fun v => by simp, rfl, rfl, by simp⟩ -- The empty graph has no Hamiltonian path. -example : DirHamPath.verifier ((([], []), 0, 0), []) = false := rfl +example : DirHamPath.verifier (((⟨[], []⟩ : Graph), 0, 0), []) = false := rfl example : (([], []), 0, 0) ∉ DirHamPath := by rintro ⟨p, -, hmem, hs, -⟩ @@ -128,13 +128,13 @@ example : (([], []), 0, 0) ∉ DirHamPath := by simp at this -- Duplicate vertex entries count once. -example : DirHamPath.verifier ((([0, 1, 0], [(0, 1)]), 0, 1), certificate [0, 1, 0] [0, 1]) = +example : DirHamPath.verifier (((⟨[0, 1, 0], [(0, 1)]⟩ : Graph), 0, 1), certificate [0, 1, 0] [0, 1]) = true := rfl -- Arcs with an undeclared endpoint are never used. -private def detour : Graph := ([0, 1], [(0, 7), (7, 1)]) +private def detour : Graph := ⟨[0, 1], [(0, 7), (7, 1)]⟩ -example : DirHamPath.verifier ((detour, 0, 1), certificate detour.1 [0, 1]) = false := rfl +example : DirHamPath.verifier ((detour, 0, 1), certificate detour.vertices [0, 1]) = false := rfl example : (detour, 0, 1) ∉ DirHamPath := by rintro ⟨p, hnd, hmem, hs, ht, hch⟩ @@ -147,28 +147,28 @@ example : (detour, 0, 1) ∉ DirHamPath := by /-! ### Directed Hamiltonian cycles -/ -private def dtriangle : Graph := ([0, 1, 2], [(0, 1), (1, 2), (2, 0)]) +private def dtriangle : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2), (2, 0)]⟩ -example : DirHamCycle.verifier (dtriangle, certificate dtriangle.1 [0, 1, 2]) = true := rfl -example : DirHamCycle.verifier (dtriangle, certificate dtriangle.1 [1, 2, 0]) = true := rfl -example : DirHamCycle.verifier (dtriangle, certificate dtriangle.1 [0, 2, 1]) = false := rfl +example : DirHamCycle.verifier (dtriangle, certificate dtriangle.vertices [0, 1, 2]) = true := rfl +example : DirHamCycle.verifier (dtriangle, certificate dtriangle.vertices [1, 2, 0]) = true := rfl +example : DirHamCycle.verifier (dtriangle, certificate dtriangle.vertices [0, 2, 1]) = false := rfl -- The closing arc is checked: the path graph has no Hamiltonian cycle. -example : DirHamCycle.verifier (dpath, certificate dpath.1 [0, 1, 2]) = false := rfl +example : DirHamCycle.verifier (dpath, certificate dpath.vertices [0, 1, 2]) = false := rfl -- Two vertices need arcs in both directions. -example : DirHamCycle.verifier (([0, 1], [(0, 1), (1, 0)]), certificate [0, 1] [0, 1]) = true := +example : DirHamCycle.verifier (⟨[0, 1], [(0, 1), (1, 0)]⟩, certificate [0, 1] [0, 1]) = true := rfl example : DirHamCycle.verifier (([0, 1], [(0, 1)]), certificate [0, 1] [0, 1]) = false := rfl -- A single vertex with a self-loop has a directed Hamiltonian cycle; without it, it has none. -example : DirHamCycle.verifier (([0], [(0, 0)]), [false]) = true := rfl +example : DirHamCycle.verifier (⟨[0], [(0, 0)]⟩, [false]) = true := rfl example : DirHamCycle.verifier (([0], []), [false]) = false := rfl example : ([0], [(0, 0)]) ∈ DirHamCycle := ⟨[0], by decide, fun v => by simp, by simp, by simp⟩ -- The empty graph has no directed Hamiltonian cycle. -example : DirHamCycle.verifier (([], []), []) = false := rfl +example : DirHamCycle.verifier (⟨[], []⟩, []) = false := rfl example : ([], []) ∉ DirHamCycle := by rintro ⟨p, -, hmem, hne, -⟩ @@ -177,44 +177,44 @@ example : ([], []) ∉ DirHamCycle := by /-! ### Undirected Hamiltonian paths -/ -example : HamPath.verifier ((dpath, 0, 2), certificate dpath.1 [0, 1, 2]) = true := rfl +example : HamPath.verifier ((dpath, 0, 2), certificate dpath.vertices [0, 1, 2]) = true := rfl -- Edges are undirected. -example : HamPath.verifier ((dpath, 2, 0), certificate dpath.1 [2, 1, 0]) = true := rfl -example : HamPath.verifier ((dpath, 1, 2), certificate dpath.1 [1, 0, 2]) = false := rfl +example : HamPath.verifier ((dpath, 2, 0), certificate dpath.vertices [2, 1, 0]) = true := rfl +example : HamPath.verifier ((dpath, 1, 2), certificate dpath.vertices [1, 0, 2]) = false := rfl example : (dpath, 2, 0) ∈ HamPath := by rw [mem_HamPath_iff] - refine ⟨[2, 1, 0], by decide, fun _ => (by decide : [2, 1, 0].Perm dpath.1).mem_iff, rfl, rfl, ?_⟩ + refine ⟨[2, 1, 0], by decide, fun _ => (by decide : [2, 1, 0].Perm dpath.vertices).mem_iff, rfl, rfl, ?_⟩ simp only [List.isChain_cons_cons, List.isChain_singleton, and_true] exact ⟨(Graph.adjBool_eq_true_iff _ _ _).1 rfl, (Graph.adjBool_eq_true_iff _ _ _).1 rfl⟩ -- An undeclared endpoint is rejected. -example : HamPath.verifier ((dpath, 9, 2), certificate dpath.1 [0, 1, 2]) = false := rfl +example : HamPath.verifier ((dpath, 9, 2), certificate dpath.vertices [0, 1, 2]) = false := rfl example : (dpath, 9, 2) ∉ HamPath := by rintro ⟨hs, -⟩ simp [dpath] at hs -- `s = t` on a one-vertex graph is accepted. -example : HamPath.verifier (((([5], []), 5, 5)), [false]) = true := rfl +example : HamPath.verifier ((((⟨[5], []⟩ : Graph), 5, 5)), [false]) = true := rfl example : (([5], []), 5, 5) ∈ HamPath := by rw [mem_HamPath_iff] exact ⟨[5], by decide, fun v => by simp, rfl, rfl, by simp⟩ -- The empty graph has no Hamiltonian path. -example : HamPath.verifier ((([], []), 0, 0), []) = false := rfl +example : HamPath.verifier (((⟨[], []⟩ : Graph), 0, 0), []) = false := rfl example : (([], []), 0, 0) ∉ HamPath := by rintro ⟨hs, -⟩ simp at hs -- Duplicate vertex entries count once. -example : HamPath.verifier ((([0, 1, 0], [(1, 0)]), 0, 1), certificate [0, 1, 0] [0, 1]) = true := +example : HamPath.verifier (((⟨[0, 1, 0], [(1, 0)]⟩ : Graph), 0, 1), certificate [0, 1, 0] [0, 1]) = true := rfl -- Edges with an undeclared endpoint are never used. -example : HamPath.verifier ((detour, 0, 1), certificate detour.1 [0, 1]) = false := rfl +example : HamPath.verifier ((detour, 0, 1), certificate detour.vertices [0, 1]) = false := rfl example : (detour, 0, 1) ∉ HamPath := by rw [mem_HamPath_iff] @@ -229,11 +229,11 @@ example : (detour, 0, 1) ∉ HamPath := by /-! ### Undirected Hamiltonian cycles -/ -example : HamCycle.verifier (dtriangle, certificate dtriangle.1 [0, 1, 2]) = true := rfl +example : HamCycle.verifier (dtriangle, certificate dtriangle.vertices [0, 1, 2]) = true := rfl -- Edges are undirected. -example : HamCycle.verifier (dtriangle, certificate dtriangle.1 [0, 2, 1]) = true := rfl +example : HamCycle.verifier (dtriangle, certificate dtriangle.vertices [0, 2, 1]) = true := rfl -- The path graph has a Hamiltonian path but no Hamiltonian cycle. -example : HamCycle.verifier (dpath, certificate dpath.1 [0, 1, 2]) = false := rfl +example : HamCycle.verifier (dpath, certificate dpath.vertices [0, 1, 2]) = false := rfl example : dtriangle ∈ HamCycle := by rw [mem_HamCycle_iff] @@ -243,8 +243,8 @@ example : dtriangle ∈ HamCycle := by -- A Hamiltonian cycle needs three vertices: one or two vertices never suffice, even with a -- self-loop or with edges in both directions. -example : HamCycle.verifier (([0], [(0, 0)]), [false]) = false := rfl -example : HamCycle.verifier (([0, 1], [(0, 1), (1, 0)]), certificate [0, 1] [0, 1]) = false := rfl +example : HamCycle.verifier (⟨[0], [(0, 0)]⟩, [false]) = false := rfl +example : HamCycle.verifier (⟨[0, 1], [(0, 1), (1, 0)]⟩, certificate [0, 1] [0, 1]) = false := rfl example : ([0], [(0, 0)]) ∉ HamCycle := by rw [mem_HamCycle_iff] @@ -254,7 +254,7 @@ example : ([0], [(0, 0)]) ∉ HamCycle := by omega -- The empty graph has no Hamiltonian cycle. -example : HamCycle.verifier (([], []), []) = false := rfl +example : HamCycle.verifier (⟨[], []⟩, []) = false := rfl example : ([], []) ∉ HamCycle := by rw [mem_HamCycle_iff] @@ -263,7 +263,7 @@ example : ([], []) ∉ HamCycle := by simpa using (hmem a).1 ha -- Duplicate vertex entries count once. -example : HamCycle.verifier (([0, 1, 2, 0], [(0, 1), (1, 2), (2, 0)]), +example : HamCycle.verifier (⟨[0, 1, 2, 0], [(0, 1), (1, 2), (2, 0)]⟩, certificate [0, 1, 2, 0] [0, 1, 2]) = true := rfl end CslibFrontierTests.NPCompleteGraphs.Hamiltonian diff --git a/CslibFrontierTests/NPCompleteGraphs/TSP.lean b/CslibFrontierTests/NPCompleteGraphs/TSP.lean index 910d24831d..58f26df417 100644 --- a/CslibFrontierTests/NPCompleteGraphs/TSP.lean +++ b/CslibFrontierTests/NPCompleteGraphs/TSP.lean @@ -164,7 +164,7 @@ example : TSP.verifier (⟨[[0, 2 ^ 100], [2 ^ 100, 0]], 2 ^ 101 - 1⟩, tour 2 /-! ### The reduction from `HamCycle` -/ -- The cycle on four vertices: distance `1` along the cycle, `2` across and on the diagonal. -private def c4 : Graph := ([0, 1, 2, 3], [(0, 1), (1, 2), (2, 3), (3, 0)]) +private def c4 : Graph := ⟨[0, 1, 2, 3], [(0, 1), (1, 2), (2, 3), (3, 0)]⟩ example : HamCycle.toTSP c4 = ⟨[[2, 1, 2, 1], [1, 2, 1, 2], [2, 1, 2, 1], [1, 2, 1, 2]], 4⟩ := by decide @@ -173,13 +173,14 @@ example : HamCycle.toTSP c4 ∈ TSP := ⟨[0, 1, 2, 3], by decide, by decide⟩ example : c4 ∈ HamCycle := (HamCycle.toTSP_mem_iff c4).1 ⟨[0, 1, 2, 3], by decide, by decide⟩ -- The path on four vertices has no Hamiltonian cycle: every tour pays `2` for a missing edge. -private def p4 : Graph := ([0, 1, 2, 3], [(0, 1), (1, 2), (2, 3)]) +private def p4 : Graph := ⟨[0, 1, 2, 3], [(0, 1), (1, 2), (2, 3)]⟩ example : p4 ∉ HamCycle := fun h => notMem_TSP (by decide) ((HamCycle.toTSP_mem_iff p4).2 h) -- Cities are the distinct declared vertices in the order of their first occurrences; labels -- need not be contiguous, and self-loops are ignored. -private def triangle : Graph := ([10, 20, 10, 30], [(10, 20), (20, 30), (30, 10), (10, 10)]) +private def triangle : Graph := + ⟨[10, 20, 10, 30], [(10, 20), (20, 30), (30, 10), (10, 10)]⟩ example : triangle.distinctVertices = [10, 20, 30] := by decide example : HamCycle.toTSP triangle = ⟨[[2, 1, 1], [1, 2, 1], [1, 1, 2]], 3⟩ := by decide @@ -187,18 +188,18 @@ example : triangle ∈ HamCycle := (HamCycle.toTSP_mem_iff triangle).1 ⟨[0, 1, 2], by decide, by decide⟩ -- Edges with an undeclared endpoint are never used: `0 - 7 - 2` does not close the path. -private def detour : Graph := ([0, 1, 2], [(0, 1), (1, 2), (2, 7), (7, 0)]) +private def detour : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2), (2, 7), (7, 0)]⟩ example : HamCycle.toTSP detour = ⟨[[2, 1, 2], [1, 2, 1], [2, 1, 2]], 3⟩ := by decide example : detour ∉ HamCycle := fun h => notMem_TSP (by decide) ((HamCycle.toTSP_mem_iff detour).2 h) -- Graphs with fewer than three distinct vertices map to a fixed no-instance. -example : HamCycle.toTSP ([0, 1, 0], [(0, 1)]) = ⟨[[1]], 0⟩ := by decide -example : HamCycle.toTSP ([], []) = ⟨[[1]], 0⟩ := by decide -example : (([0, 1, 0], [(0, 1)]) : Graph) ∉ HamCycle := +example : HamCycle.toTSP ⟨[0, 1, 0], [(0, 1)]⟩ = ⟨[[1]], 0⟩ := by decide +example : HamCycle.toTSP ⟨[], []⟩ = ⟨[[1]], 0⟩ := by decide +example : (⟨[0, 1, 0], [(0, 1)]⟩ : Graph) ∉ HamCycle := fun h => notMem_TSP (by decide) ((HamCycle.toTSP_mem_iff _).2 h) -example : (([], []) : Graph) ∉ HamCycle := +example : (⟨[], []⟩ : Graph) ∉ HamCycle := fun h => notMem_TSP (by decide) ((HamCycle.toTSP_mem_iff _).2 h) end CslibFrontierTests.NPCompleteGraphs.TSP diff --git a/CslibFrontierTests/NPCompleteGraphs/Undirected.lean b/CslibFrontierTests/NPCompleteGraphs/Undirected.lean index 5212e30b16..91b484ab1d 100644 --- a/CslibFrontierTests/NPCompleteGraphs/Undirected.lean +++ b/CslibFrontierTests/NPCompleteGraphs/Undirected.lean @@ -40,26 +40,26 @@ example : ComputableInPolyTime (fun G : Graph => G.split.split) encGraph encGrap /-! ### The split graph -/ -- Gadget vertices are `Nat.pair` of the first-occurrence index and the position in the gadget. -example : Graph.split ([0, 1], [(0, 1)]) = - ([0, 1, 4, 2, 3, 5], [(0, 1), (1, 4), (2, 3), (3, 5), (4, 2)]) := by decide +example : Graph.split ⟨[0, 1], [(0, 1)]⟩ = + ⟨[0, 1, 4, 2, 3, 5], [(0, 1), (1, 4), (2, 3), (3, 5), (4, 2)]⟩ := by decide -- A self-loop `(v, v)` becomes the edge `out(v) - in(v)`: the split graph is a triangle. -example : Graph.split ([0], [(0, 0)]) = ([0, 1, 4], [(0, 1), (1, 4), (4, 0)]) := by decide +example : Graph.split ⟨[0], [(0, 0)]⟩ = ⟨[0, 1, 4], [(0, 1), (1, 4), (4, 0)]⟩ := by decide -- An arc to an undeclared vertex becomes an edge to the undeclared label `Nat.pair 1 0 = 2`. -example : Graph.split ([0], [(0, 7)]) = ([0, 1, 4], [(0, 1), (1, 4), (4, 2)]) := by decide -example : 2 ∉ (Graph.split ([0], [(0, 7)])).1 := by decide +example : Graph.split ⟨[0], [(0, 7)]⟩ = ⟨[0, 1, 4], [(0, 1), (1, 4), (4, 2)]⟩ := by decide +example : 2 ∉ (Graph.split ⟨[0], [(0, 7)]⟩).vertices := by decide -- Repeated vertex entries share the gadget of their first occurrence. -example : Graph.split ([5, 5], []) = - ([0, 1, 4, 0, 1, 4], [(0, 1), (1, 4), (0, 1), (1, 4)]) := by decide +example : Graph.split ⟨[5, 5], []⟩ = + ⟨[0, 1, 4, 0, 1, 4], [(0, 1), (1, 4), (0, 1), (1, 4)]⟩ := by decide -- The labels of an undeclared vertex are never declared vertices of the split graph. -example : Graph.splitVertex ([0, 1], []) 9 0 ∉ (Graph.split ([0, 1], [])).1 := by decide +example : Graph.splitVertex ⟨[0, 1], []⟩ 9 0 ∉ (Graph.split ⟨[0, 1], []⟩).vertices := by decide /-! ### Hamiltonian paths -/ -private def path3 : Graph := ([0, 1, 2], [(0, 1), (1, 2)]) +private def path3 : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2)]⟩ private theorem path3_mem_DirHamPath : (path3, 0, 2) ∈ DirHamPath := ⟨[0, 1, 2], by decide, fun _ => Iff.rfl, rfl, rfl, by decide⟩ @@ -87,15 +87,15 @@ example : (path3.split, path3.splitVertex 7 0, path3.splitVertex 2 2) ∉ HamPat simpa [path3] using (hcov 7).1 (List.mem_of_head? hs) -- `s = t` in a one-vertex graph: the path `in(0) - mid(0) - out(0)`. -example : (Graph.split ([0], []), Graph.splitVertex ([0], []) 0 0, - Graph.splitVertex ([0], []) 0 2) ∈ HamPath := +example : (Graph.split ⟨[0], []⟩, Graph.splitVertex ⟨[0], []⟩ 0 0, + Graph.splitVertex ⟨[0], []⟩ 0 2) ∈ HamPath := (Graph.split_mem_HamPath_iff _ _ _).2 ⟨[0], by decide, fun _ => Iff.rfl, rfl, rfl, by decide⟩ /-! ### Hamiltonian cycles -/ /-- A declared vertex without outgoing arcs rules out a directed Hamiltonian cycle. -/ -private theorem notMem_DirHamCycle_of_sink {G : Graph} {v : ℕ} (hv : v ∈ G.1) - (hsink : ∀ w, (v, w) ∉ G.2) : G ∉ DirHamCycle := by +private theorem notMem_DirHamCycle_of_sink {G : Graph} {v : ℕ} (hv : v ∈ G.vertices) + (hsink : ∀ w, (v, w) ∉ G.edges) : G ∉ DirHamCycle := by rintro ⟨p, -, hcov, -, hch⟩ obtain ⟨A, B, rfl⟩ := List.append_of_mem ((hcov v).2 hv) rw [Cycle.coe_eq_coe.2 (List.isRotated_append (l := A) (l' := v :: B)), List.cons_append, @@ -104,7 +104,7 @@ private theorem notMem_DirHamCycle_of_sink {G : Graph} {v : ℕ} (hv : v ∈ G.1 · exact hsink v (hch.1 v (by simp [hBA])) · exact hsink y (hch.1 y (by simp [hBA])) -private def triangle : Graph := ([0, 1, 2], [(0, 1), (1, 2), (2, 0)]) +private def triangle : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2), (2, 0)]⟩ example : triangle ∈ DirHamCycle := ⟨[0, 1, 2], by decide, fun _ => Iff.rfl, by decide, by @@ -122,28 +122,28 @@ example : path3.split ∉ HamCycle := by exact notMem_DirHamCycle_of_sink (v := 2) (by decide) (by simp [path3]) -- A directed 2-cycle: its split graph is a 6-cycle. -example : Graph.split ([0, 1], [(0, 1), (1, 0)]) ∈ HamCycle := +example : Graph.split ⟨[0, 1], [(0, 1), (1, 0)]⟩ ∈ HamCycle := (Graph.split_mem_HamCycle_iff _).2 ⟨[0, 1], by decide, fun _ => Iff.rfl, by decide, by rw [Cycle.chain_coe_cons] decide⟩ -- A single arc between two vertices gives a path, not a cycle. -example : Graph.split ([0, 1], [(0, 1)]) ∉ HamCycle := by +example : Graph.split ⟨[0, 1], [(0, 1)]⟩ ∉ HamCycle := by rw [Graph.split_mem_HamCycle_iff] exact notMem_DirHamCycle_of_sink (v := 1) (by decide) (by simp) -- A single vertex is a directed Hamiltonian cycle exactly with a self-loop; the split graph is -- then a triangle, and otherwise a path on three vertices. -example : Graph.split ([0], [(0, 0)]) ∈ HamCycle := +example : Graph.split ⟨[0], [(0, 0)]⟩ ∈ HamCycle := (Graph.split_mem_HamCycle_iff _).2 ⟨[0], by decide, fun _ => Iff.rfl, by decide, (Cycle.chain_singleton _ _).2 (by decide)⟩ -example : Graph.split ([0], []) ∉ HamCycle := by +example : Graph.split ⟨[0], []⟩ ∉ HamCycle := by rw [Graph.split_mem_HamCycle_iff] exact notMem_DirHamCycle_of_sink (v := 0) (by decide) (by simp) -- The empty graph has no Hamiltonian cycle. -example : Graph.split ([], []) ∉ HamCycle := by +example : Graph.split ⟨[], []⟩ ∉ HamCycle := by rw [Graph.split_mem_HamCycle_iff] rintro ⟨p, -, hcov, hne, -⟩ obtain ⟨v, hv⟩ := List.exists_mem_of_ne_nil p hne diff --git a/CslibFrontierTests/NPCompleteness.lean b/CslibFrontierTests/NPCompleteness.lean index 3a2b889ce1..d3169e8b68 100644 --- a/CslibFrontierTests/NPCompleteness.lean +++ b/CslibFrontierTests/NPCompleteness.lean @@ -56,13 +56,14 @@ theorem clique_npComplete : NPComplete (encPair encGraph encNat) Clique := ⟨clique_mem_NP, IndependentSet_npHard.trans independentSet_to_clique⟩ -- The reduction keeps a zero target and complements only the declared vertices. -example : complementInstance (([0, 1], []), 0) = (([0, 1], [(0, 1), (1, 0)]), 0) := by decide +example : complementInstance ((⟨[0, 1], []⟩ : Graph), 0) = + ((⟨[0, 1], [(0, 1), (1, 0)]⟩ : Graph), 0) := by decide -- The empty graph has a zero-size clique, but no clique of size one. -example : ((([], []) : Graph), 0) ∈ Clique := +example : ((⟨[], []⟩ : Graph), 0) ∈ Clique := ⟨∅, by simp, rfl⟩ -example : ((([], []) : Graph), 1) ∉ Clique := by +example : ((⟨[], []⟩ : Graph), 1) ∉ Clique := by intro h have := Graph.le_card_of_mem_Clique h simp at this diff --git a/CslibFrontierTests/NPWitness.lean b/CslibFrontierTests/NPWitness.lean index ab567ab2fa..dc46e55445 100644 --- a/CslibFrontierTests/NPWitness.lean +++ b/CslibFrontierTests/NPWitness.lean @@ -153,14 +153,14 @@ example : ¬∃ w : List Bool, w.length = Polynomial.X.eval (encPair (encList en -- elements (`[3, 5, 3, 7].toFinset.card = 3`). example : selectDistinct [3, 5, 3, 7] (List.replicate 4 true) = [3, 5, 7] ∧ - Graph.distinctVertices ([2, 1, 2, 1], []) = [2, 1] ∧ + Graph.distinctVertices ⟨[2, 1, 2, 1], []⟩ = [2, 1] ∧ [3, 5, 3, 7].toFinset.card = 3 := ⟨rfl, rfl, (length_selectDistinct_replicate [3, 5, 3, 7]).symm.trans rfl⟩ /-! ### Vertex covers through the subset verifier -/ /-- The path `1 - 2 - 3`. -/ -def path3 : Graph := ([1, 2, 3], [(1, 2), (2, 3)]) +def path3 : Graph := ⟨[1, 2, 3], [(1, 2), (2, 3)]⟩ -- The middle vertex covers both edges; an end vertex or wrong size is rejected; a repeated -- declared vertex is selected only at its first occurrence. @@ -168,9 +168,9 @@ example : VertexCover.verifier ((path3, 1), [false, true, false]) = true ∧ VertexCover.verifier ((path3, 1), [true, false, false]) = false ∧ VertexCover.verifier ((path3, 1), [true, true, false]) = false ∧ - VertexCover.verifier (((([2, 2, 1, 3], [(1, 2), (2, 3)]) : Graph), 1), + VertexCover.verifier ((((⟨[2, 2, 1, 3], [(1, 2), (2, 3)]⟩ : Graph), 1)), [true, true, false, false]) = true ∧ - VertexCover.verifier (((([2, 2, 1, 3], [(1, 2), (2, 3)]) : Graph), 1), + VertexCover.verifier ((((⟨[2, 2, 1, 3], [(1, 2), (2, 3)]⟩ : Graph), 1)), [false, true, false, false]) = false := ⟨rfl, rfl, rfl, rfl, rfl⟩ diff --git a/CslibFrontierTests/OrthogonalPath.lean b/CslibFrontierTests/OrthogonalPath.lean index 94f950c0eb..ff5bdd9f20 100644 --- a/CslibFrontierTests/OrthogonalPath.lean +++ b/CslibFrontierTests/OrthogonalPath.lean @@ -10,14 +10,15 @@ import CslibFrontier.Computability.Complexity.Problems.Graph.OrthogonalPath open Cslib.Complexity Cslib.Orthogonal -private def exampleGraph : Graph := ([0, 1, 2, 3, 4, 5], [(0, 2), (1, 3), (4, 5)]) +private def exampleGraph : Graph := ⟨[0, 1, 2, 3, 4, 5], [(0, 2), (1, 3), (4, 5)]⟩ -private theorem declared : ∀ e ∈ exampleGraph.2, e.1 ∈ exampleGraph.1 ∧ e.2 ∈ exampleGraph.1 := by +private theorem declared : + ∀ e ∈ exampleGraph.edges, e.1 ∈ exampleGraph.vertices ∧ e.2 ∈ exampleGraph.vertices := by intro e he simp only [exampleGraph, List.mem_cons, List.not_mem_nil, or_false] at he rcases he with rfl | rfl | rfl <;> decide -private theorem loopless : ∀ e ∈ exampleGraph.2, e.1 ≠ e.2 := by +private theorem loopless : ∀ e ∈ exampleGraph.edges, e.1 ≠ e.2 := by intro e he simp only [exampleGraph, List.mem_cons, List.not_mem_nil, or_false] at he rcases he with rfl | rfl | rfl <;> decide @@ -82,22 +83,24 @@ example : (by decide) (by decide) (by decide) not_center first_member second_member⟩ · intro h have hindex := Graph.unitPath_atMostTwo exampleGraph declared loopless degree - (by decide : 0 < exampleGraph.2.length) (by decide : 1 < exampleGraph.2.length) - (by decide : 2 < exampleGraph.2.length) (by decide) not_center first_member second_member h + (by decide : 0 < exampleGraph.edges.length) (by decide : 1 < exampleGraph.edges.length) + (by decide : 2 < exampleGraph.edges.length) (by decide) not_center first_member + second_member h omega · rw [Graph.unitPath_center_iff exampleGraph declared loopless (by decide) (by decide)] decide -private def reverseGraph : Graph := ([0, 1, 2, 3, 4, 5], [(2, 0), (3, 1), (4, 5)]) -private def starGraph : Graph := ([0, 1, 2, 3], [(0, 1), (0, 2), (0, 3)]) -private def returnStar : Graph := ([0, 1, 2, 3], [(1, 0), (2, 0), (3, 0)]) +private def reverseGraph : Graph := ⟨[0, 1, 2, 3, 4, 5], [(2, 0), (3, 1), (4, 5)]⟩ +private def starGraph : Graph := ⟨[0, 1, 2, 3], [(0, 1), (0, 2), (0, 3)]⟩ +private def returnStar : Graph := ⟨[0, 1, 2, 3], [(1, 0), (2, 0), (3, 0)]⟩ -private theorem star_declared : ∀ e ∈ starGraph.2, e.1 ∈ starGraph.1 ∧ e.2 ∈ starGraph.1 := by +private theorem star_declared : + ∀ e ∈ starGraph.edges, e.1 ∈ starGraph.vertices ∧ e.2 ∈ starGraph.vertices := by intro e he simp only [starGraph, List.mem_cons, List.not_mem_nil, or_false] at he rcases he with rfl | rfl | rfl <;> decide -private theorem star_loopless : ∀ e ∈ starGraph.2, e.1 ≠ e.2 := by +private theorem star_loopless : ∀ e ∈ starGraph.edges, e.1 ≠ e.2 := by intro e he simp only [starGraph, List.mem_cons, List.not_mem_nil, or_false] at he rcases he with rfl | rfl | rfl <;> decide @@ -127,10 +130,10 @@ example (d : Point) : returnStar.endpointNeighbor 1 true = (4, 5) ∧ returnStar.endpointNeighbor 2 true = (5, 4)) ∧ (exampleGraph.unitPath 0).getD 1 d = - Layout.center (exampleGraph.1.idxOf (exampleGraph.edgeEndpoint 0 false)) + + Layout.center (exampleGraph.vertices.idxOf (exampleGraph.edgeEndpoint 0 false)) + Layout.portStep (Graph.portAt exampleGraph.canonicalPorts 0 false) ∧ (exampleGraph.unitPath 0).getD ((exampleGraph.unitPath 0).length - 2) d = - Layout.center (exampleGraph.1.idxOf (exampleGraph.edgeEndpoint 0 true)) + + Layout.center (exampleGraph.vertices.idxOf (exampleGraph.edgeEndpoint 0 true)) + Layout.portStep (Graph.portAt exampleGraph.canonicalPorts 0 true) ∧ starGraph.endpointNeighbor 0 false ≠ starGraph.endpointNeighbor 1 false := ⟨by decide, by decide, by decide, by decide, by decide, diff --git a/CslibFrontierTests/Parameterized.lean b/CslibFrontierTests/Parameterized.lean index 70d25f15f4..4e96c31b53 100644 --- a/CslibFrontierTests/Parameterized.lean +++ b/CslibFrontierTests/Parameterized.lean @@ -154,7 +154,7 @@ example : /-! ### Vertex cover parameterized by target size -/ /-- The triangle on `{0, 1, 2}`. -/ -private def triangle : Graph := ([0, 1, 2], [(0, 1), (1, 2), (0, 2)]) +private def triangle : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2), (0, 2)]⟩ /-- Bounded search tree evaluations and membership witnesses on the triangle graph, plus `XP` closure for `VertexCoverᶜ`. -/ diff --git a/CslibFrontierTests/PartialMCSP.lean b/CslibFrontierTests/PartialMCSP.lean index 2bac772aa8..13897f434f 100644 --- a/CslibFrontierTests/PartialMCSP.lean +++ b/CslibFrontierTests/PartialMCSP.lean @@ -148,21 +148,21 @@ example : /-! ### Bipartite Permutation Independent Set -/ /-- An edgeless $n \times n$ bipartite permutation graph is always satisfiable, whereas a self-loop -on $(0, 0)$ for $n = 1$ (including `bpisOfColoring (([], []), 0)`) is unsatisfiable. -/ +on $(0, 0)$ for $n = 1$ (including `bpisOfColoring (⟨[], []⟩, 0)`) is unsatisfiable. -/ example (n : ℕ) : ((n, []) : BipartitePermutationGraph) ∈ BipartitePermutationIndependentSet ∧ ((1, [((0, 0), (0, 0))]) : BipartitePermutationGraph) ∉ BipartitePermutationIndependentSet ∧ - (BipartitePermutationIndependentSet.bpisOfColoring (([], []), 0)).1 = 1 ∧ - (BipartitePermutationIndependentSet.bpisOfColoring (([], []), 0)).2 = + (BipartitePermutationIndependentSet.bpisOfColoring (⟨[], []⟩, 0)).1 = 1 ∧ + (BipartitePermutationIndependentSet.bpisOfColoring (⟨[], []⟩, 0)).2 = [((0, 0), (0, 0))] ∧ - BipartitePermutationIndependentSet.bpisOfColoring (([], []), 0) ∉ + BipartitePermutationIndependentSet.bpisOfColoring (⟨[], []⟩, 0) ∉ BipartitePermutationIndependentSet := by refine ⟨⟨1, 1, fun _ h => by simp at h⟩, ?_, by decide, by decide, ?_⟩ · rintro ⟨π₁, π₂, h⟩ exact h ((0, 0), (0, 0)) (by simp) (by decide) (by decide) (by decide) (by decide) (Subsingleton.elim (π₁ 0) 0) (Subsingleton.elim (π₂ 0) 0) - · have hG : BipartitePermutationIndependentSet.bpisOfColoring (([], []), 0) = + · have hG : BipartitePermutationIndependentSet.bpisOfColoring (⟨[], []⟩, 0) = (1, [((0, 0), (0, 0))]) := by decide rw [hG] rintro ⟨π₁, π₂, h⟩ diff --git a/CslibFrontierTests/PlanarCompile.lean b/CslibFrontierTests/PlanarCompile.lean index db6206a9f5..7bb9d94347 100644 --- a/CslibFrontierTests/PlanarCompile.lean +++ b/CslibFrontierTests/PlanarCompile.lean @@ -41,7 +41,7 @@ example : decide -- A nonempty instance of the actual continuous drawing predicate. -example : Graph.IsPlanarOrthogonalDrawing ([0, 1], [(0, 1)]) +example : Graph.IsPlanarOrthogonalDrawing ⟨[0, 1], [(0, 1)]⟩ ([(0, 0), (1, 0)], [[(0, 0), (1, 0)]]) := by refine ⟨rfl, rfl, by decide, by decide, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ · intro e he diff --git a/CslibFrontierTests/PlanarNonSelfDualRotation.lean b/CslibFrontierTests/PlanarNonSelfDualRotation.lean index 7dc1110bf1..574e798e3b 100644 --- a/CslibFrontierTests/PlanarNonSelfDualRotation.lean +++ b/CslibFrontierTests/PlanarNonSelfDualRotation.lean @@ -18,7 +18,7 @@ open Cslib.Complexity -- Inner edges I0,I1,I2 followed by the parallel outer edges O0,O1,O2. private abbrev triangleGraph : Graph := - ([0, 1, 2], [(0, 1), (1, 2), (2, 0), (0, 1), (1, 2), (2, 0)]) + ⟨[0, 1, 2], [(0, 1), (1, 2), (2, 0), (0, 1), (1, 2), (2, 0)]⟩ private def triangleRho : Equiv.Perm (Graph.Dart triangleGraph) := [(0, false), (3, false), (5, true), (2, true)].formPerm * @@ -54,7 +54,7 @@ example : ((triangleFaces.drop 1).take 3).map (List.map Graph.Dart.vertex) = [[0, 1], [1, 2], [2, 0]] := by decide -- The only binary intersection used for the constant lemma is three parallel edges. -private abbrev binaryGraph : Graph := ([0, 1], [(0, 1), (0, 1), (0, 1)]) +private abbrev binaryGraph : Graph := ⟨[0, 1], [(0, 1), (0, 1), (0, 1)]⟩ private def binaryRho : Equiv.Perm (Graph.Dart binaryGraph) := [(0, false), (2, false), (1, false)].formPerm * diff --git a/CslibFrontierTests/RotationCertificate.lean b/CslibFrontierTests/RotationCertificate.lean index dfaa3aec50..80c8626421 100644 --- a/CslibFrontierTests/RotationCertificate.lean +++ b/CslibFrontierTests/RotationCertificate.lean @@ -15,15 +15,15 @@ open RotationCertificate namespace RotationCertificateTests -example : verifyGenusZero ([], []) ⟨[]⟩ = true := by decide -example : verifyGenusZero ([0, 10, 10], []) ⟨[]⟩ = true := by decide +example : verifyGenusZero ⟨[], []⟩ ⟨[]⟩ = true := by decide +example : verifyGenusZero ⟨[0, 10, 10], []⟩ ⟨[]⟩ = true := by decide -- A bridge has one two-dart face orbit. -example : verifyGenusZero ([0, 1], [(0, 1)]) ⟨[0, 1]⟩ = true := by decide -example : faceTable ([0, 1], [(0, 1)]) ⟨[0, 1]⟩ = [1, 0] := by decide +example : verifyGenusZero ⟨[0, 1], [(0, 1)]⟩ ⟨[0, 1]⟩ = true := by decide +example : faceTable ⟨[0, 1], [(0, 1)]⟩ ⟨[0, 1]⟩ = [1, 0] := by decide example : (List.range 2).countP (faceRepBool 2 [1, 0]) = 1 := by decide -private def bridgeRotation : RotationSystem ([0, 1], [(0, 1)]) where +private def bridgeRotation : RotationSystem ⟨[0, 1], [(0, 1)]⟩ where rotate := Equiv.refl _ sameCycle_iff := by decide @@ -35,12 +35,12 @@ example : ¬bridgeRotation.IsFaceRepresentative (0, true) := by omega -- A loop retains two darts, and both singleton face orbits must be counted. -example : verifyGenusZero ([0], [(0, 0)]) ⟨[1, 0]⟩ = true := by decide -example : faceTable ([0], [(0, 0)]) ⟨[1, 0]⟩ = [0, 1] := by decide +example : verifyGenusZero ⟨[0], [(0, 0)]⟩ ⟨[1, 0]⟩ = true := by decide +example : faceTable ⟨[0], [(0, 0)]⟩ ⟨[1, 0]⟩ = [0, 1] := by decide example : (List.range 2).countP (faceRepBool 2 [0, 1]) = 2 := by decide -example : verifyRotation ([0], [(0, 0)]) ⟨[0, 1]⟩ = false := by decide +example : verifyRotation ⟨[0], [(0, 0)]⟩ ⟨[0, 1]⟩ = false := by decide -private def parallelThree : Graph := ([0, 1], [(0, 1), (0, 1), (0, 1)]) +private def parallelThree : Graph := ⟨[0, 1], [(0, 1), (0, 1), (0, 1)]⟩ example : verifyGenusZero parallelThree ⟨[2, 5, 4, 1, 0, 3]⟩ = true := by decide example : verifyRotation parallelThree ⟨[2, 3, 4, 5, 0, 1]⟩ = true := by decide @@ -52,30 +52,30 @@ example : (List.range 6).countP (faceRepBool 6 (faceTable parallelThree ⟨[2, 3, 4, 5, 0, 1]⟩)) = 1 := by decide -- Each component gets its own Euler test; isolates use their point-component face convention. -example : verifyGenusZero ([0, 1, 2, 3, 99], [(0, 1), (2, 3)]) ⟨[0, 1, 2, 3]⟩ = true := by +example : verifyGenusZero ⟨[0, 1, 2, 3, 99], [(0, 1), (2, 3)]⟩ ⟨[0, 1, 2, 3]⟩ = true := by decide -example : verifyGenusZero ([0, 1, 2, 3], [(0, 1), (0, 1), (0, 1), (2, 3)]) +example : verifyGenusZero ⟨[0, 1, 2, 3], [(0, 1), (0, 1), (0, 1), (2, 3)]⟩ ⟨[2, 3, 4, 5, 0, 1, 6, 7]⟩ = false := by decide -example : verifyRotation ([0, 1], [(0, 1)]) ⟨[0]⟩ = false := by decide -example : verifyRotation ([0, 1], [(0, 1)]) ⟨[0, 1, 2]⟩ = false := by decide -example : verifyRotation ([0, 1], [(0, 1)]) ⟨[0, 0]⟩ = false := by decide -example : verifyRotation ([0, 1], [(0, 1)]) ⟨[0, 2 ^ 100]⟩ = false := by decide -example : verifyRotation ([0], [(0, 1)]) ⟨[0, 1]⟩ = false := by decide +example : verifyRotation ⟨[0, 1], [(0, 1)]⟩ ⟨[0]⟩ = false := by decide +example : verifyRotation ⟨[0, 1], [(0, 1)]⟩ ⟨[0, 1, 2]⟩ = false := by decide +example : verifyRotation ⟨[0, 1], [(0, 1)]⟩ ⟨[0, 0]⟩ = false := by decide +example : verifyRotation ⟨[0, 1], [(0, 1)]⟩ ⟨[0, 2 ^ 100]⟩ = false := by decide +example : verifyRotation ⟨[0], [(0, 1)]⟩ ⟨[0, 1]⟩ = false := by decide example : walk [2 ^ 100] 3 (2 ^ 100) = 0 := by decide -example : verifyGenusZero ([2 ^ 100, 2 ^ 100 + 1], [(2 ^ 100, 2 ^ 100 + 1)]) +example : verifyGenusZero ⟨[2 ^ 100, 2 ^ 100 + 1], [(2 ^ 100, 2 ^ 100 + 1)]⟩ ⟨[0, 1]⟩ = true := by decide -- A checked mathematical client decodes the finite loop certificate to the existing rotation. -private noncomputable def loopRotation : RotationSystem ([0], [(0, 0)]) := +private noncomputable def loopRotation : RotationSystem ⟨[0], [(0, 0)]⟩ := decode _ ⟨[1, 0]⟩ (by decide) example : loopRotation.NumericalGenusZero := - (verifyEuler_eq_true_iff (by decide : verifyRotation ([0], [(0, 0)]) ⟨[1, 0]⟩ = true)).mp + (verifyEuler_eq_true_iff (by decide : verifyRotation ⟨[0], [(0, 0)]⟩ ⟨[1, 0]⟩ = true)).mp (by decide) example : (encRotationCertificate (ofRotation loopRotation)).length ≤ - 10 * ((encGraph ([0], [(0, 0)])).length + 1) ^ 2 := length_enc_ofRotation_le _ + 10 * ((encGraph ⟨[0], [(0, 0)]⟩).length + 1) ^ 2 := length_enc_ofRotation_le _ -- Automation composes the native checker with a concrete certificate supplier. example : ComputableInPolyTime (fun G : Graph => verifyGenusZero G ⟨[]⟩) encGraph encBool := by diff --git a/CslibFrontierTests/RotationDual.lean b/CslibFrontierTests/RotationDual.lean index 06ede41b08..129364d5f4 100644 --- a/CslibFrontierTests/RotationDual.lean +++ b/CslibFrontierTests/RotationDual.lean @@ -11,10 +11,10 @@ import Mathlib.Tactic.FinCases open Cslib Cslib.Complexity Cslib.Complexity.Graph -private abbrev bridge : Graph := ([0, 1], [(0, 1)]) -private abbrev loopGraph : Graph := ([0], [(0, 0)]) -private abbrev digon : Graph := ([0, 1], [(0, 1), (0, 1)]) -private abbrev torusBouquet : Graph := ([0], [(0, 0), (0, 0)]) +private abbrev bridge : Graph := ⟨[0, 1], [(0, 1)]⟩ +private abbrev loopGraph : Graph := ⟨[0], [(0, 0)]⟩ +private abbrev digon : Graph := ⟨[0, 1], [(0, 1), (0, 1)]⟩ +private abbrev torusBouquet : Graph := ⟨[0], [(0, 0), (0, 0)]⟩ private def bridgeRotation : RotationSystem bridge where rotate := Equiv.refl _ @@ -35,7 +35,7 @@ private def torusRotation : RotationSystem torusBouquet where sameCycle_iff := by decide -- The bridge's single source face becomes a loop, retaining its two different darts. -example : bridgeRotation.dualGraph = ([0], [(0, 0)]) := by decide +example : bridgeRotation.dualGraph = ⟨[0], [(0, 0)]⟩ := by decide example : bridgeRotation.dualRotation.faceNext (bridgeRotation.dualDartEquiv (0, false)) = @@ -46,14 +46,14 @@ example : bridgeRotation.dualRotation.faceNext bridgeRotation.dualDartEquiv (0, true) := by decide -- A sphere loop has two singleton source faces; its dual is an ordinary edge. -example : loopRotation.dualGraph = ([0, 1], [(0, 1)]) := by decide +example : loopRotation.dualGraph = ⟨[0, 1], [(0, 1)]⟩ := by decide example : loopRotation.dualRotation.rotate (loopRotation.dualDartEquiv (0, false)) = loopRotation.dualDartEquiv (0, false) := by decide -- Distinct parallel edge positions survive dualization. -example : digonRotation.dualGraph.2 = [(0, 1), (1, 0)] := by decide +example : digonRotation.dualGraph.edges = [(0, 1), (1, 0)] := by decide example : digonRotation.faceId (0, false) = digonRotation.faceId (1, true) := by decide @@ -120,17 +120,17 @@ example : ¬ ∃ spin : ℕ → Bool, simp [edgeGradient, Dart.vertex, edgeEndpoint] exact Bool.false_ne_true (by simpa only [hz, decide_true] using he) -private abbrev pointGraph : Graph := ([2 ^ 100, 2 ^ 100, 7], []) +private abbrev pointGraph : Graph := ⟨[2 ^ 100, 2 ^ 100, 7], []⟩ private def pointRotation : RotationSystem pointGraph where rotate := Equiv.refl _ sameCycle_iff := by intro d; exact d.1.elim0 -example : pointRotation.dualGraph = ([], []) := by decide +example : pointRotation.dualGraph = ⟨[], []⟩ := by decide -- Sparse labels and duplicate declared point records require no finite instance on Nat. example (x : Fin 0 → Bool) : ∃ spin : ℕ → Bool, pointGraph.edgeGradient spin = x := by - have hG : ∀ v ∈ pointGraph.incidenceVertices, v ∈ pointGraph.1 := by decide + have hG : ∀ v ∈ pointGraph.incidenceVertices, v ∈ pointGraph.vertices := by decide have hgenus : pointRotation.NumericalGenusZero := by intro root _ have hn : ¬ ∃ d : Dart pointGraph, d.vertex = root := by @@ -159,7 +159,7 @@ example (x : Fin 2 → Bool) : rfl private abbrev sparseMixed : Graph := - ([2 ^ 100, 2 ^ 100 + 7, 2 ^ 100 + 7, 5, 900000], [(2 ^ 100, 2 ^ 100 + 7), (5, 5)]) + ⟨[2 ^ 100, 2 ^ 100 + 7, 2 ^ 100 + 7, 5, 900000], [(2 ^ 100, 2 ^ 100 + 7), (5, 5)]⟩ private def sparseMixedRotation : RotationSystem sparseMixed where rotate := @@ -172,7 +172,7 @@ private def sparseMixedRotation : RotationSystem sparseMixed where -- Disconnected bridge, loop and point components; duplicate and very large vertex names. example (x : Fin 2 → Bool) (hx : x 1 = false) : ∃ spin : ℕ → Bool, sparseMixed.edgeGradient spin = x := by - have hG : ∀ v ∈ sparseMixed.incidenceVertices, v ∈ sparseMixed.1 := by decide + have hG : ∀ v ∈ sparseMixed.incidenceVertices, v ∈ sparseMixed.vertices := by decide have hgenus := (RotationCertificate.verifyEuler_eq_true_iff (RotationCertificate.verifyRotation_ofRotation sparseMixedRotation hG)).mp (by decide) have hgenus' : sparseMixedRotation.NumericalGenusZero := @@ -185,7 +185,7 @@ example (x : Fin 2 → Bool) (hx : x 1 = false) : rw [← hseed] exact sparseMixedRotation.faceEven_edgeGradient seed -private abbrev undeclaredBouquet : Graph := ([], [(0, 0), (0, 0)]) +private abbrev undeclaredBouquet : Graph := ⟨[], [(0, 0), (0, 0)]⟩ private def undeclaredRotation : RotationSystem undeclaredBouquet where rotate := torusRotation.rotate @@ -194,6 +194,9 @@ private def undeclaredRotation : RotationSystem undeclaredBouquet where -- The endpoint guard is essential: undeclared roots make the Euler predicate vacuous. example : undeclaredRotation.NumericalGenusZero := by intro _ h - cases h + simp at h -example : ¬ (∀ v ∈ undeclaredBouquet.incidenceVertices, v ∈ undeclaredBouquet.1) := by decide +example : ¬ (∀ v ∈ undeclaredBouquet.incidenceVertices, v ∈ undeclaredBouquet.vertices) := by + intro h + have := h 0 (by decide) + simp at this diff --git a/CslibFrontierTests/RotationSystem.lean b/CslibFrontierTests/RotationSystem.lean index 7c5da82f49..7c708aa187 100644 --- a/CslibFrontierTests/RotationSystem.lean +++ b/CslibFrontierTests/RotationSystem.lean @@ -10,8 +10,8 @@ import CslibFrontier.Computability.Complexity.Problems.Graph.RotationSystem open Cslib.Complexity Cslib.Complexity.Graph -private abbrev bridge : Graph := ([0, 1], [(0, 1)]) -private abbrev loopGraph : Graph := ([0], [(0, 0)]) +private abbrev bridge : Graph := ⟨[0, 1], [(0, 1)]⟩ +private abbrev loopGraph : Graph := ⟨[0], [(0, 0)]⟩ private def bridgeRotation : RotationSystem bridge where rotate := Equiv.refl _ @@ -44,11 +44,11 @@ example : (Quotient.mk _ (0, false) : bridgeRotation.Face) = Quotient.mk _ (0, t change bridgeRotation.faceNext.SameCycle (0, false) (0, true) decide -example : ((⟨0, by decide⟩, false) : Dart ([0, 1], [(0, 1), (0, 1)])) ≠ +example : ((⟨0, by decide⟩, false) : Dart ⟨[0, 1], [(0, 1), (0, 1)]⟩) ≠ (⟨1, by decide⟩, false) := by decide -example : Fintype.card (Dart ([], [])) = 0 := by decide +example : Fintype.card (Dart ⟨[], []⟩) = 0 := by decide -example : RotationSystem ([], []) where +example : RotationSystem ⟨[], []⟩ where rotate := Equiv.refl _ sameCycle_iff := by intro d; exact d.1.elim0 diff --git a/CslibFrontierTests/SATIncidence.lean b/CslibFrontierTests/SATIncidence.lean index d6be07d70c..fa0f4dfde7 100644 --- a/CslibFrontierTests/SATIncidence.lean +++ b/CslibFrontierTests/SATIncidence.lean @@ -16,16 +16,16 @@ open Cslib.Complexity Cslib.Logic.PL -- Vertex names depend on the number of distinct variables, not their numeric magnitudes. example : SAT.incidenceGraph ([[(17, true), (17, false)], [], [(100000000000000000000, false)]] : CNF ℕ) = - ([0, 1, 2, 3, 4], [(0, 2), (0, 2), (1, 4)]) := by decide + ⟨[0, 1, 2, 3, 4], [(0, 2), (0, 2), (1, 4)]⟩ := by decide -- An empty clause remains an isolated clause vertex. Repeated clauses have distinct vertices. -example : SAT.incidenceGraph ([[], []] : CNF ℕ) = ([0, 1], []) := by decide +example : SAT.incidenceGraph ([[], []] : CNF ℕ) = ⟨[0, 1], []⟩ := by decide -example : SAT.incidenceGraph ([] : CNF ℕ) = ([], []) := by decide +example : SAT.incidenceGraph ([] : CNF ℕ) = ⟨[], []⟩ := by decide -- The construction works for ordinary finite variable types too. example : SAT.incidenceGraph ([[(true, false)], [(false, true)]] : CNF Bool) = - ([0, 1, 2, 3], [(0, 2), (1, 3)]) := by decide + ⟨[0, 1, 2, 3], [(0, 2), (1, 3)]⟩ := by decide -- One actual width/occurrence-bounded formula exercises canonical degree-three ports. private def boundedFormula : CNF ℕ := @@ -34,7 +34,7 @@ private def boundedFormula : CNF ℕ := private def boundedGraph : Graph := SAT.incidenceGraph boundedFormula example : boundedGraph = - ([0, 1, 2, 3, 4, 5], [(0, 3), (1, 3), (0, 4), (1, 4), (2, 4), (0, 5), (2, 5)]) := by decide + ⟨[0, 1, 2, 3, 4, 5], [(0, 3), (1, 3), (0, 4), (1, 4), (2, 4), (0, 5), (2, 5)]⟩ := by decide private theorem bounded_degree : ∀ v, boundedGraph.incidenceVertices.count v ≤ 3 := by intro v @@ -58,7 +58,7 @@ example : Graph.portAt boundedGraph.canonicalPorts 0 false ≠ Graph.portAt boundedGraph.canonicalPorts 2 false := by have h := Graph.canonical_index_port_ne boundedGraph (fun _ => SAT.incidenceGraph_endpoints) bounded_degree - (by decide : 0 < boundedGraph.2.length) (by decide : 2 < boundedGraph.2.length) + (by decide : 0 < boundedGraph.edges.length) (by decide : 2 < boundedGraph.edges.length) (by decide) false false exact fun hp => h (Prod.ext (by decide) hp) diff --git a/CslibFrontierTests/SpanningTrees.lean b/CslibFrontierTests/SpanningTrees.lean index 968f2c70b7..52bd9926fd 100644 --- a/CslibFrontierTests/SpanningTrees.lean +++ b/CslibFrontierTests/SpanningTrees.lean @@ -36,23 +36,23 @@ example (A : Matrix (Fin 3) (Fin 2) ℤ) (B : Matrix (Fin 2) (Fin 3) ℤ) : (A * /-! ### Concrete spanning-tree counts -/ /-- The triangle `K₃`. -/ -def triangle : Graph := ([0, 1, 2], [(0, 1), (1, 2), (2, 0)]) +def triangle : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2), (2, 0)]⟩ /-- The complete graph `K₄`. -/ -def k4 : Graph := ([0, 1, 2, 3], [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]) +def k4 : Graph := ⟨[0, 1, 2, 3], [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]⟩ /-- The `4`-cycle. -/ -def cycle4 : Graph := ([0, 1, 2, 3], [(0, 1), (1, 2), (2, 3), (3, 0)]) +def cycle4 : Graph := ⟨[0, 1, 2, 3], [(0, 1), (1, 2), (2, 3), (3, 0)]⟩ /-- The path `0 - 1 - 2`. -/ -def path3 : Graph := ([0, 1, 2], [(0, 1), (1, 2)]) +def path3 : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2)]⟩ /-- An edge and an isolated vertex. -/ -def disconnected : Graph := ([0, 1, 2], [(0, 1)]) +def disconnected : Graph := ⟨[0, 1, 2], [(0, 1)]⟩ /-- The path `0 - 1 - 2`, with a repeated vertex, a loop, both orientations of an edge and an edge to the undeclared vertex `5`. -/ -def noisyPath : Graph := ([0, 1, 1, 2], [(0, 1), (1, 0), (1, 1), (1, 2), (2, 5)]) +def noisyPath : Graph := ⟨[0, 1, 1, 2], [(0, 1), (1, 0), (1, 1), (1, 2), (2, 5)]⟩ example : triangle.spanningTreeCount = 3 := by decide +kernel example : k4.spanningTreeCount = 16 := by decide +kernel @@ -60,14 +60,14 @@ example : cycle4.spanningTreeCount = 4 := by decide +kernel example : path3.spanningTreeCount = 1 := by decide +kernel example : disconnected.spanningTreeCount = 0 := by decide +kernel -- A single vertex: the reduced Laplacian is empty, with determinant `1`. -example : Graph.spanningTreeCount ([7], []) = 1 := by decide +kernel +example : Graph.spanningTreeCount ⟨[7], []⟩ = 1 := by decide +kernel -- No vertex: no tree, since trees are nonempty. -example : Graph.spanningTreeCount ([], [(0, 1)]) = 0 := by decide +kernel +example : Graph.spanningTreeCount ⟨[], [(0, 1)]⟩ = 0 := by decide +kernel example : noisyPath.spanningTreeCount = 1 := by decide +kernel /-- info: [3, 16, 4, 1, 0, 1, 0, 1] -/ #guard_msgs in -#eval [triangle, k4, cycle4, path3, disconnected, ([7], []), ([], [(0, 1)]), noisyPath].map +#eval [triangle, k4, cycle4, path3, disconnected, ⟨[7], []⟩, ⟨[], [(0, 1)]⟩, noisyPath].map Graph.spanningTreeCount -- The reduced Laplacian of the triangle, on the vertices `1` and `2`. @@ -77,21 +77,21 @@ example : triangle.reducedLaplacianRows = [[2, -1], [-1, 2]] := by decide +kerne /-- `K₄` has `16` spanning trees (Cayley's formula `n ^ (n - 2)`). -/ theorem natCard_isTree_k4 : - Nat.card {T : SimpleGraph {v // v ∈ k4.1} // T ≤ k4.induced ∧ T.IsTree} = 16 := by + Nat.card {T : SimpleGraph {v // v ∈ k4.vertices} // T ≤ k4.induced ∧ T.IsTree} = 16 := by have h := Graph.spanningTreeCount_eq_natCard k4 rw [show k4.spanningTreeCount = 16 by decide +kernel] at h exact_mod_cast h.symm -- A disconnected graph has no spanning tree. example : - Nat.card {T : SimpleGraph {v // v ∈ disconnected.1} // + Nat.card {T : SimpleGraph {v // v ∈ disconnected.vertices} // T ≤ disconnected.induced ∧ T.IsTree} = 0 := by have h := Graph.spanningTreeCount_eq_natCard disconnected rw [show disconnected.spanningTreeCount = 0 by decide +kernel] at h exact_mod_cast h.symm -- The matrix-tree theorem over `ℚ`: every reduced Laplacian determinant of `K₄` is `16`. -example (r : {v // v ∈ k4.1}) : +example (r : {v // v ∈ k4.vertices}) : ((k4.induced.lapMatrix ℚ).submatrix (Subtype.val : {v // v ≠ r} → _) Subtype.val).det = 16 := by rw [SimpleGraph.det_lapMatrix_submatrix_eq_natCard_isTree' ℚ, natCard_isTree_k4] @@ -120,7 +120,7 @@ example : ComputableInPolyTime Graph.spanningTreeCount encGraph encIntBin := Graph.computableInPolyTime_spanningTreeCount example : ComputableInPolyTime - (fun G : Graph => Nat.card {T : SimpleGraph {v // v ∈ G.1} // T ≤ G.induced ∧ T.IsTree}) + (fun G : Graph => Nat.card {T : SimpleGraph {v // v ∈ G.vertices} // T ≤ G.induced ∧ T.IsTree}) encGraph encNat := Graph.computableInPolyTime_natCard_isTree diff --git a/CslibFrontierTests/TSPHeldKarp.lean b/CslibFrontierTests/TSPHeldKarp.lean index c45e6f93cb..67e6461be0 100644 --- a/CslibFrontierTests/TSPHeldKarp.lean +++ b/CslibFrontierTests/TSPHeldKarp.lean @@ -91,11 +91,11 @@ example : (⟨D3, 2⟩ : TSPInstance) ∉ TSP := fun h => by decide /-- A triangle has a Hamiltonian cycle. -/ -example : (([0, 1, 2], [(0, 1), (1, 2), (2, 0)]) : Graph) ∈ HamCycle := +example : (⟨[0, 1, 2], [(0, 1), (1, 2), (2, 0)]⟩ : Graph) ∈ HamCycle := (HamCycle.heldKarpDecide_toTSP_eq_true_iff _).1 (by decide) /-- A path on three vertices has none. -/ -example : (([0, 1, 2], [(0, 1), (1, 2)]) : Graph) ∉ HamCycle := fun h => by +example : (⟨[0, 1, 2], [(0, 1), (1, 2)]⟩ : Graph) ∉ HamCycle := fun h => by have := (HamCycle.heldKarpDecide_toTSP_eq_true_iff _).2 h revert this decide diff --git a/CslibFrontierTests/VertexCoverApproximation.lean b/CslibFrontierTests/VertexCoverApproximation.lean index 85bab3eb36..c6427518d0 100644 --- a/CslibFrontierTests/VertexCoverApproximation.lean +++ b/CslibFrontierTests/VertexCoverApproximation.lean @@ -39,9 +39,9 @@ open Turing.MultiTapeTM /-! ### 1. The path on four vertices attains the factor 2 -/ /-- The path `0 - 1 - 2 - 3`. -/ -def path4 : Graph := ([0, 1, 2, 3], [(0, 1), (1, 2), (2, 3)]) +def path4 : Graph := ⟨[0, 1, 2, 3], [(0, 1), (1, 2), (2, 3)]⟩ -example : greedyMatching path4.2 = [(0, 1), (2, 3)] := by decide +kernel +example : greedyMatching path4.edges = [(0, 1), (2, 3)] := by decide +kernel example : approxCover path4 = [0, 1, 2, 3] := by decide +kernel @@ -70,7 +70,7 @@ example : (approxCover path4).length = 2 * path4.vertexCoverNum := by /-! ### 2. The star attains the factor 2 -/ /-- The star with centre `0` and leaves `1`, `2`, `3`. -/ -def star3 : Graph := ([0, 1, 2, 3], [(0, 1), (0, 2), (0, 3)]) +def star3 : Graph := ⟨[0, 1, 2, 3], [(0, 1), (0, 2), (0, 3)]⟩ example : approxCover star3 = [0, 1] := by decide +kernel @@ -95,13 +95,13 @@ example : star3.vertexCoverNum = 1 ∧ (approxCover star3).length = 2 * star3.ve example : matchingCover [(1, 1), (1, 2), (2, 1), (1, 2), (3, 3)] = [1, 2] := by decide +kernel /-- A loop and an edge to the undeclared vertex `5` impose nothing; the cover is empty. -/ -example : approxCover ([0, 1, 2], [(2, 2), (0, 5)]) = [] := by decide +kernel +example : approxCover ⟨[0, 1, 2], [(2, 2), (0, 5)]⟩ = [] := by decide +kernel /-- Undeclared edges are discarded before the scan, so they cannot block a declared edge. -/ -example : approxCover ([0, 1, 2], [(0, 5), (0, 1)]) = [0, 1] := by decide +kernel +example : approxCover ⟨[0, 1, 2], [(0, 5), (0, 1)]⟩ = [0, 1] := by decide +kernel /-- The empty graph gets the empty cover. -/ -example : approxCover ([], []) = [] := by decide +kernel +example : approxCover ⟨[], []⟩ = [] := by decide +kernel /-! ### 4. Polynomial time -/ diff --git a/CslibFrontierTests/VertexCoverKernel.lean b/CslibFrontierTests/VertexCoverKernel.lean index 7a4ba86f3e..0d5c1461ec 100644 --- a/CslibFrontierTests/VertexCoverKernel.lean +++ b/CslibFrontierTests/VertexCoverKernel.lean @@ -23,7 +23,7 @@ vertices `20` and `30` share an edge (active low), and `40, 50` have no low neig low). The kernel relabels `20, 30` to `0, 1`, pads with one isolated vertex `2` to reach `numVertices = 3`, and reduces the target to `param = 1`. -/ private def sampleGraph : Graph := - ([10, 20, 30, 40, 50], [(10, 20), (10, 30), (10, 40), (20, 30)]) + ⟨[10, 20, 30, 40, 50], [(10, 20), (10, 30), (10, 40), (20, 30)]⟩ example : high (sampleGraph, 2) = [10] ∧ @@ -31,7 +31,7 @@ example : active (sampleGraph, 2) = [20, 30] ∧ param (sampleGraph, 2) = 1 ∧ numVertices (sampleGraph, 2) = 3 ∧ - bussKernel (sampleGraph, 2) = (([0, 1, 2], [(0, 1), (1, 0)]), 1) ∧ + bussKernel (sampleGraph, 2) = (⟨[0, 1, 2], [(0, 1), (1, 0)]⟩, 1) ∧ (sampleGraph, 2) ∈ VertexCover ∧ bussKernel (sampleGraph, 2) ∈ VertexCover := ⟨by decide, by decide, by decide, by decide, by decide, by decide, @@ -43,11 +43,11 @@ example : leaves no active vertices (`active = []`) and reduced target `param = 1`, so one isolated low vertex is kept as padding to witness the cover `{10, 20}` of size `2`. -/ private def starGraph : Graph := - ([10, 20, 30, 40], [(10, 20), (10, 30), (10, 40)]) + ⟨[10, 20, 30, 40], [(10, 20), (10, 30), (10, 40)]⟩ example : active (starGraph, 2) = [] ∧ - bussKernel (starGraph, 2) = (([0], []), 1) ∧ + bussKernel (starGraph, 2) = (⟨[0], []⟩, 1) ∧ (starGraph, 2) ∈ VertexCover ∧ bussKernel (starGraph, 2) ∈ VertexCover := ⟨by decide, by decide, @@ -59,12 +59,12 @@ example : has one high vertex, leaving `param = 0`, whereas two stars on `{0, 1, 2, 3, 4, 5}` with centres `0` and `3` have two high vertices of degree `2 > 1`. -/ private def twoStars : Graph := - ([0, 1, 2, 3, 4, 5], [(0, 1), (0, 2), (3, 4), (3, 5)]) + ⟨[0, 1, 2, 3, 4, 5], [(0, 1), (0, 2), (3, 4), (3, 5)]⟩ /-- Rejection when the number of active low vertices exceeds `k (k + 1)`: three disjoint edges with `k = 1` have no high vertices (`degree = 1 ≤ 1`) and `6 > 1 * 2` active vertices. -/ private def threeEdges : Graph := - ([0, 1, 2, 3, 4, 5], [(0, 1), (2, 3), (4, 5)]) + ⟨[0, 1, 2, 3, 4, 5], [(0, 1), (2, 3), (4, 5)]⟩ example : high (twoStars, 1) = [0, 3] ∧ diff --git a/CslibFrontierTests/WHierarchy.lean b/CslibFrontierTests/WHierarchy.lean index 640c7d0928..7903d9b204 100644 --- a/CslibFrontierTests/WHierarchy.lean +++ b/CslibFrontierTests/WHierarchy.lean @@ -76,7 +76,7 @@ example : WCS.yesInstance 1 ∈ WCS 1 1 ∧ WCS.noInstance 1 ∉ WCS 1 1 := /-! ### The circuit of a graph -/ /-- The triangle on `0, 1, 2`. -/ -def K3 : Graph := ([0, 1, 2], [(0, 1), (1, 2), (0, 2)]) +def K3 : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2), (0, 2)]⟩ example : IndependentSet.edgeIndices K3 = [(0, 1), (1, 2), (0, 2)] := by decide @@ -96,15 +96,15 @@ example : (K3, 2) ∉ IndependentSet := by decide -- An edge with an undeclared end is not an edge of the circuit. -example : IndependentSet.edgeIndices ([0, 1], [(0, 5)]) = [] := by decide +example : IndependentSet.edgeIndices ⟨[0, 1], [(0, 5)]⟩ = [] := by decide -example : ((([0, 1], [(0, 5)]), 2) : Graph × ℕ) ∈ IndependentSet := by +example : ((⟨[0, 1], [(0, 5)]⟩, 2) : Graph × ℕ) ∈ IndependentSet := by rw [← IndependentSet.toWCS_mem_WCS_iff] exact ⟨fun _ => true, by decide, by dsimp [IndependentSet.toWCS]; rw [IndependentSet.eval_toCircuit]; decide⟩ -- A self-loop is not an edge of the circuit. -example : ((([0], [(0, 0)]), 1) : Graph × ℕ) ∈ IndependentSet := by +example : ((⟨[0], [(0, 0)]⟩, 1) : Graph × ℕ) ∈ IndependentSet := by rw [← IndependentSet.toWCS_mem_WCS_iff] exact ⟨fun _ => true, by decide, by dsimp [IndependentSet.toWCS]; rw [IndependentSet.eval_toCircuit]; decide⟩ diff --git a/guide/CslibFrontierGuide/NPCompleteness.lean b/guide/CslibFrontierGuide/NPCompleteness.lean index 03fa14cfd2..1351da0dfb 100644 --- a/guide/CslibFrontierGuide/NPCompleteness.lean +++ b/guide/CslibFrontierGuide/NPCompleteness.lean @@ -611,7 +611,7 @@ least one bit per vertex. The verifier is executable, so you can test it on a tr ```lean (name := triangle) #eval Clique.verifier - ((([0, 1, 2], [(0, 1), (1, 2), (0, 2)]), 3), [true, true, true]) + ((⟨[0, 1, 2], [(0, 1), (1, 2), (0, 2)]⟩, 3), [true, true, true]) ``` ```leanOutput triangle true @@ -645,9 +645,10 @@ on the same vertices ({name}`Graph.compl`), so complementing reduces {name}`Inde every pair of distinct declared vertices that no edge joins: ```lean -example (G : Graph) : G.compl = (G.1, G.1.flatMap fun u => - (G.1.filter fun v => decide (u ≠ v ∧ (u, v) ∉ G.2 ∧ (v, u) ∉ G.2)).map - fun v => (u, v)) := +example (G : Graph) : G.compl = ⟨G.vertices, G.vertices.flatMap fun u => + (G.vertices.filter fun v => + decide (u ≠ v ∧ (u, v) ∉ G.edges ∧ (v, u) ∉ G.edges)).map + fun v => (u, v)⟩ := rfl ``` @@ -834,7 +835,7 @@ successor. Complete face/incident-vertex and component bijections establish: ```lean open Cslib.Complexity in example {G : Graph} (r : Graph.RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) : r.dualRotation.NumericalGenusZero ↔ r.NumericalGenusZero := r.numericalGenusZero_dual_iff hG ``` @@ -849,8 +850,8 @@ it is the endpoint XOR of vertex spins: ```lean open Cslib.Complexity in example {G : Graph} (r : Graph.RotationSystem G) - (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.1) - (hgenus : r.NumericalGenusZero) (x : Fin G.2.length → Bool) : + (hG : ∀ v ∈ G.incidenceVertices, v ∈ G.vertices) + (hgenus : r.NumericalGenusZero) (x : Fin G.edges.length → Bool) : r.FaceEven x ↔ ∃ spin : ℕ → Bool, G.edgeGradient spin = x := r.faceEven_iff_exists_vertexSpins hG hgenus x ``` diff --git a/scripts/projection_chains_baseline.json b/scripts/projection_chains_baseline.json index c0f4f36411..5b0bb975b7 100644 --- a/scripts/projection_chains_baseline.json +++ b/scripts/projection_chains_baseline.json @@ -1,419 +1,411 @@ { - "CslibFrontier/Algorithms/ExactTriangle/FailureAccounting.lean": 1, - "CslibFrontier/Algorithms/ExactTriangle/Hashing.lean": 10, - "CslibFrontier/Algorithms/ExactTriangle/Lopsided.lean": 3, - "CslibFrontier/Algorithms/ExactTriangle/Splitting.lean": 4, - "CslibFrontier/Algorithms/ExactTriangle/Witnesses.lean": 3, - "CslibFrontier/Algorithms/Lean/Dijkstra/Native/Domain.lean": 5, - "CslibFrontier/Algorithms/Lean/EdgeCSP/Forest/Events.lean": 1, - "CslibFrontier/Algorithms/Lean/IterativeCompression/OddCycleTransversal.lean": 2, - "CslibFrontier/Algorithms/Lean/Knapsack/FPTAS.lean": 1, - "CslibFrontier/Algorithms/Lean/Kruskal/Basic.lean": 2, - "CslibFrontier/Algorithms/Lean/LempelZiv/LZ77.lean": 2, - "CslibFrontier/Algorithms/Lean/LubyMIS.lean": 2, - "CslibFrontier/Algorithms/Matching/MVV.lean": 4, - "CslibFrontier/Algorithms/Reachability/Reingold.lean": 2, - "CslibFrontier/Algorithms/ThreeSum/ChanHe/FromNumbers.lean": 2, - "CslibFrontier/Algorithms/ThreeSum/ThreeSets.lean": 2, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/BoxRecordWitness/Exec.lean": 1, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/BoxRecordWitness/Record.lean": 5, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/FixedPrime/Accounting.lean": 4, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/PreparationAdjacency.lean": 4, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/PreparationSpec.lean": 9, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordProduct/Detection.lean": 2, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordProduct/Readback.lean": 3, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordProduct/RecordData.lean": 6, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordWitness/AfterProduct.lean": 1, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordWitness/AfterRecordProduct.lean": 3, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordWitness/Entry.lean": 5, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordWitness/Exec.lean": 1, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordWitness/FailureCount.lean": 2, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordWitness/Failures.lean": 6, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordWitness/Flat.lean": 5, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordWitness/Readback.lean": 2, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/StreamingFixedPrime/Readback.lean": 1, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/StreamingFixedPrime/Record.lean": 5, - "CslibFrontier/Algorithms/WordRAM/ExactTriangle/StreamingFixedPrime/Slot.lean": 4, - "CslibFrontier/Algorithms/WordRAM/ThreeSum/Frontend/Layout.lean": 2, - "CslibFrontier/Computability/BlumComplexity/Cancellation.lean": 2, - "CslibFrontier/Computability/BlumComplexity/Honesty.lean": 2, - "CslibFrontier/Computability/BlumComplexity/Naming.lean": 4, - "CslibFrontier/Computability/CellProbe/Randomized.lean": 2, - "CslibFrontier/Computability/Circuit/Arithmetic.lean": 2, - "CslibFrontier/Computability/Circuit/Arithmetic/WordPolynomial.lean": 2, - "CslibFrontier/Computability/Circuit/Boolean/Iteration/InPlace.lean": 17, - "CslibFrontier/Computability/Circuit/Boolean/Layered/Size.lean": 2, - "CslibFrontier/Computability/Circuit/Boolean/MassProduction/Pipeline/Circuit.lean": 2, - "CslibFrontier/Computability/Circuit/Boolean/MassProduction/Pipeline/Packing.lean": 4, - "CslibFrontier/Computability/Circuit/Boolean/Tableau/Basic.lean": 4, - "CslibFrontier/Computability/Circuit/Boolean/WeightedThreshold/AndreevWindowed.lean": 11, - "CslibFrontier/Computability/Circuit/Encoding/Check.lean": 3, - "CslibFrontier/Computability/Circuit/Frontier/BranchingProgram.lean": 2, - "CslibFrontier/Computability/Circuit/Frontier/Compiler.lean": 1, - "CslibFrontier/Computability/Circuit/Frontier/Compiler/CNF.lean": 5, - "CslibFrontier/Computability/Circuit/Frontier/Compiler/Network.lean": 5, - "CslibFrontier/Computability/Circuit/Frontier/Multigraph.lean": 2, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/CauchyModes.lean": 10, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/ChildCursor.lean": 24, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/ChildNumbering.lean": 4, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/CoeffScheme.lean": 19, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/DescribedContraction.lean": 36, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/DirectContraction.lean": 19, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean": 3, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean": 2, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean": 10, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean": 1, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/FastContractionShape.lean": 4, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean": 10, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/FastContractionStep.lean": 10, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/IncidenceEntryCertificate.lean": 1, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/NativeCall.lean": 1, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean": 17, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/NativeChildBounds.lean": 1, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/NativeChildPolyTime.lean": 4, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean": 2, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/PacketEntry.lean": 2, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/PacketTerms.lean": 33, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean": 4, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/Schedule.lean": 2, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/ScheduledRecursion.lean": 1, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/TruncatedEntry.lean": 2, - "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/VertexDesc.lean": 5, - "CslibFrontier/Computability/Circuit/Fusion/Encoding.lean": 2, - "CslibFrontier/Computability/Circuit/Fusion/Graph/Canonical.lean": 2, - "CslibFrontier/Computability/Circuit/Monotone/Clique/Negative.lean": 1, - "CslibFrontier/Computability/Circuit/Quantum/Classical.lean": 24, - "CslibFrontier/Computability/Circuit/Quantum/Code.lean": 3, - "CslibFrontier/Computability/Circuit/Quantum/Controlled.lean": 11, - "CslibFrontier/Computability/Circuit/Quantum/Parallel.lean": 2, - "CslibFrontier/Computability/Circuit/Quantum/PhaseCode.lean": 4, - "CslibFrontier/Computability/Circuit/Quantum/PhaseOracle.lean": 2, - "CslibFrontier/Computability/Circuit/Quantum/Verifier.lean": 2, - "CslibFrontier/Computability/Communication/Disjointness.lean": 8, - "CslibFrontier/Computability/Complexity/Classes/Alternation/Savitch.lean": 7, - "CslibFrontier/Computability/Complexity/Classes/ArthurMerlin/GraphNonIsomorphism.lean": 5, - "CslibFrontier/Computability/Complexity/Classes/BQP/Amplification.lean": 2, - "CslibFrontier/Computability/Complexity/Classes/CountingSimulation/Accumulate.lean": 6, - "CslibFrontier/Computability/Complexity/Classes/CountingSimulation/Assembly.lean": 3, - "CslibFrontier/Computability/Complexity/Classes/CountingSimulation/BlockNetworks.lean": 4, - "CslibFrontier/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean": 1, - "CslibFrontier/Computability/Complexity/Classes/FPAUS/Counting.lean": 4, - "CslibFrontier/Computability/Complexity/Classes/IP/GameTree.lean": 5, - "CslibFrontier/Computability/Complexity/Classes/IP/Games/Repetition.lean": 5, - "CslibFrontier/Computability/Complexity/Classes/IP/PSPACE.lean": 1, - "CslibFrontier/Computability/Complexity/Classes/IP/Simulation.lean": 13, - "CslibFrontier/Computability/Complexity/Classes/IP/TQBF.lean": 13, - "CslibFrontier/Computability/Complexity/Classes/IP/TQBF/Verifier.lean": 18, - "CslibFrontier/Computability/Complexity/Classes/LogSpace/FirstOrder/ConfigurationGraph.lean": 5, - "CslibFrontier/Computability/Complexity/Classes/MIP/NEXP.lean": 12, - "CslibFrontier/Computability/Complexity/Classes/Oracle/BakerGillSolovay.lean": 3, - "CslibFrontier/Computability/Complexity/Classes/QMA/Amplification.lean": 2, - "CslibFrontier/Computability/Complexity/Classes/QMA/NP.lean": 1, - "CslibFrontier/Computability/Complexity/Classes/SPP/FewP.lean": 14, - "CslibFrontier/Computability/Complexity/Classes/SharpP/Space.lean": 4, - "CslibFrontier/Computability/Complexity/Classes/Space/GeneralizedGeography/Membership.lean": 4, - "CslibFrontier/Computability/Complexity/Classes/TFNP/Subclasses.lean": 13, - "CslibFrontier/Computability/Complexity/Classes/TFNP/Subclasses/Sperner.lean": 10, - "CslibFrontier/Computability/Complexity/CookLevin/Correctness.lean": 2, - "CslibFrontier/Computability/Complexity/CookLevin/Reduction.lean": 2, - "CslibFrontier/Computability/Complexity/CookLevin/Tableau.lean": 4, - "CslibFrontier/Computability/Complexity/ExplicitRamsey/FranklWilson.lean": 7, - "CslibFrontier/Computability/Complexity/Fagin/Tableau/Defs.lean": 2, - "CslibFrontier/Computability/Complexity/Fagin/Witness.lean": 3, - "CslibFrontier/Computability/Complexity/FineGrained/CountingParameterBounds.lean": 1, - "CslibFrontier/Computability/Complexity/FineGrained/CountingSparsification.lean": 23, - "CslibFrontier/Computability/Complexity/FineGrained/CountingSparsificationUniform.lean": 29, - "CslibFrontier/Computability/Complexity/FineGrained/Diameter.lean": 23, - "CslibFrontier/Computability/Complexity/FineGrained/GraphProblems.lean": 10, - "CslibFrontier/Computability/Complexity/FineGrained/LocalMapCounting.lean": 5, - "CslibFrontier/Computability/Complexity/FineGrained/PermutationIndependentSet.lean": 17, - "CslibFrontier/Computability/Complexity/ImmermanVardi/Tableau/Step.lean": 2, - "CslibFrontier/Computability/Complexity/Parameterized/VertexCover.lean": 1, - "CslibFrontier/Computability/Complexity/Parameterized/VertexCover/BussKernel.lean": 7, - "CslibFrontier/Computability/Complexity/Problems/CSP/Incidence.lean": 1, - "CslibFrontier/Computability/Complexity/Problems/CSP/SelfDualThreeSAT.lean": 2, - "CslibFrontier/Computability/Complexity/Problems/CircuitSAT.lean": 4, - "CslibFrontier/Computability/Complexity/Problems/CircuitValue.lean": 8, - "CslibFrontier/Computability/Complexity/Problems/CircuitValue/Writer.lean": 17, - "CslibFrontier/Computability/Complexity/Problems/Clique.lean": 1, - "CslibFrontier/Computability/Complexity/Problems/Coloring.lean": 5, - "CslibFrontier/Computability/Complexity/Problems/FeedbackArcSet.lean": 1, - "CslibFrontier/Computability/Complexity/Problems/FeedbackVertexSet.lean": 2, - "CslibFrontier/Computability/Complexity/Problems/Graph.lean": 5, - "CslibFrontier/Computability/Complexity/Problems/GraphIsomorphism.lean": 1, - "CslibFrontier/Computability/Complexity/Problems/Hamiltonian.lean": 15, - "CslibFrontier/Computability/Complexity/Problems/Hamiltonian/Diamond/Defs.lean": 28, - "CslibFrontier/Computability/Complexity/Problems/Hamiltonian/Sparse.lean": 4, - "CslibFrontier/Computability/Complexity/Problems/Hamiltonian/Undirected.lean": 2, - "CslibFrontier/Computability/Complexity/Problems/IndependentSet.lean": 9, - "CslibFrontier/Computability/Complexity/Problems/IndependentSet/Sparse.lean": 4, - "CslibFrontier/Computability/Complexity/Problems/Knapsack.lean": 8, - "CslibFrontier/Computability/Complexity/Problems/Knapsack/Unary.lean": 2, - "CslibFrontier/Computability/Complexity/Problems/LinearProgramming.lean": 3, - "CslibFrontier/Computability/Complexity/Problems/LongPath/Hardness.lean": 4, - "CslibFrontier/Computability/Complexity/Problems/MaxTwoSAT.lean": 4, - "CslibFrontier/Computability/Complexity/Problems/MonotoneCircuitValue.lean": 6, - "CslibFrontier/Computability/Complexity/Problems/SAT/ExactOne/DrawingAssembly.lean": 3, - "CslibFrontier/Computability/Complexity/Problems/SAT/ExactOne/DrawingAvoidance.lean": 8, - "CslibFrontier/Computability/Complexity/Problems/SAT/ExactOne/DrawingData.lean": 5, - "CslibFrontier/Computability/Complexity/Problems/SAT/ExactOne/DrawingPorts.lean": 4, - "CslibFrontier/Computability/Complexity/Problems/SAT/ExactOne/DrawingValid.lean": 6, - "CslibFrontier/Computability/Complexity/Problems/SAT/ExactOne/GeometryCollarData.lean": 6, - "CslibFrontier/Computability/Complexity/Problems/SAT/Incidence.lean": 1, - "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/CrossoverLayout.lean": 14, - "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/CrossoverLayoutChecks.lean": 21, - "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/OrdinaryRing.lean": 31, - "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PairedCompile.lean": 4, - "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PairedCorrectness.lean": 2, - "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PairedCrossoverLayout.lean": 14, - "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PairedCrossoverLayoutChecks.lean": 21, - "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PairedGeometry.lean": 21, - "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PairedOccurrence.lean": 3, - "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PairedSemantics.lean": 14, - "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PathAssignments.lean": 8, - "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/Paths.lean": 2, - "CslibFrontier/Computability/Complexity/Problems/SubsetSum.lean": 2, - "CslibFrontier/Computability/Complexity/Problems/ThreeSATThree.lean": 2, - "CslibFrontier/Computability/Complexity/Problems/VertexCover.lean": 7, - "CslibFrontier/Computability/GenericRAM/Native/BooleanFold.lean": 9, - "CslibFrontier/Computability/GenericRAM/Native/BooleanSearch.lean": 12, - "CslibFrontier/Computability/GenericRAM/Native/ExhaustiveCountFallback.lean": 16, - "CslibFrontier/Computability/GenericRAM/Native/StackRecursion.lean": 7, - "CslibFrontier/Computability/GenericRAM/Simulation/Run.lean": 6, - "CslibFrontier/Computability/GenericRAM/Simulation/Turing.lean": 3, - "CslibFrontier/Computability/GenericRAM/SparseMemory/Turing.lean": 4, - "CslibFrontier/Computability/Interactive/Bundle.lean": 2, - "CslibFrontier/Computability/Interactive/GKR/Protocol.lean": 19, - "CslibFrontier/Computability/Interactive/Security.lean": 8, - "CslibFrontier/Computability/LogSpaceDerandomization/ConditionalProductMean.lean": 1, - "CslibFrontier/Computability/LogSpaceDerandomization/PilotDetectorTransport.lean": 10, - "CslibFrontier/Computability/Machines/ReadMachine/Tidy.lean": 22, - "CslibFrontier/Computability/Machines/ReadMachine/VirtualWord.lean": 9, - "CslibFrontier/Computability/Machines/ReadMachine/VirtualWord/Evaluator.lean": 14, - "CslibFrontier/Computability/Machines/ReadMachine/VirtualWord/Generate.lean": 1, - "CslibFrontier/Computability/Machines/ReadMachine/VirtualWord/Native.lean": 12, - "CslibFrontier/Computability/Machines/ReadMachine/VirtualWord/Simulation.lean": 46, - "CslibFrontier/Computability/Machines/Turing/MultiTape/BlockComputation/EncodedTree.lean": 2, - "CslibFrontier/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding.lean": 3, - "CslibFrontier/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeDecode.lean": 2, - "CslibFrontier/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplay.lean": 4, - "CslibFrontier/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadSearch.lean": 3, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/BigO/ZipFold.lean": 4, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/InSpace/NatPrime.lean": 7, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/InSpaceLib.lean": 8, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/Composition/SmallSpace.lean": 32, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/ConfigurationGraph/Basic.lean": 13, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/ConfigurationGraph/Correctness.lean": 2, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/ConfigurationGraph/Expr.lean": 8, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/ConfigurationGraph/Tape.lean": 8, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/CounterProgram/Nondeterministic.lean": 2, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/InductiveCounting/Machine.lean": 2, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/QueryMachine.lean": 9, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/Savitch.lean": 9, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/PolyTime/Nat/Jacobi.lean": 15, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/PolyTime/RotationMap/TowerTable/Family.lean": 5, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/PolyTime/RotationMap/Walk.lean": 14, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/Space/Reachability.lean": 2, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/Translate.lean": 4, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Counting/Checker.lean": 2, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Counting/Oracle/Replay.lean": 6, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Encodings/Nat/Subtraction.lean": 7, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Encodings/StepMachineComp.lean": 5, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Feature.lean": 6, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Halting/Partrec.lean": 23, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Machine/ChoiceTable.lean": 5, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Probabilistic/BlockNetwork.lean": 1, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Probabilistic/IntervalCount.lean": 11, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Probabilistic/IntervalCount/PolyTime.lean": 11, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Probabilistic/IntervalSimulation.lean": 4, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Probabilistic/IntervalTensor.lean": 4, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Probabilistic/RunsWords.lean": 2, - "CslibFrontier/Computability/Machines/Turing/MultiTape/StepMachine/Basic.lean": 5, - "CslibFrontier/Computability/Machines/Turing/MultiTape/StepMachine/Comp.lean": 4, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/Correct.lean": 6, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/Decode.lean": 7, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/InPlace.lean": 15, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/LookupRun.lean": 11, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/Machine.lean": 9, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/Nondeterministic.lean": 6, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/PolyTime.lean": 8, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/SpaceBudget.lean": 11, - "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/Windowed.lean": 5, - "CslibFrontier/Computability/Machines/Turing/SingleTape/Partrec.lean": 2, - "CslibFrontier/Computability/PCP/Approximation/IndependentSet.lean": 1, - "CslibFrontier/Computability/PCP/AssignmentTester.lean": 12, - "CslibFrontier/Computability/PCP/AssignmentTester/Selection.lean": 4, - "CslibFrontier/Computability/PCP/DegreeReduction.lean": 3, - "CslibFrontier/Computability/PCP/Encoded/DegreeReduction.lean": 7, - "CslibFrontier/Computability/PCP/Encoded/GenericComposition.lean": 13, - "CslibFrontier/Computability/PCP/Encoded/Power.lean": 5, - "CslibFrontier/Computability/PCP/Encoded/Preprocess/Programs.lean": 6, - "CslibFrontier/Computability/PCP/Encoded/Verifier.lean": 2, - "CslibFrontier/Computability/PCP/Encoded/Verifier/Reduction.lean": 2, - "CslibFrontier/Computability/PCP/Encoded/Verifier/Repetition.lean": 3, - "CslibFrontier/Computability/PCP/Power.lean": 33, - "CslibFrontier/Computability/PCP/Power/WithInput.lean": 10, - "CslibFrontier/Computability/PCP/PoweringBound.lean": 1, - "CslibFrontier/Computability/PCP/TwoProverGame.lean": 4, - "CslibFrontier/Computability/PCP/TwoProverGame/Quantum.lean": 4, - "CslibFrontier/Computability/PolyTime/CSP/Edge/Augmenting.lean": 1, - "CslibFrontier/Computability/PolyTime/CommonCountingAccum.lean": 3, - "CslibFrontier/Computability/PolyTime/LinearAlgebra/BooleanMeasurements.lean": 1, - "CslibFrontier/Computability/PolyTime/LinearAlgebra/ReplacementDesignBuilder.lean": 1, - "CslibFrontier/Computability/QueryComplexity/AndOrTree/Randomized.lean": 2, - "CslibFrontier/Computability/QueryComplexity/Quantum/Composition.lean": 52, - "CslibFrontier/Computability/QueryComplexity/Quantum/Grover.lean": 5, - "CslibFrontier/Computability/QueryComplexity/Quantum/Search.lean": 3, - "CslibFrontier/Computability/QueryComplexity/Quantum/Simon.lean": 28, - "CslibFrontier/Computability/QueryComplexity/Quantum/SimonSearch.lean": 3, - "CslibFrontier/Computability/QueryComplexity/Quantum/UnknownSearch.lean": 4, - "CslibFrontier/Computability/Streaming/FkEstimator.lean": 10, - "CslibFrontier/Computability/TreeEval/Ary/Packed/GateInterpolation.lean": 5, - "CslibFrontier/Computability/TreeEval/Ary/Packed/Interpolation.lean": 10, - "CslibFrontier/Computability/TreeEval/Ary/Packed/QueryGate.lean": 28, - "CslibFrontier/Computability/TreeEval/Ary/Packed/QueryGateInit.lean": 10, - "CslibFrontier/Computability/TreeEval/Ary/Packed/QueryGateSpace.lean": 7, - "CslibFrontier/Computability/TreeEval/Ary/Packed/RawController.lean": 19, - "CslibFrontier/Computability/TreeEval/Ary/Packed/RawControllerCorrect.lean": 5, - "CslibFrontier/Computability/TreeEval/Ary/Packed/RawControllerOutput.lean": 1, - "CslibFrontier/Computability/TreeEval/Ary/Packed/RawControllerQuery.lean": 39, - "CslibFrontier/Computability/TreeEval/Ary/Packed/RawControllerSpace.lean": 28, - "CslibFrontier/Computability/TreeEval/Reader/Cert/Step.lean": 2, - "CslibFrontier/Computability/TreeEval/Reader/Defs.lean": 4, - "CslibFrontier/Computability/TreeEval/Reader/Encode.lean": 3, - "CslibFrontier/Computability/TreeEval/Reader/Size.lean": 4, - "CslibFrontier/Computability/TreeEval/Reader/Tuple.lean": 3, - "CslibFrontier/Computability/USTCON/Reader/Encode.lean": 13, - "CslibFrontier/Computability/WordRAM/Heap.lean": 2, - "CslibFrontier/Computability/WordRAM/Realization/Absorb.lean": 4, - "CslibFrontier/Computability/WordRAM/Realization/Loop.lean": 12, - "CslibFrontier/Crypto/Computational/DiscreteLog/SchnorrGroup.lean": 4, - "CslibFrontier/Crypto/Computational/Encryption/CCA.lean": 12, - "CslibFrontier/Crypto/Computational/GMW/Sequential.lean": 20, - "CslibFrontier/Crypto/Computational/GMW/Simulator.lean": 1, - "CslibFrontier/Crypto/Computational/GoldwasserMicali.lean": 8, - "CslibFrontier/Crypto/Computational/HashAndSign.lean": 6, - "CslibFrontier/Crypto/Computational/Homomorphic/KeyChain/CoinTape.lean": 3, - "CslibFrontier/Crypto/Computational/Homomorphic/Leveled/GameCoinTape.lean": 2, - "CslibFrontier/Crypto/Computational/Homomorphic/Leveled/GuessCoinTape.lean": 5, - "CslibFrontier/Crypto/Computational/Kilian/MicaliSoundness.lean": 12, - "CslibFrontier/Crypto/Computational/Kilian/Soundness.lean": 1, - "CslibFrontier/Crypto/Computational/LWE/DualRegev.lean": 1, - "CslibFrontier/Crypto/Computational/LWE/Regev.lean": 4, - "CslibFrontier/Crypto/Computational/MLDSA/KeyOnly.lean": 5, - "CslibFrontier/Crypto/Computational/NaorYung.lean": 4, - "CslibFrontier/Crypto/Computational/RSATrapdoor.lean": 4, - "CslibFrontier/Crypto/Computational/SIS/ModulusSwitch/CenteredBinomial.lean": 2, - "CslibFrontier/Crypto/Computational/SIS/ModulusSwitch/Failure.lean": 1, - "CslibFrontier/Crypto/Computational/SchnorrSignature/EncodedROM.lean": 1, - "CslibFrontier/Crypto/Computational/SchnorrSignature/NativeROM.lean": 8, - "CslibFrontier/Crypto/Computational/Signature/WordRandomOracle.lean": 4, - "CslibFrontier/Crypto/Computational/Stretch/Quantitative.lean": 6, - "CslibFrontier/Crypto/Computational/TCRSignature.lean": 6, - "CslibFrontier/Crypto/Computational/TCRSignature/ManyTime.lean": 8, - "CslibFrontier/Crypto/Computational/TargetCollisionResistant.lean": 5, - "CslibFrontier/Crypto/Game/CloseTo/RandomOracle.lean": 1, - "CslibFrontier/Crypto/Primitives/BlockCipher/CBCMAC.lean": 20, - "CslibFrontier/Crypto/Primitives/BlockCipher/Modes.lean": 4, - "CslibFrontier/Crypto/Primitives/Garbling/CircuitInverters.lean": 6, - "CslibFrontier/Crypto/Primitives/Garbling/CorrelationRobust.lean": 4, - "CslibFrontier/Crypto/Primitives/Garbling/FreeXOR.lean": 1, - "CslibFrontier/Crypto/Primitives/Garbling/HalfGates/Privacy.lean": 1, - "CslibFrontier/Crypto/Primitives/Garbling/SizeLowerBound.lean": 5, - "CslibFrontier/Crypto/Primitives/Garbling/Switch/Oblivious.lean": 3, - "CslibFrontier/Crypto/Protocols/MPC/DDHOT.lean": 16, - "CslibFrontier/Crypto/Protocols/MPC/IKNP.lean": 4, - "CslibFrontier/Crypto/Protocols/MPC/OTCommitment/Opening.lean": 6, - "CslibFrontier/Crypto/Protocols/MPC/Yao.lean": 6, - "CslibFrontier/Crypto/Protocols/SigmaProtocol/Abort/RandomOracle.lean": 2, - "CslibFrontier/Crypto/Protocols/SigmaProtocol/Lyubashevsky/NativeExtract.lean": 6, - "CslibFrontier/Crypto/Protocols/SigmaProtocol/Lyubashevsky/Signature/View.lean": 2, - "CslibFrontier/Crypto/Protocols/Signature/RandomOracle.lean": 4, - "CslibFrontier/Crypto/Protocols/VOLE/AdaptiveMulCheck.lean": 8, - "CslibFrontier/Crypto/Protocols/VOLE/Circuit.lean": 2, - "CslibFrontier/Crypto/Protocols/VOLE/CircuitPrivacy.lean": 5, - "CslibFrontier/Crypto/Protocols/VOLE/MaskedMulCheck.lean": 10, - "CslibFrontier/Crypto/Protocols/VOLE/Native.lean": 2, - "CslibFrontier/DataStructure/AVLTree/Impl.lean": 8, - "CslibFrontier/DataStructure/ApproximateMembership/WordRAM/QuotientFilter/Cluster.lean": 393, - "CslibFrontier/DataStructure/Dictionary/WordRAM/Succinct.lean": 2, - "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctBucketOps.lean": 47, - "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctChainOps.lean": 75, - "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctDeleteOps.lean": 108, - "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctHash.lean": 83, - "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctMicroChain.lean": 248, - "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctProg.lean": 102, - "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctSearch.lean": 274, - "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctSlabDemote.lean": 28, - "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctSlabMove.lean": 28, - "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctSlabOps.lean": 43, - "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctSlabPool.lean": 64, - "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctTailOps.lean": 38, - "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctValidHistory.lean": 54, - "CslibFrontier/DataStructure/DisjointSet/Spec.lean": 2, - "CslibFrontier/DataStructure/DisjointSet/UnionFind.lean": 14, - "CslibFrontier/DataStructure/DynamicArray/WordRAM/Basic.lean": 13, - "CslibFrontier/DataStructure/FenwickTree/Impl.lean": 8, - "CslibFrontier/DataStructure/FusionTree/WordRAM/Preprocessing.lean": 4, - "CslibFrontier/DataStructure/OrderedSet/ExtractMin.lean": 2, - "CslibFrontier/DataStructure/PrefixSums/WordRAM/Dynamic.lean": 3, - "CslibFrontier/DataStructure/PriorityQueue/BinaryHeap/Native/Encoding.lean": 2, - "CslibFrontier/DataStructure/PriorityQueue/BinaryHeap/Native/Read.lean": 6, - "CslibFrontier/DataStructure/PriorityQueue/BinaryHeap/Native/SiftDown.lean": 26, - "CslibFrontier/DataStructure/PriorityQueue/BinaryHeap/Native/SiftUp.lean": 10, - "CslibFrontier/DataStructure/PriorityQueue/BinaryHeap/Native/Update.lean": 16, - "CslibFrontier/DataStructure/PriorityQueue/FibonacciHeap/Native/Scatter.lean": 4, - "CslibFrontier/DataStructure/PriorityQueue/FibonacciHeap/Native/ScatterList.lean": 2, - "CslibFrontier/DataStructure/PriorityQueue/FibonacciHeap/Native/Source.lean": 4, - "CslibFrontier/DataStructure/PriorityQueue/WordRAM/FibonacciHeap.lean": 3, - "CslibFrontier/DataStructure/Queue/Memory/Transfer.lean": 2, - "CslibFrontier/DataStructure/Queue/Spec.lean": 6, - "CslibFrontier/DataStructure/Queue/WordRAM/RegisterCorrectness.lean": 4, - "CslibFrontier/DataStructure/Queue/WordRAM/RegisterDynamic.lean": 6, - "CslibFrontier/DataStructure/Queue/WordRAM/RegisterExecution.lean": 4, - "CslibFrontier/DataStructure/Queue/WordRAM/RegisterMethods.lean": 1, - "CslibFrontier/DataStructure/Queue/WordRAM/RegisterTransfer.lean": 2, - "CslibFrontier/DataStructure/Retroactive/Basic.lean": 1, - "CslibFrontier/DataStructure/SearchTree/Impl.lean": 12, - "CslibFrontier/DataStructure/StateTransition.lean": 4, - "CslibFrontier/DataStructure/VanEmdeBoas/Impl.lean": 8, - "CslibFrontier/DataStructure/VanEmdeBoas/WordRAM/Call.lean": 7, - "CslibFrontier/DataStructure/WordRAM/Randomized/ObliviousSequence.lean": 2, - "CslibFrontier/Foundations/BooleanAnalysis/BucketWeight.lean": 4, - "CslibFrontier/Foundations/BooleanAnalysis/SparseHermiteRecovery.lean": 3, - "CslibFrontier/Foundations/Combinatorics/BudgetTuples.lean": 6, - "CslibFrontier/Foundations/Combinatorics/Expander/AffinePlane.lean": 3, - "CslibFrontier/Foundations/Combinatorics/Expander/Contraction.lean": 4, - "CslibFrontier/Foundations/Combinatorics/Expander/Hom.lean": 2, - "CslibFrontier/Foundations/Combinatorics/Expander/KilledWalk.lean": 27, - "CslibFrontier/Foundations/Combinatorics/Expander/Perm.lean": 5, - "CslibFrontier/Foundations/Combinatorics/Expander/TannerCode.lean": 2, - "CslibFrontier/Foundations/Combinatorics/Expander/Tensor.lean": 4, - "CslibFrontier/Foundations/Combinatorics/Expander/Walk.lean": 6, - "CslibFrontier/Foundations/Combinatorics/Expander/Zigzag.lean": 4, - "CslibFrontier/Foundations/Combinatorics/Sperner.lean": 2, - "CslibFrontier/Foundations/Data/Finset/Expect.lean": 1, - "CslibFrontier/Foundations/Geometry/Orthogonal.lean": 8, - "CslibFrontier/Foundations/Geometry/Orthogonal/Neighborhood.lean": 14, - "CslibFrontier/Foundations/LinearAlgebra/VariableCopyPlacement.lean": 5, - "CslibFrontier/Logics/FirstOrder/Atom.lean": 1, - "CslibFrontier/Logics/FirstOrder/Interpretation/Tagged.lean": 9, - "CslibFrontier/Logics/FirstOrder/Ordered/DomainInterpretation/Composition.lean": 2, - "CslibFrontier/Logics/FirstOrder/Ordered/FullTuple/Composition.lean": 4, - "CslibFrontier/Logics/FirstOrder/Ordered/FullTuple/Power.lean": 2, - "CslibFrontier/Logics/FirstOrder/Problems/CNF.lean": 5, - "CslibFrontier/Logics/FirstOrder/Problems/ColoringReduction.lean": 7, - "CslibFrontier/Logics/FirstOrder/Raw/Graph.lean": 6, - "CslibFrontier/Logics/FirstOrder/ZeroOneLaw/Relational/ExtensionAxiom.lean": 2, - "CslibFrontier/Logics/Propositional/CNF/Compact/Relabel.lean": 3, - "CslibFrontier/Logics/Propositional/CNF/ModelCount.lean": 4, - "CslibFrontier/Logics/Propositional/CNF/Occurrence.lean": 1, - "CslibFrontier/Logics/Propositional/CNF/Planar/DirectedWire.lean": 11, - "CslibFrontier/Logics/Propositional/CNF/ThreeOccurrence.lean": 2, - "CslibFrontier/Logics/Propositional/CSP/Edge/Augmenting.lean": 4, - "CslibFrontier/Logics/Propositional/CSP/Occurrence.lean": 3, - "CslibFrontier/Logics/Propositional/Resolution/Interpolation/Extraction.lean": 16, - "CslibFrontier/Logics/SecondOrder/Encoding.lean": 1, - "CslibFrontier/Logics/SecondOrder/Grounding.lean": 3, - "CslibFrontier/Logics/SecondOrder/WordMSO/McNaughtonPapert.lean": 5 + "CslibFrontier/Algorithms/ExactTriangle/FailureAccounting.lean": 1, + "CslibFrontier/Algorithms/ExactTriangle/Hashing.lean": 10, + "CslibFrontier/Algorithms/ExactTriangle/Lopsided.lean": 3, + "CslibFrontier/Algorithms/ExactTriangle/Splitting.lean": 4, + "CslibFrontier/Algorithms/ExactTriangle/Witnesses.lean": 3, + "CslibFrontier/Algorithms/Lean/Dijkstra/Native/Domain.lean": 5, + "CslibFrontier/Algorithms/Lean/EdgeCSP/Forest/Events.lean": 1, + "CslibFrontier/Algorithms/Lean/IterativeCompression/OddCycleTransversal.lean": 2, + "CslibFrontier/Algorithms/Lean/Knapsack/FPTAS.lean": 1, + "CslibFrontier/Algorithms/Lean/Kruskal/Basic.lean": 2, + "CslibFrontier/Algorithms/Lean/LempelZiv/LZ77.lean": 2, + "CslibFrontier/Algorithms/Lean/LubyMIS.lean": 2, + "CslibFrontier/Algorithms/Matching/MVV.lean": 4, + "CslibFrontier/Algorithms/Reachability/Reingold.lean": 2, + "CslibFrontier/Algorithms/ThreeSum/ChanHe/FromNumbers.lean": 2, + "CslibFrontier/Algorithms/ThreeSum/ThreeSets.lean": 2, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/BoxRecordWitness/Exec.lean": 1, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/BoxRecordWitness/Record.lean": 5, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/FixedPrime/Accounting.lean": 4, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/PreparationAdjacency.lean": 4, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/PreparationSpec.lean": 9, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordProduct/Detection.lean": 2, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordProduct/Readback.lean": 3, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordProduct/RecordData.lean": 6, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordWitness/AfterProduct.lean": 1, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordWitness/AfterRecordProduct.lean": 3, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordWitness/Entry.lean": 5, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordWitness/Exec.lean": 1, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordWitness/FailureCount.lean": 2, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordWitness/Failures.lean": 6, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordWitness/Flat.lean": 5, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/RecordWitness/Readback.lean": 2, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/StreamingFixedPrime/Readback.lean": 1, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/StreamingFixedPrime/Record.lean": 5, + "CslibFrontier/Algorithms/WordRAM/ExactTriangle/StreamingFixedPrime/Slot.lean": 4, + "CslibFrontier/Algorithms/WordRAM/ThreeSum/Frontend/Layout.lean": 2, + "CslibFrontier/Computability/BlumComplexity/Cancellation.lean": 2, + "CslibFrontier/Computability/BlumComplexity/Honesty.lean": 2, + "CslibFrontier/Computability/BlumComplexity/Naming.lean": 4, + "CslibFrontier/Computability/CellProbe/Randomized.lean": 2, + "CslibFrontier/Computability/Circuit/Arithmetic.lean": 2, + "CslibFrontier/Computability/Circuit/Arithmetic/WordPolynomial.lean": 2, + "CslibFrontier/Computability/Circuit/Boolean/Iteration/InPlace.lean": 17, + "CslibFrontier/Computability/Circuit/Boolean/Layered/Size.lean": 2, + "CslibFrontier/Computability/Circuit/Boolean/MassProduction/Pipeline/Circuit.lean": 2, + "CslibFrontier/Computability/Circuit/Boolean/MassProduction/Pipeline/Packing.lean": 4, + "CslibFrontier/Computability/Circuit/Boolean/Tableau/Basic.lean": 4, + "CslibFrontier/Computability/Circuit/Boolean/WeightedThreshold/AndreevWindowed.lean": 11, + "CslibFrontier/Computability/Circuit/Encoding/Check.lean": 3, + "CslibFrontier/Computability/Circuit/Frontier/BranchingProgram.lean": 2, + "CslibFrontier/Computability/Circuit/Frontier/Compiler.lean": 1, + "CslibFrontier/Computability/Circuit/Frontier/Compiler/CNF.lean": 5, + "CslibFrontier/Computability/Circuit/Frontier/Compiler/Network.lean": 5, + "CslibFrontier/Computability/Circuit/Frontier/Multigraph.lean": 2, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/CauchyModes.lean": 10, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/ChildCursor.lean": 24, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/ChildNumbering.lean": 4, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/CoeffScheme.lean": 19, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/DescribedContraction.lean": 36, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/DirectContraction.lean": 19, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean": 3, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean": 2, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean": 10, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean": 1, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/FastContractionShape.lean": 4, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean": 10, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/FastContractionStep.lean": 10, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/IncidenceEntryCertificate.lean": 1, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/NativeCall.lean": 1, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean": 17, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/NativeChildBounds.lean": 1, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/NativeChildPolyTime.lean": 4, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean": 2, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/PacketEntry.lean": 2, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/PacketTerms.lean": 33, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean": 4, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/Schedule.lean": 2, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/ScheduledRecursion.lean": 1, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/TruncatedEntry.lean": 2, + "CslibFrontier/Computability/Circuit/Frontier/TensorNetwork/VertexDesc.lean": 5, + "CslibFrontier/Computability/Circuit/Fusion/Encoding.lean": 2, + "CslibFrontier/Computability/Circuit/Fusion/Graph/Canonical.lean": 2, + "CslibFrontier/Computability/Circuit/Monotone/Clique/Negative.lean": 1, + "CslibFrontier/Computability/Circuit/Quantum/Classical.lean": 24, + "CslibFrontier/Computability/Circuit/Quantum/Code.lean": 3, + "CslibFrontier/Computability/Circuit/Quantum/Controlled.lean": 11, + "CslibFrontier/Computability/Circuit/Quantum/Parallel.lean": 2, + "CslibFrontier/Computability/Circuit/Quantum/PhaseCode.lean": 4, + "CslibFrontier/Computability/Circuit/Quantum/PhaseOracle.lean": 2, + "CslibFrontier/Computability/Circuit/Quantum/Verifier.lean": 2, + "CslibFrontier/Computability/Communication/Disjointness.lean": 8, + "CslibFrontier/Computability/Complexity/Classes/Alternation/Savitch.lean": 7, + "CslibFrontier/Computability/Complexity/Classes/ArthurMerlin/GraphNonIsomorphism.lean": 5, + "CslibFrontier/Computability/Complexity/Classes/BQP/Amplification.lean": 2, + "CslibFrontier/Computability/Complexity/Classes/CountingSimulation/Accumulate.lean": 6, + "CslibFrontier/Computability/Complexity/Classes/CountingSimulation/Assembly.lean": 3, + "CslibFrontier/Computability/Complexity/Classes/CountingSimulation/BlockNetworks.lean": 4, + "CslibFrontier/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean": 1, + "CslibFrontier/Computability/Complexity/Classes/FPAUS/Counting.lean": 4, + "CslibFrontier/Computability/Complexity/Classes/IP/GameTree.lean": 5, + "CslibFrontier/Computability/Complexity/Classes/IP/Games/Repetition.lean": 5, + "CslibFrontier/Computability/Complexity/Classes/IP/PSPACE.lean": 1, + "CslibFrontier/Computability/Complexity/Classes/IP/Simulation.lean": 13, + "CslibFrontier/Computability/Complexity/Classes/IP/TQBF.lean": 13, + "CslibFrontier/Computability/Complexity/Classes/IP/TQBF/Verifier.lean": 18, + "CslibFrontier/Computability/Complexity/Classes/LogSpace/FirstOrder/ConfigurationGraph.lean": 5, + "CslibFrontier/Computability/Complexity/Classes/MIP/NEXP.lean": 12, + "CslibFrontier/Computability/Complexity/Classes/Oracle/BakerGillSolovay.lean": 3, + "CslibFrontier/Computability/Complexity/Classes/QMA/Amplification.lean": 2, + "CslibFrontier/Computability/Complexity/Classes/QMA/NP.lean": 1, + "CslibFrontier/Computability/Complexity/Classes/SPP/FewP.lean": 14, + "CslibFrontier/Computability/Complexity/Classes/SharpP/Space.lean": 4, + "CslibFrontier/Computability/Complexity/Classes/Space/GeneralizedGeography/Membership.lean": 4, + "CslibFrontier/Computability/Complexity/Classes/TFNP/Subclasses.lean": 13, + "CslibFrontier/Computability/Complexity/Classes/TFNP/Subclasses/Sperner.lean": 10, + "CslibFrontier/Computability/Complexity/CookLevin/Correctness.lean": 2, + "CslibFrontier/Computability/Complexity/CookLevin/Reduction.lean": 2, + "CslibFrontier/Computability/Complexity/CookLevin/Tableau.lean": 4, + "CslibFrontier/Computability/Complexity/ExplicitRamsey/FranklWilson.lean": 7, + "CslibFrontier/Computability/Complexity/Fagin/Tableau/Defs.lean": 2, + "CslibFrontier/Computability/Complexity/Fagin/Witness.lean": 3, + "CslibFrontier/Computability/Complexity/FineGrained/CountingParameterBounds.lean": 1, + "CslibFrontier/Computability/Complexity/FineGrained/CountingSparsification.lean": 23, + "CslibFrontier/Computability/Complexity/FineGrained/CountingSparsificationUniform.lean": 29, + "CslibFrontier/Computability/Complexity/FineGrained/Diameter.lean": 23, + "CslibFrontier/Computability/Complexity/FineGrained/LocalMapCounting.lean": 5, + "CslibFrontier/Computability/Complexity/FineGrained/PermutationIndependentSet.lean": 16, + "CslibFrontier/Computability/Complexity/ImmermanVardi/Tableau/Step.lean": 2, + "CslibFrontier/Computability/Complexity/Parameterized/VertexCover/BussKernel.lean": 5, + "CslibFrontier/Computability/Complexity/Problems/CSP/Incidence.lean": 1, + "CslibFrontier/Computability/Complexity/Problems/CSP/SelfDualThreeSAT.lean": 2, + "CslibFrontier/Computability/Complexity/Problems/CircuitSAT.lean": 4, + "CslibFrontier/Computability/Complexity/Problems/CircuitValue.lean": 8, + "CslibFrontier/Computability/Complexity/Problems/CircuitValue/Writer.lean": 17, + "CslibFrontier/Computability/Complexity/Problems/Hamiltonian.lean": 10, + "CslibFrontier/Computability/Complexity/Problems/Hamiltonian/Diamond/Defs.lean": 28, + "CslibFrontier/Computability/Complexity/Problems/Hamiltonian/Sparse.lean": 4, + "CslibFrontier/Computability/Complexity/Problems/Hamiltonian/Undirected.lean": 2, + "CslibFrontier/Computability/Complexity/Problems/IndependentSet.lean": 7, + "CslibFrontier/Computability/Complexity/Problems/IndependentSet/Sparse.lean": 2, + "CslibFrontier/Computability/Complexity/Problems/Knapsack.lean": 8, + "CslibFrontier/Computability/Complexity/Problems/Knapsack/Unary.lean": 2, + "CslibFrontier/Computability/Complexity/Problems/LinearProgramming.lean": 3, + "CslibFrontier/Computability/Complexity/Problems/LongPath/Hardness.lean": 2, + "CslibFrontier/Computability/Complexity/Problems/MaxTwoSAT.lean": 4, + "CslibFrontier/Computability/Complexity/Problems/MonotoneCircuitValue.lean": 6, + "CslibFrontier/Computability/Complexity/Problems/SAT/ExactOne/DrawingAssembly.lean": 3, + "CslibFrontier/Computability/Complexity/Problems/SAT/ExactOne/DrawingAvoidance.lean": 8, + "CslibFrontier/Computability/Complexity/Problems/SAT/ExactOne/DrawingData.lean": 5, + "CslibFrontier/Computability/Complexity/Problems/SAT/ExactOne/DrawingPorts.lean": 4, + "CslibFrontier/Computability/Complexity/Problems/SAT/ExactOne/DrawingValid.lean": 6, + "CslibFrontier/Computability/Complexity/Problems/SAT/ExactOne/GeometryCollarData.lean": 6, + "CslibFrontier/Computability/Complexity/Problems/SAT/Incidence.lean": 1, + "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/CrossoverLayout.lean": 14, + "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/CrossoverLayoutChecks.lean": 21, + "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/OrdinaryRing.lean": 31, + "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PairedCompile.lean": 4, + "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PairedCorrectness.lean": 2, + "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PairedCrossoverLayout.lean": 14, + "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PairedCrossoverLayoutChecks.lean": 21, + "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PairedGeometry.lean": 21, + "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PairedOccurrence.lean": 3, + "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PairedSemantics.lean": 14, + "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PathAssignments.lean": 8, + "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/Paths.lean": 2, + "CslibFrontier/Computability/Complexity/Problems/SubsetSum.lean": 2, + "CslibFrontier/Computability/Complexity/Problems/ThreeSATThree.lean": 2, + "CslibFrontier/Computability/Complexity/Problems/VertexCover.lean": 2, + "CslibFrontier/Computability/GenericRAM/Native/BooleanFold.lean": 9, + "CslibFrontier/Computability/GenericRAM/Native/BooleanSearch.lean": 12, + "CslibFrontier/Computability/GenericRAM/Native/ExhaustiveCountFallback.lean": 16, + "CslibFrontier/Computability/GenericRAM/Native/StackRecursion.lean": 7, + "CslibFrontier/Computability/GenericRAM/Simulation/Run.lean": 6, + "CslibFrontier/Computability/GenericRAM/Simulation/Turing.lean": 3, + "CslibFrontier/Computability/GenericRAM/SparseMemory/Turing.lean": 4, + "CslibFrontier/Computability/Interactive/Bundle.lean": 2, + "CslibFrontier/Computability/Interactive/GKR/Protocol.lean": 19, + "CslibFrontier/Computability/Interactive/Security.lean": 8, + "CslibFrontier/Computability/LogSpaceDerandomization/ConditionalProductMean.lean": 1, + "CslibFrontier/Computability/LogSpaceDerandomization/PilotDetectorTransport.lean": 10, + "CslibFrontier/Computability/Machines/ReadMachine/Tidy.lean": 22, + "CslibFrontier/Computability/Machines/ReadMachine/VirtualWord.lean": 9, + "CslibFrontier/Computability/Machines/ReadMachine/VirtualWord/Evaluator.lean": 14, + "CslibFrontier/Computability/Machines/ReadMachine/VirtualWord/Generate.lean": 1, + "CslibFrontier/Computability/Machines/ReadMachine/VirtualWord/Native.lean": 12, + "CslibFrontier/Computability/Machines/ReadMachine/VirtualWord/Simulation.lean": 46, + "CslibFrontier/Computability/Machines/Turing/MultiTape/BlockComputation/EncodedTree.lean": 2, + "CslibFrontier/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding.lean": 3, + "CslibFrontier/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeDecode.lean": 2, + "CslibFrontier/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplay.lean": 4, + "CslibFrontier/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadSearch.lean": 3, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/BigO/ZipFold.lean": 4, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/InSpace/NatPrime.lean": 7, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/InSpaceLib.lean": 8, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/Composition/SmallSpace.lean": 32, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/ConfigurationGraph/Basic.lean": 13, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/ConfigurationGraph/Correctness.lean": 2, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/ConfigurationGraph/Expr.lean": 8, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/ConfigurationGraph/Tape.lean": 8, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/CounterProgram/Nondeterministic.lean": 2, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/InductiveCounting/Machine.lean": 2, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/QueryMachine.lean": 9, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/Savitch.lean": 9, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/PolyTime/Nat/Jacobi.lean": 15, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/PolyTime/RotationMap/TowerTable/Family.lean": 5, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/PolyTime/RotationMap/Walk.lean": 14, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/Space/Reachability.lean": 2, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Complexity/Translate.lean": 4, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Counting/Checker.lean": 2, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Counting/Oracle/Replay.lean": 6, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Encodings/Nat/Subtraction.lean": 7, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Encodings/StepMachineComp.lean": 5, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Feature.lean": 6, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Halting/Partrec.lean": 23, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Machine/ChoiceTable.lean": 5, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Probabilistic/BlockNetwork.lean": 1, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Probabilistic/IntervalCount.lean": 11, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Probabilistic/IntervalCount/PolyTime.lean": 11, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Probabilistic/IntervalSimulation.lean": 4, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Probabilistic/IntervalTensor.lean": 4, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Probabilistic/RunsWords.lean": 2, + "CslibFrontier/Computability/Machines/Turing/MultiTape/StepMachine/Basic.lean": 5, + "CslibFrontier/Computability/Machines/Turing/MultiTape/StepMachine/Comp.lean": 4, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/Correct.lean": 6, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/Decode.lean": 7, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/InPlace.lean": 15, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/LookupRun.lean": 11, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/Machine.lean": 9, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/Nondeterministic.lean": 6, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/PolyTime.lean": 8, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/SpaceBudget.lean": 11, + "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/Windowed.lean": 5, + "CslibFrontier/Computability/Machines/Turing/SingleTape/Partrec.lean": 2, + "CslibFrontier/Computability/PCP/Approximation/IndependentSet.lean": 1, + "CslibFrontier/Computability/PCP/AssignmentTester.lean": 12, + "CslibFrontier/Computability/PCP/AssignmentTester/Selection.lean": 4, + "CslibFrontier/Computability/PCP/DegreeReduction.lean": 3, + "CslibFrontier/Computability/PCP/Encoded/DegreeReduction.lean": 7, + "CslibFrontier/Computability/PCP/Encoded/GenericComposition.lean": 13, + "CslibFrontier/Computability/PCP/Encoded/Power.lean": 5, + "CslibFrontier/Computability/PCP/Encoded/Preprocess/Programs.lean": 6, + "CslibFrontier/Computability/PCP/Encoded/Verifier.lean": 2, + "CslibFrontier/Computability/PCP/Encoded/Verifier/Reduction.lean": 2, + "CslibFrontier/Computability/PCP/Encoded/Verifier/Repetition.lean": 3, + "CslibFrontier/Computability/PCP/Power.lean": 33, + "CslibFrontier/Computability/PCP/Power/WithInput.lean": 10, + "CslibFrontier/Computability/PCP/PoweringBound.lean": 1, + "CslibFrontier/Computability/PCP/TwoProverGame.lean": 4, + "CslibFrontier/Computability/PCP/TwoProverGame/Quantum.lean": 4, + "CslibFrontier/Computability/PolyTime/CSP/Edge/Augmenting.lean": 1, + "CslibFrontier/Computability/PolyTime/CommonCountingAccum.lean": 3, + "CslibFrontier/Computability/PolyTime/LinearAlgebra/BooleanMeasurements.lean": 1, + "CslibFrontier/Computability/PolyTime/LinearAlgebra/ReplacementDesignBuilder.lean": 1, + "CslibFrontier/Computability/QueryComplexity/AndOrTree/Randomized.lean": 2, + "CslibFrontier/Computability/QueryComplexity/Quantum/Composition.lean": 52, + "CslibFrontier/Computability/QueryComplexity/Quantum/Grover.lean": 5, + "CslibFrontier/Computability/QueryComplexity/Quantum/Search.lean": 3, + "CslibFrontier/Computability/QueryComplexity/Quantum/Simon.lean": 28, + "CslibFrontier/Computability/QueryComplexity/Quantum/SimonSearch.lean": 3, + "CslibFrontier/Computability/QueryComplexity/Quantum/UnknownSearch.lean": 4, + "CslibFrontier/Computability/Streaming/FkEstimator.lean": 10, + "CslibFrontier/Computability/TreeEval/Ary/Packed/GateInterpolation.lean": 5, + "CslibFrontier/Computability/TreeEval/Ary/Packed/Interpolation.lean": 10, + "CslibFrontier/Computability/TreeEval/Ary/Packed/QueryGate.lean": 28, + "CslibFrontier/Computability/TreeEval/Ary/Packed/QueryGateInit.lean": 10, + "CslibFrontier/Computability/TreeEval/Ary/Packed/QueryGateSpace.lean": 7, + "CslibFrontier/Computability/TreeEval/Ary/Packed/RawController.lean": 19, + "CslibFrontier/Computability/TreeEval/Ary/Packed/RawControllerCorrect.lean": 5, + "CslibFrontier/Computability/TreeEval/Ary/Packed/RawControllerOutput.lean": 1, + "CslibFrontier/Computability/TreeEval/Ary/Packed/RawControllerQuery.lean": 39, + "CslibFrontier/Computability/TreeEval/Ary/Packed/RawControllerSpace.lean": 28, + "CslibFrontier/Computability/TreeEval/Reader/Cert/Step.lean": 2, + "CslibFrontier/Computability/TreeEval/Reader/Defs.lean": 4, + "CslibFrontier/Computability/TreeEval/Reader/Encode.lean": 3, + "CslibFrontier/Computability/TreeEval/Reader/Size.lean": 4, + "CslibFrontier/Computability/TreeEval/Reader/Tuple.lean": 3, + "CslibFrontier/Computability/USTCON/Reader/Encode.lean": 13, + "CslibFrontier/Computability/WordRAM/Heap.lean": 2, + "CslibFrontier/Computability/WordRAM/Realization/Absorb.lean": 4, + "CslibFrontier/Computability/WordRAM/Realization/Loop.lean": 12, + "CslibFrontier/Crypto/Computational/DiscreteLog/SchnorrGroup.lean": 4, + "CslibFrontier/Crypto/Computational/Encryption/CCA.lean": 12, + "CslibFrontier/Crypto/Computational/GMW/Sequential.lean": 20, + "CslibFrontier/Crypto/Computational/GMW/Simulator.lean": 1, + "CslibFrontier/Crypto/Computational/GoldwasserMicali.lean": 8, + "CslibFrontier/Crypto/Computational/HashAndSign.lean": 6, + "CslibFrontier/Crypto/Computational/Homomorphic/KeyChain/CoinTape.lean": 3, + "CslibFrontier/Crypto/Computational/Homomorphic/Leveled/GameCoinTape.lean": 2, + "CslibFrontier/Crypto/Computational/Homomorphic/Leveled/GuessCoinTape.lean": 5, + "CslibFrontier/Crypto/Computational/Kilian/MicaliSoundness.lean": 12, + "CslibFrontier/Crypto/Computational/Kilian/Soundness.lean": 1, + "CslibFrontier/Crypto/Computational/LWE/DualRegev.lean": 1, + "CslibFrontier/Crypto/Computational/LWE/Regev.lean": 4, + "CslibFrontier/Crypto/Computational/MLDSA/KeyOnly.lean": 5, + "CslibFrontier/Crypto/Computational/NaorYung.lean": 4, + "CslibFrontier/Crypto/Computational/RSATrapdoor.lean": 4, + "CslibFrontier/Crypto/Computational/SIS/ModulusSwitch/CenteredBinomial.lean": 2, + "CslibFrontier/Crypto/Computational/SIS/ModulusSwitch/Failure.lean": 1, + "CslibFrontier/Crypto/Computational/SchnorrSignature/EncodedROM.lean": 1, + "CslibFrontier/Crypto/Computational/SchnorrSignature/NativeROM.lean": 8, + "CslibFrontier/Crypto/Computational/Signature/WordRandomOracle.lean": 4, + "CslibFrontier/Crypto/Computational/Stretch/Quantitative.lean": 6, + "CslibFrontier/Crypto/Computational/TCRSignature.lean": 6, + "CslibFrontier/Crypto/Computational/TCRSignature/ManyTime.lean": 8, + "CslibFrontier/Crypto/Computational/TargetCollisionResistant.lean": 5, + "CslibFrontier/Crypto/Game/CloseTo/RandomOracle.lean": 1, + "CslibFrontier/Crypto/Primitives/BlockCipher/CBCMAC.lean": 20, + "CslibFrontier/Crypto/Primitives/BlockCipher/Modes.lean": 4, + "CslibFrontier/Crypto/Primitives/Garbling/CircuitInverters.lean": 6, + "CslibFrontier/Crypto/Primitives/Garbling/CorrelationRobust.lean": 4, + "CslibFrontier/Crypto/Primitives/Garbling/FreeXOR.lean": 1, + "CslibFrontier/Crypto/Primitives/Garbling/HalfGates/Privacy.lean": 1, + "CslibFrontier/Crypto/Primitives/Garbling/SizeLowerBound.lean": 5, + "CslibFrontier/Crypto/Primitives/Garbling/Switch/Oblivious.lean": 3, + "CslibFrontier/Crypto/Protocols/MPC/DDHOT.lean": 16, + "CslibFrontier/Crypto/Protocols/MPC/IKNP.lean": 4, + "CslibFrontier/Crypto/Protocols/MPC/OTCommitment/Opening.lean": 6, + "CslibFrontier/Crypto/Protocols/MPC/Yao.lean": 6, + "CslibFrontier/Crypto/Protocols/SigmaProtocol/Abort/RandomOracle.lean": 2, + "CslibFrontier/Crypto/Protocols/SigmaProtocol/Lyubashevsky/NativeExtract.lean": 6, + "CslibFrontier/Crypto/Protocols/SigmaProtocol/Lyubashevsky/Signature/View.lean": 2, + "CslibFrontier/Crypto/Protocols/Signature/RandomOracle.lean": 4, + "CslibFrontier/Crypto/Protocols/VOLE/AdaptiveMulCheck.lean": 8, + "CslibFrontier/Crypto/Protocols/VOLE/Circuit.lean": 2, + "CslibFrontier/Crypto/Protocols/VOLE/CircuitPrivacy.lean": 5, + "CslibFrontier/Crypto/Protocols/VOLE/MaskedMulCheck.lean": 10, + "CslibFrontier/Crypto/Protocols/VOLE/Native.lean": 2, + "CslibFrontier/DataStructure/AVLTree/Impl.lean": 8, + "CslibFrontier/DataStructure/ApproximateMembership/WordRAM/QuotientFilter/Cluster.lean": 393, + "CslibFrontier/DataStructure/Dictionary/WordRAM/Succinct.lean": 2, + "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctBucketOps.lean": 47, + "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctChainOps.lean": 75, + "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctDeleteOps.lean": 108, + "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctHash.lean": 83, + "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctMicroChain.lean": 248, + "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctProg.lean": 102, + "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctSearch.lean": 274, + "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctSlabDemote.lean": 28, + "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctSlabMove.lean": 28, + "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctSlabOps.lean": 43, + "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctSlabPool.lean": 64, + "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctTailOps.lean": 38, + "CslibFrontier/DataStructure/Dictionary/WordRAM/SuccinctValidHistory.lean": 54, + "CslibFrontier/DataStructure/DisjointSet/Spec.lean": 2, + "CslibFrontier/DataStructure/DisjointSet/UnionFind.lean": 14, + "CslibFrontier/DataStructure/DynamicArray/WordRAM/Basic.lean": 13, + "CslibFrontier/DataStructure/FenwickTree/Impl.lean": 8, + "CslibFrontier/DataStructure/FusionTree/WordRAM/Preprocessing.lean": 4, + "CslibFrontier/DataStructure/OrderedSet/ExtractMin.lean": 2, + "CslibFrontier/DataStructure/PrefixSums/WordRAM/Dynamic.lean": 3, + "CslibFrontier/DataStructure/PriorityQueue/BinaryHeap/Native/Encoding.lean": 2, + "CslibFrontier/DataStructure/PriorityQueue/BinaryHeap/Native/Read.lean": 6, + "CslibFrontier/DataStructure/PriorityQueue/BinaryHeap/Native/SiftDown.lean": 26, + "CslibFrontier/DataStructure/PriorityQueue/BinaryHeap/Native/SiftUp.lean": 10, + "CslibFrontier/DataStructure/PriorityQueue/BinaryHeap/Native/Update.lean": 16, + "CslibFrontier/DataStructure/PriorityQueue/FibonacciHeap/Native/Scatter.lean": 4, + "CslibFrontier/DataStructure/PriorityQueue/FibonacciHeap/Native/ScatterList.lean": 2, + "CslibFrontier/DataStructure/PriorityQueue/FibonacciHeap/Native/Source.lean": 4, + "CslibFrontier/DataStructure/PriorityQueue/WordRAM/FibonacciHeap.lean": 3, + "CslibFrontier/DataStructure/Queue/Memory/Transfer.lean": 2, + "CslibFrontier/DataStructure/Queue/Spec.lean": 6, + "CslibFrontier/DataStructure/Queue/WordRAM/RegisterCorrectness.lean": 4, + "CslibFrontier/DataStructure/Queue/WordRAM/RegisterDynamic.lean": 6, + "CslibFrontier/DataStructure/Queue/WordRAM/RegisterExecution.lean": 4, + "CslibFrontier/DataStructure/Queue/WordRAM/RegisterMethods.lean": 1, + "CslibFrontier/DataStructure/Queue/WordRAM/RegisterTransfer.lean": 2, + "CslibFrontier/DataStructure/Retroactive/Basic.lean": 1, + "CslibFrontier/DataStructure/SearchTree/Impl.lean": 12, + "CslibFrontier/DataStructure/StateTransition.lean": 4, + "CslibFrontier/DataStructure/VanEmdeBoas/Impl.lean": 8, + "CslibFrontier/DataStructure/VanEmdeBoas/WordRAM/Call.lean": 7, + "CslibFrontier/DataStructure/WordRAM/Randomized/ObliviousSequence.lean": 2, + "CslibFrontier/Foundations/BooleanAnalysis/BucketWeight.lean": 4, + "CslibFrontier/Foundations/BooleanAnalysis/SparseHermiteRecovery.lean": 3, + "CslibFrontier/Foundations/Combinatorics/BudgetTuples.lean": 6, + "CslibFrontier/Foundations/Combinatorics/Expander/AffinePlane.lean": 3, + "CslibFrontier/Foundations/Combinatorics/Expander/Contraction.lean": 4, + "CslibFrontier/Foundations/Combinatorics/Expander/Hom.lean": 2, + "CslibFrontier/Foundations/Combinatorics/Expander/KilledWalk.lean": 27, + "CslibFrontier/Foundations/Combinatorics/Expander/Perm.lean": 5, + "CslibFrontier/Foundations/Combinatorics/Expander/TannerCode.lean": 2, + "CslibFrontier/Foundations/Combinatorics/Expander/Tensor.lean": 4, + "CslibFrontier/Foundations/Combinatorics/Expander/Walk.lean": 6, + "CslibFrontier/Foundations/Combinatorics/Expander/Zigzag.lean": 4, + "CslibFrontier/Foundations/Combinatorics/Sperner.lean": 2, + "CslibFrontier/Foundations/Data/Finset/Expect.lean": 1, + "CslibFrontier/Foundations/Geometry/Orthogonal.lean": 8, + "CslibFrontier/Foundations/Geometry/Orthogonal/Neighborhood.lean": 14, + "CslibFrontier/Foundations/LinearAlgebra/VariableCopyPlacement.lean": 5, + "CslibFrontier/Logics/FirstOrder/Atom.lean": 1, + "CslibFrontier/Logics/FirstOrder/Interpretation/Tagged.lean": 9, + "CslibFrontier/Logics/FirstOrder/Ordered/DomainInterpretation/Composition.lean": 2, + "CslibFrontier/Logics/FirstOrder/Ordered/FullTuple/Composition.lean": 4, + "CslibFrontier/Logics/FirstOrder/Ordered/FullTuple/Power.lean": 2, + "CslibFrontier/Logics/FirstOrder/Problems/CNF.lean": 5, + "CslibFrontier/Logics/FirstOrder/Problems/ColoringReduction.lean": 7, + "CslibFrontier/Logics/FirstOrder/Raw/Graph.lean": 6, + "CslibFrontier/Logics/FirstOrder/ZeroOneLaw/Relational/ExtensionAxiom.lean": 2, + "CslibFrontier/Logics/Propositional/CNF/Compact/Relabel.lean": 3, + "CslibFrontier/Logics/Propositional/CNF/ModelCount.lean": 4, + "CslibFrontier/Logics/Propositional/CNF/Occurrence.lean": 1, + "CslibFrontier/Logics/Propositional/CNF/Planar/DirectedWire.lean": 11, + "CslibFrontier/Logics/Propositional/CNF/ThreeOccurrence.lean": 2, + "CslibFrontier/Logics/Propositional/CSP/Edge/Augmenting.lean": 4, + "CslibFrontier/Logics/Propositional/CSP/Occurrence.lean": 3, + "CslibFrontier/Logics/Propositional/Resolution/Interpolation/Extraction.lean": 16, + "CslibFrontier/Logics/SecondOrder/Encoding.lean": 1, + "CslibFrontier/Logics/SecondOrder/Grounding.lean": 3, + "CslibFrontier/Logics/SecondOrder/WordMSO/McNaughtonPapert.lean": 5 } From 9a5e68566834afeb83328151a43b76db19fb78c4 Mon Sep 17 00:00:00 2001 From: Samuel Schlesinger Date: Wed, 11 Nov 2026 14:59:46 -0500 Subject: [PATCH 2/8] fix(ratchets): update projection chains baseline for Graph structure migration --- scripts/projection_chains_baseline.json | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/projection_chains_baseline.json b/scripts/projection_chains_baseline.json index b6baa48c98..bb6318525a 100644 --- a/scripts/projection_chains_baseline.json +++ b/scripts/projection_chains_baseline.json @@ -91,7 +91,6 @@ "CslibFrontier/Computability/Circuit/Quantum/Verifier.lean": 2, "CslibFrontier/Computability/Communication/Disjointness.lean": 8, "CslibFrontier/Computability/Complexity/Classes/Alternation/Savitch.lean": 7, - "CslibFrontier/Computability/Complexity/Classes/ArthurMerlin/GraphNonIsomorphism.lean": 5, "CslibFrontier/Computability/Complexity/Classes/BQP/Amplification.lean": 2, "CslibFrontier/Computability/Complexity/Classes/CountingSimulation/Accumulate.lean": 6, "CslibFrontier/Computability/Complexity/Classes/CountingSimulation/Assembly.lean": 3, @@ -135,13 +134,10 @@ "CslibFrontier/Computability/Complexity/Problems/Hamiltonian.lean": 10, "CslibFrontier/Computability/Complexity/Problems/Hamiltonian/Sparse.lean": 4, "CslibFrontier/Computability/Complexity/Problems/Hamiltonian/Undirected.lean": 2, - "CslibFrontier/Computability/Complexity/Problems/IndependentSet.lean": 7, + "CslibFrontier/Computability/Complexity/Problems/IndependentSet.lean": 9, "CslibFrontier/Computability/Complexity/Problems/IndependentSet/Sparse.lean": 2, - "CslibFrontier/Computability/Complexity/Problems/Knapsack.lean": 8, - "CslibFrontier/Computability/Complexity/Problems/Knapsack/Unary.lean": 2, "CslibFrontier/Computability/Complexity/Problems/LinearProgramming.lean": 3, "CslibFrontier/Computability/Complexity/Problems/LongPath/Hardness.lean": 2, - "CslibFrontier/Computability/Complexity/Problems/MaxTwoSAT.lean": 4, "CslibFrontier/Computability/Complexity/Problems/MonotoneCircuitValue.lean": 6, "CslibFrontier/Computability/Complexity/Problems/SAT/ExactOne/DrawingAssembly.lean": 3, "CslibFrontier/Computability/Complexity/Problems/SAT/ExactOne/DrawingAvoidance.lean": 8, @@ -163,6 +159,7 @@ "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PathAssignments.lean": 8, "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/Paths.lean": 2, "CslibFrontier/Computability/Complexity/Problems/ThreeSATThree.lean": 2, + "CslibFrontier/Computability/Complexity/Problems/VertexCover.lean": 2, "CslibFrontier/Computability/GenericRAM/Native/BooleanFold.lean": 9, "CslibFrontier/Computability/GenericRAM/Native/BooleanSearch.lean": 12, "CslibFrontier/Computability/GenericRAM/Native/ExhaustiveCountFallback.lean": 16, @@ -274,6 +271,7 @@ "CslibFrontier/Computability/USTCON/Reader/Encode.lean": 13, "CslibFrontier/Computability/WordRAM/Heap.lean": 2, "CslibFrontier/Computability/WordRAM/Realization/Absorb.lean": 4, + "CslibFrontier/Computability/WordRAM/Realization/BoundedTactic.lean": 4, "CslibFrontier/Computability/WordRAM/Realization/Loop.lean": 12, "CslibFrontier/Crypto/Computational/DiscreteLog/SchnorrGroup.lean": 4, "CslibFrontier/Crypto/Computational/Encryption/CCA.lean": 12, @@ -393,6 +391,7 @@ "CslibFrontier/Logics/FirstOrder/Problems/ColoringReduction.lean": 7, "CslibFrontier/Logics/FirstOrder/Raw/Graph.lean": 6, "CslibFrontier/Logics/FirstOrder/ZeroOneLaw/Relational/ExtensionAxiom.lean": 2, + "CslibFrontier/Logics/FirstOrder/ZeroOneLaw/Relational/ExtensionProbability.lean": 2, "CslibFrontier/Logics/Propositional/CNF/Compact/Relabel.lean": 3, "CslibFrontier/Logics/Propositional/CNF/ModelCount.lean": 4, "CslibFrontier/Logics/Propositional/CNF/Occurrence.lean": 1, From 5cc6195092b41564b6265c5c3fe0d14ece0cba44 Mon Sep 17 00:00:00 2001 From: Samuel Schlesinger Date: Wed, 11 Nov 2026 15:10:37 -0500 Subject: [PATCH 3/8] fix(Problems/IndependentSet): construct Graph structure in toIndependentSet (#4385) --- .../Computability/Complexity/Problems/IndependentSet.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CslibFrontier/Computability/Complexity/Problems/IndependentSet.lean b/CslibFrontier/Computability/Complexity/Problems/IndependentSet.lean index 6383d3d56c..657abc6a8b 100644 --- a/CslibFrontier/Computability/Complexity/Problems/IndependentSet.lean +++ b/CslibFrontier/Computability/Complexity/Problems/IndependentSet.lean @@ -110,10 +110,10 @@ instance : DecidableRel Occurrence.Conflict := fun _ _ => by /-- Karp's reduction from `SAT` to `IndependentSet`. -/ def toIndependentSet (φ : CNF ℕ) : Graph × ℕ := - (((occurrences φ).map Occurrence.vertex, + (⟨(occurrences φ).map Occurrence.vertex, (occurrences φ).flatMap fun o => ((occurrences φ).filter fun o' => decide (o.Conflict o')).map - fun o' => (o.vertex, o'.vertex)), + fun o' => (o.vertex, o'.vertex)⟩, φ.length) theorem _root_.Cslib.Logic.PL.Occurrence.Conflict.symm {o o' : Occurrence ℕ} From f996714fc0145f50ba7eef70b44c0bba50446e76 Mon Sep 17 00:00:00 2001 From: Samuel Schlesinger Date: Wed, 11 Nov 2026 15:34:38 -0500 Subject: [PATCH 4/8] test(NPCompleteGraphs): migrate Graph constructors in Hamiltonian tests (#4385) --- CslibFrontierTests/NPCompleteGraphs/Hamiltonian.lean | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CslibFrontierTests/NPCompleteGraphs/Hamiltonian.lean b/CslibFrontierTests/NPCompleteGraphs/Hamiltonian.lean index 1e1aba4ca5..62700a8c8a 100644 --- a/CslibFrontierTests/NPCompleteGraphs/Hamiltonian.lean +++ b/CslibFrontierTests/NPCompleteGraphs/Hamiltonian.lean @@ -158,19 +158,19 @@ example : DirHamCycle.verifier (dpath, certificate dpath.vertices [0, 1, 2]) = f -- Two vertices need arcs in both directions. example : DirHamCycle.verifier (⟨[0, 1], [(0, 1), (1, 0)]⟩, certificate [0, 1] [0, 1]) = true := rfl -example : DirHamCycle.verifier (([0, 1], [(0, 1)]), certificate [0, 1] [0, 1]) = false := rfl +example : DirHamCycle.verifier (⟨[0, 1], [(0, 1)]⟩, certificate [0, 1] [0, 1]) = false := rfl -- A single vertex with a self-loop has a directed Hamiltonian cycle; without it, it has none. example : DirHamCycle.verifier (⟨[0], [(0, 0)]⟩, [false]) = true := rfl -example : DirHamCycle.verifier (([0], []), [false]) = false := rfl +example : DirHamCycle.verifier (⟨[0], []⟩, [false]) = false := rfl -example : ([0], [(0, 0)]) ∈ DirHamCycle := +example : (⟨[0], [(0, 0)]⟩ : Graph) ∈ DirHamCycle := ⟨[0], by decide, fun v => by simp, by simp, by simp⟩ -- The empty graph has no directed Hamiltonian cycle. example : DirHamCycle.verifier (⟨[], []⟩, []) = false := rfl -example : ([], []) ∉ DirHamCycle := by +example : (⟨[], []⟩ : Graph) ∉ DirHamCycle := by rintro ⟨p, -, hmem, hne, -⟩ obtain ⟨a, ha⟩ := List.exists_mem_of_ne_nil p hne simpa using (hmem a).1 ha @@ -246,7 +246,7 @@ example : dtriangle ∈ HamCycle := by example : HamCycle.verifier (⟨[0], [(0, 0)]⟩, [false]) = false := rfl example : HamCycle.verifier (⟨[0, 1], [(0, 1), (1, 0)]⟩, certificate [0, 1] [0, 1]) = false := rfl -example : ([0], [(0, 0)]) ∉ HamCycle := by +example : (⟨[0], [(0, 0)]⟩ : Graph) ∉ HamCycle := by rw [mem_HamCycle_iff] rintro ⟨p, hnd, hmem, hlen, -⟩ have := ((List.perm_ext_iff_of_nodup hnd (by decide : [0].Nodup)).2 hmem).length_eq @@ -256,7 +256,7 @@ example : ([0], [(0, 0)]) ∉ HamCycle := by -- The empty graph has no Hamiltonian cycle. example : HamCycle.verifier (⟨[], []⟩, []) = false := rfl -example : ([], []) ∉ HamCycle := by +example : (⟨[], []⟩ : Graph) ∉ HamCycle := by rw [mem_HamCycle_iff] rintro ⟨p, -, hmem, hlen, -⟩ obtain ⟨a, ha⟩ := List.exists_mem_of_ne_nil p (List.ne_nil_of_length_pos (by omega)) From 81c31e0d12555ec3ad5e9794b5cb64cc6846b426 Mon Sep 17 00:00:00 2001 From: Samuel Schlesinger Date: Wed, 11 Nov 2026 17:26:49 -0500 Subject: [PATCH 5/8] refactor(Complexity/Problems): migrate Karp trio and coverings to structures (#4385) Migrate IndependentSet, Clique, and VertexCover to documented structures with enc_structure encoding instances and registered [fun_prop] polytime rules. Add deriving Inhabited to ChromaticNumberInstance, CliqueCoverInstance, and DominatingSetInstance. Update all 36 in-repository callers, reductions, and 20 test files directly without compatibility wrappers. Eliminate 8 projection chains from the repository. --- .../Fagin/Ordered/Completeness.lean | 18 +- .../Complexity/FineGrained/GraphProblems.lean | 25 +- .../Complexity/Parameterized/Clique.lean | 16 +- .../Complexity/Parameterized/VertexCover.lean | 70 ++--- .../Parameterized/VertexCover/BussKernel.lean | 283 +++++++++++------- .../Parameterized/WHierarchy/Clique.lean | 24 +- .../WHierarchy/Clique/Graph.lean | 15 +- .../WHierarchy/IndependentSet.lean | 30 +- .../Complexity/Problems/ChromaticNumber.lean | 2 +- .../Complexity/Problems/Clique.lean | 125 ++++++-- .../Complexity/Problems/Clique/OfAdj.lean | 12 +- .../Complexity/Problems/CliqueCover.lean | 2 +- .../Complexity/Problems/Copositivity.lean | 188 ++++++------ .../Complexity/Problems/DominatingSet.lean | 33 +- .../Problems/FeedbackVertexSet.lean | 28 +- .../Complexity/Problems/IndependentSet.lean | 106 ++++--- .../Problems/IndependentSet/Sparse.lean | 13 +- .../Complexity/Problems/VertexCover.lean | 171 +++++++---- .../Problems/VertexCover/Approximation.lean | 5 +- .../PCP/Approximation/IndependentSet.lean | 90 +++--- CslibFrontierTests/ColorCoding.lean | 6 +- CslibFrontierTests/Copositivity.lean | 15 +- CslibFrontierTests/CrossComposition.lean | 2 +- CslibFrontierTests/DominatingSet.lean | 14 +- CslibFrontierTests/FeedbackArcSet.lean | 3 +- CslibFrontierTests/FeedbackVertexSet.lean | 11 +- .../FineGrained/GraphProblems.lean | 21 +- CslibFrontierTests/KarpReductions.lean | 56 ++-- CslibFrontierTests/NPCompleteGraphs.lean | 55 ++-- CslibFrontierTests/NPCompleteness.lean | 23 +- CslibFrontierTests/NPWitness.lean | 12 +- CslibFrontierTests/OrderedNPCompleteness.lean | 12 +- CslibFrontierTests/Parameterized.lean | 16 +- .../VertexCoverApproximation.lean | 4 +- CslibFrontierTests/VertexCoverKernel.lean | 60 ++-- CslibFrontierTests/WHierarchy.lean | 12 +- CslibFrontierTests/WHierarchyClique.lean | 25 +- guide/CslibFrontierGuide/NPCompleteness.lean | 34 ++- manual/workflows/deterministic-workflows.md | 6 +- scripts/projection_chains_baseline.json | 5 +- scripts/test_operator_manual.py | 2 +- 41 files changed, 937 insertions(+), 713 deletions(-) diff --git a/CslibFrontier/Computability/Complexity/Fagin/Ordered/Completeness.lean b/CslibFrontier/Computability/Complexity/Fagin/Ordered/Completeness.lean index 20cffe08e4..fa2093af9f 100644 --- a/CslibFrontier/Computability/Complexity/Fagin/Ordered/Completeness.lean +++ b/CslibFrontier/Computability/Complexity/Fagin/Ordered/Completeness.lean @@ -802,41 +802,41 @@ theorem orderedThreeColorable_polyTimeReducible_threeColoring : /-- **`Complexity.IndependentSet` reduces in polynomial time to `Ordered.IndependentSet`.** -/ theorem independentSet_polyTimeReducible_orderedIndependentSet : - PolyTimeReducible (encPair encGraph encNat) encStructure + PolyTimeReducible encIndependentSetInstance encStructure Complexity.IndependentSet (IndependentSet.encodedLanguage sizedGraphVocabulary) := - independentSet_npHard (encPair encGraph encNat) Complexity.IndependentSet + independentSet_npHard encIndependentSetInstance Complexity.IndependentSet Complexity.IndependentSet.mem_NP /-- **`Ordered.IndependentSet` reduces in polynomial time to `Complexity.IndependentSet`.** -/ theorem orderedIndependentSet_polyTimeReducible_independentSet : - PolyTimeReducible encStructure (encPair encGraph encNat) + PolyTimeReducible encStructure encIndependentSetInstance (IndependentSet.encodedLanguage sizedGraphVocabulary) Complexity.IndependentSet := IndependentSet_npHard encStructure (IndependentSet.encodedLanguage sizedGraphVocabulary) independentSet_encodedLanguage_mem_NP /-- **`Complexity.Clique` reduces in polynomial time to `Ordered.Clique`.** -/ theorem clique_polyTimeReducible_orderedClique : - PolyTimeReducible (encPair encGraph encNat) encStructure + PolyTimeReducible encCliqueInstance encStructure Complexity.Clique (Clique.encodedLanguage sizedGraphVocabulary) := - clique_npHard (encPair encGraph encNat) Complexity.Clique Complexity.Clique.mem_NP + clique_npHard encCliqueInstance Complexity.Clique Complexity.Clique.mem_NP /-- **`Ordered.Clique` reduces in polynomial time to `Complexity.Clique`.** -/ theorem orderedClique_polyTimeReducible_clique : - PolyTimeReducible encStructure (encPair encGraph encNat) + PolyTimeReducible encStructure encCliqueInstance (Clique.encodedLanguage sizedGraphVocabulary) Complexity.Clique := Clique_npHard encStructure (Clique.encodedLanguage sizedGraphVocabulary) clique_encodedLanguage_mem_NP /-- **`Complexity.VertexCover` reduces in polynomial time to `Ordered.VertexCover`.** -/ theorem vertexCover_polyTimeReducible_orderedVertexCover : - PolyTimeReducible (encPair encGraph encNat) encStructure + PolyTimeReducible encVertexCoverInstance encStructure Complexity.VertexCover (VertexCover.encodedLanguage sizedGraphVocabulary) := - vertexCover_npHard (encPair encGraph encNat) Complexity.VertexCover + vertexCover_npHard encVertexCoverInstance Complexity.VertexCover Complexity.VertexCover.mem_NP /-- **`Ordered.VertexCover` reduces in polynomial time to `Complexity.VertexCover`.** -/ theorem orderedVertexCover_polyTimeReducible_vertexCover : - PolyTimeReducible encStructure (encPair encGraph encNat) + PolyTimeReducible encStructure encVertexCoverInstance (VertexCover.encodedLanguage sizedGraphVocabulary) Complexity.VertexCover := VertexCover_npHard encStructure (VertexCover.encodedLanguage sizedGraphVocabulary) vertexCover_encodedLanguage_mem_NP diff --git a/CslibFrontier/Computability/Complexity/FineGrained/GraphProblems.lean b/CslibFrontier/Computability/Complexity/FineGrained/GraphProblems.lean index 93a1938610..509e76ee01 100644 --- a/CslibFrontier/Computability/Complexity/FineGrained/GraphProblems.lean +++ b/CslibFrontier/Computability/Complexity/FineGrained/GraphProblems.lean @@ -109,8 +109,8 @@ for some `δ > 0`, `IndependentSet` is not decidable within time `2 ^ (δ (|V| + polynomial in the length of the encoded instance, for graphs with `|V|` declared vertices and `|E|` edges. -/ theorem ETH.not_decidableInSubexpTime_IndependentSet (h : ETH) : - ¬DecidableInSubexpTime IndependentSet (encPair encGraph encNat) - fun x => x.1.vertices.length + x.1.edges.length := + ¬DecidableInSubexpTime IndependentSet encIndependentSetInstance + fun x => x.graph.vertices.length + x.graph.edges.length := h.not_decidableInSubexpTime_of_reduction SAT.computableInPolyTime_toSparseIndependentSet (fun φ _ => SAT.toSparseIndependentSet_mem_iff φ) (k := 24) fun φ hφ => by have := SAT.length_toSparseIndependentSet_le hφ @@ -120,7 +120,8 @@ theorem ETH.not_decidableInSubexpTime_IndependentSet (h : ETH) : some `δ > 0`, `IndependentSet` is not decidable within time `2 ^ (δ |V|)` times any polynomial in the length of the encoded instance, for graphs with `|V|` declared vertices. -/ theorem ETH.not_decidableInSubexpTime_IndependentSet_vertices (h : ETH) : - ¬DecidableInSubexpTime IndependentSet (encPair encGraph encNat) fun x => x.1.vertices.length := + ¬DecidableInSubexpTime IndependentSet encIndependentSetInstance + fun x => x.graph.vertices.length := h.not_decidableInSubexpTime_of_reduction SAT.computableInPolyTime_toSparseIndependentSet (fun φ _ => SAT.toSparseIndependentSet_mem_iff φ) (k := 24) fun φ hφ => by have := SAT.length_toSparseIndependentSet_le hφ @@ -131,8 +132,8 @@ for some `δ > 0`, `VertexCover` is not decidable within time `2 ^ (δ (|V| + |E polynomial in the length of the encoded instance, for graphs with `|V|` declared vertices and `|E|` edges. -/ theorem ETH.not_decidableInSubexpTime_VertexCover (h : ETH) : - ¬DecidableInSubexpTime VertexCover (encPair encGraph encNat) - fun x => x.1.vertices.length + x.1.edges.length := + ¬DecidableInSubexpTime VertexCover encVertexCoverInstance + fun x => x.graph.vertices.length + x.graph.edges.length := h.not_decidableInSubexpTime_of_reduction (IndependentSet.computableInPolyTime_toVertexCover.comp SAT.computableInPolyTime_toSparseIndependentSet) @@ -146,16 +147,14 @@ theorem ETH.not_decidableInSubexpTime_VertexCover (h : ETH) : `δ > 0`, `Clique` is not decidable within time `2 ^ (δ |V|)` times any polynomial in the length of the encoded instance, for graphs with `|V|` declared vertices. -/ theorem ETH.not_decidableInSubexpTime_Clique (h : ETH) : - ¬DecidableInSubexpTime Clique (encPair encGraph encNat) fun x => x.1.vertices.length := - h.not_decidableInSubexpTime_of_reduction (f := fun φ => - ((SAT.toSparseIndependentSet φ).1.compl, (SAT.toSparseIndependentSet φ).2)) - ((show ComputableInPolyTime (fun x : Graph × ℕ => (x.1.compl, x.2)) - (encPair encGraph encNat) (encPair encGraph encNat) by fun_prop).comp - SAT.computableInPolyTime_toSparseIndependentSet) - (fun φ _ => (Graph.compl_mem_Clique_iff _ _).trans (SAT.toSparseIndependentSet_mem_iff φ)) + ¬DecidableInSubexpTime Clique encCliqueInstance fun x => x.graph.vertices.length := + h.not_decidableInSubexpTime_of_reduction + (computableInPolyTime_independentSetToClique.comp + SAT.computableInPolyTime_toSparseIndependentSet) + (fun φ _ => (independentSetToClique_mem_iff _).trans (SAT.toSparseIndependentSet_mem_iff φ)) (k := 24) fun φ hφ => by have := SAT.length_toSparseIndependentSet_le hφ - simp only [Graph.compl] at this ⊢ + simp only [Function.comp_apply, independentSetToClique, Graph.compl] at this ⊢ omega /-- **No `2 ^ o(|V| + |E|)` algorithm for Hamiltonian cycles under ETH** [CyganEtAl2015, diff --git a/CslibFrontier/Computability/Complexity/Parameterized/Clique.lean b/CslibFrontier/Computability/Complexity/Parameterized/Clique.lean index 609f5f1dbe..17cfa8f0df 100644 --- a/CslibFrontier/Computability/Complexity/Parameterized/Clique.lean +++ b/CslibFrontier/Computability/Complexity/Parameterized/Clique.lean @@ -43,29 +43,29 @@ open Turing.MultiTapeTM /-- **`IndependentSet` fpt-reduces to `Clique`** [CyganEtAl2015, Section 13.1] by complementing the graph, keeping the parameter `k`. -/ theorem fptReducible_IndependentSet_Clique : - FPTReducible (encPair encGraph encNat) (encPair encGraph encNat) Prod.snd Prod.snd + FPTReducible encIndependentSetInstance encCliqueInstance (·.m) (·.m) IndependentSet Clique := - FPTReducible.of_computableInPolyTime (f := fun x : Graph × ℕ => (x.1.compl, x.2)) (h := id) + FPTReducible.of_computableInPolyTime (f := independentSetToClique) (h := id) (by fun_prop) (fun _ => le_rfl) fun ⟨G, m⟩ => (Graph.compl_mem_Clique_iff G m).symm /-- **`Clique` fpt-reduces to `IndependentSet`** [CyganEtAl2015, Section 13.1] by complementing the graph, keeping the parameter `k`. -/ theorem fptReducible_Clique_IndependentSet : - FPTReducible (encPair encGraph encNat) (encPair encGraph encNat) Prod.snd Prod.snd + FPTReducible encCliqueInstance encIndependentSetInstance (·.m) (·.m) Clique IndependentSet := - FPTReducible.of_computableInPolyTime (f := fun x : Graph × ℕ => (x.1.compl, x.2)) (h := id) + FPTReducible.of_computableInPolyTime (f := cliqueToIndependentSet) (h := id) (by fun_prop) (fun _ => le_rfl) fun ⟨G, m⟩ => (Graph.compl_mem_IndependentSet_iff G m).symm /-- `Clique` is fixed-parameter tractable in `k` exactly when `IndependentSet` is. -/ theorem Clique_mem_FPT_iff : - Clique ∈ FPT (encPair encGraph encNat) Prod.snd ↔ - IndependentSet ∈ FPT (encPair encGraph encNat) Prod.snd := + Clique ∈ FPT encCliqueInstance (·.m) ↔ + IndependentSet ∈ FPT encIndependentSetInstance (·.m) := ⟨fptReducible_IndependentSet_Clique.mem_FPT, fptReducible_Clique_IndependentSet.mem_FPT⟩ /-- `Clique` is in `XP` for the parameter `k` exactly when `IndependentSet` is. -/ theorem Clique_mem_XP_iff : - Clique ∈ XP (encPair encGraph encNat) Prod.snd ↔ - IndependentSet ∈ XP (encPair encGraph encNat) Prod.snd := + Clique ∈ XP encCliqueInstance (·.m) ↔ + IndependentSet ∈ XP encIndependentSetInstance (·.m) := ⟨fptReducible_IndependentSet_Clique.mem_XP, fptReducible_Clique_IndependentSet.mem_XP⟩ end Cslib.Complexity diff --git a/CslibFrontier/Computability/Complexity/Parameterized/VertexCover.lean b/CslibFrontier/Computability/Complexity/Parameterized/VertexCover.lean index c0a38098af..a81da8e65a 100644 --- a/CslibFrontier/Computability/Complexity/Parameterized/VertexCover.lean +++ b/CslibFrontier/Computability/Complexity/Parameterized/VertexCover.lean @@ -231,24 +231,24 @@ theorem exists_lt_two_pow_isCover_foldBranch (G : Graph) (m : ℕ) : /-! ### The search procedure -/ /-- The search depth `min |V| k`, where `|V|` is the number of distinct declared vertices of `G`. -/ -def cap (x : Graph × ℕ) : ℕ := - min x.1.distinctVertices.length x.2 +def cap (x : VertexCoverInstance) : ℕ := + min x.graph.distinctVertices.length x.k -theorem cap_eq (x : Graph × ℕ) : cap x = min x.1.vertices.toFinset.card x.2 := by +theorem cap_eq (x : VertexCoverInstance) : cap x = min x.graph.vertices.toFinset.card x.k := by rw [cap, Graph.length_distinctVertices] -theorem cap_le_snd (x : Graph × ℕ) : cap x ≤ x.2 := +theorem cap_le_snd (x : VertexCoverInstance) : cap x ≤ x.k := Nat.min_le_right _ _ /-- **Bounded-search-tree decision procedure for `VertexCover`**: try all `2 ^ cap x` paths of length `cap x` and check that `k ≤ |V|` (`cap x = k`). -/ -def search (x : Graph × ℕ) : Bool := - decide (cap x = x.2) && +def search (x : VertexCoverInstance) : Bool := + decide (cap x = x.k) && (List.range (2 ^ cap x)).any fun i => - isCover x.1 (foldBranch x.1 ((List.range (cap x)).map i.testBit)) + isCover x.graph (foldBranch x.graph ((List.range (cap x)).map i.testBit)) /-- **`VertexCover.search` decides `VertexCover`.** -/ -theorem search_eq_true_iff (x : Graph × ℕ) : search x = true ↔ x ∈ VertexCover := by +theorem search_eq_true_iff (x : VertexCoverInstance) : search x = true ↔ x ∈ VertexCover := by rcases x with ⟨G, k⟩ rw [search, mem_VertexCover_iff_exists_card_le] simp only [Bool.and_eq_true, decide_eq_true_eq, List.any_eq_true, List.mem_range, cap_eq, @@ -262,9 +262,9 @@ open ComputableInPolyTime in /-- The capped depth `cap` is computable in polynomial time in unary. -/ @[fun_prop] theorem computableInPolyTime_cap : - ComputableInPolyTime cap (encPair encGraph encNat) encUnary := by + ComputableInPolyTime cap encVertexCoverInstance encUnary := by unfold cap - exact binary_min_unary' (by fun_prop) (snd _ _) + exact binary_min_unary' (by fun_prop) (by fun_prop) open ComputableInPolyTime in /-- A single step of the bounded search tree is polynomial-time. -/ @@ -291,11 +291,11 @@ private theorem length_encList_branchStep_le (G : Graph) (C : List ℕ) (b : Boo · simp only [branchStep, h, ↓reduceIte] omega -/-- Candidate path tuples `((G, k), cap (G, k), i)`, with `cap (G, k)` in unary. -/ -private abbrev Cand : Type := ((Graph × ℕ) × ℕ) × ℕ +/-- Candidate path tuples `(x, cap x, i)`, with `cap x` in unary. -/ +private abbrev Cand : Type := (VertexCoverInstance × ℕ) × ℕ private abbrev encCand : Cand ↪ List Bool := - encPair (encPair (encPair encGraph encNat) encUnary) encNat + encPair (encPair encVertexCoverInstance encUnary) encNat open ComputableInPolyTime in /-- Computing the `m` branching bits of path `i` is polynomial-time. -/ @@ -318,15 +318,15 @@ open ComputableInPolyTime in /-- Following a root-to-leaf path in the bounded search tree is polynomial-time. -/ private theorem computableInPolyTime_foldBranch : ComputableInPolyTime - (fun s : Cand => foldBranch s.1.1.1 ((List.range s.1.2).map s.2.testBit)) + (fun s : Cand => foldBranch s.1.1.graph ((List.range s.1.2).map s.2.testBit)) encCand (encList encNat) := by - have hG : ComputableInPolyTime (fun s : Cand => s.1.1.1) encCand encGraph := by fun_prop + have hG : ComputableInPolyTime (fun s : Cand => s.1.1.graph) encCand encGraph := by fun_prop have hgrowth_poly : PolyBounded (fun s : Cand => (encCand s).length) - (fun s : Cand => 2 * (encGraph s.1.1.1).length + 2) := + (fun s : Cand => 2 * (encGraph s.1.1.graph).length + 2) := ((PolyBounded.const 2).mul hG.polyBounded_length).add (.const 2) exact foldlWith_of_bounded_growth hG computableInPolyTime_branchBits (const []) computableInPolyTime_branchStep hgrowth_poly - fun s C b _ => length_encList_branchStep_le s.1.1.1 C b + fun s C b _ => length_encList_branchStep_le s.1.1.graph C b open ComputableInPolyTime in /-- Checking whether candidate path `i` yields a valid vertex cover of size `k ≤ |V|` is @@ -334,18 +334,19 @@ polynomial-time. -/ private theorem computableInPolyTime_checkCand : ComputableInPolyTime (fun s : Cand => - decide (s.1.2 = s.1.1.2) && - isCover s.1.1.1 (foldBranch s.1.1.1 ((List.range s.1.2).map s.2.testBit))) + decide (s.1.2 = s.1.1.k) && + isCover s.1.1.graph (foldBranch s.1.1.graph ((List.range s.1.2).map s.2.testBit))) encCand encBool := by have hcov : ComputableInPolyTime - (fun s : Cand => isCover s.1.1.1 (foldBranch s.1.1.1 ((List.range s.1.2).map s.2.testBit))) + (fun s : Cand => isCover s.1.1.graph + (foldBranch s.1.1.graph ((List.range s.1.2).map s.2.testBit))) encCand encBool := computableInPolyTime_isCover.comp - ((show ComputableInPolyTime (fun s : Cand => s.1.1.1) encCand encGraph + ((show ComputableInPolyTime (fun s : Cand => s.1.1.graph) encCand encGraph by fun_prop).prod computableInPolyTime_foldBranch) exact bool_and' (((unary_to_binary.comp ((snd _ _).comp (fst _ _))).beq - ((snd _ _).comp ((fst _ _).comp (fst _ _)))).of_enc_eq + (show ComputableInPolyTime (fun s : Cand => s.1.1.k) encCand encNat by fun_prop)).of_enc_eq fun _ => by simp [beq_eq_decide]) hcov @@ -354,23 +355,24 @@ end VertexCover /-- **`VertexCover` by bounded search tree**: `VertexCover` is decidable within time `2 ^ k` times a polynomial in the length of the encoded instance, for target cover size `k`. -/ theorem VertexCover_decidableInExpTime_one : - DecidableInExpTime VertexCover (encPair encGraph encNat) Prod.snd 1 := by + DecidableInExpTime VertexCover encVertexCoverInstance (·.k) 1 := by have hB : DecidableInExpTime {s : VertexCover.Cand | - (decide (s.1.2 = s.1.1.2) && - VertexCover.isCover s.1.1.1 - (VertexCover.foldBranch s.1.1.1 ((List.range s.1.2).map s.2.testBit))) = true} + (decide (s.1.2 = s.1.1.k) && + VertexCover.isCover s.1.1.graph + (VertexCover.foldBranch s.1.1.graph ((List.range s.1.2).map s.2.testBit))) = true} VertexCover.encCand (fun _ => 0) 0 := .of_computableInPolyTime VertexCover.computableInPolyTime_checkCand (fun _ => Iff.rfl) have hf : ComputableInPolyTime - (fun q : (Graph × ℕ) × ℕ => ((q.1, VertexCover.cap q.1), q.2)) - (encPair (encPair encGraph encNat) encNat) VertexCover.encCand := by fun_prop - have hfA (x : Graph × ℕ) : x ∈ VertexCover ↔ ∃ i < 2 ^ VertexCover.cap x, + (fun q : VertexCoverInstance × ℕ => ((q.1, VertexCover.cap q.1), q.2)) + (encPair encVertexCoverInstance encNat) VertexCover.encCand := by fun_prop + have hfA (x : VertexCoverInstance) : x ∈ VertexCover ↔ ∃ i < 2 ^ VertexCover.cap x, ((x, VertexCover.cap x), i) ∈ {s : VertexCover.Cand | - (decide (s.1.2 = s.1.1.2) && - VertexCover.isCover s.1.1.1 - (VertexCover.foldBranch s.1.1.1 ((List.range s.1.2).map s.2.testBit))) = true} := by + (decide (s.1.2 = s.1.1.k) && + VertexCover.isCover s.1.1.graph + (VertexCover.foldBranch s.1.1.graph + ((List.range s.1.2).map s.2.testBit))) = true} := by rw [← VertexCover.search_eq_true_iff, VertexCover.search] simp only [Bool.and_eq_true, decide_eq_true_eq, List.any_eq_true, List.mem_range, Set.mem_ofPred_eq] @@ -384,11 +386,11 @@ theorem VertexCover_decidableInExpTime_one : (fun x => by simpa using VertexCover.cap_le_snd x) le_rfl /-- **`VertexCover` is fixed-parameter tractable** in the target cover size `k`. -/ -theorem VertexCover_mem_FPT : VertexCover ∈ FPT (encPair encGraph encNat) Prod.snd := +theorem VertexCover_mem_FPT : VertexCover ∈ FPT encVertexCoverInstance (·.k) := VertexCover_decidableInExpTime_one.mem_FPT /-- **`VertexCover` has a kernel** in the target cover size `k`. -/ -theorem VertexCover_hasKernel : HasKernel (encPair encGraph encNat) Prod.snd VertexCover := +theorem VertexCover_hasKernel : HasKernel encVertexCoverInstance (·.k) VertexCover := hasKernel_of_mem_FPT VertexCover_mem_FPT end Cslib.Complexity diff --git a/CslibFrontier/Computability/Complexity/Parameterized/VertexCover/BussKernel.lean b/CslibFrontier/Computability/Complexity/Parameterized/VertexCover/BussKernel.lean index fa02f5cb5c..1460ae1a41 100644 --- a/CslibFrontier/Computability/Complexity/Parameterized/VertexCover/BussKernel.lean +++ b/CslibFrontier/Computability/Complexity/Parameterized/VertexCover/BussKernel.lean @@ -85,53 +85,53 @@ def degree (G : Graph) (v : ℕ) : ℕ := G.distinctVertices.countP (G.adjBool v) /-- A declared vertex is *high* when its degree exceeds the capped target `cap x = min n k`. -/ -def isHigh (x : Graph × ℕ) (v : ℕ) : Bool := - decide (cap x < degree x.1 v) +def isHigh (x : VertexCoverInstance) (v : ℕ) : Bool := + decide (cap x < degree x.graph v) /-- The distinct declared vertices of degree greater than `min n k`. -/ -def high (x : Graph × ℕ) : List ℕ := - x.1.distinctVertices.filter (isHigh x) +def high (x : VertexCoverInstance) : List ℕ := + x.graph.distinctVertices.filter (isHigh x) /-- The distinct declared vertices of degree at most `min n k`. -/ -def low (x : Graph × ℕ) : List ℕ := - x.1.distinctVertices.filter fun v => !isHigh x v +def low (x : VertexCoverInstance) : List ℕ := + x.graph.distinctVertices.filter fun v => !isHigh x v /-- Whether `v` has a neighbour among the low vertices. -/ -def isActive (x : Graph × ℕ) (v : ℕ) : Bool := - (low x).any (x.1.adjBool v) +def isActive (x : VertexCoverInstance) (v : ℕ) : Bool := + (low x).any (x.graph.adjBool v) /-- The low vertices with a low neighbour: the non-isolated vertices once the high vertices are removed. -/ -def active (x : Graph × ℕ) : List ℕ := +def active (x : VertexCoverInstance) : List ℕ := (low x).filter (isActive x) /-- The target of the kernel: the capped target minus the number of high vertices. -/ -def param (x : Graph × ℕ) : ℕ := +def param (x : VertexCoverInstance) : ℕ := cap x - (high x).length /-- The number of vertices of the kernel: the active vertices, padded by isolated vertices up to the number of low vertices but by no more than `param x`. -/ -def numVertices (x : Graph × ℕ) : ℕ := +def numVertices (x : VertexCoverInstance) : ℕ := min (low x).length ((active x).length + param x) /-- The edges of the kernel: the active vertices are renamed to their positions in `active x`. -/ -def edges (x : Graph × ℕ) : List (ℕ × ℕ) := +def edges (x : VertexCoverInstance) : List (ℕ × ℕ) := (List.range (active x).length).flatMap fun i => ((List.range (active x).length).filter fun j => - x.1.adjBool ((active x).getD i 0) ((active x).getD j 0)).map fun j => (i, j) + x.graph.adjBool ((active x).getD i 0) ((active x).getD j 0)).map fun j => (i, j) /-- The graph of the kernel, on the vertices `0, …, numVertices x - 1`. -/ -def graph (x : Graph × ℕ) : Graph := +def graph (x : VertexCoverInstance) : Graph := ⟨List.range (numVertices x), edges x⟩ /-- Whether the kernel is the reduced graph: the target is at most the number of distinct declared vertices, there are at most `k` high vertices, and at most `k (k + 1)` active vertices. -/ -def accept (x : Graph × ℕ) : Bool := - decide (cap x = x.2) && decide ((high x).length ≤ cap x) && +def accept (x : VertexCoverInstance) : Bool := + decide (cap x = x.k) && decide ((high x).length ≤ cap x) && decide ((active x).length ≤ cap x * (cap x + 1)) /-- A fixed instance outside `VertexCover`: one cover vertex in the empty graph. -/ -def noInstance : Graph × ℕ := (⟨[], []⟩, 1) +def noInstance : VertexCoverInstance := ⟨⟨[], []⟩, 1⟩ /-! ### Correctness -/ @@ -154,16 +154,16 @@ private theorem card_neighbors (G : Graph) (v : ℕ) : (neighbors G v).card = de private theorem degree_le_card (G : Graph) (v : ℕ) : degree G v ≤ G.vertices.toFinset.card := (List.countP_le_length).trans (Graph.length_distinctVertices G).le -variable (x : Graph × ℕ) +variable (x : VertexCoverInstance) -private theorem mem_high {v : ℕ} : v ∈ high x ↔ v ∈ x.1.vertices ∧ isHigh x v = true := by +private theorem mem_high {v : ℕ} : v ∈ high x ↔ v ∈ x.graph.vertices ∧ isHigh x v = true := by simp [high, Graph.mem_distinctVertices] -private theorem mem_low {v : ℕ} : v ∈ low x ↔ v ∈ x.1.vertices ∧ isHigh x v = false := by +private theorem mem_low {v : ℕ} : v ∈ low x ↔ v ∈ x.graph.vertices ∧ isHigh x v = false := by simp [low, Graph.mem_distinctVertices] private theorem mem_active {v : ℕ} : - v ∈ active x ↔ v ∈ low x ∧ ∃ u ∈ low x, x.1.toSimpleGraph.Adj v u := by + v ∈ active x ↔ v ∈ low x ∧ ∃ u ∈ low x, x.graph.toSimpleGraph.Adj v u := by simp [active, isActive] private theorem nodup_high : (high x).Nodup := (Graph.nodup_distinctVertices _).filter _ @@ -176,7 +176,7 @@ private theorem length_active_le_length_low : (active x).length ≤ (low x).leng List.length_filter_le _ _ private theorem length_high_add_length_low : - (high x).length + (low x).length = x.1.1.toFinset.card := by + (high x).length + (low x).length = x.graph.vertices.toFinset.card := by rw [← Graph.length_distinctVertices] exact (List.length_eq_length_filter_add _).symm @@ -188,21 +188,24 @@ variable {x} /-- **The high-degree rule** [CyganEtAl2015, Section 2.2.1]: a vertex of degree greater than `k` belongs to every vertex cover of at most `k` vertices. -/ theorem mem_of_isHigh {S : Finset ℕ} - (hS : ∀ u ∈ x.1.1, ∀ v ∈ x.1.1, x.1.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) - (hcard : S.card ≤ x.2) {v : ℕ} (hv : v ∈ x.1.1) (hhigh : isHigh x v = true) : v ∈ S := by + (hS : ∀ u ∈ x.graph.vertices, ∀ v ∈ x.graph.vertices, + x.graph.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) + (hcard : S.card ≤ x.k) {v : ℕ} (hv : v ∈ x.graph.vertices) + (hhigh : isHigh x v = true) : v ∈ S := by by_contra hvS - have hsub : neighbors x.1 v ⊆ S := fun u hu => by + have hsub : neighbors x.graph v ⊆ S := fun u hu => by obtain ⟨hu, hadj⟩ := mem_neighbors.1 hu exact (hS v hv u hu hadj).resolve_left hvS have h₁ := (Finset.card_le_card hsub).trans hcard rw [card_neighbors] at h₁ - have h₂ := degree_le_card x.1 v + have h₂ := degree_le_card x.graph v simp only [isHigh, decide_eq_true_eq, cap_eq] at hhigh omega private theorem length_high_le_card {S : Finset ℕ} - (hS : ∀ u ∈ x.1.1, ∀ v ∈ x.1.1, x.1.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) - (hcard : S.card ≤ x.2) : (high x).length ≤ S.card := by + (hS : ∀ u ∈ x.graph.vertices, ∀ v ∈ x.graph.vertices, + x.graph.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) + (hcard : S.card ≤ x.k) : (high x).length ≤ S.card := by rw [← List.toFinset_card_of_nodup (nodup_high x)] exact Finset.card_le_card fun v hv => by obtain ⟨hv, hhigh⟩ := (mem_high x).1 (List.mem_toFinset.1 hv) @@ -212,17 +215,18 @@ private theorem length_high_le_card {S : Finset ℕ} vertices has degree at most `k`, a vertex cover of at most `k` vertices leaves at most `k (k + 1)` non-isolated low vertices. -/ theorem length_active_le {S : Finset ℕ} - (hS : ∀ u ∈ x.1.1, ∀ v ∈ x.1.1, x.1.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) - (hcard : S.card ≤ x.2) : (active x).length ≤ x.2 * (x.2 + 1) := by + (hS : ∀ u ∈ x.graph.vertices, ∀ v ∈ x.graph.vertices, + x.graph.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) + (hcard : S.card ≤ x.k) : (active x).length ≤ x.k * (x.k + 1) := by classical set T := S.filter fun v => isHigh x v = false - have hT : T.card ≤ x.2 := (Finset.card_filter_le _ _).trans hcard - have hdeg : ∀ u ∈ T, (neighbors x.1 u).card ≤ x.2 := fun u hu => by + have hT : T.card ≤ x.k := (Finset.card_filter_le _ _).trans hcard + have hdeg : ∀ u ∈ T, (neighbors x.graph u).card ≤ x.k := fun u hu => by have h := (Finset.mem_filter.1 hu).2 simp only [isHigh, decide_eq_false_iff_not, not_lt, cap_eq] at h rw [card_neighbors] omega - have hsub : (active x).toFinset ⊆ T ∪ T.biUnion (neighbors x.1) := fun v hv => by + have hsub : (active x).toFinset ⊆ T ∪ T.biUnion (neighbors x.graph) := fun v hv => by obtain ⟨hvlow, u, hulow, hadj⟩ := (mem_active x).1 (List.mem_toFinset.1 hv) obtain ⟨hv, hvhigh⟩ := (mem_low x).1 hvlow obtain ⟨hu, huhigh⟩ := (mem_low x).1 hulow @@ -231,25 +235,26 @@ theorem length_active_le {S : Finset ℕ} · exact Finset.mem_union_right _ (Finset.mem_biUnion.2 ⟨u, Finset.mem_filter.2 ⟨huS, huhigh⟩, mem_neighbors.2 ⟨hv, hadj.symm⟩⟩) rw [← List.toFinset_card_of_nodup (nodup_active x)] - calc (active x).toFinset.card ≤ (T ∪ T.biUnion (neighbors x.1)).card := Finset.card_le_card hsub - _ ≤ T.card + ∑ u ∈ T, (neighbors x.1 u).card := + calc (active x).toFinset.card ≤ (T ∪ T.biUnion (neighbors x.graph)).card := + Finset.card_le_card hsub + _ ≤ T.card + ∑ u ∈ T, (neighbors x.graph u).card := (Finset.card_union_le _ _).trans (Nat.add_le_add_left Finset.card_biUnion_le _) - _ ≤ T.card + T.card * x.2 := Nat.add_le_add_left (Finset.sum_le_card_nsmul _ _ _ hdeg) _ - _ ≤ x.2 * (x.2 + 1) := by nlinarith + _ ≤ T.card + T.card * x.k := Nat.add_le_add_left (Finset.sum_le_card_nsmul _ _ _ hdeg) _ + _ ≤ x.k * (x.k + 1) := by nlinarith /-- Every instance of `VertexCover` passes the tests of the kernel. -/ private theorem accept_of_mem (hx : x ∈ VertexCover) : accept x = true := by obtain ⟨G, k⟩ := x obtain ⟨hk, S, -, hS, hcard⟩ := mem_VertexCover_iff_exists_card_le.1 hx - have hcap : cap (G, k) = k := by rw [cap_eq]; exact min_eq_right hk - have h₁ := length_high_le_card (x := (G, k)) hS hcard - have h₂ := length_active_le (x := (G, k)) hS hcard + have hcap : cap ⟨G, k⟩ = k := by rw [cap_eq]; exact min_eq_right hk + have h₁ := length_high_le_card (x := ⟨G, k⟩) hS hcard + have h₂ := length_active_le (x := ⟨G, k⟩) hS hcard simp only [accept, hcap, Bool.and_eq_true, decide_eq_true_eq, true_and] exact ⟨h₁.trans hcard, h₂⟩ private theorem mem_edges {i j : ℕ} : (i, j) ∈ edges x ↔ i < (active x).length ∧ j < (active x).length ∧ - x.1.toSimpleGraph.Adj ((active x).getD i 0) ((active x).getD j 0) := by + x.graph.toSimpleGraph.Adj ((active x).getD i 0) ((active x).getD j 0) := by simp only [edges, List.mem_flatMap, List.mem_range, List.mem_map, List.mem_filter, Graph.adjBool_eq_true_iff, Prod.mk.injEq] constructor @@ -265,18 +270,18 @@ private theorem graph_adj {i j : ℕ} : (graph x).toSimpleGraph.Adj i j ↔ i /-- **Correctness of the reduction rules**: once the high vertices fit in the target, the reduced graph has a cover of the reduced target exactly when the original graph has one of the original target. -/ -theorem graph_mem_iff (hk : cap x = x.2) (hh : (high x).length ≤ cap x) : - (graph x, param x) ∈ VertexCover ↔ x ∈ VertexCover := by +theorem graph_mem_iff (hk : cap x = x.k) (hh : (high x).length ≤ cap x) : + (⟨graph x, param x⟩ : VertexCoverInstance) ∈ VertexCover ↔ x ∈ VertexCover := by classical obtain ⟨G, k⟩ := x - have hm := length_active_le_numVertices (G, k) - have hn := length_high_add_length_low (G, k) - have hlow := length_active_le_length_low (G, k) - set a := active (G, k) with ha - have hparam : param (G, k) = k - (high (G, k)).length := by rw [param, hk] + have hm := length_active_le_numVertices ⟨G, k⟩ + have hn := length_high_add_length_low ⟨G, k⟩ + have hlow := length_active_le_length_low ⟨G, k⟩ + set a := active ⟨G, k⟩ with ha + have hparam : param ⟨G, k⟩ = k - (high ⟨G, k⟩).length := by rw [param, hk] simp only at hk hn - have hget : ∀ i < a.length, a.getD i 0 ∈ low (G, k) ∧ - ∃ u ∈ low (G, k), G.toSimpleGraph.Adj (a.getD i 0) u := fun i hi => + have hget : ∀ i < a.length, a.getD i 0 ∈ low ⟨G, k⟩ ∧ + ∃ u ∈ low ⟨G, k⟩, G.toSimpleGraph.Adj (a.getD i 0) u := fun i hi => (mem_active _).1 (by rw [List.getD_eq_getElem _ _ hi]; exact List.getElem_mem hi) rw [mem_VertexCover_iff_exists_card_le, mem_VertexCover_iff_exists_card_le] refine and_congr ?_ ⟨?_, ?_⟩ @@ -284,20 +289,20 @@ theorem graph_mem_iff (hk : cap x = x.2) (hh : (high x).length ≤ cap x) : Nat.le_add_left, and_true] omega · rintro ⟨S', -, hS', hcard'⟩ - refine ⟨(high (G, k)).toFinset ∪ (S'.filter (· < a.length)).image (a.getD · 0), ?_, ?_, ?_⟩ + refine ⟨(high ⟨G, k⟩).toFinset ∪ (S'.filter (· < a.length)).image (a.getD · 0), ?_, ?_, ?_⟩ · intro v hv rcases Finset.mem_union.1 hv with hv | hv · exact ((mem_high _).1 (List.mem_toFinset.1 hv)).1 · obtain ⟨i, hi, rfl⟩ := Finset.mem_image.1 hv exact ((mem_low _).1 (hget i (Finset.mem_filter.1 hi).2).1).1 · intro u hu v hv hadj - by_cases huh : isHigh (G, k) u = true + by_cases huh : isHigh ⟨G, k⟩ u = true · exact Or.inl (Finset.mem_union_left _ (List.mem_toFinset.2 ((mem_high _).2 ⟨hu, huh⟩))) - by_cases hvh : isHigh (G, k) v = true + by_cases hvh : isHigh ⟨G, k⟩ v = true · exact Or.inr (Finset.mem_union_left _ (List.mem_toFinset.2 ((mem_high _).2 ⟨hv, hvh⟩))) simp only [Bool.not_eq_true] at huh hvh - have hul : u ∈ low (G, k) := (mem_low _).2 ⟨hu, huh⟩ - have hvl : v ∈ low (G, k) := (mem_low _).2 ⟨hv, hvh⟩ + have hul : u ∈ low ⟨G, k⟩ := (mem_low _).2 ⟨hu, huh⟩ + have hvl : v ∈ low ⟨G, k⟩ := (mem_low _).2 ⟨hv, hvh⟩ have hua : u ∈ a := (mem_active _).2 ⟨hul, v, hvl, hadj⟩ have hva : v ∈ a := (mem_active _).2 ⟨hvl, u, hul, hadj.symm⟩ have hi := List.idxOf_lt_length_iff.2 hua @@ -306,11 +311,11 @@ theorem graph_mem_iff (hk : cap x = x.2) (hh : (high x).length ≤ cap x) : rw [List.getD_eq_getElem _ _ hi, List.getElem_idxOf] have hgj : a.getD (a.idxOf v) 0 = v := by rw [List.getD_eq_getElem _ _ hj, List.getElem_idxOf] - have hadj' : (graph (G, k)).toSimpleGraph.Adj (a.idxOf u) (a.idxOf v) := by + have hadj' : (graph ⟨G, k⟩).toSimpleGraph.Adj (a.idxOf u) (a.idxOf v) := by refine graph_adj.2 ⟨fun h => hadj.ne (by rw [← hgi, ← hgj, h]), Or.inl ?_⟩ rw [mem_edges, ← ha, hgi, hgj] exact ⟨hi, hj, hadj⟩ - have hmem : ∀ i < a.length, i ∈ (graph (G, k)).1 := fun i hi => by + have hmem : ∀ i < a.length, i ∈ (graph ⟨G, k⟩).vertices := fun i hi => by simp only [graph, List.mem_range] omega rcases hS' _ (hmem _ hi) _ (hmem _ hj) hadj' with h | h @@ -318,14 +323,16 @@ theorem graph_mem_iff (hk : cap x = x.2) (hh : (high x).length ≤ cap x) : exact Finset.mem_filter.2 ⟨h, hi⟩ · refine Or.inr (Finset.mem_union_right _ (Finset.mem_image.2 ⟨_, ?_, hgj⟩)) exact Finset.mem_filter.2 ⟨h, hj⟩ - · calc _ ≤ (high (G, k)).toFinset.card + ((S'.filter (· < a.length)).image (a.getD · 0)).card := + · calc _ ≤ (high ⟨G, k⟩).toFinset.card + ((S'.filter (· < a.length)).image (a.getD · 0)).card := Finset.card_union_le _ _ - _ ≤ (high (G, k)).length + S'.card := Nat.add_le_add (List.toFinset_card_le _) + _ ≤ (high ⟨G, k⟩).length + S'.card := Nat.add_le_add (List.toFinset_card_le _) (Finset.card_image_le.trans (Finset.card_filter_le _ _)) - _ ≤ k := by omega + _ ≤ k := by + dsimp only at hcard' ⊢ + omega · rintro ⟨S, -, hS, hcard⟩ - have hhS : ∀ v ∈ high (G, k), v ∈ S := fun v hv => - mem_of_isHigh (x := (G, k)) hS hcard ((mem_high _).1 hv).1 ((mem_high _).1 hv).2 + have hhS : ∀ v ∈ high ⟨G, k⟩, v ∈ S := fun v hv => + mem_of_isHigh (x := ⟨G, k⟩) hS hcard ((mem_high _).1 hv).1 ((mem_high _).1 hv).2 refine ⟨(Finset.range a.length).filter fun i => a.getD i 0 ∈ S, ?_, ?_, ?_⟩ · intro i hi simp only [Finset.mem_filter, Finset.mem_range] at hi @@ -345,7 +352,7 @@ theorem graph_mem_iff (hk : cap x = x.2) (hh : (high x).length ≤ cap x) : h | h · exact Or.inr ⟨hj, h⟩ · exact Or.inl ⟨hi, h⟩ - · have hsub : (high (G, k)).toFinset ⊆ S := fun v hv => hhS v (List.mem_toFinset.1 hv) + · have hsub : (high ⟨G, k⟩).toFinset ⊆ S := fun v hv => hhS v (List.mem_toFinset.1 hv) have hinj : Set.InjOn (a.getD · 0) ((Finset.range a.length).filter fun i => a.getD i 0 ∈ S : Finset ℕ) := by intro i hi j hj hij @@ -353,7 +360,7 @@ theorem graph_mem_iff (hk : cap x = x.2) (hh : (high x).length ≤ cap x) : simp only [List.getD_eq_getElem _ _ hi.1, List.getD_eq_getElem _ _ hj.1] at hij exact (nodup_active _).getElem_inj_iff.1 hij have hmaps : ∀ i ∈ (Finset.range a.length).filter fun i => a.getD i 0 ∈ S, - a.getD i 0 ∈ S \ (high (G, k)).toFinset := fun i hi => by + a.getD i 0 ∈ S \ (high ⟨G, k⟩).toFinset := fun i hi => by simp only [Finset.mem_filter, Finset.mem_range] at hi refine Finset.mem_sdiff.2 ⟨hi.2, fun h => ?_⟩ have h₁ := ((mem_high _).1 (List.mem_toFinset.1 h)).2 @@ -362,6 +369,8 @@ theorem graph_mem_iff (hk : cap x = x.2) (hh : (high x).length ≤ cap x) : exact Bool.noConfusion h₂ have := Finset.card_le_card_of_injOn _ hmaps hinj rw [Finset.card_sdiff_of_subset hsub, List.toFinset_card_of_nodup (nodup_high _)] at this + dsimp only at hcard this ⊢ + rw [hparam] omega end VertexCover.Buss @@ -370,12 +379,12 @@ namespace VertexCover.Buss /-! ### Size of the kernel -/ -variable (x : Graph × ℕ) +variable (x : VertexCoverInstance) private theorem param_le_cap : param x ≤ cap x := Nat.sub_le _ _ -private theorem numVertices_le_of_accept (h : accept x = true) : numVertices x ≤ x.2 * - (x.2 + 2) := by +private theorem numVertices_le_of_accept (h : accept x = true) : + numVertices x ≤ x.k * (x.k + 2) := by simp only [accept, Bool.and_eq_true, decide_eq_true_eq] at h obtain ⟨⟨hc, -⟩, ha⟩ := h have h₁ := param_le_cap x @@ -392,7 +401,7 @@ private theorem length_edges_le : (edges x).length ≤ (active x).length * (acti · simp private theorem length_edges_le_of_accept (h : accept x = true) : - (edges x).length ≤ (x.2 * (x.2 + 1)) * (x.2 * (x.2 + 1)) := by + (edges x).length ≤ (x.k * (x.k + 1)) * (x.k * (x.k + 1)) := by simp only [accept, Bool.and_eq_true, decide_eq_true_eq] at h obtain ⟨⟨hc, -⟩, ha⟩ := h rw [hc] at ha @@ -418,12 +427,12 @@ private theorem size_arith {k K : ℕ} (hK : K ≤ k * (k + 2)) : /-- The encoded kernel has length at most `40 (k + 1) ^ 6`. -/ private theorem length_encode_le_of_accept (h : accept x = true) : - (encPair encGraph encNat (graph x, param x)).length ≤ 40 * (x.2 + 1) ^ 6 := by - set K := x.2 * (x.2 + 2) + (encVertexCoverInstance ⟨graph x, param x⟩).length ≤ 40 * (x.k + 1) ^ 6 := by + set K := x.k * (x.k + 2) have hV : numVertices x ≤ K := numVertices_le_of_accept x h have hE := length_edges_le_of_accept x h have hm := length_active_le_numVertices x - have hp : param x ≤ x.2 := (param_le_cap x).trans (cap_le_snd x) + have hp : param x ≤ x.k := (param_le_cap x).trans (cap_le_snd x) have hvert : (encList encNat (List.range (numVertices x))).length ≤ K * (2 * K + 1) := by refine (length_encList_le encNat _ (b := K) fun i hi => ?_).trans ?_ · exact (length_encNat_le_self i).trans ((List.mem_range.1 hi).le.trans hV) @@ -441,18 +450,20 @@ private theorem length_encode_le_of_accept (h : accept x = true) : omega · refine Nat.mul_le_mul_right _ (hE.trans ?_) simp only [K] - have : x.2 * (x.2 + 1) ≤ x.2 * (x.2 + 2) := Nat.mul_le_mul_left _ (by omega) + have : x.k * (x.k + 1) ≤ x.k * (x.k + 2) := Nat.mul_le_mul_left _ (by omega) exact Nat.mul_le_mul this this have := length_encNat_le_self (param x) - simp only [graph, encGraph_apply, length_encPair] - exact le_trans (by omega) (size_arith (k := x.2) (K := K) le_rfl) + rw [encVertexCoverInstance_apply] + dsimp only [encVertexCoverInstanceFields, graph] + simp only [encGraph_apply, length_encPair] + exact le_trans (by omega) (size_arith (k := x.k) (K := K) le_rfl) end VertexCover.Buss open VertexCover.Buss in /-- **Buss's kernel for vertex cover.** -/ -def VertexCover.bussKernel (x : Graph × ℕ) : Graph × ℕ := - if accept x then (graph x, param x) else noInstance +def VertexCover.bussKernel (x : VertexCoverInstance) : VertexCoverInstance := + if accept x then ⟨graph x, param x⟩ else noInstance namespace VertexCover.Buss @@ -471,51 +482,51 @@ theorem computableInPolyTime_degree : /-- Testing for a high vertex is polynomial-time. -/ @[fun_prop] theorem computableInPolyTime_isHigh : - ComputableInPolyTime (fun p : (Graph × ℕ) × ℕ => isHigh p.1 p.2) - (encPair (encPair encGraph encNat) encNat) encBool := by + ComputableInPolyTime (fun p : VertexCoverInstance × ℕ => isHigh p.1 p.2) + (encPair encVertexCoverInstance encNat) encBool := by unfold isHigh fun_prop /-- Listing the high vertices is polynomial-time. -/ @[fun_prop] theorem computableInPolyTime_high : - ComputableInPolyTime high (encPair encGraph encNat) (encList encNat) := by + ComputableInPolyTime high encVertexCoverInstance (encList encNat) := by unfold high fun_prop /-- Listing the low vertices is polynomial-time. -/ @[fun_prop] theorem computableInPolyTime_low : - ComputableInPolyTime low (encPair encGraph encNat) (encList encNat) := by + ComputableInPolyTime low encVertexCoverInstance (encList encNat) := by unfold low fun_prop /-- Testing for an active vertex is polynomial-time. -/ @[fun_prop] theorem computableInPolyTime_isActive : - ComputableInPolyTime (fun p : (Graph × ℕ) × ℕ => isActive p.1 p.2) - (encPair (encPair encGraph encNat) encNat) encBool := by + ComputableInPolyTime (fun p : VertexCoverInstance × ℕ => isActive p.1 p.2) + (encPair encVertexCoverInstance encNat) encBool := by unfold isActive fun_prop /-- Listing the active vertices is polynomial-time. -/ @[fun_prop] theorem computableInPolyTime_active : - ComputableInPolyTime active (encPair encGraph encNat) (encList encNat) := by + ComputableInPolyTime active encVertexCoverInstance (encList encNat) := by unfold active fun_prop /-- The kernel target is computed in unary in polynomial time. -/ @[fun_prop] theorem computableInPolyTime_param : - ComputableInPolyTime param (encPair encGraph encNat) encUnary := by + ComputableInPolyTime param encVertexCoverInstance encUnary := by unfold param fun_prop /-- The number of kernel vertices is computed in unary in polynomial time. -/ @[fun_prop] theorem computableInPolyTime_numVertices : - ComputableInPolyTime numVertices (encPair encGraph encNat) encUnary := by + ComputableInPolyTime numVertices encVertexCoverInstance encUnary := by unfold numVertices fun_prop @@ -523,33 +534,73 @@ theorem computableInPolyTime_numVertices : and converted to binary to compare it with the binary target. -/ @[fun_prop] theorem computableInPolyTime_accept : - ComputableInPolyTime accept (encPair encGraph encNat) encBool := by + ComputableInPolyTime accept encVertexCoverInstance encBool := by unfold accept refine bool_and' (bool_and' ?_ (by fun_prop)) (by fun_prop) - exact ((unary_to_binary.comp computableInPolyTime_cap).beq (snd _ _)).of_enc_eq + exact ((unary_to_binary.comp computableInPolyTime_cap).beq + (show ComputableInPolyTime (fun x : VertexCoverInstance => x.k) + encVertexCoverInstance encNat by fun_prop)).of_enc_eq fun _ => by simp [beq_eq_decide] /-- Listing the kernel edges is polynomial-time. -/ @[fun_prop] theorem computableInPolyTime_edges : - ComputableInPolyTime edges (encPair encGraph encNat) + ComputableInPolyTime edges encVertexCoverInstance (encList (encPair encNat encNat)) := by unfold edges - refine flatMapWithInput (by fun_prop) ?_ - refine mapWithInput (filterWithInput (by fun_prop) ?_) (by fun_prop) - exact Graph.computableInPolyTime_adjBool_comp (by fun_prop) - (listGetDWithInput (by fun_prop) (by fun_prop) 0) - (listGetDWithInput (by fun_prop) (by fun_prop) 0) + have hrange : + ComputableInPolyTime (fun x : VertexCoverInstance => List.range (active x).length) + encVertexCoverInstance (encList encUnary) := + ComputableInPolyTime.range.comp + ((ComputableTidilyInPolyTime.length_list encNat).computableInPolyTime.comp + computableInPolyTime_active) + refine flatMapWithInput hrange ?_ + have hfilt_range : + ComputableInPolyTime (fun p : VertexCoverInstance × ℕ => List.range (active p.1).length) + (encPair encVertexCoverInstance encUnary) (encList encUnary) := + hrange.comp (ComputableInPolyTime.fst _ _) + have hG : ComputableInPolyTime (fun p : (VertexCoverInstance × ℕ) × ℕ => p.1.1.graph) + (encPair (encPair encVertexCoverInstance encUnary) encUnary) encGraph := + (by fun_prop : ComputableInPolyTime (fun x : VertexCoverInstance => x.graph) + encVertexCoverInstance encGraph).comp + ((ComputableInPolyTime.fst encVertexCoverInstance encUnary).comp + (ComputableInPolyTime.fst _ _)) + have hact : ComputableInPolyTime (fun p : (VertexCoverInstance × ℕ) × ℕ => active p.1.1) + (encPair (encPair encVertexCoverInstance encUnary) encUnary) (encList encNat) := + computableInPolyTime_active.comp + ((ComputableInPolyTime.fst encVertexCoverInstance encUnary).comp + (ComputableInPolyTime.fst _ _)) + have hi : ComputableInPolyTime (fun p : (VertexCoverInstance × ℕ) × ℕ => p.1.2) + (encPair (encPair encVertexCoverInstance encUnary) encUnary) encUnary := + (ComputableInPolyTime.snd encVertexCoverInstance encUnary).comp + (ComputableInPolyTime.fst _ _) + have hj : ComputableInPolyTime (fun p : (VertexCoverInstance × ℕ) × ℕ => p.2) + (encPair (encPair encVertexCoverInstance encUnary) encUnary) encUnary := + ComputableInPolyTime.snd _ _ + have hadj : + ComputableInPolyTime + (fun p : (VertexCoverInstance × ℕ) × ℕ => + p.1.1.graph.adjBool ((active p.1.1).getD p.1.2 0) ((active p.1.1).getD p.2 0)) + (encPair (encPair encVertexCoverInstance encUnary) encUnary) encBool := + Graph.computableInPolyTime_adjBool_comp hG + (listGetDWithInput hact hi 0) + (listGetDWithInput hact hj 0) + have hpair : + ComputableInPolyTime (fun p : (VertexCoverInstance × ℕ) × ℕ => (p.1.2, p.2)) + (encPair (encPair encVertexCoverInstance encUnary) encUnary) + (encPair encNat encNat) := + (unary_to_binary.comp hi).prod (unary_to_binary.comp hj) + exact mapWithInput (filterWithInput hfilt_range hadj) hpair /-- Building the kernel graph is polynomial-time. -/ @[fun_prop] theorem computableInPolyTime_graph : - ComputableInPolyTime graph (encPair encGraph encNat) encGraph := by - have hV : ComputableInPolyTime (fun x : Graph × ℕ => List.range (numVertices x)) - (encPair encGraph encNat) (encList encNat) := by + ComputableInPolyTime graph encVertexCoverInstance encGraph := by + have hV : ComputableInPolyTime (fun x : VertexCoverInstance => List.range (numVertices x)) + encVertexCoverInstance (encList encNat) := by simpa only [Function.comp_def, List.map_id'] using (map unary_to_binary).comp (range.comp computableInPolyTime_numVertices) - exact encGraph_mk_polyTime hV computableInPolyTime_edges + exact (encGraph_mk_polyTime hV computableInPolyTime_edges).of_enc_eq fun _ => rfl end VertexCover.Buss @@ -558,7 +609,8 @@ namespace VertexCover open Buss /-- **Buss's kernel preserves membership.** -/ -theorem bussKernel_mem_iff (x : Graph × ℕ) : bussKernel x ∈ VertexCover ↔ x ∈ VertexCover := by +theorem bussKernel_mem_iff (x : VertexCoverInstance) : + bussKernel x ∈ VertexCover ↔ x ∈ VertexCover := by unfold bussKernel cases h : accept x · simp only [Bool.false_eq_true, ↓reduceIte] @@ -570,44 +622,43 @@ theorem bussKernel_mem_iff (x : Graph × ℕ) : bussKernel x ∈ VertexCover ↔ exact graph_mem_iff h.1.1 h.1.2 /-- Buss's kernel has at most `k (k + 2)` vertices. -/ -theorem length_bussKernel_vertices_le (x : Graph × ℕ) : - (bussKernel x).1.vertices.length ≤ x.2 * (x.2 + 2) := by +theorem length_bussKernel_vertices_le (x : VertexCoverInstance) : + (bussKernel x).graph.vertices.length ≤ x.k * (x.k + 2) := by unfold bussKernel split_ifs with h · simpa [graph] using numVertices_le_of_accept x h · simp [noInstance] /-- Buss's kernel lists at most `(k (k + 1)) ^ 2` edges. -/ -theorem length_bussKernel_edges_le (x : Graph × ℕ) : - (bussKernel x).1.edges.length ≤ (x.2 * (x.2 + 1)) * (x.2 * (x.2 + 1)) := by +theorem length_bussKernel_edges_le (x : VertexCoverInstance) : + (bussKernel x).graph.edges.length ≤ (x.k * (x.k + 1)) * (x.k * (x.k + 1)) := by unfold bussKernel split_ifs with h · exact length_edges_le_of_accept x h · simp [noInstance] /-- Buss's kernel has encoded length at most `40 (k + 1) ^ 6`. -/ -theorem length_encode_bussKernel_le (x : Graph × ℕ) : - (encPair encGraph encNat (bussKernel x)).length ≤ 40 * (x.2 + 1) ^ 6 := by +theorem length_encode_bussKernel_le (x : VertexCoverInstance) : + (encVertexCoverInstance (bussKernel x)).length ≤ 40 * (x.k + 1) ^ 6 := by unfold bussKernel split_ifs with h · exact length_encode_le_of_accept x h - · have : (encPair encGraph encNat noInstance).length = 4 := by decide + · have : (encVertexCoverInstance noInstance).length = 4 := by decide rw [this] - have : 1 ≤ (x.2 + 1) ^ 6 := Nat.one_le_pow _ _ (Nat.succ_pos _) + have : 1 ≤ (x.k + 1) ^ 6 := Nat.one_le_pow _ _ (Nat.succ_pos _) omega /-- **Buss's kernel is polynomial-time.** -/ @[fun_prop] theorem computableInPolyTime_bussKernel : - ComputableInPolyTime bussKernel (encPair encGraph encNat) (encPair encGraph encNat) := by + ComputableInPolyTime bussKernel encVertexCoverInstance encVertexCoverInstance := by unfold bussKernel exact ComputableInPolyTime.ite' computableInPolyTime_accept - (computableInPolyTime_graph.prod - (ComputableInPolyTime.unary_to_binary.comp computableInPolyTime_param)) + (by fun_prop) (ComputableInPolyTime.const _) /-- Buss's kernel has target at most `k`, or `1`. -/ -theorem bussKernel_snd_le (x : Graph × ℕ) : (bussKernel x).2 ≤ max x.2 1 := by +theorem bussKernel_snd_le (x : VertexCoverInstance) : (bussKernel x).k ≤ max x.k 1 := by unfold bussKernel split_ifs with h · exact le_max_of_le_left ((param_le_cap x).trans (cap_le_snd x)) @@ -616,15 +667,15 @@ theorem bussKernel_snd_le (x : Graph × ℕ) : (bussKernel x).2 ≤ max x.2 1 := /-- **Buss's kernel** is a kernelization of `VertexCover` parameterized by the target `k`, with size bound `40 (k + 1) ^ 6`. -/ theorem isKernelization_bussKernel : - IsKernelization (encPair encGraph encNat) Prod.snd VertexCover bussKernel + IsKernelization encVertexCoverInstance (fun x => x.k) VertexCover bussKernel fun k => 40 * (k + 1) ^ 6 where polyTime := computableInPolyTime_bussKernel mem_iff := bussKernel_mem_iff length_le := length_encode_bussKernel_le parameter_le x := by have h := bussKernel_snd_le x - have : 1 ≤ (x.2 + 1) ^ 6 := Nat.one_le_pow _ _ (Nat.succ_pos _) - have : x.2 + 1 ≤ (x.2 + 1) ^ 6 := Nat.le_self_pow (by norm_num) _ + have : 1 ≤ (x.k + 1) ^ 6 := Nat.one_le_pow _ _ (Nat.succ_pos _) + have : x.k + 1 ≤ (x.k + 1) ^ 6 := Nat.le_self_pow (by norm_num) _ omega end VertexCover @@ -632,8 +683,10 @@ end VertexCover /-- **`VertexCover` has a polynomial kernel** in the target cover size `k` [BussGoldsmith1993] [FlumGrohe2006, Section 9.1] [CyganEtAl2015, Section 2.2.1]: Buss's kernel has at most `k (k + 2)` vertices and encoded length at most `40 (k + 1) ^ 6`. -/ -theorem VertexCover_hasPolyKernel : HasPolyKernel (encPair encGraph encNat) Prod.snd VertexCover := +theorem VertexCover_hasPolyKernel : + HasPolyKernel encVertexCoverInstance (fun x => x.k) VertexCover := ⟨VertexCover.bussKernel, fun k => 40 * (k + 1) ^ 6, - polynomiallyBounded_iff_le.2 ⟨40, 6, fun _ => le_rfl⟩, VertexCover.isKernelization_bussKernel⟩ + polynomiallyBounded_iff_le.2 ⟨40, 6, fun _ => le_rfl⟩, + VertexCover.isKernelization_bussKernel⟩ end Cslib.Complexity diff --git a/CslibFrontier/Computability/Complexity/Parameterized/WHierarchy/Clique.lean b/CslibFrontier/Computability/Complexity/Parameterized/WHierarchy/Clique.lean index 6737aa71ee..7eca8dd9c6 100644 --- a/CslibFrontier/Computability/Complexity/Parameterized/WHierarchy/Clique.lean +++ b/CslibFrontier/Computability/Complexity/Parameterized/WHierarchy/Clique.lean @@ -259,7 +259,7 @@ open Circuits Circuits.Boolean Circuits.Boolean.Unbounded Turing.MultiTapeTM WCS /-- **The graph construction is polynomial-time** for every fixed depth `d`. -/ theorem WCS.computableInPolyTime_toClique (d : ℕ) : - ComputableInPolyTime (toClique (d := d)) (encWCS 1 d) (encPair encGraph encNat) := by + ComputableInPolyTime (toClique (d := d)) (encWCS 1 d) encCliqueInstance := by have hwc : ComputableInPolyTime (fun x : WCSInstance 1 d => x.circuit) (encWCS 1 d) (encWeftCircuit 1 d) := encWCS_circuit_polyTime .id' @@ -280,15 +280,17 @@ theorem WCS.computableInPolyTime_toClique (d : ℕ) : encWCS_weight_polyTime .id' have hres : ComputableInPolyTime (fun x : WCSInstance 1 d => - (graph d x.circuit.toCircuit.1 x.circuit.toCircuit.2.code.1 + (⟨graph d x.circuit.toCircuit.1 x.circuit.toCircuit.2.code.1 (x.circuit.toCircuit.2.code.2.getD 0 0) x.weight, - size d x.circuit.toCircuit.1 x.weight)) - (encWCS 1 d) (encPair encGraph encNat) := - (computableInPolyTime_graph d hn hg ho hk).prod (computableInPolyTime_size d hn hk) + size d x.circuit.toCircuit.1 x.weight⟩ : CliqueInstance)) + (encWCS 1 d) encCliqueInstance := + encCliqueInstance_mk_polyTime + (computableInPolyTime_graph d hn hg ho hk) + (computableInPolyTime_size d hn hk) have heq : (toClique (d := d)) = fun x : WCSInstance 1 d => - (graph d x.circuit.toCircuit.1 x.circuit.toCircuit.2.code.1 + ⟨graph d x.circuit.toCircuit.1 x.circuit.toCircuit.2.code.1 (x.circuit.toCircuit.2.code.2.getD 0 0) x.weight, - size d x.circuit.toCircuit.1 x.weight) := by + size d x.circuit.toCircuit.1 x.weight⟩ := by funext x rw [getD_code_outputs] rfl @@ -299,26 +301,26 @@ theorem WCS.computableInPolyTime_toClique (d : ℕ) : `Clique`**, by a polynomial-time reduction that bounds the clique size by `(k + 1) ^ 2 ^ (d + 1) + 1`. -/ theorem WCS.fptReducible_Clique (d : ℕ) : - FPTReducible (encWCS 1 d) (encPair encGraph encNat) WCSInstance.weight Prod.snd + FPTReducible (encWCS 1 d) encCliqueInstance WCSInstance.weight (·.m) (WCS 1 d) Clique := FPTReducible.of_computableInPolyTime (computableInPolyTime_toClique d) (h := fun k => (k + 1) ^ 2 ^ (d + 1) + 1) (fun _ => size_le d _ _) fun x => (toClique_mem_Clique_iff x).symm /-- **`Clique` is `W[1]`-hard** [Cygan et al., Chapter 13][CyganEtAl2015] [DowneyFellows1999]. -/ -theorem Clique_wHard : WHard 1 (encPair encGraph encNat) Prod.snd Clique := +theorem Clique_wHard : WHard 1 encCliqueInstance (·.m) Clique := wHard_iff.2 WCS.fptReducible_Clique /-- **`IndependentSet` is `W[1]`-hard** [Cygan et al., Chapter 13][CyganEtAl2015], by complementing the graph. -/ -theorem IndependentSet_wHard : WHard 1 (encPair encGraph encNat) Prod.snd IndependentSet := +theorem IndependentSet_wHard : WHard 1 encIndependentSetInstance (·.m) IndependentSet := Clique_wHard.fptReducible fptReducible_Clique_IndependentSet /-- **`W[1] = FPT` exactly when `Clique` is in `FPT`**, for every problem type, encoding and parameter. -/ theorem W_one_eq_FPT_iff : (∀ ⦃α : Type⦄ (enc : α ↪ List Bool) (κ : α → ℕ), W 1 enc κ = FPT enc κ) ↔ - Clique ∈ FPT (encPair encGraph encNat) Prod.snd := by + Clique ∈ FPT encCliqueInstance (·.m) := by constructor · intro h rw [← h] diff --git a/CslibFrontier/Computability/Complexity/Parameterized/WHierarchy/Clique/Graph.lean b/CslibFrontier/Computability/Complexity/Parameterized/WHierarchy/Clique/Graph.lean index 7fbb40644f..748e912456 100644 --- a/CslibFrontier/Computability/Complexity/Parameterized/WHierarchy/Clique/Graph.lean +++ b/CslibFrontier/Computability/Complexity/Parameterized/WHierarchy/Clique/Graph.lean @@ -145,9 +145,9 @@ end ToClique open ToClique /-- **The graph of a circuit of weft one and a weight**, with its clique size. -/ -def toClique {d : ℕ} (x : WCSInstance 1 d) : Graph × ℕ := - (graph d x.circuit.toCircuit.1 x.circuit.toCircuit.2.code.1 - (x.circuit.toCircuit.2.outputs 0).toNat x.weight, size d x.circuit.toCircuit.1 x.weight) +def toClique {d : ℕ} (x : WCSInstance 1 d) : CliqueInstance := + ⟨graph d x.circuit.toCircuit.1 x.circuit.toCircuit.2.code.1 + (x.circuit.toCircuit.2.outputs 0).toNat x.weight, size d x.circuit.toCircuit.1 x.weight⟩ namespace ToClique @@ -365,7 +365,7 @@ private theorem vadj_correct_brackets {n kk : ℕ} {gates : List (Unbounded.Op theorem mem_Clique_of_eval {d n : ℕ} (c : Circuit Unbounded.signature n 1) (hw : c.weft ≤ 1) (hd : c.depth ≤ d) {k : ℕ} (x : BitString n) (hk : BitString.weight x = k) (hx : c.eval Unbounded.interpretation x 0 = true) : - (graph d n c.code.1 (c.outputs 0).toNat k, size d n k) ∈ Clique := by + (⟨graph d n c.code.1 (c.outputs 0).toNat k, size d n k⟩ : CliqueInstance) ∈ Clique := by classical have hkn : k ≤ n := hk ▸ BitString.weight_le_length x -- The ones of `x`, enumerated in increasing order. @@ -393,7 +393,7 @@ theorem mem_Clique_of_eval {d n : ℕ} (c : Circuit Unbounded.signature n 1) (hw exact ⟨(mem_indexLists.1 hC).2 j hj, rfl⟩ rw [know_sound hu hPu hb, getD_ofFn_selected u hs] -- The clique: the certificate and the correct bracket of every list of indices. - rw [graph, size, min_eq_right (by omega : k ≤ n + 1), + rw [ToClique.graph, size, min_eq_right (by omega : k ≤ n + 1), Graph.mem_Clique_ofAdj_iff_entries (vadj_self _ _ _)] refine ⟨insert (⟨true, cl, []⟩ : Vertex) ((indexLists d k).toFinset.image fun C => (⟨false, [], B C⟩ : Vertex)), @@ -543,11 +543,12 @@ private theorem checks_of_not_refutes {d n kk : ℕ} (c : Circuit Unbounded.sign /-- **Soundness of the graph.** If the graph of a circuit has a clique of the size, the circuit has a satisfying input with `k` ones. -/ theorem satisfiableWithWeight_of_mem_Clique {d n : ℕ} (c : Circuit Unbounded.signature n 1) - {k : ℕ} (h : (graph d n c.code.1 (c.outputs 0).toNat k, size d n k) ∈ Clique) : + {k : ℕ} + (h : (⟨graph d n c.code.1 (c.outputs 0).toNat k, size d n k⟩ : CliqueInstance) ∈ Clique) : c.SatisfiableWithWeight Unbounded.interpretation k := by classical set kk := min (n + 1) k with hkk - rw [graph, size, ← hkk, Graph.mem_Clique_ofAdj_iff_entries (vadj_self _ _ _)] at h + rw [ToClique.graph, size, ← hkk, Graph.mem_Clique_ofAdj_iff_entries (vadj_self _ _ _)] at h obtain ⟨T, hTcard, hTv, hTadj⟩ := h obtain ⟨⟨cl, hclT, hcl⟩, hcover⟩ := clique_shape hTcard hTv hTadj -- The selection, read off the brackets of the one-index lists. diff --git a/CslibFrontier/Computability/Complexity/Parameterized/WHierarchy/IndependentSet.lean b/CslibFrontier/Computability/Complexity/Parameterized/WHierarchy/IndependentSet.lean index 94a1954e1c..2b7f62d5c6 100644 --- a/CslibFrontier/Computability/Complexity/Parameterized/WHierarchy/IndependentSet.lean +++ b/CslibFrontier/Computability/Complexity/Parameterized/WHierarchy/IndependentSet.lean @@ -269,8 +269,8 @@ private theorem edgeIndices_iff (G : Graph) (x : BitString (toCircuit G).1) : when `G` has an independent set of `k` declared vertices. -/ theorem toCircuit_satisfiableWithWeight_iff (G : Graph) (k : ℕ) : (toCircuit G).2.SatisfiableWithWeight Unbounded.interpretation k ↔ - (G, k) ∈ IndependentSet := by - rw [mem_IndependentSet_iff] + (⟨G, k⟩ : IndependentSetInstance) ∈ IndependentSet := by + rw [mem_IndependentSet_iff_mk] constructor · rintro ⟨x, hk, hx⟩ rw [eval_toCircuit, decide_eq_true_eq, edgeIndices_iff] at hx @@ -428,22 +428,24 @@ theorem computableInPolyTime_circuitCode : /-- **The instance of `WCS 1 3` of a graph and a target**: the circuit of the graph, of weft at most `1` and depth at most `3`, with the target as weight. -/ -def toWCS : Graph × ℕ → WCSInstance 1 3 - | (G, k) => ⟨⟨toCircuit G, weft_toCircuit_le G, depth_toCircuit_le G⟩, k⟩ +def toWCS (x : IndependentSetInstance) : WCSInstance 1 3 := + ⟨⟨toCircuit x.graph, weft_toCircuit_le x.graph, depth_toCircuit_le x.graph⟩, x.m⟩ -/-- The instance of `WCS 1 3` of `(G, k)` is a yes-instance exactly when `G` has an independent -set of `k` declared vertices. -/ -theorem toWCS_mem_WCS_iff (x : Graph × ℕ) : toWCS x ∈ WCS 1 3 ↔ x ∈ IndependentSet := - toCircuit_satisfiableWithWeight_iff x.1 x.2 +/-- The instance of `WCS 1 3` of `x` is a yes-instance exactly when `x.graph` has an independent +set of `x.m` declared vertices. -/ +theorem toWCS_mem_WCS_iff (x : IndependentSetInstance) : + toWCS x ∈ WCS 1 3 ↔ x ∈ IndependentSet := + toCircuit_satisfiableWithWeight_iff x.graph x.m /-- Building the instance of `WCS 1 3` is polynomial-time. -/ theorem computableInPolyTime_toWCS : - ComputableInPolyTime toWCS (encPair encGraph encNat) (encWCS 1 3) := by + ComputableInPolyTime toWCS encIndependentSetInstance (encWCS 1 3) := by have hc := computableInPolyTime_circuitCode have hV := Graph.computableInPolyTime_distinctVertices have h : ComputableInPolyTime - (fun x : Graph × ℕ => ((x.1.distinctVertices.length, circuitCode x.1), x.2)) - (encPair encGraph encNat) + (fun x : IndependentSetInstance => + ((x.graph.distinctVertices.length, circuitCode x.graph), x.m)) + encIndependentSetInstance (encPair (encPair encUnary (Circuit.encCode Unbounded.encOp)) encNat) := by fun_prop refine h.of_enc_eq fun x => ?_ @@ -455,7 +457,7 @@ theorem computableInPolyTime_toWCS : /-- **`IndependentSet` fpt-reduces to `WCS 1 3`**, by a polynomial-time reduction that keeps the parameter. -/ theorem fptReducible_WCS : - FPTReducible (encPair encGraph encNat) (encWCS 1 3) Prod.snd WCSInstance.weight IndependentSet + FPTReducible encIndependentSetInstance (encWCS 1 3) (·.m) WCSInstance.weight IndependentSet (WCS 1 3) := FPTReducible.of_computableInPolyTime computableInPolyTime_toWCS (h := id) (fun _ => le_rfl) fun x => (toWCS_mem_WCS_iff x).symm @@ -463,11 +465,11 @@ theorem fptReducible_WCS : end IndependentSet /-- **Independent Set is in `W[1]`** [Cygan et al., Section 13.3][CyganEtAl2015]. -/ -theorem IndependentSet_mem_W : IndependentSet ∈ W 1 (encPair encGraph encNat) Prod.snd := +theorem IndependentSet_mem_W : IndependentSet ∈ W 1 encIndependentSetInstance (·.m) := ⟨3, IndependentSet.fptReducible_WCS⟩ /-- **Clique is in `W[1]`** [Cygan et al., Section 13.3][CyganEtAl2015]. -/ -theorem Clique_mem_W : Clique ∈ W 1 (encPair encGraph encNat) Prod.snd := +theorem Clique_mem_W : Clique ∈ W 1 encCliqueInstance (·.m) := fptReducible_Clique_IndependentSet.mem_W IndependentSet_mem_W end Cslib.Complexity diff --git a/CslibFrontier/Computability/Complexity/Problems/ChromaticNumber.lean b/CslibFrontier/Computability/Complexity/Problems/ChromaticNumber.lean index 08c8d9fd23..90f2571ebd 100644 --- a/CslibFrontier/Computability/Complexity/Problems/ChromaticNumber.lean +++ b/CslibFrontier/Computability/Complexity/Problems/ChromaticNumber.lean @@ -70,7 +70,7 @@ structure ChromaticNumberInstance where graph : Graph /-- The maximum number of colors allowed. -/ colors : ℕ - deriving DecidableEq, Repr + deriving DecidableEq, Repr, Inhabited /-- Encoding of `ChromaticNumberInstance` as a pair `(graph, colors)`. -/ enc_structure encChromaticNumber : ChromaticNumberInstance where diff --git a/CslibFrontier/Computability/Complexity/Problems/Clique.lean b/CslibFrontier/Computability/Complexity/Problems/Clique.lean index e1e2ce5528..65defcc521 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Clique.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Clique.lean @@ -8,6 +8,7 @@ module -- upstream: candidate public import CslibFrontier.Computability.Complexity.Problems.IndependentSet +public import CslibFrontier.Computability.Machines.Turing.MultiTape.Encodings.Structure import CslibFrontier.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List import CslibFrontier.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Membership import CslibFrontier.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat @@ -31,7 +32,7 @@ first-order interpretations. ## Main definitions -* `Cslib.Complexity.Clique`, `Cslib.Complexity.Graph.compl`. +* `Cslib.Complexity.CliqueInstance`, `Cslib.Complexity.Clique`, `Cslib.Complexity.Graph.compl`. * `Cslib.Complexity.Graph.cliqueNum`: the clique number (`G.induced.cliqueNum`). * `Cslib.Complexity.Clique.verifier`. @@ -46,7 +47,7 @@ first-order interpretations. number is the largest target of `Clique`, and that of the complement is the independence number. * `Cslib.Complexity.Graph.computableInPolyTime_compl`: complementing a graph on its declared vertices is polynomial-time. -* `Cslib.Complexity.Clique.mem_NP`: `Clique ∈ NP (encPair encGraph encNat)`. +* `Cslib.Complexity.Clique.mem_NP`: `Clique ∈ NP encCliqueInstance`. * `Cslib.Complexity.polyTimeReducible_IndependentSet_Clique`, `Cslib.Complexity.polyTimeReducible_Clique_IndependentSet`: `IndependentSet` and `Clique` reduce to each other in polynomial time. @@ -65,17 +66,41 @@ namespace Cslib.Complexity open Turing.MultiTapeTM +/-- An instance `(graph, m)` of **Clique**. -/ +@[ext] +structure CliqueInstance where + /-- The graph in which to find a clique. -/ + graph : Graph + /-- The target number of declared vertices. -/ + m : ℕ + deriving DecidableEq, Repr, Inhabited + +/-- Encoding of `CliqueInstance` as `(graph, m)`. -/ +enc_structure encCliqueInstance : CliqueInstance where + graph : encGraph, + m : encNat + +attribute [fun_prop] encCliqueInstance_graph_polyTime + encCliqueInstance_m_polyTime encCliqueInstance_mk_polyTime + /-- The graphs with a clique of `m` of their vertices. -/ -def Clique : Set (Graph × ℕ) := - {x | ∃ S : Finset {v // v ∈ x.1.vertices}, x.1.induced.IsNClique x.2 S} +def Clique : Set CliqueInstance := + {x | ∃ S : Finset {v // v ∈ x.graph.vertices}, x.graph.induced.IsNClique x.m S} /-- Membership in `Clique` as a finite set of `m` declared vertices that is a clique in -`G.toSimpleGraph`. -/ -theorem mem_Clique_iff {G : Graph} {m : ℕ} : - (G, m) ∈ Clique ↔ - ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.vertices) ∧ G.toSimpleGraph.IsNClique m S := by +`x.graph.toSimpleGraph`. -/ +theorem mem_Clique_iff {x : CliqueInstance} : + x ∈ Clique ↔ + ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ x.graph.vertices) ∧ + x.graph.toSimpleGraph.IsNClique x.m S := by simp only [Clique, Set.mem_ofPred_eq, SimpleGraph.isNClique_iff, SimpleGraph.IsClique] - exact Graph.exists_subtype_pairwise_iff (P := G.toSimpleGraph.Adj) + exact Graph.exists_subtype_pairwise_iff (P := x.graph.toSimpleGraph.Adj) + +@[simp] +theorem mem_Clique_iff_mk {G : Graph} {m : ℕ} : + (⟨G, m⟩ : CliqueInstance) ∈ Clique ↔ + ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.vertices) ∧ G.toSimpleGraph.IsNClique m S := + mem_Clique_iff namespace Graph @@ -117,13 +142,15 @@ theorem induced_compl (G : Graph) : /-- **Independent sets are cliques of the complement.** -/ theorem compl_mem_Clique_iff (G : Graph) (m : ℕ) : - (G.compl, m) ∈ Clique ↔ (G, m) ∈ IndependentSet := by + (⟨G.compl, m⟩ : CliqueInstance) ∈ Clique ↔ + (⟨G, m⟩ : IndependentSetInstance) ∈ IndependentSet := by simp_rw [IndependentSet, Set.mem_ofPred_eq, ← SimpleGraph.isNClique_compl, ← induced_compl] rfl /-- **Cliques are independent sets of the complement.** -/ theorem compl_mem_IndependentSet_iff (G : Graph) (m : ℕ) : - (G.compl, m) ∈ IndependentSet ↔ (G, m) ∈ Clique := by + (⟨G.compl, m⟩ : IndependentSetInstance) ∈ IndependentSet ↔ + (⟨G, m⟩ : CliqueInstance) ∈ Clique := by simp_rw [Clique, Set.mem_ofPred_eq, ← SimpleGraph.isNIndepSet_compl, ← induced_compl] rfl @@ -174,51 +201,89 @@ namespace Clique /-- The verifier for `Clique`: select distinct declared vertices from the certificate mask, check that their count equals `m`, and check that every two distinct selected vertices are adjacent. -/ -def verifier : (Graph × ℕ) × List Bool → Bool := - Graph.verifySubset Graph.adjBool +def verifier : CliqueInstance × List Bool → Bool := + fun (x, w) => Graph.verifySubset Graph.adjBool ((x.graph, x.m), w) /-- `Clique.verifier` verifies `Clique` with certificates as long as the encoded instance. -/ theorem isVerifier : - IsVerifier (encPair encGraph encNat) Polynomial.X verifier Clique := by + IsVerifier encCliqueInstance Polynomial.X verifier Clique := by have h := Graph.isVerifier_verifySubset (rel := Graph.adjBool) (P := fun G u v => G.toSimpleGraph.Adj u v) (fun G u v _ => by rw [Graph.adjBool_eq_true_iff]) intro x + have hx := h (x.graph, x.m) simp only [Clique, Set.mem_ofPred_eq, SimpleGraph.isNClique_iff, SimpleGraph.IsClique, verifier] - exact h x + rw [encCliqueInstance_apply] + exact hx /-- The `Clique` verifier is polynomial-time. -/ @[fun_prop] theorem computableInPolyTime_verifier : ComputableInPolyTime verifier - (encPair (encPair encGraph encNat) encBits) encBool := - Graph.computableInPolyTime_verifySubset Graph.computableInPolyTime_adjBool + (encPair encCliqueInstance encBits) encBool := by + have h1 : ComputableInPolyTime + (fun p : CliqueInstance × List Bool => ((p.1.graph, p.1.m), p.2)) + (encPair encCliqueInstance encBits) + (encPair (encPair encGraph encNat) encBits) := by + fun_prop + exact (Graph.computableInPolyTime_verifySubset Graph.computableInPolyTime_adjBool).comp h1 /-- `Clique` belongs to `NP`. -/ -theorem mem_NP : Clique ∈ NP (encPair encGraph encNat) := - ⟨Polynomial.X, verifier, by fun_prop, isVerifier⟩ +theorem mem_NP : Clique ∈ NP encCliqueInstance := + ⟨Polynomial.X, verifier, computableInPolyTime_verifier, isVerifier⟩ end Clique +/-- Complementing the graph reduces `IndependentSet` to `Clique`. -/ +def independentSetToClique (x : IndependentSetInstance) : CliqueInstance := + ⟨x.graph.compl, x.m⟩ + +@[fun_prop] +theorem computableInPolyTime_independentSetToClique : + ComputableInPolyTime independentSetToClique encIndependentSetInstance encCliqueInstance := by + unfold independentSetToClique + fun_prop + +theorem independentSetToClique_mem_iff (x : IndependentSetInstance) : + independentSetToClique x ∈ Clique ↔ x ∈ IndependentSet := by + obtain ⟨G, m⟩ := x + exact Graph.compl_mem_Clique_iff G m + +/-- Complementing the graph reduces `Clique` to `IndependentSet`. -/ +def cliqueToIndependentSet (x : CliqueInstance) : IndependentSetInstance := + ⟨x.graph.compl, x.m⟩ + +@[fun_prop] +theorem computableInPolyTime_cliqueToIndependentSet : + ComputableInPolyTime cliqueToIndependentSet encCliqueInstance encIndependentSetInstance := by + unfold cliqueToIndependentSet + fun_prop + +theorem cliqueToIndependentSet_mem_iff (x : CliqueInstance) : + cliqueToIndependentSet x ∈ IndependentSet ↔ x ∈ Clique := by + obtain ⟨G, m⟩ := x + exact Graph.compl_mem_IndependentSet_iff G m + /-- `IndependentSet` reduces to `Clique` in polynomial time by complementing the graph on its declared vertices. -/ theorem polyTimeReducible_IndependentSet_Clique : - PolyTimeReducible (encPair encGraph encNat) (encPair encGraph encNat) IndependentSet Clique := - ⟨fun x => (x.1.compl, x.2), by fun_prop, fun ⟨G, m⟩ => (Graph.compl_mem_Clique_iff G m).symm⟩ + PolyTimeReducible encIndependentSetInstance encCliqueInstance IndependentSet Clique := + ⟨independentSetToClique, computableInPolyTime_independentSetToClique, + fun x => (independentSetToClique_mem_iff x).symm⟩ /-- `Clique` reduces to `IndependentSet` in polynomial time by complementing the graph on its declared vertices. -/ theorem polyTimeReducible_Clique_IndependentSet : - PolyTimeReducible (encPair encGraph encNat) (encPair encGraph encNat) Clique IndependentSet := - ⟨fun x => (x.1.compl, x.2), by fun_prop, - fun ⟨G, m⟩ => (Graph.compl_mem_IndependentSet_iff G m).symm⟩ + PolyTimeReducible encCliqueInstance encIndependentSetInstance Clique IndependentSet := + ⟨cliqueToIndependentSet, computableInPolyTime_cliqueToIndependentSet, + fun x => (cliqueToIndependentSet_mem_iff x).symm⟩ /-- `Clique` is `NP`-hard. -/ -theorem Clique_npHard : NPHard (encPair encGraph encNat) Clique := +theorem Clique_npHard : NPHard encCliqueInstance Clique := IndependentSet_npHard.trans polyTimeReducible_IndependentSet_Clique /-- **`Clique` is NP-complete.** -/ -theorem Clique_npComplete : NPComplete (encPair encGraph encNat) Clique := +theorem Clique_npComplete : NPComplete encCliqueInstance Clique := ⟨Clique.mem_NP, Clique_npHard⟩ /-! ### The clique number -/ @@ -232,18 +297,20 @@ noncomputable def cliqueNum (G : Graph) : ℕ := G.induced.cliqueNum variable {G : Graph} {k k' : ℕ} /-- Fewer vertices of a clique form a clique. -/ -theorem mem_Clique_of_le (h : (G, k) ∈ Clique) (hk : k' ≤ k) : (G, k') ∈ Clique := by +theorem mem_Clique_of_le (h : (⟨G, k⟩ : CliqueInstance) ∈ Clique) (hk : k' ≤ k) : + (⟨G, k'⟩ : CliqueInstance) ∈ Clique := by obtain ⟨S, hS, hcard : S.card = k⟩ := h obtain ⟨T, hTS, hT⟩ := Finset.exists_subset_card_eq (hcard ▸ hk) exact ⟨T, hS.subset (Finset.coe_subset.2 hTS), hT⟩ /-- **The clique number is the largest target of `Clique`.** -/ -theorem le_cliqueNum_iff : k ≤ G.cliqueNum ↔ (G, k) ∈ Clique := +theorem le_cliqueNum_iff : k ≤ G.cliqueNum ↔ (⟨G, k⟩ : CliqueInstance) ∈ Clique := ⟨mem_Clique_of_le G.induced.exists_isNClique_cliqueNum, fun ⟨_, hS, (hcard : _ = k)⟩ => hcard ▸ hS.card_le_cliqueNum⟩ /-- A clique has at most as many vertices as the distinct declared vertices. -/ -theorem le_card_of_mem_Clique (h : (G, k) ∈ Clique) : k ≤ G.vertices.toFinset.card := +theorem le_card_of_mem_Clique (h : (⟨G, k⟩ : CliqueInstance) ∈ Clique) : + k ≤ G.vertices.toFinset.card := (le_cliqueNum_iff.2 h).trans (G.natCard_vertexSubtype ▸ G.induced.cliqueNum_le_natCard) /-- **Cliques of the complement are independent sets**: the clique number of the complement on diff --git a/CslibFrontier/Computability/Complexity/Problems/Clique/OfAdj.lean b/CslibFrontier/Computability/Complexity/Problems/Clique/OfAdj.lean index 5e65e689fd..7cc5fec944 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Clique/OfAdj.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Clique/OfAdj.lean @@ -85,9 +85,10 @@ theorem ofAdj_adj {vs : List V} {adj : V → V → Bool} {i j : ℕ} : /-- The cliques of `ofAdj vs adj`: sets of positions whose entries are pairwise adjacent in some direction. -/ theorem mem_Clique_ofAdj_iff {vs : List V} {adj : V → V → Bool} {m : ℕ} : - (ofAdj vs adj, m) ∈ Clique ↔ ∃ S : Finset ℕ, S.card = m ∧ (∀ i ∈ S, i < vs.length) ∧ - ∀ i ∈ S, ∀ j ∈ S, ∀ (hi : i < vs.length) (hj : j < vs.length), i ≠ j → - (adj vs[i] vs[j] || adj vs[j] vs[i]) = true := by + (⟨ofAdj vs adj, m⟩ : CliqueInstance) ∈ Clique ↔ + ∃ S : Finset ℕ, S.card = m ∧ (∀ i ∈ S, i < vs.length) ∧ + ∀ i ∈ S, ∀ j ∈ S, ∀ (hi : i < vs.length) (hj : j < vs.length), i ≠ j → + (adj vs[i] vs[j] || adj vs[j] vs[i]) = true := by rw [mem_Clique_iff] constructor · rintro ⟨S, hS, hclique⟩ @@ -103,8 +104,9 @@ theorem mem_Clique_ofAdj_iff {vs : List V} {adj : V → V → Bool} {m : ℕ} : pairwise adjacent in some direction. -/ theorem mem_Clique_ofAdj_iff_entries {vs : List V} {adj : V → V → Bool} (hirr : ∀ v, adj v v = false) {m : ℕ} : - (ofAdj vs adj, m) ∈ Clique ↔ ∃ T : Finset V, T.card = m ∧ (∀ v ∈ T, v ∈ vs) ∧ - ∀ v ∈ T, ∀ w ∈ T, v ≠ w → (adj v w || adj w v) = true := by + (⟨ofAdj vs adj, m⟩ : CliqueInstance) ∈ Clique ↔ + ∃ T : Finset V, T.card = m ∧ (∀ v ∈ T, v ∈ vs) ∧ + ∀ v ∈ T, ∀ w ∈ T, v ≠ w → (adj v w || adj w v) = true := by classical rw [mem_Clique_ofAdj_iff] constructor diff --git a/CslibFrontier/Computability/Complexity/Problems/CliqueCover.lean b/CslibFrontier/Computability/Complexity/Problems/CliqueCover.lean index 2e948f3cc0..efac1cd4da 100644 --- a/CslibFrontier/Computability/Complexity/Problems/CliqueCover.lean +++ b/CslibFrontier/Computability/Complexity/Problems/CliqueCover.lean @@ -62,7 +62,7 @@ structure CliqueCoverInstance where graph : Graph /-- The maximum number of cliques allowed in the cover. -/ cliques : ℕ - deriving DecidableEq, Repr + deriving DecidableEq, Repr, Inhabited /-- Encoding of `CliqueCoverInstance` as a pair `(graph, cliques)`. -/ enc_structure encCliqueCover : CliqueCoverInstance where diff --git a/CslibFrontier/Computability/Complexity/Problems/Copositivity.lean b/CslibFrontier/Computability/Complexity/Problems/Copositivity.lean index 3fbf96f4cd..7d11988564 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Copositivity.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Copositivity.lean @@ -100,10 +100,6 @@ open Matrix Turing.MultiTapeTM abbrev encRatMatrixRows : List (List ℚ) ↪ List Bool := encList (encList encRat) -/-- Binary encoding of `Clique` instances `(G, k)`. -/ -abbrev encClique : Graph × ℕ ↪ List Bool := - encPair encGraph encNat - /-- A square symmetric rational matrix represented by its list of rows. -/ structure SymRatMatrix where /-- The rows of the matrix. -/ @@ -656,8 +652,8 @@ theorem Copositive.mem_coNP : Copositive ∈ coNP encSymRatMatrix := by /-- Row list produced by the reduction from a `Clique` instance `(G, k)` to a symmetric rational matrix. For `k ≥ 2` on a graph with nonempty vertex list, this is the Motzkin–Straus matrix `(k - 2) J - (k - 1) A_G`. -/ -@[expose] def cliqueToRows (gk : Graph × ℕ) : List (List ℚ) := - let (G, k) := gk +@[expose] def cliqueToRows (gk : CliqueInstance) : List (List ℚ) := + let ⟨G, k⟩ := gk if k = 0 then [[(-1 : ℚ)]] else if G.distinctVertices.isEmpty then [[(1 : ℚ)]] else if k = 1 then [[(-1 : ℚ)]] @@ -670,7 +666,7 @@ private theorem graph_adjBool_comm (G : Graph) (u v : ℕ) : rw [Bool.eq_iff_iff, G.adjBool_eq_true_iff u v, G.adjBool_eq_true_iff v u, G.toSimpleGraph.adj_comm] -private theorem cliqueToRows_square (gk : Graph × ℕ) : +private theorem cliqueToRows_square (gk : CliqueInstance) : ∀ r ∈ cliqueToRows gk, r.length = (cliqueToRows gk).length := by rcases gk with ⟨G, k⟩ dsimp only [cliqueToRows] @@ -691,7 +687,7 @@ private theorem cliqueToRows_square (gk : Graph × ℕ) : obtain ⟨u, _, rfl⟩ := List.mem_map.1 hr simp only [List.length_map] -private theorem cliqueToRows_symm (gk : Graph × ℕ) : +private theorem cliqueToRows_symm (gk : CliqueInstance) : ∀ i j, ((cliqueToRows gk).getD i []).getD j 0 = ((cliqueToRows gk).getD j []).getD i 0 := by rcases gk with ⟨G, k⟩ @@ -732,13 +728,13 @@ private theorem cliqueToRows_symm (gk : Graph × ℕ) : /-- Polynomial-time many-one reduction mapping a `Clique` instance `(G, k)` to a symmetric rational matrix `M` such that `(G, k) ∈ Clique ↔ M ∈ NonCopositive`. -/ -def cliqueToSymRatMatrix (gk : Graph × ℕ) : SymRatMatrix where +def cliqueToSymRatMatrix (gk : CliqueInstance) : SymRatMatrix where rows := cliqueToRows gk square := cliqueToRows_square gk symm := cliqueToRows_symm gk @[simp] -theorem cliqueToSymRatMatrix_rows (gk : Graph × ℕ) : +theorem cliqueToSymRatMatrix_rows (gk : CliqueInstance) : (cliqueToSymRatMatrix gk).rows = cliqueToRows gk := by dsimp only [cliqueToSymRatMatrix] @@ -785,35 +781,35 @@ private theorem singleton_pos_copositive : simp only [List.getD_cons_zero, one_mul] exact mul_nonneg (hx i0) (hx i0) -theorem cliqueToSymRatMatrix_mem_iff (gk : Graph × ℕ) : +theorem cliqueToSymRatMatrix_mem_iff (gk : CliqueInstance) : gk ∈ Clique ↔ cliqueToSymRatMatrix gk ∈ NonCopositive := by rcases gk with ⟨G, k⟩ by_cases hk0 : k = 0 · subst hk0 - have hin : (G, 0) ∈ Clique := by + have hin : (⟨G, 0⟩ : CliqueInstance) ∈ Clique := by rw [mem_Clique_iff] exact ⟨∅, fun _ h => absurd h (Finset.notMem_empty _), by simp only [SimpleGraph.isNClique_iff, Finset.coe_empty, SimpleGraph.isClique_empty, Finset.card_empty, and_self]⟩ - have hM : cliqueToSymRatMatrix (G, 0) ∈ NonCopositive := by - have hrows : (cliqueToSymRatMatrix (G, 0)).rows = (singletonSymRatMatrix (-1)).rows := by + have hM : cliqueToSymRatMatrix ⟨G, 0⟩ ∈ NonCopositive := by + have hrows : (cliqueToSymRatMatrix ⟨G, 0⟩).rows = (singletonSymRatMatrix (-1)).rows := by simp only [cliqueToSymRatMatrix_rows, cliqueToRows, ↓reduceIte, singletonSymRatMatrix] rw [SymRatMatrix.ext hrows] exact singleton_neg_nonCopositive exact iff_of_true hin hM · by_cases hemp : G.distinctVertices.isEmpty = true - · have hnot : (G, k) ∉ Clique := by + · have hnot : (⟨G, k⟩ : CliqueInstance) ∉ Clique := by intro hmem rw [mem_Clique_iff] at hmem obtain ⟨S, hSG, hS⟩ := hmem - have hpos : 0 < S.card := by rw [hS.card_eq]; omega + have hpos : 0 < S.card := by rw [hS.card_eq]; dsimp only; omega obtain ⟨v, hvS⟩ := Finset.card_pos.1 hpos have hv : v ∈ G.distinctVertices := Graph.mem_distinctVertices.2 (hSG v hvS) have hnil : G.distinctVertices = [] := List.isEmpty_iff.1 hemp rw [hnil] at hv cases hv - have hM : cliqueToSymRatMatrix (G, k) ∉ NonCopositive := by - have hrows : (cliqueToSymRatMatrix (G, k)).rows = (singletonSymRatMatrix 1).rows := by + have hM : cliqueToSymRatMatrix ⟨G, k⟩ ∉ NonCopositive := by + have hrows : (cliqueToSymRatMatrix ⟨G, k⟩).rows = (singletonSymRatMatrix 1).rows := by simp only [cliqueToSymRatMatrix_rows, cliqueToRows, hk0, ↓reduceIte, hemp, singletonSymRatMatrix] rw [SymRatMatrix.ext hrows] @@ -826,14 +822,14 @@ theorem cliqueToSymRatMatrix_mem_iff (gk : Graph × ℕ) : apply hemp simp only [hnil, List.isEmpty_nil] obtain ⟨v, hv⟩ := List.exists_mem_of_ne_nil G.distinctVertices hne - have hvG : v ∈ G.1 := Graph.mem_distinctVertices.1 hv - have hin : (G, 1) ∈ Clique := by + have hvG : v ∈ G.vertices := Graph.mem_distinctVertices.1 hv + have hin : (⟨G, 1⟩ : CliqueInstance) ∈ Clique := by rw [mem_Clique_iff] refine ⟨{v}, fun _ hw => by simp only [Finset.mem_singleton] at hw; rwa [hw], ?_⟩ simp only [SimpleGraph.isNClique_iff, Finset.coe_singleton, SimpleGraph.isClique_singleton, Finset.card_singleton, and_self] - have hM : cliqueToSymRatMatrix (G, 1) ∈ NonCopositive := by - have hrows : (cliqueToSymRatMatrix (G, 1)).rows = (singletonSymRatMatrix (-1)).rows := by + have hM : cliqueToSymRatMatrix ⟨G, 1⟩ ∈ NonCopositive := by + have hrows : (cliqueToSymRatMatrix ⟨G, 1⟩).rows = (singletonSymRatMatrix (-1)).rows := by simp only [cliqueToSymRatMatrix_rows, cliqueToRows, hk0, hemp, Bool.false_eq_true, ↓reduceIte, singletonSymRatMatrix] rw [SymRatMatrix.ext hrows] @@ -856,23 +852,23 @@ theorem cliqueToSymRatMatrix_mem_iff (gk : Graph × ℕ) : simp only [H, SimpleGraph.comap_adj] exact G.adjBool_eq_true_iff (ι i) (ι j)) have hrows_eq : - (cliqueToSymRatMatrix (G, k)).rows = + (cliqueToSymRatMatrix ⟨G, k⟩).rows = G.distinctVertices.map fun u => G.distinctVertices.map fun v => cliqueEntry G k u v := by simp only [cliqueToSymRatMatrix_rows, cliqueToRows, hk0, hemp, Bool.false_eq_true, hk1, ↓reduceIte] - have hlen_rows : (cliqueToSymRatMatrix (G, k)).rows.length = G.distinctVertices.length := by + have hlen_rows : (cliqueToSymRatMatrix ⟨G, k⟩).rows.length = G.distinctVertices.length := by rw [hrows_eq, List.length_map] - let eFin : Fin (cliqueToSymRatMatrix (G, k)).rows.length ≃ Fin G.distinctVertices.length := + let eFin : Fin (cliqueToSymRatMatrix ⟨G, k⟩).rows.length ≃ Fin G.distinctVertices.length := ⟨Fin.cast hlen_rows, Fin.cast hlen_rows.symm, fun _ => rfl, fun _ => rfl⟩ have hmat_eq : - ∀ i j : Fin (cliqueToSymRatMatrix (G, k)).rows.length, - (cliqueToSymRatMatrix (G, k)).toMatrix i j = + ∀ i j : Fin (cliqueToSymRatMatrix ⟨G, k⟩).rows.length, + (cliqueToSymRatMatrix ⟨G, k⟩).toMatrix i j = ((H.copositiveCliqueMatrix k).map (Int.cast : ℤ → ℚ)) (eFin i) (eFin j) := by intro i j have hi : i.1 < G.distinctVertices.length := hlen_rows ▸ i.2 have hj : j.1 < G.distinctVertices.length := hlen_rows ▸ j.2 - have hget1 : (cliqueToSymRatMatrix (G, k)).rows.getD i.1 [] = + have hget1 : (cliqueToSymRatMatrix ⟨G, k⟩).rows.getD i.1 [] = G.distinctVertices.map fun v => cliqueEntry G k G.distinctVertices[i.1] v := by have hrows_get := congrArg (fun l => List.getD l i.1 []) hrows_eq rw [hrows_get, List.getD_eq_getElem?_getD, List.getElem?_map, @@ -909,17 +905,17 @@ theorem cliqueToSymRatMatrix_mem_iff (gk : Graph × ℕ) : simp only [cliqueEntry, hij_val, ↓reduceIte, hadj, Bool.false_eq_true, hH, Int.cast_sub, Int.cast_natCast, Int.cast_ofNat] have hnc_iff : - cliqueToSymRatMatrix (G, k) ∈ NonCopositive ↔ + cliqueToSymRatMatrix ⟨G, k⟩ ∈ NonCopositive ↔ ∃ x : Fin G.distinctVertices.length → ℚ, (∀ v, 0 ≤ x v) ∧ x ⬝ᵥ ((H.copositiveCliqueMatrix k).map (Int.cast : ℤ → ℚ) *ᵥ x) < 0 := by - have hsub : (cliqueToSymRatMatrix (G, k)).toMatrix = + have hsub : (cliqueToSymRatMatrix ⟨G, k⟩).toMatrix = ((H.copositiveCliqueMatrix k).map (Int.cast : ℤ → ℚ)).submatrix eFin eFin := Matrix.ext fun i j => hmat_eq i j rw [mem_NonCopositive_iff, hsub, Matrix.IsCopositive.submatrix_equiv_iff, Matrix.IsCopositive] push Not rfl - have hclique_iff : (G, k) ∈ Clique ↔ k ≤ H.cliqueNum := by + have hclique_iff : (⟨G, k⟩ : CliqueInstance) ∈ Clique ↔ k ≤ H.cliqueNum := by rw [mem_Clique_iff] constructor · rintro ⟨S, hSG, hS⟩ @@ -965,117 +961,125 @@ theorem cliqueToSymRatMatrix_mem_iff (gk : Graph × ℕ) : exact (SimpleGraph.exists_nonneg_dotProduct_copositiveCliqueMatrix_lt_zero_iff H hk2).symm private theorem computableInPolyTime_distinctVertices_gk : - ComputableInPolyTime (fun gk : Graph × ℕ => gk.1.distinctVertices) encClique (encList encNat) := - Graph.computableInPolyTime_distinctVertices.comp (ComputableInPolyTime.fst encGraph encNat) + ComputableInPolyTime (fun gk : CliqueInstance => gk.graph.distinctVertices) + encCliqueInstance (encList encNat) := + Graph.computableInPolyTime_distinctVertices.comp (by fun_prop) private theorem computableInPolyTime_toRows : - ComputableInPolyTime cliqueToRows encClique encRatMatrixRows := by + ComputableInPolyTime cliqueToRows encCliqueInstance encRatMatrixRows := by have hk0 : - ComputableInPolyTime (fun gk : Graph × ℕ => decide (gk.2 = 0)) encClique encBool := + ComputableInPolyTime (fun gk : CliqueInstance => decide (gk.m = 0)) + encCliqueInstance encBool := (ComputableTidilyInPolyTime.decide_eq encNat).computableInPolyTime.comp - (ComputableInPolyTime.prod (ComputableInPolyTime.snd encGraph encNat) - (ComputableInPolyTime.const 0)) + (ComputableInPolyTime.prod (by fun_prop) (ComputableInPolyTime.const 0)) have hk1 : - ComputableInPolyTime (fun gk : Graph × ℕ => decide (gk.2 = 1)) encClique encBool := + ComputableInPolyTime (fun gk : CliqueInstance => decide (gk.m = 1)) + encCliqueInstance encBool := (ComputableTidilyInPolyTime.decide_eq encNat).computableInPolyTime.comp - (ComputableInPolyTime.prod (ComputableInPolyTime.snd encGraph encNat) - (ComputableInPolyTime.const 1)) + (ComputableInPolyTime.prod (by fun_prop) (ComputableInPolyTime.const 1)) have hemp : - ComputableInPolyTime (fun gk : Graph × ℕ => gk.1.distinctVertices.isEmpty) - encClique encBool := by - have hlen : ComputableInPolyTime (fun gk : Graph × ℕ => gk.1.distinctVertices.length) - encClique encUnary := + ComputableInPolyTime (fun gk : CliqueInstance => gk.graph.distinctVertices.isEmpty) + encCliqueInstance encBool := by + have hlen : + ComputableInPolyTime (fun gk : CliqueInstance => gk.graph.distinctVertices.length) + encCliqueInstance encUnary := (ComputableTidilyInPolyTime.length_list encNat).computableInPolyTime.comp computableInPolyTime_distinctVertices_gk have hdec : - ComputableInPolyTime (fun gk : Graph × ℕ => decide (gk.1.distinctVertices.length = 0)) - encClique encBool := + ComputableInPolyTime + (fun gk : CliqueInstance => decide (gk.graph.distinctVertices.length = 0)) + encCliqueInstance encBool := (ComputableTidilyInPolyTime.decide_eq encUnary).computableInPolyTime.comp (ComputableInPolyTime.prod hlen (ComputableInPolyTime.const 0)) exact hdec.of_enc_eq fun gk => by simp only [encBool_apply] - cases gk.1.distinctVertices <;> rfl + cases gk.graph.distinctVertices <;> rfl have hk2Rat : - ComputableInPolyTime (fun gk : Graph × ℕ => (((gk.2 : ℤ) - 2 : ℤ) : ℚ)) encClique encRat := + ComputableInPolyTime (fun gk : CliqueInstance => (((gk.m : ℤ) - 2 : ℤ) : ℚ)) + encCliqueInstance encRat := ComputableInPolyTime.rat_intCast.comp (by fun_prop : - ComputableInPolyTime (fun gk : Graph × ℕ => (gk.2 : ℤ) - 2) encClique encIntBin) + ComputableInPolyTime (fun gk : CliqueInstance => (gk.m : ℤ) - 2) + encCliqueInstance encIntBin) have hentry : ComputableTidilyInPolyTime - (fun x : ((Graph × ℕ) × ℕ) × ℕ => cliqueEntry x.1.1.1 x.1.1.2 x.1.2 x.2) - (encPair (encPair encClique encNat) encNat) encRat := by + (fun x : (CliqueInstance × ℕ) × ℕ => cliqueEntry x.1.1.graph x.1.1.m x.1.2 x.2) + (encPair (encPair encCliqueInstance encNat) encNat) encRat := by have heq_uv : - ComputableInPolyTime (fun x : ((Graph × ℕ) × ℕ) × ℕ => decide (x.1.2 = x.2)) - (encPair (encPair encClique encNat) encNat) encBool := + ComputableInPolyTime (fun x : (CliqueInstance × ℕ) × ℕ => decide (x.1.2 = x.2)) + (encPair (encPair encCliqueInstance encNat) encNat) encBool := (ComputableTidilyInPolyTime.decide_eq encNat).computableInPolyTime.comp (ComputableInPolyTime.prod - ((ComputableInPolyTime.snd encClique encNat).comp (ComputableInPolyTime.fst _ _)) + ((ComputableInPolyTime.snd encCliqueInstance encNat).comp (ComputableInPolyTime.fst _ _)) (ComputableInPolyTime.snd _ _)) have hadj : ComputableInPolyTime - (fun x : ((Graph × ℕ) × ℕ) × ℕ => x.1.1.1.adjBool x.1.2 x.2) - (encPair (encPair encClique encNat) encNat) encBool := + (fun x : (CliqueInstance × ℕ) × ℕ => x.1.1.graph.adjBool x.1.2 x.2) + (encPair (encPair encCliqueInstance encNat) encNat) encBool := Graph.computableInPolyTime_adjBool_comp - ((ComputableInPolyTime.fst encGraph encNat).comp - ((ComputableInPolyTime.fst encClique encNat).comp (ComputableInPolyTime.fst _ _))) - ((ComputableInPolyTime.snd encClique encNat).comp (ComputableInPolyTime.fst _ _)) + ((by fun_prop : + ComputableInPolyTime (fun c : CliqueInstance => c.graph) + encCliqueInstance encGraph).comp + ((ComputableInPolyTime.fst encCliqueInstance encNat).comp (ComputableInPolyTime.fst _ _))) + ((ComputableInPolyTime.snd encCliqueInstance encNat).comp (ComputableInPolyTime.fst _ _)) (ComputableInPolyTime.snd _ _) have hk2' : - ComputableInPolyTime (fun x : ((Graph × ℕ) × ℕ) × ℕ => (((x.1.1.2 : ℤ) - 2 : ℤ) : ℚ)) - (encPair (encPair encClique encNat) encNat) encRat := - hk2Rat.comp ((ComputableInPolyTime.fst encClique encNat).comp (ComputableInPolyTime.fst _ _)) + ComputableInPolyTime (fun x : (CliqueInstance × ℕ) × ℕ => (((x.1.1.m : ℤ) - 2 : ℤ) : ℚ)) + (encPair (encPair encCliqueInstance encNat) encNat) encRat := + hk2Rat.comp + ((ComputableInPolyTime.fst encCliqueInstance encNat).comp (ComputableInPolyTime.fst _ _)) have hinner : ComputableInPolyTime - (fun x : ((Graph × ℕ) × ℕ) × ℕ => - if x.1.1.1.adjBool x.1.2 x.2 then (-1 : ℚ) - else (((x.1.1.2 : ℤ) - 2 : ℤ) : ℚ)) - (encPair (encPair encClique encNat) encNat) encRat := + (fun x : (CliqueInstance × ℕ) × ℕ => + if x.1.1.graph.adjBool x.1.2 x.2 then (-1 : ℚ) + else (((x.1.1.m : ℤ) - 2 : ℤ) : ℚ)) + (encPair (encPair encCliqueInstance encNat) encNat) encRat := ComputableInPolyTime.ite' hadj (ComputableInPolyTime.const (-1)) hk2' have hfull : ComputableInPolyTime - (fun x : ((Graph × ℕ) × ℕ) × ℕ => - if decide (x.1.2 = x.2) then (((x.1.1.2 : ℤ) - 2 : ℤ) : ℚ) - else if x.1.1.1.adjBool x.1.2 x.2 then (-1 : ℚ) - else (((x.1.1.2 : ℤ) - 2 : ℤ) : ℚ)) - (encPair (encPair encClique encNat) encNat) encRat := + (fun x : (CliqueInstance × ℕ) × ℕ => + if decide (x.1.2 = x.2) then (((x.1.1.m : ℤ) - 2 : ℤ) : ℚ) + else if x.1.1.graph.adjBool x.1.2 x.2 then (-1 : ℚ) + else (((x.1.1.m : ℤ) - 2 : ℤ) : ℚ)) + (encPair (encPair encCliqueInstance encNat) encNat) encRat := ComputableInPolyTime.ite' heq_uv hk2' hinner exact (hfull.of_enc_eq fun x => by simp only [cliqueEntry, decide_eq_true_eq]).tidy have hrow : ComputableTidilyInPolyTime - (fun p : (Graph × ℕ) × ℕ => - p.1.1.distinctVertices.map fun v => cliqueEntry p.1.1 p.1.2 p.2 v) - (encPair encClique encNat) (encList encRat) := + (fun p : CliqueInstance × ℕ => + p.1.graph.distinctVertices.map fun v => cliqueEntry p.1.graph p.1.m p.2 v) + (encPair encCliqueInstance encNat) (encList encRat) := ComputableTidilyInPolyTime.mapWithInput (computableInPolyTime_distinctVertices_gk.comp - (ComputableInPolyTime.fst encClique encNat)).tidy + (ComputableInPolyTime.fst encCliqueInstance encNat)).tidy hentry have hmat : ComputableInPolyTime - (fun gk : Graph × ℕ => - gk.1.distinctVertices.map fun u => - gk.1.distinctVertices.map fun v => cliqueEntry gk.1 gk.2 u v) - encClique encRatMatrixRows := + (fun gk : CliqueInstance => + gk.graph.distinctVertices.map fun u => + gk.graph.distinctVertices.map fun v => cliqueEntry gk.graph gk.m u v) + encCliqueInstance encRatMatrixRows := (ComputableTidilyInPolyTime.mapWithInput computableInPolyTime_distinctVertices_gk.tidy hrow).computableInPolyTime have hb1 : ComputableInPolyTime - (fun gk : Graph × ℕ => - if decide (gk.2 = 1) then [[(-1 : ℚ)]] + (fun gk : CliqueInstance => + if decide (gk.m = 1) then [[(-1 : ℚ)]] else - gk.1.distinctVertices.map fun u => - gk.1.distinctVertices.map fun v => cliqueEntry gk.1 gk.2 u v) - encClique encRatMatrixRows := + gk.graph.distinctVertices.map fun u => + gk.graph.distinctVertices.map fun v => cliqueEntry gk.graph gk.m u v) + encCliqueInstance encRatMatrixRows := ComputableInPolyTime.ite' hk1 (ComputableInPolyTime.const [[(-1 : ℚ)]]) hmat have hb2 : ComputableInPolyTime - (fun gk : Graph × ℕ => - if gk.1.distinctVertices.isEmpty then [[(1 : ℚ)]] - else if decide (gk.2 = 1) then [[(-1 : ℚ)]] + (fun gk : CliqueInstance => + if gk.graph.distinctVertices.isEmpty then [[(1 : ℚ)]] + else if decide (gk.m = 1) then [[(-1 : ℚ)]] else - gk.1.distinctVertices.map fun u => - gk.1.distinctVertices.map fun v => cliqueEntry gk.1 gk.2 u v) - encClique encRatMatrixRows := + gk.graph.distinctVertices.map fun u => + gk.graph.distinctVertices.map fun v => cliqueEntry gk.graph gk.m u v) + encCliqueInstance encRatMatrixRows := ComputableInPolyTime.ite' hemp (ComputableInPolyTime.const [[(1 : ℚ)]]) hb1 exact (ComputableInPolyTime.ite' hk0 (ComputableInPolyTime.const [[(-1 : ℚ)]]) hb2).of_enc_eq fun gk => by @@ -1083,12 +1087,12 @@ private theorem computableInPolyTime_toRows : simp only [cliqueToRows, decide_eq_true_eq] theorem computableInPolyTime_cliqueToSymRatMatrix : - ComputableInPolyTime cliqueToSymRatMatrix encClique encSymRatMatrix := + ComputableInPolyTime cliqueToSymRatMatrix encCliqueInstance encSymRatMatrix := computableInPolyTime_toRows.of_enc_eq fun _ => rfl /-- Polynomial-time many-one reduction from `Clique` to `NonCopositive`. -/ theorem polyTimeReducible_Clique_NonCopositive : - PolyTimeReducible encClique encSymRatMatrix Clique NonCopositive := + PolyTimeReducible encCliqueInstance encSymRatMatrix Clique NonCopositive := ⟨cliqueToSymRatMatrix, computableInPolyTime_cliqueToSymRatMatrix, cliqueToSymRatMatrix_mem_iff⟩ /-- `NonCopositive` is `NP`-hard (proved here via `Clique` and Motzkin–Straus; [MurtyKabadi1987] diff --git a/CslibFrontier/Computability/Complexity/Problems/DominatingSet.lean b/CslibFrontier/Computability/Complexity/Problems/DominatingSet.lean index bd73d1a662..f538a463f1 100644 --- a/CslibFrontier/Computability/Complexity/Problems/DominatingSet.lean +++ b/CslibFrontier/Computability/Complexity/Problems/DominatingSet.lean @@ -104,7 +104,7 @@ structure DominatingSetInstance where graph : Graph /-- The number of vertices in the dominating set. -/ bound : ℕ - deriving DecidableEq, Repr + deriving DecidableEq, Repr, Inhabited /-- Encoding of `DominatingSetInstance` as a pair `(graph, bound)`. -/ enc_structure encDominatingSet : DominatingSetInstance where @@ -327,15 +327,15 @@ private theorem decodeVertex_edgeVertex (e : ℕ × ℕ) : decodeVertex (edgeVer target `k + 1`, or the unsatisfiable target `0` when `k` exceeds the number of distinct declared vertices. -/ @[expose] -def toDominatingSet : Graph × ℕ → DominatingSetInstance := fun (G, k) => - ⟨dominationGraph G, if k ≤ G.1.toFinset.card then k + 1 else 0⟩ +def toDominatingSet : VertexCoverInstance → DominatingSetInstance := fun x => + ⟨dominationGraph x.graph, if x.k ≤ x.graph.vertices.toFinset.card then x.k + 1 else 0⟩ /-- A vertex cover of `k` vertices, with the hub, dominates `dominationGraph G`. -/ private theorem mem_DominatingSet_of_mem_VertexCover {G : Graph} {k : ℕ} - (h : (G, k) ∈ VertexCover) : + (h : (⟨G, k⟩ : VertexCoverInstance) ∈ VertexCover) : (⟨dominationGraph G, k + 1⟩ : DominatingSetInstance) ∈ DominatingSet := by classical - obtain ⟨C, hCV, hC, rfl⟩ := mem_VertexCover_iff.1 h + obtain ⟨C, hCV, hC, rfl⟩ := mem_VertexCover_iff_mk.1 h refine mem_DominatingSet_iff.2 ⟨insert 1 (C.image (2 * ·)), fun w hw => ?_, fun w hw => ?_, ?_⟩ · rw [mem_dominationGraph_fst] rcases Finset.mem_insert.1 hw with rfl | hw @@ -412,15 +412,15 @@ private theorem exists_cover_of_mem_DominatingSet {G : Graph} {k : ℕ} omega /-- **Vertex covers of `G` are dominating sets of `dominationGraph G` with the hub.** -/ -theorem toDominatingSet_mem_iff (x : Graph × ℕ) : +theorem toDominatingSet_mem_iff (x : VertexCoverInstance) : toDominatingSet x ∈ DominatingSet ↔ x ∈ VertexCover := by obtain ⟨G, k⟩ := x simp only [toDominatingSet] split_ifs with hk · refine ⟨fun h => ?_, mem_DominatingSet_of_mem_VertexCover⟩ obtain ⟨S, hSV, hS, hcard⟩ := exists_cover_of_mem_DominatingSet h - exact mem_VertexCover_iff_exists_card_le.2 ⟨hk, S, hSV, hS, hcard⟩ - · refine ⟨fun h => ?_, fun h => absurd (mem_VertexCover_iff_exists_card_le.1 h).1 hk⟩ + exact mem_VertexCover_iff_exists_card_le_mk.2 ⟨hk, S, hSV, hS, hcard⟩ + · refine ⟨fun h => ?_, fun h => absurd (mem_VertexCover_iff_exists_card_le_mk.1 h).1 hk⟩ -- No dominating set of `0` vertices dominates the hub. obtain ⟨D, -, hD, hcard⟩ := mem_DominatingSet_iff.1 h rw [Finset.card_eq_zero] at hcard @@ -479,20 +479,21 @@ theorem computableInPolyTime_dominationGraph : number of distinct declared vertices, computed in unary. -/ @[fun_prop] theorem computableInPolyTime_toDominatingSet : - ComputableInPolyTime toDominatingSet (encPair encGraph encNat) encDominatingSet := by + ComputableInPolyTime toDominatingSet encVertexCoverInstance encDominatingSet := by have hle : ComputableInPolyTime - (fun x : Graph × ℕ => decide (x.2 ≤ x.1.vertices.toFinset.card)) - (encPair encGraph encNat) encBool := by + (fun x : VertexCoverInstance => decide (x.k ≤ x.graph.vertices.toFinset.card)) + encVertexCoverInstance encBool := by simp_rw [← Graph.length_distinctVertices] fun_prop - have heq : toDominatingSet = fun x : Graph × ℕ => - (⟨dominationGraph x.1, - if decide (x.2 ≤ x.1.vertices.toFinset.card) then x.2 + 1 else 0⟩ : + have heq : toDominatingSet = fun x : VertexCoverInstance => + (⟨dominationGraph x.graph, + if decide (x.k ≤ x.graph.vertices.toFinset.card) then x.k + 1 else 0⟩ : DominatingSetInstance) := by funext ⟨G, k⟩ simp only [toDominatingSet, decide_eq_true_eq] rw [heq] - exact encDominatingSet_mk_polyTime (computableInPolyTime_dominationGraph.comp (fst _ _)) + exact encDominatingSet_mk_polyTime + (computableInPolyTime_dominationGraph.comp (by fun_prop)) (ite' hle (by fun_prop) (const 0)) end VertexCover @@ -500,7 +501,7 @@ end VertexCover /-- `VertexCover` reduces to `DominatingSet` in polynomial time via `VertexCover.toDominatingSet`. -/ theorem polyTimeReducible_VertexCover_DominatingSet : - PolyTimeReducible (encPair encGraph encNat) encDominatingSet VertexCover + PolyTimeReducible encVertexCoverInstance encDominatingSet VertexCover DominatingSet := ⟨VertexCover.toDominatingSet, VertexCover.computableInPolyTime_toDominatingSet, fun x => (VertexCover.toDominatingSet_mem_iff x).symm⟩ diff --git a/CslibFrontier/Computability/Complexity/Problems/FeedbackVertexSet.lean b/CslibFrontier/Computability/Complexity/Problems/FeedbackVertexSet.lean index 6371ec2b62..cd55d0ff67 100644 --- a/CslibFrontier/Computability/Complexity/Problems/FeedbackVertexSet.lean +++ b/CslibFrontier/Computability/Complexity/Problems/FeedbackVertexSet.lean @@ -278,14 +278,14 @@ theorem dirAdj_bidirect {G : Graph} {u v : ℕ} : arcs, or produce a self-loop with target `0` when the target exceeds the number of distinct declared vertices. -/ @[expose] -def toFeedbackVertexSet (x : Graph × ℕ) : FeedbackVertexSetInstance := - if x.2 ≤ x.1.vertices.toFinset.card then ⟨bidirect x.1, x.2⟩ else ⟨⟨[0], [(0, 0)]⟩, 0⟩ +def toFeedbackVertexSet (x : VertexCoverInstance) : FeedbackVertexSetInstance := + if x.k ≤ x.graph.vertices.toFinset.card then ⟨bidirect x.graph, x.k⟩ else ⟨⟨[0], [(0, 0)]⟩, 0⟩ /-- **Vertex covers are feedback vertex sets of the bidirected graph.** -/ -theorem toFeedbackVertexSet_mem_iff (x : Graph × ℕ) : +theorem toFeedbackVertexSet_mem_iff (x : VertexCoverInstance) : toFeedbackVertexSet x ∈ FeedbackVertexSet ↔ x ∈ VertexCover := by obtain ⟨G, k⟩ := x - rw [mem_VertexCover_iff_exists_card_le, toFeedbackVertexSet] + rw [mem_VertexCover_iff_exists_card_le_mk, toFeedbackVertexSet] split_ifs with hk · rw [mem_FeedbackVertexSet_iff] constructor @@ -320,28 +320,28 @@ theorem toFeedbackVertexSet_mem_iff (x : Graph × ℕ) : unary and compared with the binary target. -/ @[fun_prop] theorem computableInPolyTime_toFeedbackVertexSet : - ComputableInPolyTime toFeedbackVertexSet (encPair encGraph encNat) encFeedbackVertexSet := by - have hn : ComputableInPolyTime (fun x : Graph × ℕ => x.1.vertices.toFinset.card) - (encPair encGraph encNat) encUnary := by + ComputableInPolyTime toFeedbackVertexSet encVertexCoverInstance encFeedbackVertexSet := by + have hn : ComputableInPolyTime (fun x : VertexCoverInstance => x.graph.vertices.toFinset.card) + encVertexCoverInstance encUnary := by simp_rw [← Graph.length_distinctVertices] - exact (length_list encNat).comp (Graph.computableInPolyTime_distinctVertices.comp (fst _ _)) - have hb : ComputableInPolyTime (fun x : Graph × ℕ => bidirect x.1) - (encPair encGraph encNat) encGraph := by + exact (length_list encNat).comp (Graph.computableInPolyTime_distinctVertices.comp (by fun_prop)) + have hb : ComputableInPolyTime (fun x : VertexCoverInstance => bidirect x.graph) + encVertexCoverInstance encGraph := by unfold bidirect fun_prop - have heq : toFeedbackVertexSet = fun x : Graph × ℕ => - if decide (x.2 ≤ x.1.vertices.toFinset.card) then ⟨bidirect x.1, x.2⟩ + have heq : toFeedbackVertexSet = fun x : VertexCoverInstance => + if decide (x.k ≤ x.graph.vertices.toFinset.card) then ⟨bidirect x.graph, x.k⟩ else ⟨⟨[0], [(0, 0)]⟩, 0⟩ := by funext x simp only [toFeedbackVertexSet, decide_eq_true_eq] rw [heq] - exact ite' (binary_le' (snd _ _) (unary_to_binary.comp hn)) (by fun_prop) (const _) + exact ite' (binary_le' (by fun_prop) (unary_to_binary.comp hn)) (by fun_prop) (const _) end VertexCover /-- `VertexCover` reduces to `FeedbackVertexSet` in polynomial time [Karp1972]. -/ theorem polyTimeReducible_VertexCover_FeedbackVertexSet : - PolyTimeReducible (encPair encGraph encNat) encFeedbackVertexSet VertexCover + PolyTimeReducible encVertexCoverInstance encFeedbackVertexSet VertexCover FeedbackVertexSet := ⟨VertexCover.toFeedbackVertexSet, VertexCover.computableInPolyTime_toFeedbackVertexSet, fun x => (VertexCover.toFeedbackVertexSet_mem_iff x).symm⟩ diff --git a/CslibFrontier/Computability/Complexity/Problems/IndependentSet.lean b/CslibFrontier/Computability/Complexity/Problems/IndependentSet.lean index 657abc6a8b..74a83c90a8 100644 --- a/CslibFrontier/Computability/Complexity/Problems/IndependentSet.lean +++ b/CslibFrontier/Computability/Complexity/Problems/IndependentSet.lean @@ -10,6 +10,7 @@ module public import CslibFrontier.Computability.Complexity.Problems.Graph public import CslibFrontier.Computability.Complexity.Problems.SAT.PolyTime public import CslibFrontier.Computability.Complexity.Reduction +public import CslibFrontier.Computability.Machines.Turing.MultiTape.Encodings.Structure public import CslibFrontier.Logics.Propositional.CNF.ModelCount public import Mathlib.Combinatorics.SimpleGraph.Clique import CslibFrontier.Computability.Complexity.CookLevin.Completeness @@ -46,7 +47,7 @@ first-order interpretations. ## Main definitions -* `Cslib.Complexity.IndependentSet`. +* `Cslib.Complexity.IndependentSetInstance`, `Cslib.Complexity.IndependentSet`. * `Cslib.Complexity.IndependentSet.verifier`. * `Cslib.Complexity.SAT.toIndependentSet`. * `Cslib.Complexity.Graph.indepNum`: the independence number (`G.induced.indepNum`). @@ -63,7 +64,7 @@ first-order interpretations. * `Cslib.Complexity.SAT.computableInPolyTime_toIndependentSet`: Karp's reduction is polynomial-time. * `Cslib.Complexity.IndependentSet.isVerifier`, `Cslib.Complexity.IndependentSet.mem_NP`: - `IndependentSet ∈ NP (encPair encGraph encNat)`. + `IndependentSet ∈ NP encIndependentSetInstance`. * `Cslib.Complexity.polyTimeReducible_SAT_IndependentSet`, `Cslib.Complexity.IndependentSet_npHard`, `Cslib.Complexity.IndependentSet_npComplete`: `IndependentSet` is `NP`-complete. @@ -80,19 +81,43 @@ namespace Cslib.Complexity open Cslib.Logic.PL Turing.MultiTapeTM ComputableInPolyTime +/-- An instance `(graph, m)` of **Independent Set**. -/ +@[ext] +structure IndependentSetInstance where + /-- The graph in which to find an independent set. -/ + graph : Graph + /-- The target number of declared vertices. -/ + m : ℕ + deriving DecidableEq, Repr, Inhabited + +/-- Encoding of `IndependentSetInstance` as `(graph, m)`. -/ +enc_structure encIndependentSetInstance : IndependentSetInstance where + graph : encGraph, + m : encNat + +attribute [fun_prop] encIndependentSetInstance_graph_polyTime + encIndependentSetInstance_m_polyTime encIndependentSetInstance_mk_polyTime + /-- The graphs with an independent set of `m` of their vertices [Arora and Barak, Section 2.4][AroraBarak09]. -/ -def IndependentSet : Set (Graph × ℕ) := - {x | ∃ S : Finset {v // v ∈ x.1.1}, x.1.induced.IsNIndepSet x.2 S} +def IndependentSet : Set IndependentSetInstance := + {x | ∃ S : Finset {v // v ∈ x.graph.vertices}, x.graph.induced.IsNIndepSet x.m S} /-- Membership in `IndependentSet` as a finite set of `m` declared vertices that is independent in -`G.toSimpleGraph`. -/ -theorem mem_IndependentSet_iff {G : Graph} {m : ℕ} : - (G, m) ∈ IndependentSet ↔ - ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.1) ∧ G.toSimpleGraph.IsNIndepSet m S := by +`x.graph.toSimpleGraph`. -/ +theorem mem_IndependentSet_iff {x : IndependentSetInstance} : + x ∈ IndependentSet ↔ + ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ x.graph.vertices) ∧ + x.graph.toSimpleGraph.IsNIndepSet x.m S := by simpa only [IndependentSet, Set.mem_ofPred_eq, SimpleGraph.isNIndepSet_iff, SimpleGraph.IsIndepSet, Graph.induced_adj] using - Graph.exists_subtype_pairwise_iff (P := fun u v => ¬G.toSimpleGraph.Adj u v) + Graph.exists_subtype_pairwise_iff (P := fun u v => ¬x.graph.toSimpleGraph.Adj u v) + +@[simp] +theorem mem_IndependentSet_iff_mk {G : Graph} {m : ℕ} : + (⟨G, m⟩ : IndependentSetInstance) ∈ IndependentSet ↔ + ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.vertices) ∧ G.toSimpleGraph.IsNIndepSet m S := + mem_IndependentSet_iff namespace SAT @@ -109,12 +134,12 @@ instance : DecidableRel Occurrence.Conflict := fun _ _ => by unfold Occurrence.Conflict; infer_instance /-- Karp's reduction from `SAT` to `IndependentSet`. -/ -def toIndependentSet (φ : CNF ℕ) : Graph × ℕ := - (⟨(occurrences φ).map Occurrence.vertex, +def toIndependentSet (φ : CNF ℕ) : IndependentSetInstance := + ⟨⟨(occurrences φ).map Occurrence.vertex, (occurrences φ).flatMap fun o => ((occurrences φ).filter fun o' => decide (o.Conflict o')).map fun o' => (o.vertex, o'.vertex)⟩, - φ.length) + φ.length⟩ theorem _root_.Cslib.Logic.PL.Occurrence.Conflict.symm {o o' : Occurrence ℕ} (h : o.Conflict o') : o'.Conflict o := by @@ -142,9 +167,9 @@ private theorem vertex_injOn {φ : CNF ℕ} {o o' : Occurrence ℕ} (ho : o ∈ /-- In the graph of Karp's reduction, two occurrences are adjacent exactly when they conflict. -/ theorem adj_toIndependentSet_iff {φ : CNF ℕ} {o o' : Occurrence ℕ} (ho : o ∈ occurrences φ) (ho' : o' ∈ occurrences φ) : - (toIndependentSet φ).1.toSimpleGraph.Adj o.vertex o'.vertex ↔ o.Conflict o' := by + (toIndependentSet φ).graph.toSimpleGraph.Adj o.vertex o'.vertex ↔ o.Conflict o' := by have hedge : ∀ {p p'}, p ∈ occurrences φ → p' ∈ occurrences φ → - ((p.vertex, p'.vertex) ∈ (toIndependentSet φ).1.2 ↔ p.Conflict p') := by + ((p.vertex, p'.vertex) ∈ (toIndependentSet φ).graph.edges ↔ p.Conflict p') := by intro p p' hp hp' simp only [toIndependentSet, List.mem_flatMap, List.mem_map, List.mem_filter, decide_eq_true_eq, Prod.mk.injEq] @@ -165,8 +190,8 @@ theorem adj_toIndependentSet_iff {φ : CNF ℕ} {o o' : Occurrence ℕ} (ho : o assignment making the chosen positive literals true satisfies the clause of every chosen occurrence, and distinct chosen occurrences lie in distinct clauses. -/ theorem exists_card_add_countUnsat_le {φ : CNF ℕ} {S : Finset ℕ} - (hSV : ∀ v ∈ S, v ∈ (toIndependentSet φ).1.1) - (hS : (toIndependentSet φ).1.toSimpleGraph.IsIndepSet (S : Set ℕ)) : + (hSV : ∀ v ∈ S, v ∈ (toIndependentSet φ).graph.vertices) + (hS : (toIndependentSet φ).graph.toSimpleGraph.IsIndepSet (S : Set ℕ)) : ∃ σ : ℕ → Bool, S.card + φ.countUnsat σ ≤ φ.length := by classical have hocc : ∀ v ∈ S, ∃ o ∈ occurrences φ, o.vertex = v := fun v hv => by @@ -293,10 +318,10 @@ theorem _root_.Cslib.Logic.PL.Occurrence.computableInPolyTime_conflict : /-- Karp's reduction `toIndependentSet` is computable in polynomial time. -/ @[fun_prop] theorem computableInPolyTime_toIndependentSet : - ComputableInPolyTime toIndependentSet encCNF (encPair encGraph encNat) := by + ComputableInPolyTime toIndependentSet encCNF encIndependentSetInstance := by have hlen : ComputableInPolyTime (fun φ : CNF ℕ => φ.length) encCNF encNat := length_list_binary _ - unfold toIndependentSet encGraph + unfold toIndependentSet fun_prop end SAT @@ -305,44 +330,52 @@ namespace IndependentSet /-- The verifier for `IndependentSet`: select distinct declared vertices from the certificate mask, check that their count equals `m`, and check that no two distinct selected vertices are adjacent. -/ -def verifier : (Graph × ℕ) × List Bool → Bool := - Graph.verifySubset fun G u v => !G.adjBool u v +def verifier : IndependentSetInstance × List Bool → Bool := + fun (x, w) => Graph.verifySubset (fun G u v => !G.adjBool u v) ((x.graph, x.m), w) /-- `IndependentSet.verifier` verifies `IndependentSet` with certificates as long as the encoded instance. -/ theorem isVerifier : - IsVerifier (encPair encGraph encNat) Polynomial.X verifier IndependentSet := by + IsVerifier encIndependentSetInstance Polynomial.X verifier IndependentSet := by have h := Graph.isVerifier_verifySubset (P := fun G u v => ¬G.toSimpleGraph.Adj u v) (fun G u v _ => by rw [Bool.not_eq_true', Graph.adjBool_eq_false_iff]) intro x - simpa only [IndependentSet, Set.mem_ofPred_eq, SimpleGraph.isNIndepSet_iff, - SimpleGraph.IsIndepSet, Graph.induced_adj, verifier] using h x + have hx := h (x.graph, x.m) + simp only [IndependentSet, Set.mem_ofPred_eq, SimpleGraph.isNIndepSet_iff, + SimpleGraph.IsIndepSet, Graph.induced_adj, verifier] + rw [encIndependentSetInstance_apply] + exact hx /-- The `IndependentSet` verifier is polynomial-time. -/ @[fun_prop] theorem computableInPolyTime_verifier : ComputableInPolyTime verifier - (encPair (encPair encGraph encNat) encBits) encBool := - Graph.computableInPolyTime_verifySubset (by fun_prop) + (encPair encIndependentSetInstance encBits) encBool := by + have h1 : ComputableInPolyTime + (fun p : IndependentSetInstance × List Bool => ((p.1.graph, p.1.m), p.2)) + (encPair encIndependentSetInstance encBits) + (encPair (encPair encGraph encNat) encBits) := by + fun_prop + exact (Graph.computableInPolyTime_verifySubset (by fun_prop)).comp h1 /-- `IndependentSet` belongs to `NP`. -/ -theorem mem_NP : IndependentSet ∈ NP (encPair encGraph encNat) := +theorem mem_NP : IndependentSet ∈ NP encIndependentSetInstance := ⟨Polynomial.X, verifier, computableInPolyTime_verifier, isVerifier⟩ end IndependentSet /-- `SAT` reduces to `IndependentSet` in polynomial time via `SAT.toIndependentSet`. -/ theorem polyTimeReducible_SAT_IndependentSet : - PolyTimeReducible encCNF (encPair encGraph encNat) SAT IndependentSet := + PolyTimeReducible encCNF encIndependentSetInstance SAT IndependentSet := ⟨SAT.toIndependentSet, SAT.computableInPolyTime_toIndependentSet, fun φ => (SAT.toIndependentSet_mem_iff φ).symm⟩ /-- `IndependentSet` is `NP`-hard. -/ -theorem IndependentSet_npHard : NPHard (encPair encGraph encNat) IndependentSet := +theorem IndependentSet_npHard : NPHard encIndependentSetInstance IndependentSet := CookLevin.SAT_npHard.trans polyTimeReducible_SAT_IndependentSet /-- **`IndependentSet` is NP-complete** [Arora and Barak, Section 2.4][AroraBarak09]. -/ -theorem IndependentSet_npComplete : NPComplete (encPair encGraph encNat) IndependentSet := +theorem IndependentSet_npComplete : NPComplete encIndependentSetInstance IndependentSet := ⟨IndependentSet.mem_NP, IndependentSet_npHard⟩ /-! ### The independence number -/ @@ -356,29 +389,30 @@ noncomputable def indepNum (G : Graph) : ℕ := G.induced.indepNum variable {G : Graph} {k k' : ℕ} /-- Fewer vertices of an independent set are independent. -/ -theorem mem_IndependentSet_of_le (h : (G, k) ∈ IndependentSet) (hk : k' ≤ k) : - (G, k') ∈ IndependentSet := by +theorem mem_IndependentSet_of_le (h : (⟨G, k⟩ : IndependentSetInstance) ∈ IndependentSet) + (hk : k' ≤ k) : (⟨G, k'⟩ : IndependentSetInstance) ∈ IndependentSet := by obtain ⟨S, hS, hcard : S.card = k⟩ := h obtain ⟨T, hTS, hT⟩ := Finset.exists_subset_card_eq (hcard ▸ hk) exact ⟨T, hS.mono (Finset.coe_subset.2 hTS), hT⟩ /-- The independence number is the size of an independent set. -/ -theorem indepNum_mem_IndependentSet (G : Graph) : (G, G.indepNum) ∈ IndependentSet := +theorem indepNum_mem_IndependentSet (G : Graph) : + (⟨G, G.indepNum⟩ : IndependentSetInstance) ∈ IndependentSet := G.induced.exists_isNIndepSet_indepNum /-- **The independence number is the largest target of `IndependentSet`.** -/ -theorem le_indepNum_iff : k ≤ G.indepNum ↔ (G, k) ∈ IndependentSet := +theorem le_indepNum_iff : k ≤ G.indepNum ↔ (⟨G, k⟩ : IndependentSetInstance) ∈ IndependentSet := ⟨mem_IndependentSet_of_le G.indepNum_mem_IndependentSet, fun ⟨_, hS, (hcard : _ = k)⟩ => hcard ▸ hS.card_le_indepNum⟩ /-- The independence number is at most the number of distinct declared vertices. -/ -theorem indepNum_le_card (G : Graph) : G.indepNum ≤ G.1.toFinset.card := by +theorem indepNum_le_card (G : Graph) : G.indepNum ≤ G.vertices.toFinset.card := by rw [indepNum, ← G.induced.cliqueNum_compl, ← G.natCard_vertexSubtype] exact G.inducedᶜ.cliqueNum_le_natCard /-- An independent set has at most as many vertices as the distinct declared vertices. -/ -theorem le_card_of_mem_IndependentSet (h : (G, k) ∈ IndependentSet) : - k ≤ G.1.toFinset.card := +theorem le_card_of_mem_IndependentSet (h : (⟨G, k⟩ : IndependentSetInstance) ∈ IndependentSet) : + k ≤ G.vertices.toFinset.card := (le_indepNum_iff.2 h).trans G.indepNum_le_card end Graph diff --git a/CslibFrontier/Computability/Complexity/Problems/IndependentSet/Sparse.lean b/CslibFrontier/Computability/Complexity/Problems/IndependentSet/Sparse.lean index f7691cc302..a63b329409 100644 --- a/CslibFrontier/Computability/Complexity/Problems/IndependentSet/Sparse.lean +++ b/CslibFrontier/Computability/Complexity/Problems/IndependentSet/Sparse.lean @@ -107,8 +107,8 @@ open SparseIndependentSet in /-- The reduction from `SAT` to `IndependentSet` with variable edges [Garey and Johnson, Theorem 3.3][GareyJohnson1979]: the graph of `φ` with the target `n + m`, for `n` distinct variables and `m` clauses. -/ -def toSparseIndependentSet (φ : CNF ℕ) : Graph × ℕ := - (⟨vertices φ, edges φ⟩, φ.distinctVars.length + φ.length) +def toSparseIndependentSet (φ : CNF ℕ) : IndependentSetInstance := + ⟨⟨vertices φ, edges φ⟩, φ.distinctVars.length + φ.length⟩ namespace SparseIndependentSet @@ -256,7 +256,7 @@ private theorem satisfiable_of_mem (h : toSparseIndependentSet φ ∈ Independen · obtain ⟨v, hv, rfl⟩ := Finset.mem_image.1 hb exact hblock v hv · rw [Finset.card_image_of_injOn hinj, hS.card_eq, Finset.card_disjSum, Finset.card_range, - Finset.card_range] + Finset.card_range, toSparseIndependentSet] have hhit : ∀ b ∈ B, ∃ v ∈ S, block v = b := fun b hb => by rw [← himage] at hb exact Finset.mem_image.1 hb @@ -413,7 +413,8 @@ open SparseIndependentSet in of `toSparseIndependentSet` has at most `(k ^ 2 + 5 k) m` vertices and edges in total, counted as listed. -/ theorem length_toSparseIndependentSet_le {k : ℕ} {φ : CNF ℕ} (hφ : ∀ c ∈ φ, c.length ≤ k) : - (toSparseIndependentSet φ).1.vertices.length + (toSparseIndependentSet φ).1.edges.length ≤ + (toSparseIndependentSet φ).graph.vertices.length + + (toSparseIndependentSet φ).graph.edges.length ≤ (k * k + 5 * k) * φ.length := by have hzip : ∀ c ∈ φ.zipIdx, c.1.length ≤ k := fun c hc => by obtain ⟨cl, i⟩ := c @@ -450,7 +451,7 @@ open SparseIndependentSet in computed in unary from clause, position and variable indices, then converted to binary. -/ @[fun_prop] theorem computableInPolyTime_toSparseIndependentSet : - ComputableInPolyTime toSparseIndependentSet encCNF (encPair encGraph encNat) := by + ComputableInPolyTime toSparseIndependentSet encCNF encIndependentSetInstance := by have hdv : ComputableInPolyTime CNF.distinctVars (encList (encList (encPair encNat encBool))) (encList encNat) := CNF.computableInPolyTime_distinctVars encNat @@ -489,7 +490,7 @@ theorem computableInPolyTime_toSparseIndependentSet : have hG : ComputableInPolyTime (fun φ => (⟨vertices φ, edges φ⟩ : Graph)) (encList (encList (encPair encNat encBool))) encGraph := by fun_prop - exact hG.prod hk + exact encIndependentSetInstance_mk_polyTime hG hk end SAT diff --git a/CslibFrontier/Computability/Complexity/Problems/VertexCover.lean b/CslibFrontier/Computability/Complexity/Problems/VertexCover.lean index 2b676b12cf..d0ab0e2bfe 100644 --- a/CslibFrontier/Computability/Complexity/Problems/VertexCover.lean +++ b/CslibFrontier/Computability/Complexity/Problems/VertexCover.lean @@ -8,6 +8,7 @@ module -- upstream: candidate public import CslibFrontier.Computability.Complexity.Problems.IndependentSet +public import CslibFrontier.Computability.Machines.Turing.MultiTape.Encodings.Structure public import Mathlib.Combinatorics.SimpleGraph.VertexCover import CslibFrontier.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.BitString import CslibFrontier.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Length @@ -45,7 +46,7 @@ first-order interpretations. ## Main definitions -* `Cslib.Complexity.VertexCover`. +* `Cslib.Complexity.VertexCoverInstance`, `Cslib.Complexity.VertexCover`. * `Cslib.Complexity.VertexCover.declaredEdges`: the listed edges between declared vertices. * `Cslib.Complexity.VertexCover.isUncoveredEdge`, `Cslib.Complexity.VertexCover.uncoveredEdges`, `Cslib.Complexity.VertexCover.isCover`, `Cslib.Complexity.VertexCover.verifier`. @@ -64,7 +65,7 @@ first-order interpretations. polynomial-time, with targets in binary. * `Cslib.Complexity.VertexCover.isCover_eq_true_iff`, `Cslib.Complexity.VertexCover.isVerifier`, `Cslib.Complexity.VertexCover.mem_NP`: - `VertexCover ∈ NP (encPair encGraph encNat)`. + `VertexCover ∈ NP encVertexCoverInstance`. * `Cslib.Complexity.polyTimeReducible_IndependentSet_VertexCover`, `Cslib.Complexity.VertexCover_npHard`, `Cslib.Complexity.VertexCover_npComplete`: `VertexCover` is `NP`-complete. @@ -82,17 +83,35 @@ namespace Cslib.Complexity open Turing.MultiTapeTM ComputableInPolyTime +/-- An instance `(graph, k)` of **Vertex Cover**. -/ +@[ext] +structure VertexCoverInstance where + /-- The graph in which to find a vertex cover. -/ + graph : Graph + /-- The target number of declared vertices. -/ + k : ℕ + deriving DecidableEq, Repr, Inhabited + +/-- Encoding of `VertexCoverInstance` as `(graph, k)`. -/ +enc_structure encVertexCoverInstance : VertexCoverInstance where + graph : encGraph, + k : encNat + +attribute [fun_prop] encVertexCoverInstance_graph_polyTime + encVertexCoverInstance_k_polyTime encVertexCoverInstance_mk_polyTime + /-- The graphs with a vertex cover of `k` of their vertices: `k` declared vertices meeting every edge between two declared vertices [Karp1972]. -/ -def VertexCover : Set (Graph × ℕ) := - {x | ∃ S : Finset {v // v ∈ x.1.vertices}, x.1.induced.IsVertexCover S ∧ S.card = x.2} +def VertexCover : Set VertexCoverInstance := + {x | ∃ S : Finset {v // v ∈ x.graph.vertices}, x.graph.induced.IsVertexCover S ∧ S.card = x.k} /-- Membership in `VertexCover` as a finite set of `k` declared vertices meeting every edge between declared vertices. -/ -theorem mem_VertexCover_iff {G : Graph} {k : ℕ} : - (G, k) ∈ VertexCover ↔ ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.vertices) ∧ - (∀ u ∈ G.vertices, ∀ v ∈ G.vertices, G.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) ∧ - S.card = k := by +theorem mem_VertexCover_iff {x : VertexCoverInstance} : + x ∈ VertexCover ↔ ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ x.graph.vertices) ∧ + (∀ u ∈ x.graph.vertices, ∀ v ∈ x.graph.vertices, + x.graph.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) ∧ + S.card = x.k := by constructor · rintro ⟨S, hS, hcard⟩ refine ⟨S.map (.subtype _), @@ -100,30 +119,46 @@ theorem mem_VertexCover_iff {G : Graph} {k : ℕ} : fun u hu v hv huv => (@hS ⟨u, hu⟩ ⟨v, hv⟩ huv).imp (Finset.mem_map_of_mem _) (Finset.mem_map_of_mem _), (Finset.card_map _).trans hcard⟩ - · rintro ⟨S, hSV, hS, rfl⟩ - have hmap : (S.subtype (· ∈ G.vertices)).map (.subtype _) = S := + · rintro ⟨S, hSV, hS, hcard⟩ + have hmap : (S.subtype (· ∈ x.graph.vertices)).map (.subtype _) = S := (Finset.subtype_map _).trans (Finset.filter_eq_self.mpr hSV) - refine ⟨S.subtype (· ∈ G.vertices), fun u v huv => ?_, - by rw [← Finset.card_map (.subtype _), hmap]⟩ + refine ⟨S.subtype (· ∈ x.graph.vertices), fun u v huv => ?_, + by rw [← Finset.card_map (.subtype _), hmap, hcard]⟩ simpa [Finset.mem_subtype] using hS u.1 u.2 v.1 v.2 huv -/-- `VertexCover` through covers of at most `k` vertices: `(G, k) ∈ VertexCover` exactly when `k` -is at most the number of distinct declared vertices and some cover has at most `k` vertices. -/ -theorem mem_VertexCover_iff_exists_card_le {G : Graph} {k : ℕ} : - (G, k) ∈ VertexCover ↔ k ≤ G.vertices.toFinset.card ∧ ∃ S : Finset ℕ, - (∀ v ∈ S, v ∈ G.vertices) ∧ +@[simp] +theorem mem_VertexCover_iff_mk {G : Graph} {k : ℕ} : + (⟨G, k⟩ : VertexCoverInstance) ∈ VertexCover ↔ ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ G.vertices) ∧ (∀ u ∈ G.vertices, ∀ v ∈ G.vertices, G.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) ∧ - S.card ≤ k := by + S.card = k := + mem_VertexCover_iff + +/-- `VertexCover` through covers of at most `k` vertices: `x ∈ VertexCover` exactly when `x.k` +is at most the number of distinct declared vertices and some cover has at most `x.k` vertices. -/ +theorem mem_VertexCover_iff_exists_card_le {x : VertexCoverInstance} : + x ∈ VertexCover ↔ x.k ≤ x.graph.vertices.toFinset.card ∧ ∃ S : Finset ℕ, + (∀ v ∈ S, v ∈ x.graph.vertices) ∧ + (∀ u ∈ x.graph.vertices, ∀ v ∈ x.graph.vertices, + x.graph.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) ∧ + S.card ≤ x.k := by rw [mem_VertexCover_iff] constructor - · rintro ⟨S, hSV, hS, rfl⟩ - exact ⟨Finset.card_le_card fun v hv => List.mem_toFinset.2 (hSV v hv), S, hSV, hS, le_rfl⟩ + · rintro ⟨S, hSV, hS, hcard⟩ + exact ⟨hcard ▸ Finset.card_le_card fun v hv => List.mem_toFinset.2 (hSV v hv), S, hSV, hS, + hcard.le⟩ · rintro ⟨hk, S, hSV, hS, hcard⟩ obtain ⟨T, hST, hTV, hT⟩ := Finset.exists_subsuperset_card_eq (fun v hv => List.mem_toFinset.2 (hSV v hv)) hcard hk exact ⟨T, fun v hv => List.mem_toFinset.1 (hTV hv), fun u hu v hv huv => (hS u hu v hv huv).imp (@hST u) (@hST v), hT⟩ +theorem mem_VertexCover_iff_exists_card_le_mk {G : Graph} {k : ℕ} : + (⟨G, k⟩ : VertexCoverInstance) ∈ VertexCover ↔ k ≤ G.vertices.toFinset.card ∧ ∃ S : Finset ℕ, + (∀ v ∈ S, v ∈ G.vertices) ∧ + (∀ u ∈ G.vertices, ∀ v ∈ G.vertices, G.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) ∧ + S.card ≤ k := + mem_VertexCover_iff_exists_card_le + namespace IndependentSet /-! ### Reduction from independent sets -/ @@ -131,12 +166,12 @@ namespace IndependentSet /-- The reduction from `IndependentSet` to `VertexCover`: keep the graph and replace the target `m` by `n - m`, where `n` is the number of distinct declared vertices, or by `n + 1` when `m > n`. -/ -def toVertexCover (x : Graph × ℕ) : Graph × ℕ := - (x.1, if x.2 ≤ x.1.vertices.toFinset.card then x.1.vertices.toFinset.card - x.2 - else x.1.vertices.toFinset.card + 1) +def toVertexCover (x : IndependentSetInstance) : VertexCoverInstance := + ⟨x.graph, if x.m ≤ x.graph.vertices.toFinset.card then x.graph.vertices.toFinset.card - x.m + else x.graph.vertices.toFinset.card + 1⟩ /-- **Independent sets are complements of vertex covers.** -/ -theorem toVertexCover_mem_iff (x : Graph × ℕ) : +theorem toVertexCover_mem_iff (x : IndependentSetInstance) : toVertexCover x ∈ VertexCover ↔ x ∈ IndependentSet := by classical obtain ⟨G, m⟩ := x @@ -172,34 +207,36 @@ theorem toVertexCover_mem_iff (x : Graph × ℕ) : the binary target is compared with it by saturating decoding. -/ @[fun_prop] theorem computableInPolyTime_toVertexCover : - ComputableInPolyTime toVertexCover (encPair encGraph encNat) - (encPair encGraph encNat) := by - have hn : ComputableInPolyTime (fun x : Graph × ℕ => x.1.vertices.toFinset.card) - (encPair encGraph encNat) encUnary := by + ComputableInPolyTime toVertexCover encIndependentSetInstance + encVertexCoverInstance := by + have hn : ComputableInPolyTime (fun x : IndependentSetInstance => x.graph.vertices.toFinset.card) + encIndependentSetInstance encUnary := by simp_rw [← Graph.length_distinctVertices] - exact (length_list encNat).comp - (Graph.computableInPolyTime_distinctVertices.comp (fst _ _)) + fun_prop -- `min n m`, decoded from the binary target under the unary cap `n`. have hmin : ComputableInPolyTime - (fun x : Graph × ℕ => Cslib.BitString.boundedBinaryValue x.1.vertices.toFinset.card - (encNat x.2).reverse) (encPair encGraph encNat) encUnary := - bitsBoundedBinaryValueWithInput hn (bitsReverse.comp (encoding_comp (snd _ _))) - have heq : toVertexCover = fun x : Graph × ℕ => - (x.1, if decide (Cslib.BitString.boundedBinaryValue x.1.vertices.toFinset.card - (encNat x.2).reverse = x.2) - then x.1.vertices.toFinset.card - - Cslib.BitString.boundedBinaryValue x.1.vertices.toFinset.card (encNat x.2).reverse - else x.1.vertices.toFinset.card + 1) := by + (fun x : IndependentSetInstance => Cslib.BitString.boundedBinaryValue + x.graph.vertices.toFinset.card (encNat x.m).reverse) encIndependentSetInstance encUnary := + bitsBoundedBinaryValueWithInput hn + (bitsReverse.comp (encoding_comp (by fun_prop))) + have heq : toVertexCover = fun x : IndependentSetInstance => + ⟨x.graph, if decide (Cslib.BitString.boundedBinaryValue x.graph.vertices.toFinset.card + (encNat x.m).reverse = x.m) + then x.graph.vertices.toFinset.card - + Cslib.BitString.boundedBinaryValue x.graph.vertices.toFinset.card (encNat x.m).reverse + else x.graph.vertices.toFinset.card + 1⟩ := by funext x - simp only [toVertexCover, Bool.decide_iff, encNat_apply, Cslib.BitString.boundedBinaryValue_eq, - Cslib.BitString.binaryValue_reverse_bits, min_eq_right_iff] + simp only [toVertexCover, Bool.decide_iff, encNat_apply, + Cslib.BitString.boundedBinaryValue_eq, Cslib.BitString.binaryValue_reverse_bits, + min_eq_right_iff] split_ifs with h · rw [min_eq_right h] · rfl rw [heq] - refine (fst _ _).prod (ite ((unary_to_binary.comp hmin).decide_eq' (snd _ _)) - (unary_to_binary.comp (unary_sub.comp (hn.prod hmin))) - (unary_to_binary.comp (unary_add.comp (hn.prod (const 1))))) + refine encVertexCoverInstance_mk_polyTime (by fun_prop) + (ite ((unary_to_binary.comp hmin).decide_eq' (by fun_prop)) + (unary_to_binary.comp (unary_sub.comp (hn.prod hmin))) + (unary_to_binary.comp (unary_add.comp (hn.prod (const 1))))) end IndependentSet @@ -307,21 +344,23 @@ theorem computableInPolyTime_isCover : /-- The verifier for `VertexCover`, a subset verifier (`Cslib.Complexity.subsetVerifier`): select distinct declared vertices by the certificate mask, check that their count equals `k`, and check that every edge between declared vertices has a selected endpoint (`isCover`). -/ -def verifier : (Graph × ℕ) × List Bool → Bool := - subsetVerifier (fun x => x.1.vertices) fun y => decide (y.2.length = y.1.2) && isCover y.1.1 y.2 +def verifier : VertexCoverInstance × List Bool → Bool := + subsetVerifier (fun x => x.graph.vertices) + fun y => decide (y.2.length = y.1.k) && isCover y.1.graph y.2 /-- `VertexCover.verifier` verifies `VertexCover` with certificates as long as the encoded instance. -/ theorem isVerifier : - IsVerifier (encPair encGraph encNat) Polynomial.X verifier VertexCover := by - have hset : VertexCover = {x | ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ x.1.vertices) ∧ - (∀ u ∈ x.1.vertices, ∀ v ∈ x.1.vertices, x.1.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) ∧ - S.card = x.2} := + IsVerifier encVertexCoverInstance Polynomial.X verifier VertexCover := by + have hset : VertexCover = {x | ∃ S : Finset ℕ, (∀ v ∈ S, v ∈ x.graph.vertices) ∧ + (∀ u ∈ x.graph.vertices, ∀ v ∈ x.graph.vertices, + x.graph.toSimpleGraph.Adj u v → u ∈ S ∨ v ∈ S) ∧ S.card = x.k} := Set.ext fun _ => mem_VertexCover_iff rw [hset] refine isVerifier_subsetVerifier (fun x => by - have := x.1.length_vertices_le_encGraph + have := x.graph.length_vertices_le_encGraph + rw [encVertexCoverInstance_apply] simp only [Polynomial.eval_X, length_encPair] omega) fun x l hl _ => ?_ @@ -331,15 +370,14 @@ theorem isVerifier : /-- The `VertexCover` verifier is polynomial-time. -/ @[fun_prop] theorem computableInPolyTime_verifier : - ComputableInPolyTime verifier (encPair (encPair encGraph encNat) encBits) encBool := by + ComputableInPolyTime verifier (encPair encVertexCoverInstance encBits) encBool := by refine computableInPolyTime_subsetVerifier (encγ := encNat) (by fun_prop) - (bool_and' ?_ (computableInPolyTime_isCover.comp - (((fst _ _).comp (fst _ _)).prod (snd _ _)))) + (bool_and' ?_ (by fun_prop)) exact ((length_list_binary encNat).comp (snd _ _)).decide_eq' (by fun_prop) /-- `VertexCover` belongs to `NP`. -/ -theorem mem_NP : VertexCover ∈ NP (encPair encGraph encNat) := +theorem mem_NP : VertexCover ∈ NP encVertexCoverInstance := ⟨Polynomial.X, verifier, computableInPolyTime_verifier, isVerifier⟩ end VertexCover @@ -347,17 +385,17 @@ end VertexCover /-- `IndependentSet` reduces to `VertexCover` in polynomial time via `IndependentSet.toVertexCover`. -/ theorem polyTimeReducible_IndependentSet_VertexCover : - PolyTimeReducible (encPair encGraph encNat) (encPair encGraph encNat) IndependentSet + PolyTimeReducible encIndependentSetInstance encVertexCoverInstance IndependentSet VertexCover := ⟨IndependentSet.toVertexCover, IndependentSet.computableInPolyTime_toVertexCover, fun x => (IndependentSet.toVertexCover_mem_iff x).symm⟩ /-- `VertexCover` is `NP`-hard. -/ -theorem VertexCover_npHard : NPHard (encPair encGraph encNat) VertexCover := +theorem VertexCover_npHard : NPHard encVertexCoverInstance VertexCover := IndependentSet_npHard.trans polyTimeReducible_IndependentSet_VertexCover /-- **`VertexCover` is NP-complete** [Karp1972]. -/ -theorem VertexCover_npComplete : NPComplete (encPair encGraph encNat) VertexCover := +theorem VertexCover_npComplete : NPComplete encVertexCoverInstance VertexCover := ⟨VertexCover.mem_NP, VertexCover_npHard⟩ /-! ### The vertex cover number -/ @@ -369,7 +407,8 @@ edge between declared vertices. -/ noncomputable def vertexCoverNum (G : Graph) : ℕ := G.induced.vertexCoverNum.toNat /-- The vertex cover number is the size of a vertex cover. -/ -theorem vertexCoverNum_mem_VertexCover (G : Graph) : (G, G.vertexCoverNum) ∈ VertexCover := by +theorem vertexCoverNum_mem_VertexCover (G : Graph) : + (⟨G, G.vertexCoverNum⟩ : VertexCoverInstance) ∈ VertexCover := by classical obtain ⟨s, hs_card, hs⟩ := G.induced.vertexCoverNum_exists refine ⟨s.toFinset, by simpa using hs, ?_⟩ @@ -377,7 +416,8 @@ theorem vertexCoverNum_mem_VertexCover (G : Graph) : (G, G.vertexCoverNum) ∈ V exact (ENat.toNat_natCast _).symm.trans (congrArg ENat.toNat hs_card) /-- Every vertex cover has at least `G.vertexCoverNum` vertices. -/ -theorem vertexCoverNum_le_of_mem_VertexCover {G : Graph} {k : ℕ} (h : (G, k) ∈ VertexCover) : +theorem vertexCoverNum_le_of_mem_VertexCover {G : Graph} {k : ℕ} + (h : (⟨G, k⟩ : VertexCoverInstance) ∈ VertexCover) : G.vertexCoverNum ≤ k := by obtain ⟨S, hS, hcard : S.card = k⟩ := h have hle := hS.vertexCoverNum_le @@ -390,14 +430,17 @@ theorem vertexCoverNum_eq_card_sub_indepNum (G : Graph) : G.vertexCoverNum = G.vertices.toFinset.card - G.indepNum := by have hα := indepNum_mem_IndependentSet G have hαn := indepNum_le_card G - have hvc : (G, G.vertices.toFinset.card - G.indepNum) ∈ VertexCover := by - have := (IndependentSet.toVertexCover_mem_iff (G, G.indepNum)).2 hα + have hvc : (⟨G, G.vertices.toFinset.card - G.indepNum⟩ : VertexCoverInstance) ∈ + VertexCover := by + have := (IndependentSet.toVertexCover_mem_iff ⟨G, G.indepNum⟩).2 hα rwa [IndependentSet.toVertexCover, ite_eq_left hαn] at this refine le_antisymm (vertexCoverNum_le_of_mem_VertexCover hvc) ?_ - have hτ : (G, G.vertexCoverNum) ∈ VertexCover := vertexCoverNum_mem_VertexCover G + have hτ : (⟨G, G.vertexCoverNum⟩ : VertexCoverInstance) ∈ VertexCover := + vertexCoverNum_mem_VertexCover G have hτn : G.vertexCoverNum ≤ G.vertices.toFinset.card := (mem_VertexCover_iff_exists_card_le.1 hτ).1 - have hIS : (G, G.vertices.toFinset.card - G.vertexCoverNum) ∈ IndependentSet := by + have hIS : (⟨G, G.vertices.toFinset.card - G.vertexCoverNum⟩ : IndependentSetInstance) ∈ + IndependentSet := by refine (IndependentSet.toVertexCover_mem_iff _).1 ?_ rwa [IndependentSet.toVertexCover, ite_eq_left (Nat.sub_le _ _), Nat.sub_sub_self hτn] have := le_indepNum_iff.2 hIS diff --git a/CslibFrontier/Computability/Complexity/Problems/VertexCover/Approximation.lean b/CslibFrontier/Computability/Complexity/Problems/VertexCover/Approximation.lean index 922d9044a5..d452214302 100644 --- a/CslibFrontier/Computability/Complexity/Problems/VertexCover/Approximation.lean +++ b/CslibFrontier/Computability/Complexity/Problems/VertexCover/Approximation.lean @@ -86,7 +86,8 @@ theorem isCover_approxCover (G : Graph) : fun _ hu _ hv huv => isVertexCover_matchingCover _ ((fromRel_declaredEdges_adj hu hv).2 huv) /-- The returned list is a vertex cover of its length. -/ -theorem approxCover_mem_VertexCover (G : Graph) : (G, (approxCover G).length) ∈ VertexCover := +theorem approxCover_mem_VertexCover (G : Graph) : + (⟨G, (approxCover G).length⟩ : VertexCoverInstance) ∈ VertexCover := mem_VertexCover_iff.2 ⟨(approxCover G).toFinset, fun _ hv => mem_vertices_of_mem_approxCover (List.mem_toFinset.1 hv), fun u hu v hv huv => (isCover_approxCover G u hu v hv huv).imp List.mem_toFinset.2 @@ -97,7 +98,7 @@ theorem approxCover_mem_VertexCover (G : Graph) : (G, (approxCover G).length) minimum number of vertices of a cover. -/ theorem length_approxCover_le_two_mul_vertexCoverNum (G : Graph) : (approxCover G).length ≤ 2 * G.vertexCoverNum := by - obtain ⟨S, hSV, hS, hcard⟩ := mem_VertexCover_iff.1 (G.vertexCoverNum_mem_VertexCover) + obtain ⟨S, hSV, hS, hcard⟩ := mem_VertexCover_iff_mk.1 (G.vertexCoverNum_mem_VertexCover) rw [← hcard] refine length_matchingCover_le_two_mul_card _ fun u v huv => ?_ have hdecl : u ∈ G.vertices ∧ v ∈ G.vertices := by diff --git a/CslibFrontier/Computability/PCP/Approximation/IndependentSet.lean b/CslibFrontier/Computability/PCP/Approximation/IndependentSet.lean index afc31115a6..e4edf2ddd9 100644 --- a/CslibFrontier/Computability/PCP/Approximation/IndependentSet.lean +++ b/CslibFrontier/Computability/PCP/Approximation/IndependentSet.lean @@ -96,8 +96,8 @@ namespace SAT /-- Every occurrence of a formula whose clauses have exactly three literals is a vertex of Karp's graph, and there are `3 m` of them for `m` clauses. -/ theorem card_toFinset_vertices_toIndependentSet {φ : CNF ℕ} (h3 : ∀ c ∈ φ, c.length = 3) : - (toIndependentSet φ).1.1.toFinset.card = 3 * φ.length := by - have hset : (toIndependentSet φ).1.1.toFinset = + (toIndependentSet φ).graph.vertices.toFinset.card = 3 * φ.length := by + have hset : (toIndependentSet φ).graph.vertices.toFinset = (Finset.range φ.length ×ˢ Finset.range 3).image fun p => Nat.pair p.1 p.2 := by ext v rw [List.mem_toFinset, Finset.mem_image] @@ -116,17 +116,17 @@ theorem card_toFinset_vertices_toIndependentSet {φ : CNF ℕ} (h3 : ∀ c ∈ (Nat.pair_eq_pair.1 h).2, Finset.card_product, Finset.card_range, Finset.card_range, Nat.mul_comm] -private theorem toIndependentSet_snd (φ : CNF ℕ) : (toIndependentSet φ).2 = φ.length := +private theorem toIndependentSet_m (φ : CNF ℕ) : (toIndependentSet φ).m = φ.length := rfl /-- **The independence number of Karp's graph is at most the largest number of clauses an assignment satisfies**, which is at most `(1 - unsatVal φ) * m` for `m` clauses. -/ theorem indepNum_toIndependentSet_le (φ : CNF ℕ) : - ((toIndependentSet φ).1.indepNum : ℝ) ≤ (1 - φ.unsatVal) * φ.length := by + ((toIndependentSet φ).graph.indepNum : ℝ) ≤ (1 - φ.unsatVal) * φ.length := by obtain ⟨S, hSV, hS⟩ := - mem_IndependentSet_iff.1 (Graph.indepNum_mem_IndependentSet (toIndependentSet φ).1) + mem_IndependentSet_iff.1 (Graph.indepNum_mem_IndependentSet (toIndependentSet φ).graph) obtain ⟨σ, hσ⟩ := exists_card_add_countUnsat_le hSV hS.isIndepSet - have hc : S.card = (toIndependentSet φ).1.indepNum := hS.card_eq + have hc : S.card = (toIndependentSet φ).graph.indepNum := hS.card_eq rw [← hc] have hcast : ((S.card + φ.countUnsat σ : ℕ) : ℝ) ≤ φ.length := by exact_mod_cast hσ push_cast at hcast @@ -142,35 +142,37 @@ end SAT /-! ### Gap problems -/ -/-- The gap problem of **maximum independent set** with soundness `s`: on an instance `(G, k)`, -the yes-instances have an independent set of `k` declared vertices, and the no-instances have no -independent set of more than `s * k` declared vertices. -/ -noncomputable def GapIndependentSet (s : ℝ) : PromiseProblem (Graph × ℕ) := - PromiseProblem.gapMax (fun x => x.1.indepNum) (fun x => x.2) (fun x => s * x.2) +/-- The gap problem of **maximum independent set** with soundness `s`: on an instance `x`, +the yes-instances have an independent set of `x.m` declared vertices, and the no-instances have no +independent set of more than `s * x.m` declared vertices. -/ +noncomputable def GapIndependentSet (s : ℝ) : PromiseProblem IndependentSetInstance := + PromiseProblem.gapMax (fun x => x.graph.indepNum) (fun x => x.m) (fun x => s * x.m) -/-- The gap problem of **maximum clique** with soundness `s`: on an instance `(G, k)`, the -yes-instances have a clique of `k` declared vertices, and the no-instances have no clique of more -than `s * k` declared vertices. -/ -noncomputable def GapClique (s : ℝ) : PromiseProblem (Graph × ℕ) := - PromiseProblem.gapMax (fun x => x.1.cliqueNum) (fun x => x.2) (fun x => s * x.2) +/-- The gap problem of **maximum clique** with soundness `s`: on an instance `x`, the +yes-instances have a clique of `x.m` declared vertices, and the no-instances have no clique of more +than `s * x.m` declared vertices. -/ +noncomputable def GapClique (s : ℝ) : PromiseProblem CliqueInstance := + PromiseProblem.gapMax (fun x => x.graph.cliqueNum) (fun x => x.m) (fun x => s * x.m) -/-- The gap problem of **minimum vertex cover** with ratio `r`: on an instance `(G, k)`, the -yes-instances have a vertex cover of at most `k` declared vertices, and the no-instances have no -vertex cover of fewer than `r * k` declared vertices. -/ -noncomputable def GapVertexCover (r : ℝ) : PromiseProblem (Graph × ℕ) := - PromiseProblem.gapMin (fun x => x.1.vertexCoverNum) (fun x => x.2) (fun x => r * x.2) +/-- The gap problem of **minimum vertex cover** with ratio `r`: on an instance `x`, the +yes-instances have a vertex cover of at most `x.k` declared vertices, and the no-instances have no +vertex cover of fewer than `r * x.k` declared vertices. -/ +noncomputable def GapVertexCover (r : ℝ) : PromiseProblem VertexCoverInstance := + PromiseProblem.gapMin (fun x => x.graph.vertexCoverNum) (fun x => x.k) (fun x => r * x.k) variable {s r : ℝ} /-- The yes-instances of `GapIndependentSet` are the members of `IndependentSet`. -/ theorem yes_GapIndependentSet : (GapIndependentSet s).yes = IndependentSet := by ext ⟨G, k⟩ - simp only [GapIndependentSet, PromiseProblem.mem_yes_gapMax, Nat.cast_le, Graph.le_indepNum_iff] + simp only [GapIndependentSet, PromiseProblem.mem_yes_gapMax, Nat.cast_le, Graph.le_indepNum_iff, + mem_IndependentSet_iff] /-- The yes-instances of `GapClique` are the members of `Clique`. -/ theorem yes_GapClique : (GapClique s).yes = Clique := by ext ⟨G, k⟩ - simp only [GapClique, PromiseProblem.mem_yes_gapMax, Nat.cast_le, Graph.le_cliqueNum_iff] + simp only [GapClique, PromiseProblem.mem_yes_gapMax, Nat.cast_le, Graph.le_cliqueNum_iff, + mem_Clique_iff] /-! ### Reductions -/ @@ -178,7 +180,7 @@ theorem yes_GapClique : (GapClique s).yes = Clique := by satisfiable formula has an independent set with a vertex in each clause, and an independent set gives an assignment satisfying as many clauses. -/ theorem polyTimeReducible_GapThreeSAT_GapIndependentSet (hs : s < 1) : - PromiseProblem.PolyTimeReducible encCNF (encPair encGraph encNat) (GapThreeSAT s) + PromiseProblem.PolyTimeReducible encCNF encIndependentSetInstance (GapThreeSAT s) (GapIndependentSet s) := by refine ⟨SAT.toIndependentSet, SAT.computableInPolyTime_toIndependentSet, fun φ hφ => ?_, fun φ hφ => ?_⟩ @@ -192,36 +194,38 @@ theorem polyTimeReducible_GapThreeSAT_GapIndependentSet (hs : s < 1) : refine (PromiseProblem.mem_no_gapMax_of_lt ?_).2 ?_ · change s * (φ.length : ℝ) < φ.length nlinarith - · change ((SAT.toIndependentSet φ).1.indepNum : ℝ) ≤ s * φ.length + · change ((SAT.toIndependentSet φ).graph.indepNum : ℝ) ≤ s * φ.length nlinarith /-- **Maximum independent set reduces to maximum clique with the same gap**, by complementing the graph on its declared vertices. -/ theorem polyTimeReducible_GapIndependentSet_GapClique : - PromiseProblem.PolyTimeReducible (encPair encGraph encNat) (encPair encGraph encNat) + PromiseProblem.PolyTimeReducible encIndependentSetInstance encCliqueInstance (GapIndependentSet s) (GapClique s) := - ⟨fun x => (x.1.compl, x.2), by fun_prop, - fun x hx => by simpa [GapIndependentSet, GapClique, Graph.cliqueNum_compl] using hx, - fun x hx => by simpa [GapIndependentSet, GapClique, Graph.cliqueNum_compl] using hx⟩ + ⟨independentSetToClique, computableInPolyTime_independentSetToClique, + fun x hx => by simpa [GapIndependentSet, GapClique, independentSetToClique, + Graph.cliqueNum_compl] using hx, + fun x hx => by simpa [GapIndependentSet, GapClique, independentSetToClique, + Graph.cliqueNum_compl] using hx⟩ /-- **MAX-E3SAT reduces to minimum vertex cover**: Karp's graph of a formula with `m` clauses of three, not necessarily distinct, literals has `3 m` vertices, so the target `m` of an independent set becomes the target `2 m` of a vertex cover (`IndependentSet.toVertexCover`), and soundness `s` for MAX-E3SAT becomes the ratio `1 + (1 - s) / 2` for vertex covers. -/ theorem polyTimeReducible_GapExactThreeSAT_GapVertexCover (hs : s < 1) : - PromiseProblem.PolyTimeReducible encCNF (encPair encGraph encNat) (GapExactThreeSAT s) + PromiseProblem.PolyTimeReducible encCNF encVertexCoverInstance (GapExactThreeSAT s) (GapVertexCover (1 + (1 - s) / 2)) := by have hred (φ : CNF ℕ) (h3 : ∀ c ∈ φ, c.length = 3) : IndependentSet.toVertexCover (SAT.toIndependentSet φ) = - ((SAT.toIndependentSet φ).1, 2 * φ.length) := by + ⟨(SAT.toIndependentSet φ).graph, 2 * φ.length⟩ := by rw [IndependentSet.toVertexCover, SAT.card_toFinset_vertices_toIndependentSet h3, - SAT.toIndependentSet_snd, ite_eq_left (show φ.length ≤ 3 * φ.length by omega)] + SAT.toIndependentSet_m, ite_eq_left (show φ.length ≤ 3 * φ.length by omega)] congr 1 omega have hτ (φ : CNF ℕ) (h3 : ∀ c ∈ φ, c.length = 3) : - ((SAT.toIndependentSet φ).1.vertexCoverNum : ℝ) = - 3 * φ.length - (SAT.toIndependentSet φ).1.indepNum := by - have hle := Graph.indepNum_le_card (SAT.toIndependentSet φ).1 + ((SAT.toIndependentSet φ).graph.vertexCoverNum : ℝ) = + 3 * φ.length - (SAT.toIndependentSet φ).graph.indepNum := by + have hle := Graph.indepNum_le_card (SAT.toIndependentSet φ).graph rw [SAT.card_toFinset_vertices_toIndependentSet h3] at hle rw [Graph.vertexCoverNum_eq_card_sub_indepNum, SAT.card_toFinset_vertices_toIndependentSet h3, Nat.cast_sub hle] @@ -232,7 +236,7 @@ theorem polyTimeReducible_GapExactThreeSAT_GapVertexCover (hs : s < 1) : SAT.computableInPolyTime_toIndependentSet, fun φ hφ => ?_, fun φ hφ => ?_⟩ · rw [yes_GapExactThreeSAT] at hφ obtain ⟨h3, hsat⟩ := hφ - have hα : (φ.length : ℝ) ≤ (SAT.toIndependentSet φ).1.indepNum := by + have hα : (φ.length : ℝ) ≤ (SAT.toIndependentSet φ).graph.indepNum := by exact_mod_cast Graph.le_indepNum_iff.2 ((SAT.toIndependentSet_mem_iff φ).2 hsat) simp only [GapVertexCover, PromiseProblem.mem_yes_gapMin, hred φ h3, hτ φ h3] push_cast @@ -252,7 +256,8 @@ graph and a target `k`, distinguishing graphs with an independent set of `k` ver without an independent set of more than `(1 - 1 / (3 * 2 ^ 77)) * k` vertices is NP-hard. The gap comes from MAX-3SAT [AroraLundMotwaniSudanSzegedy1998] through Karp's graph. -/ theorem GapIndependentSet_npHard : - PromiseProblem.NPHard (encPair encGraph encNat) (GapIndependentSet (1 - 1 / (3 * 2 ^ 77))) := + PromiseProblem.NPHard encIndependentSetInstance + (GapIndependentSet (1 - 1 / (3 * 2 ^ 77))) := GapThreeSAT_npHard.trans (polyTimeReducible_GapThreeSAT_GapIndependentSet (by norm_num)) /-- **Maximum clique is NP-hard to approximate** within `1 - 1 / (3 * 2 ^ 77)`: given a graph and @@ -260,7 +265,7 @@ a target `k`, distinguishing graphs with a clique of `k` vertices from graphs wi more than `(1 - 1 / (3 * 2 ^ 77)) * k` vertices is NP-hard. The gap of maximum independent set transfers to the complement graph. -/ theorem GapClique_npHard : - PromiseProblem.NPHard (encPair encGraph encNat) (GapClique (1 - 1 / (3 * 2 ^ 77))) := + PromiseProblem.NPHard encCliqueInstance (GapClique (1 - 1 / (3 * 2 ^ 77))) := GapIndependentSet_npHard.trans polyTimeReducible_GapIndependentSet_GapClique /-- **Minimum vertex cover is NP-hard to approximate** within `1 + 1 / (3 * 2 ^ 78)` @@ -268,7 +273,8 @@ theorem GapClique_npHard : vertex cover of at most `k` vertices from graphs without a vertex cover of fewer than `(1 + 1 / (3 * 2 ^ 78)) * k` vertices is NP-hard. -/ theorem GapVertexCover_npHard : - PromiseProblem.NPHard (encPair encGraph encNat) (GapVertexCover (1 + 1 / (3 * 2 ^ 78))) := by + PromiseProblem.NPHard encVertexCoverInstance + (GapVertexCover (1 + 1 / (3 * 2 ^ 78))) := by have h := polyTimeReducible_GapExactThreeSAT_GapVertexCover (s := 1 - 1 / (3 * 2 ^ 77)) (by norm_num) rw [show (1 + (1 - (1 - 1 / (3 * 2 ^ 77))) / 2 : ℝ) = 1 + 1 / (3 * 2 ^ 78) by norm_num] at h @@ -278,19 +284,19 @@ theorem GapVertexCover_npHard : /-- Unless `P = NP`, no polynomial-time algorithm distinguishes graphs with an independent set of `k` vertices from graphs without one of more than `(1 - 1 / (3 * 2 ^ 77)) * k` vertices. -/ theorem GapIndependentSet_notMem_PromiseP {β : Type} {enc : β ↪ List Bool} (h : P enc ≠ NP enc) : - GapIndependentSet (1 - 1 / (3 * 2 ^ 77)) ∉ PromiseP (encPair encGraph encNat) := + GapIndependentSet (1 - 1 / (3 * 2 ^ 77)) ∉ PromiseP encIndependentSetInstance := GapIndependentSet_npHard.notMem_PromiseP h /-- Unless `P = NP`, no polynomial-time algorithm distinguishes graphs with a clique of `k` vertices from graphs without one of more than `(1 - 1 / (3 * 2 ^ 77)) * k` vertices. -/ theorem GapClique_notMem_PromiseP {β : Type} {enc : β ↪ List Bool} (h : P enc ≠ NP enc) : - GapClique (1 - 1 / (3 * 2 ^ 77)) ∉ PromiseP (encPair encGraph encNat) := + GapClique (1 - 1 / (3 * 2 ^ 77)) ∉ PromiseP encCliqueInstance := GapClique_npHard.notMem_PromiseP h /-- Unless `P = NP`, no polynomial-time algorithm distinguishes graphs with a vertex cover of at most `k` vertices from graphs without one of fewer than `(1 + 1 / (3 * 2 ^ 78)) * k` vertices. -/ theorem GapVertexCover_notMem_PromiseP {β : Type} {enc : β ↪ List Bool} (h : P enc ≠ NP enc) : - GapVertexCover (1 + 1 / (3 * 2 ^ 78)) ∉ PromiseP (encPair encGraph encNat) := + GapVertexCover (1 + 1 / (3 * 2 ^ 78)) ∉ PromiseP encVertexCoverInstance := GapVertexCover_npHard.notMem_PromiseP h end Cslib.Complexity diff --git a/CslibFrontierTests/ColorCoding.lean b/CslibFrontierTests/ColorCoding.lean index 253db926fc..22f1a96c29 100644 --- a/CslibFrontierTests/ColorCoding.lean +++ b/CslibFrontierTests/ColorCoding.lean @@ -109,13 +109,13 @@ example : ColorfulPath ∈ /-! ### Clique and Independent Set -/ -example : FPTReducible (encPair encGraph encNat) (encPair encGraph encNat) Prod.snd Prod.snd +example : FPTReducible encCliqueInstance encIndependentSetInstance (·.m) (·.m) Clique IndependentSet := fptReducible_Clique_IndependentSet -- `Clique` and `IndependentSet` stand or fall together in `FPT`. -example (h : IndependentSet ∈ FPT (encPair encGraph encNat) Prod.snd) : - Clique ∈ FPT (encPair encGraph encNat) Prod.snd := +example (h : IndependentSet ∈ FPT encIndependentSetInstance (·.m)) : + Clique ∈ FPT encCliqueInstance (·.m) := Clique_mem_FPT_iff.2 h -- Compositional `fun_prop` clients using the `enc_structure`-generated rules and diff --git a/CslibFrontierTests/Copositivity.lean b/CslibFrontierTests/Copositivity.lean index 73f89743e8..47c928578c 100644 --- a/CslibFrontierTests/Copositivity.lean +++ b/CslibFrontierTests/Copositivity.lean @@ -51,7 +51,7 @@ example : Copositive ∈ coNP encSymRatMatrix ∧ coNPHard encSymRatMatrix Copositive ∧ coNPComplete encSymRatMatrix Copositive ∧ - PolyTimeReducible encClique encSymRatMatrix Clique NonCopositive := + PolyTimeReducible encCliqueInstance encSymRatMatrix Clique NonCopositive := ⟨NonCopositive_npComplete.1, NonCopositive_npComplete.2, NonCopositive_npComplete, Copositive_coNPComplete.1, Copositive_coNPComplete.2, Copositive_coNPComplete, polyTimeReducible_Clique_NonCopositive⟩ @@ -346,7 +346,7 @@ private def path3G : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2)]⟩ private def emptyG : Graph := ⟨[], []⟩ -example : cliqueToSymRatMatrix (triangleG, 3) ∈ NonCopositive := by +example : cliqueToSymRatMatrix ⟨triangleG, 3⟩ ∈ NonCopositive := by rw [← cliqueToSymRatMatrix_mem_iff, mem_Clique_iff] refine ⟨{0, 1, 2}, fun v hv => by simp only [triangleG, List.mem_cons, List.not_mem_nil, or_false] @@ -361,7 +361,7 @@ example : cliqueToSymRatMatrix (triangleG, 3) ∈ NonCopositive := by rcases hu with rfl | rfl | rfl <;> rcases hv with rfl | rfl | rfl <;> first | exact absurd rfl huv | decide -example : cliqueToSymRatMatrix (path3G, 3) ∈ Copositive := by +example : cliqueToSymRatMatrix ⟨path3G, 3⟩ ∈ Copositive := by rw [Copositive_eq_compl, Set.mem_compl_iff, ← cliqueToSymRatMatrix_mem_iff, mem_Clique_iff] rintro ⟨S, hSG, hS⟩ have hsub : S ⊆ ({0, 1, 2} : Finset ℕ) := by @@ -380,16 +380,19 @@ example : cliqueToSymRatMatrix (path3G, 3) ∈ Copositive := by revert hadj decide -example : cliqueToSymRatMatrix (emptyG, 0) ∈ NonCopositive := by +example : cliqueToSymRatMatrix ⟨emptyG, 0⟩ ∈ NonCopositive := by rw [← cliqueToSymRatMatrix_mem_iff, mem_Clique_iff] refine ⟨∅, fun _ h => absurd h (Finset.notMem_empty _), ?_⟩ simp only [SimpleGraph.isNClique_iff, Finset.coe_empty, SimpleGraph.isClique_empty, Finset.card_empty, and_self] -example : cliqueToSymRatMatrix (emptyG, 1) ∈ Copositive := by +example : cliqueToSymRatMatrix ⟨emptyG, 1⟩ ∈ Copositive := by rw [Copositive_eq_compl, Set.mem_compl_iff, ← cliqueToSymRatMatrix_mem_iff, mem_Clique_iff] rintro ⟨S, hSG, hS⟩ - have hpos : 0 < S.card := by rw [hS.card_eq]; omega + have hpos : 0 < S.card := by + have hc := hS.card_eq + dsimp only at hc + omega obtain ⟨v, hv⟩ := Finset.card_pos.1 hpos have hv0 := hSG v hv simp only [emptyG, List.not_mem_nil] at hv0 diff --git a/CslibFrontierTests/CrossComposition.lean b/CslibFrontierTests/CrossComposition.lean index 21e785a008..ae86fdf8ea 100644 --- a/CslibFrontierTests/CrossComposition.lean +++ b/CslibFrontierTests/CrossComposition.lean @@ -94,7 +94,7 @@ example (h : HasPolyCompression (encList encCNF) (orParam encCNF) {l | ∃ a ∈ /-! ### Kernels and compressions -/ /-- Buss's kernel for vertex cover gives a polynomial compression. -/ -example : HasPolyCompression (encPair encGraph encNat) Prod.snd VertexCover := +example : HasPolyCompression encVertexCoverInstance VertexCoverInstance.k VertexCover := VertexCover_hasPolyKernel.hasPolyCompression /-- With a constant parameter, the identity on bit strings is no compression, whatever the size diff --git a/CslibFrontierTests/DominatingSet.lean b/CslibFrontierTests/DominatingSet.lean index 2c3ed25542..58460ccff7 100644 --- a/CslibFrontierTests/DominatingSet.lean +++ b/CslibFrontierTests/DominatingSet.lean @@ -32,7 +32,7 @@ private def junk : Graph := ⟨[0, 0], [(0, 0), (0, 5)]⟩ example : DominatingSet ∈ NP encDominatingSet ∧ NPHard encDominatingSet DominatingSet ∧ - PolyTimeReducible (encPair encGraph encNat) encDominatingSet VertexCover + PolyTimeReducible encVertexCoverInstance encDominatingSet VertexCover DominatingSet := ⟨DominatingSet_npComplete.1, DominatingSet_npComplete.2, polyTimeReducible_VertexCover_DominatingSet⟩ @@ -104,25 +104,25 @@ example : VertexCover.dominationGraph ⟨[0, 1], [(0, 1)]⟩ = ⟨[1, 3, 0, 2, 7], [(1, 3), (1, 0), (1, 2), (7, 0), (7, 2)]⟩ := by decide -- The target becomes `k + 1`, or `0` when `k` exceeds the number of distinct vertices. -example : VertexCover.toDominatingSet (path, 1) = ⟨VertexCover.dominationGraph path, 2⟩ := by +example : VertexCover.toDominatingSet ⟨path, 1⟩ = ⟨VertexCover.dominationGraph path, 2⟩ := by decide -example : VertexCover.toDominatingSet (junk, 2) = ⟨VertexCover.dominationGraph junk, 0⟩ := by +example : VertexCover.toDominatingSet ⟨junk, 2⟩ = ⟨VertexCover.dominationGraph junk, 0⟩ := by decide -- `{1}` covers the path, so the hub and the copy `2` dominate its graph. -example : VertexCover.toDominatingSet (path, 1) ∈ DominatingSet := +example : VertexCover.toDominatingSet ⟨path, 1⟩ ∈ DominatingSet := (VertexCover.toDominatingSet_mem_iff _).2 (mem_VertexCover_iff.2 ⟨{1}, by decide, VertexCover.isCover_eq_true_iff.1 (by decide), rfl⟩) -- The isolated vertex `2` costs nothing: `{0}` covers `edgeIso`, and the hub dominates the copy -- `4` of `2`. -example : VertexCover.toDominatingSet (edgeIso, 1) ∈ DominatingSet := +example : VertexCover.toDominatingSet ⟨edgeIso, 1⟩ ∈ DominatingSet := (VertexCover.toDominatingSet_mem_iff _).2 (mem_VertexCover_iff.2 ⟨{0}, by decide, VertexCover.isCover_eq_true_iff.1 (by decide), rfl⟩) -- No vertex cover is empty when there is an edge, so no single vertex (target `0 + 1`) dominates -- the graph of the path. -example : VertexCover.toDominatingSet (path, 0) ∉ DominatingSet := by +example : VertexCover.toDominatingSet ⟨path, 0⟩ ∉ DominatingSet := by rw [VertexCover.toDominatingSet_mem_iff, mem_VertexCover_iff] rintro ⟨S, -, hS, hcard⟩ rw [Finset.card_eq_zero] at hcard @@ -131,7 +131,7 @@ example : VertexCover.toDominatingSet (path, 0) ∉ DominatingSet := by -- `junk` has no vertex cover of `2` distinct vertices, and its target `0` is unsatisfiable, -- although the graph has a dominating set of at most `2` vertices. -example : VertexCover.toDominatingSet (junk, 2) ∉ DominatingSet := by +example : VertexCover.toDominatingSet ⟨junk, 2⟩ ∉ DominatingSet := by rw [VertexCover.toDominatingSet_mem_iff] intro h have := (mem_VertexCover_iff_exists_card_le.1 h).1 diff --git a/CslibFrontierTests/FeedbackArcSet.lean b/CslibFrontierTests/FeedbackArcSet.lean index 31cdc19d9a..4af4344697 100644 --- a/CslibFrontierTests/FeedbackArcSet.lean +++ b/CslibFrontierTests/FeedbackArcSet.lean @@ -33,7 +33,7 @@ example : FeedbackArcSet ∈ NP encFeedbackArcSet ∧ -- Karp's chain: `VertexCover` reduces to `FeedbackArcSet` through `FeedbackVertexSet`. example : - PolyTimeReducible (encPair encGraph encNat) encFeedbackArcSet VertexCover FeedbackArcSet := + PolyTimeReducible encVertexCoverInstance encFeedbackArcSet VertexCover FeedbackArcSet := polyTimeReducible_VertexCover_FeedbackVertexSet.trans polyTimeReducible_FeedbackVertexSet_FeedbackArcSet @@ -49,6 +49,7 @@ example : ComputableInPolyTime (fun p : Graph × ℕ => (⟨p.1, p.2⟩ : Feedba (encPair encGraph encNat) encFeedbackArcSet := by fun_prop + /-! ### Feedback arc sets -/ -- Removing the arc `2 → 0` breaks the triangle, but some arc must be removed. diff --git a/CslibFrontierTests/FeedbackVertexSet.lean b/CslibFrontierTests/FeedbackVertexSet.lean index aa22edb906..595b16a07f 100644 --- a/CslibFrontierTests/FeedbackVertexSet.lean +++ b/CslibFrontierTests/FeedbackVertexSet.lean @@ -31,7 +31,7 @@ private def dag : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2), (0, 2)]⟩ example : FeedbackVertexSet ∈ NP encFeedbackVertexSet ∧ NPHard encFeedbackVertexSet FeedbackVertexSet ∧ - PolyTimeReducible (encPair encGraph encNat) encFeedbackVertexSet VertexCover + PolyTimeReducible encVertexCoverInstance encFeedbackVertexSet VertexCover FeedbackVertexSet := ⟨FeedbackVertexSet_npComplete.1, FeedbackVertexSet_npComplete.2, polyTimeReducible_VertexCover_FeedbackVertexSet⟩ @@ -48,6 +48,7 @@ example : ComputableInPolyTime (fun p : Graph × ℕ => (⟨p.1, p.2⟩ : Feedba (encPair encGraph encNat) encFeedbackVertexSet := by fun_prop + /-! ### Directed cycles -/ -- The triangle is a directed cycle, read cyclically from any vertex. @@ -137,7 +138,7 @@ example : (⟨(⟨[0], [(0, 1), (1, 0)]⟩ : Graph), 0⟩ : FeedbackVertexSetIns /-! ### Karp's reduction from `VertexCover` -/ -- The edge `0 - 1` becomes the arcs `0 → 1` and `1 → 0`. -example : VertexCover.toFeedbackVertexSet (((⟨[0, 1], [(0, 1)]⟩ : Graph), 1) : Graph × ℕ) = +example : VertexCover.toFeedbackVertexSet (⟨⟨[0, 1], [(0, 1)]⟩, 1⟩ : VertexCoverInstance) = ⟨(⟨[0, 1], [(0, 1), (1, 0)]⟩ : Graph), 1⟩ := by decide @@ -146,13 +147,13 @@ example : VertexCover.bidirect (⟨[0], [(0, 0)]⟩ : Graph) = (⟨[0], []⟩ : decide -- A target above the number of distinct vertices goes to the self-loop with target `0`. -example : VertexCover.toFeedbackVertexSet (((⟨[0, 1], [(0, 1)]⟩ : Graph), 3) : Graph × ℕ) = +example : VertexCover.toFeedbackVertexSet (⟨⟨[0, 1], [(0, 1)]⟩, 3⟩ : VertexCoverInstance) = ⟨(⟨[0], [(0, 0)]⟩ : Graph), 0⟩ := by decide -example : (((⟨[0, 1], [(0, 1)]⟩ : Graph), 1) : Graph × ℕ) ∈ VertexCover ↔ +example : (⟨⟨[0, 1], [(0, 1)]⟩, 1⟩ : VertexCoverInstance) ∈ VertexCover ↔ VertexCover.toFeedbackVertexSet - (((⟨[0, 1], [(0, 1)]⟩ : Graph), 1) : Graph × ℕ) ∈ FeedbackVertexSet := + (⟨⟨[0, 1], [(0, 1)]⟩, 1⟩ : VertexCoverInstance) ∈ FeedbackVertexSet := (VertexCover.toFeedbackVertexSet_mem_iff _).symm end CslibFrontierTests.FeedbackVertexSet diff --git a/CslibFrontierTests/FineGrained/GraphProblems.lean b/CslibFrontierTests/FineGrained/GraphProblems.lean index 71bf32a840..58bb19a221 100644 --- a/CslibFrontierTests/FineGrained/GraphProblems.lean +++ b/CslibFrontierTests/FineGrained/GraphProblems.lean @@ -36,20 +36,21 @@ private def ψ : CNF ℕ := (List.range 8).map fun i => [(0, decide (i % 2 = 0)) /-! ### The reduction with variable edges -/ /-- Three variables and three clauses: the target is `3 + 3`. -/ -example : (SAT.toSparseIndependentSet φ₁).2 = 6 := by decide +example : (SAT.toSparseIndependentSet φ₁).m = 6 := by decide /-- `2 n + L = 6 + 5` vertices and `n + Σ |c| ^ 2 + L = 3 + 9 + 5` edges. -/ -example : (SAT.toSparseIndependentSet φ₁).1.1.length = 11 ∧ - (SAT.toSparseIndependentSet φ₁).1.2.length = 17 := by decide +example : (SAT.toSparseIndependentSet φ₁).graph.vertices.length = 11 ∧ + (SAT.toSparseIndependentSet φ₁).graph.edges.length = 17 := by decide -example : (SAT.toSparseIndependentSet φ₁).1.1.length + (SAT.toSparseIndependentSet φ₁).1.2.length ≤ +example : (SAT.toSparseIndependentSet φ₁).graph.vertices.length + + (SAT.toSparseIndependentSet φ₁).graph.edges.length ≤ (2 * 2 + 5 * 2) * φ₁.length := SAT.length_toSparseIndependentSet_le (by decide) /-- Karp's reduction joins every positive occurrence of `x₀` in `ψ` to every negative one, in both directions; the reduction with variable edges has `1 + 8 + 8` edges. -/ -example : (SAT.toIndependentSet ψ).1.2.length = 32 ∧ - (SAT.toSparseIndependentSet ψ).1.2.length = 17 := by decide +example : (SAT.toIndependentSet ψ).graph.edges.length = 32 ∧ + (SAT.toSparseIndependentSet ψ).graph.edges.length = 17 := by decide example : SAT.toSparseIndependentSet φ₁ ∈ IndependentSet := (SAT.toSparseIndependentSet_mem_iff φ₁).2 ⟨fun _ => true, by decide⟩ @@ -100,8 +101,8 @@ example : SAT.toSparseHamCycle φ₂ ∉ HamCycle := fun h => by /-- The formula with no clauses gives the graph of a single top, which a path visits without arcs; the empty clause gives a clause node that no detour reaches. -/ -example : SAT.toSparseDirHamPath [] = ⟨⟨[Diamond.topNode 0], []⟩, Diamond.topNode 0, - Diamond.topNode 0⟩ := by decide +example : SAT.toSparseDirHamPath [] = + ⟨⟨[Diamond.topNode 0], []⟩, Diamond.topNode 0, Diamond.topNode 0⟩ := by decide example : SAT.toSparseDirHamCycle [[]] ∉ DirHamCycle := fun h => by obtain ⟨σ, hσ⟩ := (SAT.toSparseDirHamCycle_mem_iff [[]]).1 h @@ -129,10 +130,10 @@ example : DecidableInSubexpTime (∅ : Set Graph) encGraph fun G => /-- Under `ETH`, clique is not decidable in time `2 ^ o(n)` for `n` distinct vertices either, and 3SAT is not decidable in subexponential time in the number of clauses. -/ example (h : ETH) : - ¬DecidableInSubexpTime Clique (encPair encGraph encNat) (fun x => x.1.vertices.toFinset.card) ∧ + ¬DecidableInSubexpTime Clique encCliqueInstance (fun x => x.graph.vertices.toFinset.card) ∧ ¬DecidableInSubexpTime ThreeSAT encCNF List.length := ⟨fun h' => h.not_decidableInSubexpTime_Clique (h'.mono_measure (c := 1) fun x => by - have := x.1.vertices.toFinset_card_le + have := x.graph.vertices.toFinset_card_le omega), h.not_decidableInSubexpTime_of_reduction (f := id) ComputableInPolyTime.id' (fun φ hφ => ⟨fun h => h.2, fun h' => ⟨hφ, h'⟩⟩) (k := 1) fun φ _ => by simp⟩ diff --git a/CslibFrontierTests/KarpReductions.lean b/CslibFrontierTests/KarpReductions.lean index 9942e805a0..90fa330daf 100644 --- a/CslibFrontierTests/KarpReductions.lean +++ b/CslibFrontierTests/KarpReductions.lean @@ -27,20 +27,20 @@ example : ThreeSAT ∈ NP encCNF ∧ PolyTimeReducible encCNF encCNF SAT ThreeSAT := ⟨ThreeSAT_npComplete.1, ThreeSAT_npComplete.2 encCNF SAT SAT.mem_NP⟩ -example : IndependentSet ∈ NP (encPair encGraph encNat) ∧ - PolyTimeReducible encCNF (encPair encGraph encNat) SAT IndependentSet := +example : IndependentSet ∈ NP encIndependentSetInstance ∧ + PolyTimeReducible encCNF encIndependentSetInstance SAT IndependentSet := ⟨IndependentSet_npComplete.1, IndependentSet_npComplete.2 encCNF SAT SAT.mem_NP⟩ -example : Clique ∈ NP (encPair encGraph encNat) ∧ - PolyTimeReducible encCNF (encPair encGraph encNat) SAT Clique := +example : Clique ∈ NP encCliqueInstance ∧ + PolyTimeReducible encCNF encCliqueInstance SAT Clique := ⟨Clique_npComplete.1, Clique_npComplete.2 encCNF SAT SAT.mem_NP⟩ -example : PolyTimeReducible encCNF (encPair encGraph encNat) SAT Clique := +example : PolyTimeReducible encCNF encCliqueInstance SAT Clique := polyTimeReducible_SAT_IndependentSet.trans polyTimeReducible_IndependentSet_Clique -- Hardness chains along reductions with dot notation: the hardness statement is not specialized -- to a type of instances before `.trans` is resolved. -example : NPHard (encPair encGraph encNat) Clique := +example : NPHard encCliqueInstance Clique := (CookLevin.SAT_npHard.trans polyTimeReducible_SAT_IndependentSet).trans polyTimeReducible_IndependentSet_Clique @@ -51,22 +51,22 @@ example : ComputableInPolyTime toThreeSAT encCNF encCNF := by fun_prop example : ComputableInPolyTime ThreeSAT.verifier (encPair encCNF encBits) encBool := by fun_prop -example : ComputableInPolyTime SAT.toIndependentSet encCNF (encPair encGraph encNat) := by +example : ComputableInPolyTime SAT.toIndependentSet encCNF encIndependentSetInstance := by fun_prop example : ComputableInPolyTime Graph.compl encGraph encGraph := by fun_prop example : ComputableInPolyTime IndependentSet.verifier - (encPair (encPair encGraph encNat) encBits) encBool := by + (encPair encIndependentSetInstance encBits) encBool := by fun_prop example : ComputableInPolyTime Clique.verifier - (encPair (encPair encGraph encNat) encBits) encBool := by + (encPair encCliqueInstance encBits) encBool := by fun_prop example : ComputableInPolyTime - (fun φ : CNF ℕ => let x := SAT.toIndependentSet φ; (x.1.compl, x.2)) - encCNF (encPair encGraph encNat) := by + (fun φ : CNF ℕ => let x := SAT.toIndependentSet φ; independentSetToClique x) + encCNF encCliqueInstance := by fun_prop /-! ### `ThreeSAT` concrete and boundary tests -/ @@ -92,16 +92,16 @@ example : ThreeSAT.verifier (toThreeSAT [[(2 ^ 80, true), (2 ^ 80 + 1, false)]], /-! ### `IndependentSet` and `Clique` concrete and boundary tests -/ -- Empty graph: m = 0 is accepted, m > 0 is rejected. -example : IndependentSet.verifier (((⟨[], []⟩, 0) : Graph × ℕ), []) = true := rfl -example : IndependentSet.verifier (((⟨[], []⟩, 1) : Graph × ℕ), [true]) = false := rfl -example : Clique.verifier (((⟨[], []⟩, 0) : Graph × ℕ), []) = true := rfl -example : Clique.verifier (((⟨[], []⟩, 1) : Graph × ℕ), [true]) = false := rfl +example : IndependentSet.verifier (⟨⟨[], []⟩, 0⟩, []) = true := rfl +example : IndependentSet.verifier (⟨⟨[], []⟩, 1⟩, [true]) = false := rfl +example : Clique.verifier (⟨⟨[], []⟩, 0⟩, []) = true := rfl +example : Clique.verifier (⟨⟨[], []⟩, 1⟩, [true]) = false := rfl -- Oversized binary target `m = 2 ^ 100` is rejected in binary without unary expansion. example : IndependentSet.verifier - (((⟨[0, 1], []⟩, 2 ^ 100) : Graph × ℕ), [true, true]) = false := by decide + (⟨⟨[0, 1], []⟩, 2 ^ 100⟩, [true, true]) = false := by decide example : Clique.verifier - (((⟨[0, 1], [(0, 1)]⟩, 2 ^ 100) : Graph × ℕ), [true, true]) = false := by decide + (⟨⟨[0, 1], [(0, 1)]⟩, 2 ^ 100⟩, [true, true]) = false := by decide -- Duplicate vertices in `G.vertices` are deduplicated at their first occurrence by -- `selectDistinct`, so selecting two copies of vertex `0` cannot forge an independent set or @@ -109,42 +109,42 @@ example : Clique.verifier example : selectDistinct [0, 0, 1] [true, true, false] = [0] := rfl example : selectDistinct [0, 0, 1] [true, false, true] = [0, 1] := rfl example : IndependentSet.verifier - (((⟨[0, 0, 1], []⟩, 2) : Graph × ℕ), [true, true, false]) = false := rfl + (⟨⟨[0, 0, 1], []⟩, 2⟩, [true, true, false]) = false := rfl example : IndependentSet.verifier - (((⟨[0, 0, 1], []⟩, 2) : Graph × ℕ), [true, false, true]) = true := rfl + (⟨⟨[0, 0, 1], []⟩, 2⟩, [true, false, true]) = true := rfl -- Edges with endpoints outside `G.vertices` and self-loops in `G.edges` do not create -- adjacency on `G.vertices`. example : IndependentSet.verifier - (((⟨[10, 20], [(10, 99), (99, 20), (10, 10)]⟩, 2) : Graph × ℕ), [true, true]) = true := rfl + (⟨⟨[10, 20], [(10, 99), (99, 20), (10, 10)]⟩, 2⟩, [true, true]) = true := rfl -- Sparse binary vertex labels in `IndependentSet`, `Graph.compl`, and `Clique`. private def sparseGraph : Graph := ⟨[2 ^ 80, 2 ^ 80 + 1, 2 ^ 80 + 2], [(2 ^ 80, 2 ^ 80 + 1)]⟩ -example : IndependentSet.verifier ((sparseGraph, 2), [true, false, true]) = true := by decide -example : IndependentSet.verifier ((sparseGraph, 2), [true, true, false]) = false := by decide -example : Clique.verifier ((sparseGraph.compl, 2), [true, false, true]) = true := by decide -example : Clique.verifier ((sparseGraph.compl, 2), [true, true, false]) = false := by decide +example : IndependentSet.verifier (⟨sparseGraph, 2⟩, [true, false, true]) = true := by decide +example : IndependentSet.verifier (⟨sparseGraph, 2⟩, [true, true, false]) = false := by decide +example : Clique.verifier (⟨sparseGraph.compl, 2⟩, [true, false, true]) = true := by decide +example : Clique.verifier (⟨sparseGraph.compl, 2⟩, [true, true, false]) = false := by decide -- Concrete `SAT.toIndependentSet` on satisfiable and unsatisfiable formulas. private def satFormula : CNF ℕ := [[(0, true), (1, false)], [(0, false), (1, true)]] private def unsatFormula : CNF ℕ := [[(0, true)], [(0, false)]] example : SAT.toIndependentSet satFormula = - (⟨[0, 1, 2, 3], - [(0, 1), (0, 2), (1, 0), (1, 3), (2, 0), (2, 3), (3, 1), (3, 2)]⟩, 2) := rfl + ⟨⟨[0, 1, 2, 3], + [(0, 1), (0, 2), (1, 0), (1, 3), (2, 0), (2, 3), (3, 1), (3, 2)]⟩, 2⟩ := rfl -- Choosing the first literal `(0, true)` in clause 0 and the second literal `(1, true)` in clause 1 -- yields a valid independent set of size 2 (and a 2-clique in the complement). example : IndependentSet.verifier (SAT.toIndependentSet satFormula, [true, false, false, true]) = true := rfl example : Clique.verifier - (((SAT.toIndependentSet satFormula).1.compl, (SAT.toIndependentSet satFormula).2), + (independentSetToClique (SAT.toIndependentSet satFormula), [true, false, false, true]) = true := rfl -- Conflicting occurrences `[[(0, true)], [(0, false)]]` produce an edge between the two vertices. -example : SAT.toIndependentSet unsatFormula = (⟨[0, 2], [(0, 2), (2, 0)]⟩, 2) := rfl +example : SAT.toIndependentSet unsatFormula = ⟨⟨[0, 2], [(0, 2), (2, 0)]⟩, 2⟩ := rfl example : IndependentSet.verifier (SAT.toIndependentSet unsatFormula, [true, true]) = false := rfl end CslibFrontierTests.KarpReductions diff --git a/CslibFrontierTests/NPCompleteGraphs.lean b/CslibFrontierTests/NPCompleteGraphs.lean index d061c69ba4..d621c34c85 100644 --- a/CslibFrontierTests/NPCompleteGraphs.lean +++ b/CslibFrontierTests/NPCompleteGraphs.lean @@ -27,8 +27,8 @@ open Cslib.Complexity Cslib.Logic.PL Turing.MultiTapeTM /-! ### Headline complexity class results -/ example : - (VertexCover ∈ NP (encPair encGraph encNat) ∧ - PolyTimeReducible encCNF (encPair encGraph encNat) SAT VertexCover) ∧ + (VertexCover ∈ NP encVertexCoverInstance ∧ + PolyTimeReducible encCNF encVertexCoverInstance SAT VertexCover) ∧ (KColoring 2 ∈ NP encGraph ∧ KColoring 5 ∈ NP encGraph) ∧ (ThreeColoring ∈ NP encGraph ∧ PolyTimeReducible encCNF encGraph SAT ThreeColoring) ∧ @@ -54,7 +54,7 @@ example : example : PolyTimeReducible encCNF encGraph SAT ThreeColoring ∧ - PolyTimeReducible encCNF (encPair encGraph encNat) SAT VertexCover ∧ + PolyTimeReducible encCNF encVertexCoverInstance SAT VertexCover ∧ PolyTimeReducible encCNF encPathInstance SAT DirHamPath := ⟨ThreeSAT_npComplete.2 encCNF SAT SAT.mem_NP |>.trans polyTimeReducible_ThreeSAT_ThreeColoring, polyTimeReducible_SAT_IndependentSet.trans polyTimeReducible_IndependentSet_VertexCover, @@ -63,10 +63,10 @@ example : /-! ### Compositional `fun_prop` synthesis -/ example : - ComputableInPolyTime IndependentSet.toVertexCover (encPair encGraph encNat) - (encPair encGraph encNat) ∧ + ComputableInPolyTime IndependentSet.toVertexCover encIndependentSetInstance + encVertexCoverInstance ∧ ComputableInPolyTime VertexCover.verifier - (encPair (encPair encGraph encNat) encBits) encBool ∧ + (encPair encVertexCoverInstance encBits) encBool ∧ ComputableInPolyTime (KColoring.verifier 3) (encPair encGraph encBits) encBool ∧ ComputableInPolyTime SAT.toThreeColoring encCNF encGraph ∧ ComputableInPolyTime (SAT.toKColoring 2) encCNF encGraph ∧ @@ -74,7 +74,7 @@ example : ComputableInPolyTime SAT.toDirHamCycle encCNF encGraph ∧ ComputableInPolyTime (fun φ => IndependentSet.toVertexCover (SAT.toIndependentSet φ)) - encCNF (encPair encGraph encNat) := by + encCNF encVertexCoverInstance := by refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ <;> fun_prop /-! ### `VertexCover` concrete and boundary tests -/ @@ -83,20 +83,20 @@ private def triangle : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2), (2, 0)]⟩ -- A triangle has vertex covers with two vertices but none with one. example : - VertexCover.verifier ((triangle, 2), [true, true, false]) = true ∧ - VertexCover.verifier ((triangle, 2), [false, true, true]) = true ∧ - VertexCover.verifier ((triangle, 1), [true, false, false]) = false ∧ - VertexCover.verifier ((triangle, 2), [true, false, false]) = false := + VertexCover.verifier (⟨triangle, 2⟩, [true, true, false]) = true ∧ + VertexCover.verifier (⟨triangle, 2⟩, [false, true, true]) = true ∧ + VertexCover.verifier (⟨triangle, 1⟩, [true, false, false]) = false ∧ + VertexCover.verifier (⟨triangle, 2⟩, [true, false, false]) = false := ⟨rfl, rfl, rfl, rfl⟩ -example : (triangle, 2) ∈ VertexCover := by +example : (⟨triangle, 2⟩ : VertexCoverInstance) ∈ VertexCover := by rw [mem_VertexCover_iff] refine ⟨{0, 1}, by decide, fun u hu v hv huv => ?_, rfl⟩ simp only [triangle, List.mem_cons, List.not_mem_nil, or_false] at hu hv rcases hu with rfl | rfl | rfl <;> rcases hv with rfl | rfl | rfl <;> first | exact (huv.ne rfl).elim | simp -example : (triangle, 1) ∉ VertexCover := by +example : (⟨triangle, 1⟩ : VertexCoverInstance) ∉ VertexCover := by rw [mem_VertexCover_iff] rintro ⟨S, -, hS, hcard⟩ obtain ⟨v, rfl⟩ := Finset.card_eq_one.1 hcard @@ -110,7 +110,8 @@ example : (triangle, 1) ∉ VertexCover := by omega -- Covers have exactly `k` vertices: the triangle has one of all three vertices, but none of four. -example : (triangle, 3) ∈ VertexCover ∧ (triangle, 4) ∉ VertexCover := by +example : (⟨triangle, 3⟩ : VertexCoverInstance) ∈ VertexCover ∧ + (⟨triangle, 4⟩ : VertexCoverInstance) ∉ VertexCover := by refine ⟨?_, fun h => absurd (mem_VertexCover_iff_exists_card_le.1 h).1 (by decide)⟩ rw [mem_VertexCover_iff] refine ⟨{0, 1, 2}, by decide, fun u hu _ _ _ => Or.inl ?_, rfl⟩ @@ -118,10 +119,10 @@ example : (triangle, 3) ∈ VertexCover ∧ (triangle, 4) ∉ VertexCover := by -- Empty graph: `k = 0` is accepted and `k = 1` is rejected. example : - VertexCover.verifier (((⟨[], []⟩, 0) : Graph × ℕ), []) = true ∧ - VertexCover.verifier (((⟨[], []⟩, 1) : Graph × ℕ), [true]) = false ∧ - ((⟨[], []⟩ : Graph), 0) ∈ VertexCover ∧ - ((⟨[], []⟩ : Graph), 1) ∉ VertexCover := + VertexCover.verifier (⟨⟨[], []⟩, 0⟩, []) = true ∧ + VertexCover.verifier (⟨⟨[], []⟩, 1⟩, [true]) = false ∧ + (⟨⟨[], []⟩, 0⟩ : VertexCoverInstance) ∈ VertexCover ∧ + (⟨⟨[], []⟩, 1⟩ : VertexCoverInstance) ∉ VertexCover := ⟨rfl, rfl, mem_VertexCover_iff.2 ⟨∅, by simp, by simp, rfl⟩, fun h => absurd (mem_VertexCover_iff_exists_card_le.1 h).1 (by decide)⟩ @@ -129,17 +130,15 @@ example : -- oversized binary targets are rejected without unary expansion; and `toVertexCover` complements -- the target within distinct declared vertices. example : - VertexCover.verifier (((⟨[0, 0, 1], [(0, 1)]⟩, 2) : Graph × ℕ), [true, true, false]) = false ∧ - VertexCover.verifier (((⟨[0, 0, 1], [(0, 1)]⟩, 1) : Graph × ℕ), [true, false, false]) = true ∧ + VertexCover.verifier (⟨⟨[0, 0, 1], [(0, 1)]⟩, 2⟩, [true, true, false]) = false ∧ + VertexCover.verifier (⟨⟨[0, 0, 1], [(0, 1)]⟩, 1⟩, [true, false, false]) = true ∧ VertexCover.verifier - (((⟨[10, 20], [(10, 99), (99, 20), (10, 10)]⟩, 0) : Graph × ℕ), [false, false]) = true ∧ - VertexCover.verifier (((⟨[0, 1], [(0, 1)]⟩, 2 ^ 100) : Graph × ℕ), [true, true]) = false ∧ - IndependentSet.toVertexCover (triangle, 1) = (triangle, 2) ∧ - IndependentSet.toVertexCover (((⟨[0, 0, 1], []⟩, 2) : Graph × ℕ)) = - (((⟨[0, 0, 1], []⟩, 0) : Graph × ℕ)) ∧ - IndependentSet.toVertexCover (triangle, 4) = (triangle, 4) ∧ - IndependentSet.toVertexCover (((⟨[], []⟩, 0) : Graph × ℕ)) = - (((⟨[], []⟩, 0) : Graph × ℕ)) := + (⟨⟨[10, 20], [(10, 99), (99, 20), (10, 10)]⟩, 0⟩, [false, false]) = true ∧ + VertexCover.verifier (⟨⟨[0, 1], [(0, 1)]⟩, 2 ^ 100⟩, [true, true]) = false ∧ + IndependentSet.toVertexCover ⟨triangle, 1⟩ = ⟨triangle, 2⟩ ∧ + IndependentSet.toVertexCover ⟨⟨[0, 0, 1], []⟩, 2⟩ = ⟨⟨[0, 0, 1], []⟩, 0⟩ ∧ + IndependentSet.toVertexCover ⟨triangle, 4⟩ = ⟨triangle, 4⟩ ∧ + IndependentSet.toVertexCover ⟨⟨[], []⟩, 0⟩ = ⟨⟨[], []⟩, 0⟩ := ⟨rfl, rfl, rfl, by decide, by decide, by decide, by decide, by decide⟩ /-! ### `KColoring` and `ThreeColoring` concrete and boundary tests -/ diff --git a/CslibFrontierTests/NPCompleteness.lean b/CslibFrontierTests/NPCompleteness.lean index d3169e8b68..30cd7094c5 100644 --- a/CslibFrontierTests/NPCompleteness.lean +++ b/CslibFrontierTests/NPCompleteness.lean @@ -31,46 +31,45 @@ example : {w : List Bool | w.headD false = true} ∈ NP encBits := /-- For NP, the verifier also needs a proved certificate bound: here exactly the encoded instance length, represented by the polynomial `X`. -/ -theorem clique_mem_NP : Clique ∈ NP (encPair encGraph encNat) := +theorem clique_mem_NP : Clique ∈ NP encCliqueInstance := ⟨Polynomial.X, Clique.verifier, by fun_prop, Clique.isVerifier⟩ /-- Complement the graph on its declared vertices, leaving the requested size unchanged. -/ -def complementInstance (x : Graph × ℕ) : Graph × ℕ := (x.1.compl, x.2) +def complementInstance (x : IndependentSetInstance) : CliqueInstance := ⟨x.graph.compl, x.m⟩ /-- Unfold local definitions so certificate automation sees the registered operations. -/ @[fun_prop] theorem complementInstance_polyTime : - ComputableInPolyTime complementInstance - (encPair encGraph encNat) (encPair encGraph encNat) := by + ComputableInPolyTime complementInstance encIndependentSetInstance encCliqueInstance := by unfold complementInstance polytime /-- A reduction has a map, its polynomial-time certificate, and a membership equivalence. The equivalence must point from the source problem to the image in the target problem. -/ theorem independentSet_to_clique : - PolyTimeReducible (encPair encGraph encNat) (encPair encGraph encNat) IndependentSet Clique := + PolyTimeReducible encIndependentSetInstance encCliqueInstance IndependentSet Clique := ⟨complementInstance, by fun_prop, fun ⟨G, k⟩ => (Graph.compl_mem_Clique_iff G k).symm⟩ /-- Combine NP membership with hardness transferred from the known hard source problem. -/ -theorem clique_npComplete : NPComplete (encPair encGraph encNat) Clique := +theorem clique_npComplete : NPComplete encCliqueInstance Clique := ⟨clique_mem_NP, IndependentSet_npHard.trans independentSet_to_clique⟩ -- The reduction keeps a zero target and complements only the declared vertices. -example : complementInstance ((⟨[0, 1], []⟩ : Graph), 0) = - ((⟨[0, 1], [(0, 1), (1, 0)]⟩ : Graph), 0) := by decide +example : complementInstance ⟨(⟨[0, 1], []⟩ : Graph), 0⟩ = + ⟨(⟨[0, 1], [(0, 1), (1, 0)]⟩ : Graph), 0⟩ := by decide -- The empty graph has a zero-size clique, but no clique of size one. -example : ((⟨[], []⟩ : Graph), 0) ∈ Clique := +example : (⟨(⟨[], []⟩ : Graph), 0⟩ : CliqueInstance) ∈ Clique := ⟨∅, by simp, rfl⟩ -example : ((⟨[], []⟩ : Graph), 1) ∉ Clique := by +example : (⟨(⟨[], []⟩ : Graph), 1⟩ : CliqueInstance) ∉ Clique := by intro h have := Graph.le_card_of_mem_Clique h simp at this /-- Plain `fun_prop` composes the plain `Clique` verifier certificate with a plain map. -/ -example {γ : Type} {enc : γ ↪ List Bool} {f : γ → (Graph × ℕ) × List Bool} - (hf : ComputableInPolyTime f enc (encPair (encPair encGraph encNat) encBits)) : +example {γ : Type} {enc : γ ↪ List Bool} {f : γ → CliqueInstance × List Bool} + (hf : ComputableInPolyTime f enc (encPair encCliqueInstance encBits)) : ComputableInPolyTime (fun c => Clique.verifier (f c)) enc encBool := by fun_prop diff --git a/CslibFrontierTests/NPWitness.lean b/CslibFrontierTests/NPWitness.lean index dc46e55445..d50542ccf5 100644 --- a/CslibFrontierTests/NPWitness.lean +++ b/CslibFrontierTests/NPWitness.lean @@ -165,12 +165,12 @@ def path3 : Graph := ⟨[1, 2, 3], [(1, 2), (2, 3)]⟩ -- The middle vertex covers both edges; an end vertex or wrong size is rejected; a repeated -- declared vertex is selected only at its first occurrence. example : - VertexCover.verifier ((path3, 1), [false, true, false]) = true ∧ - VertexCover.verifier ((path3, 1), [true, false, false]) = false ∧ - VertexCover.verifier ((path3, 1), [true, true, false]) = false ∧ - VertexCover.verifier ((((⟨[2, 2, 1, 3], [(1, 2), (2, 3)]⟩ : Graph), 1)), + VertexCover.verifier (⟨path3, 1⟩, [false, true, false]) = true ∧ + VertexCover.verifier (⟨path3, 1⟩, [true, false, false]) = false ∧ + VertexCover.verifier (⟨path3, 1⟩, [true, true, false]) = false ∧ + VertexCover.verifier (⟨(⟨[2, 2, 1, 3], [(1, 2), (2, 3)]⟩ : Graph), 1⟩, [true, true, false, false]) = true ∧ - VertexCover.verifier ((((⟨[2, 2, 1, 3], [(1, 2), (2, 3)]⟩ : Graph), 1)), + VertexCover.verifier (⟨(⟨[2, 2, 1, 3], [(1, 2), (2, 3)]⟩ : Graph), 1⟩, [false, true, false, false]) = false := ⟨rfl, rfl, rfl, rfl, rfl⟩ @@ -187,7 +187,7 @@ example : ((List.range 3).countP fun c => example : SAT ∈ NP encCNF := polyTimeReducible_SAT_ThreeSAT.mem_NP ThreeSAT.mem_NP -example : IndependentSet ∈ NP (encPair encGraph encNat) := +example : IndependentSet ∈ NP encIndependentSetInstance := polyTimeReducible_IndependentSet_Clique.mem_NP Clique.mem_NP end CslibFrontierTests.NPWitness diff --git a/CslibFrontierTests/OrderedNPCompleteness.lean b/CslibFrontierTests/OrderedNPCompleteness.lean index 82018762aa..f34eff2711 100644 --- a/CslibFrontierTests/OrderedNPCompleteness.lean +++ b/CslibFrontierTests/OrderedNPCompleteness.lean @@ -357,19 +357,19 @@ theorem all_polyTime_problem_bridges : Complexity.ThreeColoring (Ordered.ThreeColorable.encodedLanguage graphVocabulary) ∧ PolyTimeReducible encStructure encGraph (Ordered.ThreeColorable.encodedLanguage graphVocabulary) Complexity.ThreeColoring) ∧ - (PolyTimeReducible (encPair encGraph encNat) encStructure + (PolyTimeReducible encIndependentSetInstance encStructure Complexity.IndependentSet (Ordered.IndependentSet.encodedLanguage sizedGraphVocabulary) ∧ - PolyTimeReducible encStructure (encPair encGraph encNat) + PolyTimeReducible encStructure encIndependentSetInstance (Ordered.IndependentSet.encodedLanguage sizedGraphVocabulary) Complexity.IndependentSet) ∧ - (PolyTimeReducible (encPair encGraph encNat) encStructure + (PolyTimeReducible encCliqueInstance encStructure Complexity.Clique (Ordered.Clique.encodedLanguage sizedGraphVocabulary) ∧ - PolyTimeReducible encStructure (encPair encGraph encNat) + PolyTimeReducible encStructure encCliqueInstance (Ordered.Clique.encodedLanguage sizedGraphVocabulary) Complexity.Clique) ∧ - (PolyTimeReducible (encPair encGraph encNat) encStructure + (PolyTimeReducible encVertexCoverInstance encStructure Complexity.VertexCover (Ordered.VertexCover.encodedLanguage sizedGraphVocabulary) ∧ - PolyTimeReducible encStructure (encPair encGraph encNat) + PolyTimeReducible encStructure encVertexCoverInstance (Ordered.VertexCover.encodedLanguage sizedGraphVocabulary) Complexity.VertexCover) := ⟨⟨threeSAT_polyTimeReducible_orderedThreeSAT, orderedThreeSAT_polyTimeReducible_threeSAT⟩, ⟨threeColoring_polyTimeReducible_orderedSimpleThreeColorable, diff --git a/CslibFrontierTests/Parameterized.lean b/CslibFrontierTests/Parameterized.lean index 4e96c31b53..45ab6aec64 100644 --- a/CslibFrontierTests/Parameterized.lean +++ b/CslibFrontierTests/Parameterized.lean @@ -159,15 +159,15 @@ private def triangle : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2), (0, 2)]⟩ /-- Bounded search tree evaluations and membership witnesses on the triangle graph, plus `XP` closure for `VertexCoverᶜ`. -/ example : - VertexCover.search (triangle, 1) = false ∧ - VertexCover.search (triangle, 2) = true ∧ - VertexCover.search (triangle, 3) = true ∧ - VertexCover.search (triangle, 4) = false ∧ - (triangle, 2) ∈ VertexCover ∧ - (triangle, 1) ∉ VertexCover ∧ - VertexCoverᶜ ∈ XP (encPair encGraph encNat) Prod.snd := by + VertexCover.search ⟨triangle, 1⟩ = false ∧ + VertexCover.search ⟨triangle, 2⟩ = true ∧ + VertexCover.search ⟨triangle, 3⟩ = true ∧ + VertexCover.search ⟨triangle, 4⟩ = false ∧ + (⟨triangle, 2⟩ : VertexCoverInstance) ∈ VertexCover ∧ + (⟨triangle, 1⟩ : VertexCoverInstance) ∉ VertexCover ∧ + VertexCoverᶜ ∈ XP encVertexCoverInstance VertexCoverInstance.k := by refine ⟨by decide, by decide, by decide, by decide, - (VertexCover.search_eq_true_iff (triangle, 2)).mp (by decide), ?_, + (VertexCover.search_eq_true_iff ⟨triangle, 2⟩).mp (by decide), ?_, FPT_subset_XP (compl_mem_FPT VertexCover_mem_FPT)⟩ rw [← VertexCover.search_eq_true_iff] decide diff --git a/CslibFrontierTests/VertexCoverApproximation.lean b/CslibFrontierTests/VertexCoverApproximation.lean index c6427518d0..153244df0a 100644 --- a/CslibFrontierTests/VertexCoverApproximation.lean +++ b/CslibFrontierTests/VertexCoverApproximation.lean @@ -46,7 +46,7 @@ example : greedyMatching path4.edges = [(0, 1), (2, 3)] := by decide +kernel example : approxCover path4 = [0, 1, 2, 3] := by decide +kernel /-- `{1, 2}` covers the path. -/ -theorem path4_mem : (path4, 2) ∈ VertexCover := by +theorem path4_mem : (⟨path4, 2⟩ : VertexCoverInstance) ∈ VertexCover := by refine mem_VertexCover_iff.2 ⟨{1, 2}, by decide +kernel, ?_, rfl⟩ intro u hu v hv huv simp only [Graph.toSimpleGraph, SimpleGraph.fromRel_adj, path4, List.mem_cons, @@ -75,7 +75,7 @@ def star3 : Graph := ⟨[0, 1, 2, 3], [(0, 1), (0, 2), (0, 3)]⟩ example : approxCover star3 = [0, 1] := by decide +kernel /-- The centre covers the star. -/ -theorem star3_mem : (star3, 1) ∈ VertexCover := by +theorem star3_mem : (⟨star3, 1⟩ : VertexCoverInstance) ∈ VertexCover := by refine mem_VertexCover_iff.2 ⟨{0}, by decide +kernel, ?_, rfl⟩ intro u hu v hv huv simp only [Graph.toSimpleGraph, SimpleGraph.fromRel_adj, star3, List.mem_cons, diff --git a/CslibFrontierTests/VertexCoverKernel.lean b/CslibFrontierTests/VertexCoverKernel.lean index 0d5c1461ec..c7f38a2d4f 100644 --- a/CslibFrontierTests/VertexCoverKernel.lean +++ b/CslibFrontierTests/VertexCoverKernel.lean @@ -26,18 +26,18 @@ private def sampleGraph : Graph := ⟨[10, 20, 30, 40, 50], [(10, 20), (10, 30), (10, 40), (20, 30)]⟩ example : - high (sampleGraph, 2) = [10] ∧ - low (sampleGraph, 2) = [20, 30, 40, 50] ∧ - active (sampleGraph, 2) = [20, 30] ∧ - param (sampleGraph, 2) = 1 ∧ - numVertices (sampleGraph, 2) = 3 ∧ - bussKernel (sampleGraph, 2) = (⟨[0, 1, 2], [(0, 1), (1, 0)]⟩, 1) ∧ - (sampleGraph, 2) ∈ VertexCover ∧ - bussKernel (sampleGraph, 2) ∈ VertexCover := + high ⟨sampleGraph, 2⟩ = [10] ∧ + low ⟨sampleGraph, 2⟩ = [20, 30, 40, 50] ∧ + active ⟨sampleGraph, 2⟩ = [20, 30] ∧ + param ⟨sampleGraph, 2⟩ = 1 ∧ + numVertices ⟨sampleGraph, 2⟩ = 3 ∧ + bussKernel ⟨sampleGraph, 2⟩ = ⟨⟨[0, 1, 2], [(0, 1), (1, 0)]⟩, 1⟩ ∧ + (⟨sampleGraph, 2⟩ : VertexCoverInstance) ∈ VertexCover ∧ + bussKernel ⟨sampleGraph, 2⟩ ∈ VertexCover := ⟨by decide, by decide, by decide, by decide, by decide, by decide, - (search_eq_true_iff (sampleGraph, 2)).mp (by decide), - (bussKernel_mem_iff (sampleGraph, 2)).mpr - ((search_eq_true_iff (sampleGraph, 2)).mp (by decide))⟩ + (search_eq_true_iff ⟨sampleGraph, 2⟩).mp (by decide), + (bussKernel_mem_iff ⟨sampleGraph, 2⟩).mpr + ((search_eq_true_iff ⟨sampleGraph, 2⟩).mp (by decide))⟩ /-- A star on `{10, 20, 30, 40}` with centre `10` and target `k = 2`: removing the high centre `10` leaves no active vertices (`active = []`) and reduced target `param = 1`, so one isolated low @@ -46,14 +46,14 @@ private def starGraph : Graph := ⟨[10, 20, 30, 40], [(10, 20), (10, 30), (10, 40)]⟩ example : - active (starGraph, 2) = [] ∧ - bussKernel (starGraph, 2) = (⟨[0], []⟩, 1) ∧ - (starGraph, 2) ∈ VertexCover ∧ - bussKernel (starGraph, 2) ∈ VertexCover := + active ⟨starGraph, 2⟩ = [] ∧ + bussKernel ⟨starGraph, 2⟩ = ⟨⟨[0], []⟩, 1⟩ ∧ + (⟨starGraph, 2⟩ : VertexCoverInstance) ∈ VertexCover ∧ + bussKernel ⟨starGraph, 2⟩ ∈ VertexCover := ⟨by decide, by decide, - (search_eq_true_iff (starGraph, 2)).mp (by decide), - (bussKernel_mem_iff (starGraph, 2)).mpr - ((search_eq_true_iff (starGraph, 2)).mp (by decide))⟩ + (search_eq_true_iff ⟨starGraph, 2⟩).mp (by decide), + (bussKernel_mem_iff ⟨starGraph, 2⟩).mpr + ((search_eq_true_iff ⟨starGraph, 2⟩).mp (by decide))⟩ /-- Rejection when the number of high vertices exceeds `k`: for `k = 1`, the star on four vertices has one high vertex, leaving `param = 0`, whereas two stars on `{0, 1, 2, 3, 4, 5}` with centres @@ -67,16 +67,16 @@ private def threeEdges : Graph := ⟨[0, 1, 2, 3, 4, 5], [(0, 1), (2, 3), (4, 5)]⟩ example : - high (twoStars, 1) = [0, 3] ∧ - accept (twoStars, 1) = false ∧ - bussKernel (twoStars, 1) = noInstance ∧ - (twoStars, 1) ∉ VertexCover ∧ - high (threeEdges, 1) = [] ∧ - (active (threeEdges, 1)).length = 6 ∧ - accept (threeEdges, 1) = false ∧ - bussKernel (threeEdges, 1) = noInstance ∧ - (threeEdges, 1) ∉ VertexCover ∧ - bussKernel (starGraph, 5) = noInstance := by + high ⟨twoStars, 1⟩ = [0, 3] ∧ + accept ⟨twoStars, 1⟩ = false ∧ + bussKernel ⟨twoStars, 1⟩ = noInstance ∧ + (⟨twoStars, 1⟩ : VertexCoverInstance) ∉ VertexCover ∧ + high ⟨threeEdges, 1⟩ = [] ∧ + (active ⟨threeEdges, 1⟩).length = 6 ∧ + accept ⟨threeEdges, 1⟩ = false ∧ + bussKernel ⟨threeEdges, 1⟩ = noInstance ∧ + (⟨threeEdges, 1⟩ : VertexCoverInstance) ∉ VertexCover ∧ + bussKernel ⟨starGraph, 5⟩ = noInstance := by refine ⟨by decide, by decide, by decide, ?_, by decide, by decide, by decide, by decide, ?_, by decide⟩ · rw [← bussKernel_mem_iff] @@ -87,8 +87,8 @@ example : /-! ### Plain automation over the kernel API -/ /-- Plain `fun_prop` composes the plain kernel certificates with plain hypotheses. -/ -example {γ : Type} {enc : γ ↪ List Bool} {x : γ → Graph × ℕ} {G : γ → Graph} {v : γ → ℕ} - (hx : ComputableInPolyTime x enc (encPair encGraph encNat)) +example {γ : Type} {enc : γ ↪ List Bool} {x : γ → VertexCoverInstance} {G : γ → Graph} {v : γ → ℕ} + (hx : ComputableInPolyTime x enc encVertexCoverInstance) (hG : ComputableInPolyTime G enc encGraph) (hv : ComputableInPolyTime v enc encNat) : ComputableInPolyTime (fun c => high (x c)) enc (encList encNat) ∧ ComputableInPolyTime (fun c => degree (G c) (v c)) enc encUnary := by diff --git a/CslibFrontierTests/WHierarchy.lean b/CslibFrontierTests/WHierarchy.lean index 7903d9b204..609775e9d5 100644 --- a/CslibFrontierTests/WHierarchy.lean +++ b/CslibFrontierTests/WHierarchy.lean @@ -81,13 +81,13 @@ def K3 : Graph := ⟨[0, 1, 2], [(0, 1), (1, 2), (0, 2)]⟩ example : IndependentSet.edgeIndices K3 = [(0, 1), (1, 2), (0, 2)] := by decide -- One vertex of the triangle is a satisfying input of weight `1`. -example : IndependentSet.toWCS (K3, 1) ∈ WCS 1 3 := +example : IndependentSet.toWCS ⟨K3, 1⟩ ∈ WCS 1 3 := ⟨fun i => decide (i.val = 0), by decide, by dsimp [IndependentSet.toWCS]; rw [IndependentSet.eval_toCircuit]; decide⟩ -- No input of weight `2` satisfies the circuit of the triangle, so the triangle has no -- independent set of two vertices. -example : (K3, 2) ∉ IndependentSet := by +example : (⟨K3, 2⟩ : IndependentSetInstance) ∉ IndependentSet := by rw [← IndependentSet.toWCS_mem_WCS_iff] rintro ⟨x, hk, hx⟩ dsimp [IndependentSet.toWCS] at x hk hx @@ -98,25 +98,25 @@ example : (K3, 2) ∉ IndependentSet := by -- An edge with an undeclared end is not an edge of the circuit. example : IndependentSet.edgeIndices ⟨[0, 1], [(0, 5)]⟩ = [] := by decide -example : ((⟨[0, 1], [(0, 5)]⟩, 2) : Graph × ℕ) ∈ IndependentSet := by +example : (⟨⟨[0, 1], [(0, 5)]⟩, 2⟩ : IndependentSetInstance) ∈ IndependentSet := by rw [← IndependentSet.toWCS_mem_WCS_iff] exact ⟨fun _ => true, by decide, by dsimp [IndependentSet.toWCS]; rw [IndependentSet.eval_toCircuit]; decide⟩ -- A self-loop is not an edge of the circuit. -example : ((⟨[0], [(0, 0)]⟩, 1) : Graph × ℕ) ∈ IndependentSet := by +example : (⟨⟨[0], [(0, 0)]⟩, 1⟩ : IndependentSetInstance) ∈ IndependentSet := by rw [← IndependentSet.toWCS_mem_WCS_iff] exact ⟨fun _ => true, by decide, by dsimp [IndependentSet.toWCS]; rw [IndependentSet.eval_toCircuit]; decide⟩ /-! ### Classes -/ -example : Clique ∈ W 2 (encPair encGraph encNat) Prod.snd := +example : Clique ∈ W 2 encCliqueInstance (·.m) := W_mono (by norm_num) Clique_mem_W -- A `W[1]`-hard problem in `FPT` would put `Clique` in `FPT`. example {β : Type} {enc : β ↪ List Bool} {κ : β → ℕ} {B : Set β} (hB : WHard 1 enc κ B) - (hFPT : B ∈ FPT enc κ) : Clique ∈ FPT (encPair encGraph encNat) Prod.snd := + (hFPT : B ∈ FPT enc κ) : Clique ∈ FPT encCliqueInstance (·.m) := hB.subset_FPT hFPT Clique_mem_W end CslibFrontierTests.WHierarchy diff --git a/CslibFrontierTests/WHierarchyClique.lean b/CslibFrontierTests/WHierarchyClique.lean index e4a9f52067..abb16df9f2 100644 --- a/CslibFrontierTests/WHierarchyClique.lean +++ b/CslibFrontierTests/WHierarchyClique.lean @@ -55,12 +55,13 @@ example : consistent [(0, 1)] [(1, 3)] = true ∧ consistent [(0, 1)] [(0, 2)] = /-! ### Graphs from a Boolean adjacency -/ -- Three pairwise distinct vertices, adjacent when distinct: a triangle. -example : ((Graph.ofAdj [0, 1, 2] fun a b => a != b), 3) ∈ Clique := by +example : (⟨Graph.ofAdj [0, 1, 2] fun a b => a != b, 3⟩ : CliqueInstance) ∈ Clique := by rw [Graph.mem_Clique_ofAdj_iff_entries (fun v => by simp)] exact ⟨{0, 1, 2}, by decide, by decide, by decide⟩ -- Adjacent only when both are even: the largest clique has two vertices. -example : ((Graph.ofAdj [0, 1, 2] fun a b => a != b && a % 2 == 0 && b % 2 == 0), 3) ∉ Clique := by +example : (⟨Graph.ofAdj [0, 1, 2] fun a b => a != b && a % 2 == 0 && b % 2 == 0, 3⟩ : + CliqueInstance) ∉ Clique := by rw [Graph.mem_Clique_ofAdj_iff_entries (fun v => by simp)] rintro ⟨T, hT, hTv, hTadj⟩ have hsub : T ⊆ {0, 1, 2} := fun v hv => by simpa using hTv v hv @@ -120,11 +121,13 @@ def fst2 : WeftCircuit 1 0 := -- The graph itself, computed by `decide`. For weight `0` it has two vertices, the certificate and -- the empty bracket, and no edge: the empty bracket says that input `0` is not selected, which -- refutes the certificate. The clique size is `2`, so there is no clique of the size. -example : (toClique ⟨fst2, 0⟩).1.1.length = 2 ∧ (toClique ⟨fst2, 0⟩).1.2 = [] ∧ - (toClique ⟨fst2, 0⟩).2 = 2 := by decide +example : (toClique ⟨fst2, 0⟩).graph.vertices.length = 2 ∧ + (toClique ⟨fst2, 0⟩).graph.edges = [] ∧ + (toClique ⟨fst2, 0⟩).m = 2 := by decide -- For weight `1` the graph has eight vertices and clique size `4`. -example : (toClique ⟨fst2, 1⟩).1.1.length = 8 ∧ (toClique ⟨fst2, 1⟩).2 = 4 := by decide +example : (toClique ⟨fst2, 1⟩).graph.vertices.length = 8 ∧ + (toClique ⟨fst2, 1⟩).m = 4 := by decide example : toClique ⟨fst2, 0⟩ ∉ Clique := by rw [toClique_mem_Clique_iff] @@ -145,21 +148,21 @@ example : size 0 3 5 = 22 ∧ 22 ≤ (5 + 1) ^ 2 ^ (0 + 1) + 1 := by decide example : size 0 3 5 ≤ (5 + 1) ^ 2 ^ (0 + 1) + 1 := size_le 0 3 5 -- `Clique` and `IndependentSet` are `W[1]`-complete. -example : Clique ∈ W 1 (encPair encGraph encNat) Prod.snd ∧ - WHard 1 (encPair encGraph encNat) Prod.snd Clique := +example : Clique ∈ W 1 encCliqueInstance (·.m) ∧ + WHard 1 encCliqueInstance (·.m) Clique := ⟨Clique_mem_W, Clique_wHard⟩ -example : IndependentSet ∈ W 1 (encPair encGraph encNat) Prod.snd ∧ - WHard 1 (encPair encGraph encNat) Prod.snd IndependentSet := +example : IndependentSet ∈ W 1 encIndependentSetInstance (·.m) ∧ + WHard 1 encIndependentSetInstance (·.m) IndependentSet := ⟨IndependentSet_mem_W, IndependentSet_wHard⟩ -- A problem of `W[1]` fpt-reduces to `IndependentSet`. -example : FPTReducible (encWCS 1 5) (encPair encGraph encNat) WCSInstance.weight Prod.snd (WCS 1 5) +example : FPTReducible (encWCS 1 5) encIndependentSetInstance WCSInstance.weight (·.m) (WCS 1 5) IndependentSet := IndependentSet_wHard _ _ _ (WCS_mem_W 1 5) -- If `Clique` is in `FPT`, so is weighted satisfiability of every class of weft-one circuits. -example (h : Clique ∈ FPT (encPair encGraph encNat) Prod.snd) (d : ℕ) : +example (h : Clique ∈ FPT encCliqueInstance (·.m)) (d : ℕ) : WCS 1 d ∈ FPT (encWCS 1 d) WCSInstance.weight := by rw [← W_one_eq_FPT_iff.2 h] exact WCS_mem_W 1 d diff --git a/guide/CslibFrontierGuide/NPCompleteness.lean b/guide/CslibFrontierGuide/NPCompleteness.lean index 92ac7e83b1..0551ba480a 100644 --- a/guide/CslibFrontierGuide/NPCompleteness.lean +++ b/guide/CslibFrontierGuide/NPCompleteness.lean @@ -153,8 +153,8 @@ example : encCNF = encList (encList (encPair encNat encBool)) := rfl ``` A graph is a list of vertices and a list of edges ({name}`Graph`), encoded by {name}`encGraph`. -{name}`Clique` asks whether a graph has a clique of a given size, so its instances are pairs, -encoded by `encPair encGraph encNat`. +{name}`Clique` asks whether a graph has a clique of a given size, so its instances have structure +{name}`CliqueInstance`, encoded by {name}`encCliqueInstance`. # Polynomial Time %%% @@ -262,7 +262,7 @@ proof goes. An NP-complete problem is in {name}`P` exactly when {name}`P` and {n it ({name}`NPComplete.mem_P_iff_mem_P`): ```lean -example : Clique ∈ P (encPair encGraph encNat) ↔ SAT ∈ P encCNF := +example : Clique ∈ P encCliqueInstance ↔ SAT ∈ P encCNF := Clique_npComplete.mem_P_iff_mem_P CookLevin.SAT_npComplete ``` @@ -413,7 +413,7 @@ Chaining reductions with {name}`NPHard.trans` gives the hardness of any problem directly from Cook–Levin: ```lean -example : NPHard (encPair encGraph encNat) Clique := +example : NPHard encCliqueInstance Clique := (CookLevin.SAT_npHard.trans polyTimeReducible_SAT_IndependentSet).trans polyTimeReducible_IndependentSet_Clique ``` @@ -581,8 +581,8 @@ An instance is a graph and a target size. The graph has a clique of that size wh its declared vertices is pairwise adjacent and has exactly that many elements: ```lean -example : Clique = {x | ∃ S : Finset {v // v ∈ x.1.1}, - x.1.induced.IsNClique x.2 S} := +example : Clique = {x | ∃ S : Finset {v // v ∈ x.graph.vertices}, + x.graph.induced.IsNClique x.m S} := rfl ``` @@ -598,15 +598,17 @@ problems that ask for a set of vertices with a pairwise property share this veri which leaves only the pairwise test to check: ```lean -example : IsVerifier (encPair encGraph encNat) Polynomial.X +example : IsVerifier encCliqueInstance Polynomial.X Clique.verifier Clique := by have h := Graph.isVerifier_verifySubset (rel := Graph.adjBool) (P := fun G u v => G.toSimpleGraph.Adj u v) (fun G u v _ => by rw [Graph.adjBool_eq_true_iff]) intro x + have hx := h (x.graph, x.m) simp only [Clique, Set.mem_ofPred_eq, SimpleGraph.isNClique_iff, SimpleGraph.IsClique, Clique.verifier] - exact h x + rw [encCliqueInstance_apply] + exact hx ``` The certificate length is `Polynomial.X`, one bit per bit of the encoded instance, which is at @@ -614,7 +616,7 @@ least one bit per vertex. The verifier is executable, so you can test it on a tr ```lean (name := triangle) #eval Clique.verifier - ((⟨[0, 1, 2], [(0, 1), (1, 2), (0, 2)]⟩, 3), [true, true, true]) + (⟨⟨[0, 1, 2], [(0, 1), (1, 2), (0, 2)]⟩, 3⟩, [true, true, true]) ``` ```leanOutput triangle true @@ -630,10 +632,10 @@ give membership in {name}`NP`: ```lean example : ComputableInPolyTime Clique.verifier - (encPair (encPair encGraph encNat) encBits) encBool := by + (encPair encCliqueInstance encBits) encBool := by fun_prop -example : Clique ∈ NP (encPair encGraph encNat) := +example : Clique ∈ NP encCliqueInstance := ⟨Polynomial.X, Clique.verifier, by fun_prop, Clique.isVerifier⟩ ``` @@ -678,12 +680,12 @@ The reduction is the triple of map, cost and correctness. Hardness transfers fro {name}`IndependentSet`, and membership with hardness is completeness: ```lean -example : PolyTimeReducible (encPair encGraph encNat) - (encPair encGraph encNat) IndependentSet Clique := - ⟨fun x => (x.1.compl, x.2), by fun_prop, - fun ⟨G, m⟩ => (Graph.compl_mem_Clique_iff G m).symm⟩ +example : PolyTimeReducible encIndependentSetInstance + encCliqueInstance IndependentSet Clique := + ⟨independentSetToClique, computableInPolyTime_independentSetToClique, + fun x => (independentSetToClique_mem_iff x).symm⟩ -example : NPComplete (encPair encGraph encNat) Clique := +example : NPComplete encCliqueInstance Clique := ⟨Clique.mem_NP, IndependentSet_npHard.trans polyTimeReducible_IndependentSet_Clique⟩ ``` diff --git a/manual/workflows/deterministic-workflows.md b/manual/workflows/deterministic-workflows.md index 6cb2980f04..aa100a524a 100644 --- a/manual/workflows/deterministic-workflows.md +++ b/manual/workflows/deterministic-workflows.md @@ -328,7 +328,7 @@ For NP, additionally supply a verifier and a polynomial certificate-length condi ```lean /-- For NP, the verifier also needs a proved certificate bound: here exactly the encoded instance length, represented by the polynomial `X`. -/ -theorem clique_mem_NP : Clique ∈ NP (encPair encGraph encNat) := +theorem clique_mem_NP : Clique ∈ NP encCliqueInstance := ⟨Polynomial.X, Clique.verifier, by fun_prop, Clique.isVerifier⟩ ``` @@ -351,12 +351,12 @@ Continue in [NPCompleteness](../../CslibFrontierTests/NPCompleteness.lean), keep /-- A reduction has a map, its polynomial-time certificate, and a membership equivalence. The equivalence must point from the source problem to the image in the target problem. -/ theorem independentSet_to_clique : - PolyTimeReducible (encPair encGraph encNat) (encPair encGraph encNat) IndependentSet Clique := + PolyTimeReducible encIndependentSetInstance encCliqueInstance IndependentSet Clique := ⟨complementInstance, by fun_prop, fun ⟨G, k⟩ => (Graph.compl_mem_Clique_iff G k).symm⟩ /-- Combine NP membership with hardness transferred from the known hard source problem. -/ -theorem clique_npComplete : NPComplete (encPair encGraph encNat) Clique := +theorem clique_npComplete : NPComplete encCliqueInstance Clique := ⟨clique_mem_NP, IndependentSet_npHard.trans independentSet_to_clique⟩ ``` diff --git a/scripts/projection_chains_baseline.json b/scripts/projection_chains_baseline.json index c6d8e522d8..cfcc9dfcfc 100644 --- a/scripts/projection_chains_baseline.json +++ b/scripts/projection_chains_baseline.json @@ -126,13 +126,12 @@ "CslibFrontier/Computability/Complexity/FineGrained/LocalMapCounting.lean": 5, "CslibFrontier/Computability/Complexity/FineGrained/PermutationIndependentSet.lean": 16, "CslibFrontier/Computability/Complexity/ImmermanVardi/Tableau/Step.lean": 2, - "CslibFrontier/Computability/Complexity/Parameterized/VertexCover/BussKernel.lean": 5, "CslibFrontier/Computability/Complexity/Problems/CSP/Incidence.lean": 1, "CslibFrontier/Computability/Complexity/Problems/CircuitSAT.lean": 4, "CslibFrontier/Computability/Complexity/Problems/CircuitValue.lean": 8, "CslibFrontier/Computability/Complexity/Problems/CircuitValue/Writer.lean": 17, "CslibFrontier/Computability/Complexity/Problems/Hamiltonian/Sparse.lean": 4, - "CslibFrontier/Computability/Complexity/Problems/IndependentSet.lean": 9, + "CslibFrontier/Computability/Complexity/Problems/IndependentSet.lean": 7, "CslibFrontier/Computability/Complexity/Problems/IndependentSet/Sparse.lean": 2, "CslibFrontier/Computability/Complexity/Problems/LinearProgramming.lean": 3, "CslibFrontier/Computability/Complexity/Problems/MonotoneCircuitValue.lean": 6, @@ -156,7 +155,6 @@ "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/PathAssignments.lean": 8, "CslibFrontier/Computability/Complexity/Problems/SAT/Planar/Paths.lean": 2, "CslibFrontier/Computability/Complexity/Problems/ThreeSATThree.lean": 2, - "CslibFrontier/Computability/Complexity/Problems/VertexCover.lean": 2, "CslibFrontier/Computability/GenericRAM/Native/BooleanFold.lean": 9, "CslibFrontier/Computability/GenericRAM/Native/BooleanSearch.lean": 12, "CslibFrontier/Computability/GenericRAM/Native/ExhaustiveCountFallback.lean": 16, @@ -222,7 +220,6 @@ "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/SpaceBudget.lean": 11, "CslibFrontier/Computability/Machines/Turing/MultiTape/Universal/Windowed.lean": 5, "CslibFrontier/Computability/Machines/Turing/SingleTape/Partrec.lean": 2, - "CslibFrontier/Computability/PCP/Approximation/IndependentSet.lean": 1, "CslibFrontier/Computability/PCP/AssignmentTester.lean": 12, "CslibFrontier/Computability/PCP/AssignmentTester/Selection.lean": 4, "CslibFrontier/Computability/PCP/DegreeReduction.lean": 3, diff --git a/scripts/test_operator_manual.py b/scripts/test_operator_manual.py index 339c163ef7..92ea35846a 100644 --- a/scripts/test_operator_manual.py +++ b/scripts/test_operator_manual.py @@ -641,7 +641,7 @@ class PortableLinks(unittest.TestCase): def setUp(self): self.temp = tempfile.TemporaryDirectory(prefix="cslib-frontier-manual-links-") self.addCleanup(self.temp.cleanup) - self.repo = Path(self.temp.name) + self.repo = Path(self.temp.name).resolve() self.root = self.repo / 'manual' self.root.mkdir() (self.repo / 'CslibFrontierTests').mkdir() From 83cc181985c6d7c91a954bb02ff529a041a284a1 Mon Sep 17 00:00:00 2001 From: Samuel Schlesinger Date: Wed, 11 Nov 2026 17:31:10 -0500 Subject: [PATCH 6/8] refactor(Complexity/Problems): migrate cycles, games, and drawings to structures (#4385) Add deriving Inhabited to all 7 Milestone 5 problem structures: FeedbackVertexSetInstance, FeedbackArcSetInstance, LongPathInstance, GeneralizedGeographyInstance, GraphIsomorphismInstance, DrawnPlanarThreeSATThreeInstance, DrawnPlanarPositiveOneInExactThreeSATThreeInstance. Register global [fun_prop] rules for planar drawings, modernize residual projections in GeneralizedGeography/Membership.lean, and add Inhabited unit tests. --- .../GeneralizedGeography/Membership.lean | 22 +++++++++---------- .../Complexity/Problems/FeedbackArcSet.lean | 2 +- .../Problems/FeedbackVertexSet.lean | 2 +- .../Problems/GeneralizedGeography.lean | 2 +- .../Complexity/Problems/GraphIsomorphism.lean | 2 +- .../Complexity/Problems/LongPath.lean | 2 +- .../Problems/PlanarPositiveOneInThreeSAT.lean | 6 ++++- .../Problems/PlanarThreeSATThree.lean | 5 ++++- CslibFrontierTests/FeedbackArcSet.lean | 1 + CslibFrontierTests/FeedbackVertexSet.lean | 1 + CslibFrontierTests/GapGraphsGeography.lean | 2 ++ CslibFrontierTests/InteractiveProofs.lean | 2 ++ CslibFrontierTests/LongPathHardness.lean | 2 ++ .../PlanarPositiveOneInThreeSAT.lean | 2 ++ CslibFrontierTests/PlanarThreeSATThree.lean | 2 ++ 15 files changed, 37 insertions(+), 18 deletions(-) diff --git a/CslibFrontier/Computability/Complexity/Classes/Space/GeneralizedGeography/Membership.lean b/CslibFrontier/Computability/Complexity/Classes/Space/GeneralizedGeography/Membership.lean index 310eab71ff..90d4b450a7 100644 --- a/CslibFrontier/Computability/Complexity/Classes/Space/GeneralizedGeography/Membership.lean +++ b/CslibFrontier/Computability/Complexity/Classes/Space/GeneralizedGeography/Membership.lean @@ -386,8 +386,8 @@ with `B` arcs and `n` declared vertex entries, replay `n + 1` blocks of `2 * B` from the start vertex, the first player to move; the existential player wins when the first player does. -/ def verifier (x : GeneralizedGeographyInstance × List Bool) : Bool := - ((blocks x.1.graph.2.length (x.1.graph.1.length + 1) x.2).foldl - (step x.1.graph x.1.graph.2.length) (none, true, [x.1.start], x.1.start)).1.getD false + ((blocks x.1.graph.edges.length (x.1.graph.vertices.length + 1) x.2).foldl + (step x.1.graph x.1.graph.edges.length) (none, true, [x.1.start], x.1.start)).1.getD false /-- The arcs and the declared vertex entries of a graph are no more than the length of the encoded instance. -/ @@ -536,18 +536,18 @@ theorem computableInPolyTime_verifier : (fun x : GeneralizedGeographyInstance × List Bool => x.1.graph) encInput encGraph := by fun_prop have hB : ComputableInPolyTime - (fun x : GeneralizedGeographyInstance × List Bool => x.1.graph.2.length) encInput + (fun x : GeneralizedGeographyInstance × List Bool => x.1.graph.edges.length) encInput encUnary := (length_list _).comp ((snd _ _).comp hG) have hR : ComputableInPolyTime - (fun x : GeneralizedGeographyInstance × List Bool => x.1.graph.1.length + 1) + (fun x : GeneralizedGeographyInstance × List Bool => x.1.graph.vertices.length + 1) encInput encUnary := unary_add.comp (((length_list _).comp ((fst _ _).comp hG)).prod (const 1)) have hblocks : ComputableInPolyTime (fun x : GeneralizedGeographyInstance × List Bool => - blocks x.1.graph.2.length (x.1.graph.1.length + 1) x.2) + blocks x.1.graph.edges.length (x.1.graph.vertices.length + 1) x.2) encInput (encList encBits) := by have h2B : ComputableInPolyTime - (fun p : (GeneralizedGeographyInstance × List Bool) × ℕ => 2 * p.1.1.graph.2.length) + (fun p : (GeneralizedGeographyInstance × List Bool) × ℕ => 2 * p.1.1.graph.edges.length) (encPair encInput encUnary) encUnary := unary_mul.comp ((const 2).prod (hB.comp (fst _ _))) unfold blocks @@ -560,7 +560,7 @@ theorem computableInPolyTime_verifier : fun_prop have hstep : ComputableInPolyTime (fun p : (((GeneralizedGeographyInstance × List Bool) × Play) × List Bool) => - step p.1.1.1.graph p.1.1.1.graph.2.length p.1.2 p.2) + step p.1.1.1.graph p.1.1.1.graph.edges.length p.1.2 p.2) encStep encPlay := by have hst : ComputableInPolyTime (fun p : (((GeneralizedGeographyInstance × List Bool) × Play) × List Bool) => p.1.2) @@ -570,19 +570,19 @@ theorem computableInPolyTime_verifier : encStep encInput := by fun_prop have hbits : ComputableInPolyTime (fun p : (((GeneralizedGeographyInstance × List Bool) × Play) × List Bool) => - moverBits p.1.1.1.graph.2.length p.1.2.2.1 p.2) + moverBits p.1.1.1.graph.edges.length p.1.2.2.1 p.2) encStep (encList encBool) := computableInPolyTime_moverBits (hB.comp hX) (by fun_prop) (snd _ _) have hpair : ComputableInPolyTime (fun p : (((GeneralizedGeographyInstance × List Bool) × Play) × List Bool) => - ((p.1.1.1.graph, p.1.2), moverBits p.1.1.1.graph.2.length p.1.2.2.1 p.2)) + ((p.1.1.1.graph, p.1.2), moverBits p.1.1.1.graph.edges.length p.1.2.2.1 p.2)) encStep (encPair (encPair encGraph encPlay) (encList encBool)) := ((hG.comp hX).prod hst).prod hbits have hnext : ComputableInPolyTime (fun p : (((GeneralizedGeographyInstance × List Bool) × Play) × List Bool) => next p.1.1.1.graph p.1.2.2.1 p.1.2.2.2.1 p.1.2.2.2.2 - (moverBits p.1.1.1.graph.2.length p.1.2.2.1 p.2)) + (moverBits p.1.1.1.graph.edges.length p.1.2.2.1 p.2)) encStep encPlay := by have h := computableInPolyTime_next.comp hpair simp only [Function.comp_def] at h @@ -595,7 +595,7 @@ theorem computableInPolyTime_verifier : exact ite' hsome hst hnext have hfold := foldlWithInput_of_bounded_growth (encA := encBits) (encL := encList encBits) (step := fun x : GeneralizedGeographyInstance × List Bool => - step x.1.graph x.1.graph.2.length) + step x.1.graph x.1.graph.edges.length) hinit ⟨hblocks, hstep, inferInstance, inferInstance⟩ (growth := fun x => 5 * (encInput x).length + 4) ⟨((PolyBounded.const 5).mul .self).add (.const 4), diff --git a/CslibFrontier/Computability/Complexity/Problems/FeedbackArcSet.lean b/CslibFrontier/Computability/Complexity/Problems/FeedbackArcSet.lean index 73614ccc81..4abef07470 100644 --- a/CslibFrontier/Computability/Complexity/Problems/FeedbackArcSet.lean +++ b/CslibFrontier/Computability/Complexity/Problems/FeedbackArcSet.lean @@ -87,7 +87,7 @@ structure FeedbackArcSetInstance where graph : Graph /-- The maximum number of arcs to remove. -/ bound : ℕ - deriving DecidableEq, Repr + deriving DecidableEq, Repr, Inhabited /-- Encoding of `FeedbackArcSetInstance` as a pair `(graph, bound)`. -/ enc_structure encFeedbackArcSet : FeedbackArcSetInstance where diff --git a/CslibFrontier/Computability/Complexity/Problems/FeedbackVertexSet.lean b/CslibFrontier/Computability/Complexity/Problems/FeedbackVertexSet.lean index cd55d0ff67..bfe342672a 100644 --- a/CslibFrontier/Computability/Complexity/Problems/FeedbackVertexSet.lean +++ b/CslibFrontier/Computability/Complexity/Problems/FeedbackVertexSet.lean @@ -81,7 +81,7 @@ structure FeedbackVertexSetInstance where graph : Graph /-- The maximum number of vertices to remove. -/ bound : ℕ - deriving DecidableEq, Repr + deriving DecidableEq, Repr, Inhabited /-- Encoding of `FeedbackVertexSetInstance` as a pair `(graph, bound)`. -/ enc_structure encFeedbackVertexSet : FeedbackVertexSetInstance where diff --git a/CslibFrontier/Computability/Complexity/Problems/GeneralizedGeography.lean b/CslibFrontier/Computability/Complexity/Problems/GeneralizedGeography.lean index f8cc9694c0..fc304e4be3 100644 --- a/CslibFrontier/Computability/Complexity/Problems/GeneralizedGeography.lean +++ b/CslibFrontier/Computability/Complexity/Problems/GeneralizedGeography.lean @@ -130,7 +130,7 @@ structure GeneralizedGeographyInstance where graph : Graph /-- The starting vertex of the token. -/ start : ℕ - deriving DecidableEq, Repr + deriving DecidableEq, Repr, Inhabited /-- Encoding of `GeneralizedGeographyInstance` as a pair `(graph, start)`. -/ enc_structure encGeneralizedGeography : GeneralizedGeographyInstance where diff --git a/CslibFrontier/Computability/Complexity/Problems/GraphIsomorphism.lean b/CslibFrontier/Computability/Complexity/Problems/GraphIsomorphism.lean index 5697914d50..e340043823 100644 --- a/CslibFrontier/Computability/Complexity/Problems/GraphIsomorphism.lean +++ b/CslibFrontier/Computability/Complexity/Problems/GraphIsomorphism.lean @@ -95,7 +95,7 @@ structure GraphIsomorphismInstance where first : Graph /-- The second graph in the pair. -/ second : Graph - deriving DecidableEq, Repr + deriving DecidableEq, Repr, Inhabited /-- Encoding of `GraphIsomorphismInstance` as a pair `(first, second)`. -/ enc_structure encGraphIsomorphism : GraphIsomorphismInstance where diff --git a/CslibFrontier/Computability/Complexity/Problems/LongPath.lean b/CslibFrontier/Computability/Complexity/Problems/LongPath.lean index 3381ffc6ae..2e397271bc 100644 --- a/CslibFrontier/Computability/Complexity/Problems/LongPath.lean +++ b/CslibFrontier/Computability/Complexity/Problems/LongPath.lean @@ -77,7 +77,7 @@ structure LongPathInstance where graph : Graph /-- The target number of vertices on the path. -/ target : ℕ - deriving DecidableEq, Repr + deriving DecidableEq, Repr, Inhabited /-- Encoding of `LongPathInstance` as `(graph, target)` with `target` in binary. -/ enc_structure encLongPath : LongPathInstance where diff --git a/CslibFrontier/Computability/Complexity/Problems/PlanarPositiveOneInThreeSAT.lean b/CslibFrontier/Computability/Complexity/Problems/PlanarPositiveOneInThreeSAT.lean index 1c0392629d..54688bc0ce 100644 --- a/CslibFrontier/Computability/Complexity/Problems/PlanarPositiveOneInThreeSAT.lean +++ b/CslibFrontier/Computability/Complexity/Problems/PlanarPositiveOneInThreeSAT.lean @@ -75,13 +75,17 @@ paired with a finite integer orthogonal drawing of its incidence graph. -/ formula : CNF ℕ /-- The supplied integer orthogonal incidence drawing. -/ drawing : Graph.OrthogonalDrawing - deriving DecidableEq, Repr + deriving DecidableEq, Repr, Inhabited /-- Binary encoding of a CNF formula and its supplied orthogonal incidence drawing. -/ enc_structure encDrawnPlanarPositiveOneInExactThreeSATThree : DrawnPlanarPositiveOneInExactThreeSATThreeInstance where formula : encCNF, drawing : Graph.encOrthogonalDrawing +attribute [fun_prop] encDrawnPlanarPositiveOneInExactThreeSATThree_formula_polyTime + encDrawnPlanarPositiveOneInExactThreeSATThree_drawing_polyTime + encDrawnPlanarPositiveOneInExactThreeSATThree_mk_polyTime + /-- Positive exact-three, occurrence-three exact-one formulas with an integer orthogonal incidence drawing whose full real segments meet only at common incident endpoints. -/ @[expose] def PlanarPositiveOneInExactThreeSATThree : Set (CNF ℕ) := diff --git a/CslibFrontier/Computability/Complexity/Problems/PlanarThreeSATThree.lean b/CslibFrontier/Computability/Complexity/Problems/PlanarThreeSATThree.lean index 1cd73e9c91..4d1039c4e9 100644 --- a/CslibFrontier/Computability/Complexity/Problems/PlanarThreeSATThree.lean +++ b/CslibFrontier/Computability/Complexity/Problems/PlanarThreeSATThree.lean @@ -84,12 +84,15 @@ orthogonal drawing of its incidence graph. -/ formula : CNF ℕ /-- The supplied integer orthogonal incidence drawing. -/ drawing : Graph.OrthogonalDrawing - deriving DecidableEq, Repr + deriving DecidableEq, Repr, Inhabited /-- Binary encoding of a CNF formula and its supplied orthogonal incidence drawing. -/ enc_structure encDrawnPlanarThreeSATThree : DrawnPlanarThreeSATThreeInstance where formula : encCNF, drawing : Graph.encOrthogonalDrawing +attribute [fun_prop] encDrawnPlanarThreeSATThree_formula_polyTime + encDrawnPlanarThreeSATThree_drawing_polyTime encDrawnPlanarThreeSATThree_mk_polyTime + /-- Satisfiable width-three, occurrence-three formulas whose actual incidence graph admits an integer orthogonal drawing with continuous endpoint-only contacts. -/ @[expose] def PlanarThreeSATThree : Set (CNF ℕ) := diff --git a/CslibFrontierTests/FeedbackArcSet.lean b/CslibFrontierTests/FeedbackArcSet.lean index 4af4344697..fe4c143089 100644 --- a/CslibFrontierTests/FeedbackArcSet.lean +++ b/CslibFrontierTests/FeedbackArcSet.lean @@ -49,6 +49,7 @@ example : ComputableInPolyTime (fun p : Graph × ℕ => (⟨p.1, p.2⟩ : Feedba (encPair encGraph encNat) encFeedbackArcSet := by fun_prop +example : (default : FeedbackArcSetInstance) = ⟨default, default⟩ := rfl /-! ### Feedback arc sets -/ diff --git a/CslibFrontierTests/FeedbackVertexSet.lean b/CslibFrontierTests/FeedbackVertexSet.lean index 595b16a07f..4707d9f640 100644 --- a/CslibFrontierTests/FeedbackVertexSet.lean +++ b/CslibFrontierTests/FeedbackVertexSet.lean @@ -48,6 +48,7 @@ example : ComputableInPolyTime (fun p : Graph × ℕ => (⟨p.1, p.2⟩ : Feedba (encPair encGraph encNat) encFeedbackVertexSet := by fun_prop +example : (default : FeedbackVertexSetInstance) = ⟨default, default⟩ := rfl /-! ### Directed cycles -/ diff --git a/CslibFrontierTests/GapGraphsGeography.lean b/CslibFrontierTests/GapGraphsGeography.lean index e7d38033ae..5627553e91 100644 --- a/CslibFrontierTests/GapGraphsGeography.lean +++ b/CslibFrontierTests/GapGraphsGeography.lean @@ -172,4 +172,6 @@ example : ComputableInPolyTime (encPair encGraph encNat) encGeneralizedGeography := by fun_prop +example : (default : GeneralizedGeographyInstance) = ⟨default, default⟩ := rfl + end CslibFrontierTests.GapGraphsGeography diff --git a/CslibFrontierTests/InteractiveProofs.lean b/CslibFrontierTests/InteractiveProofs.lean index 3a5032041b..b019227cdb 100644 --- a/CslibFrontierTests/InteractiveProofs.lean +++ b/CslibFrontierTests/InteractiveProofs.lean @@ -65,6 +65,8 @@ example : (encPair encGraph encGraph) encGraphIsomorphism := by fun_prop +example : (default : GraphIsomorphismInstance) = ⟨default, default⟩ := rfl + -- The certificate decoding `[7, 5, 9]` (positions `0, 1, 2` of -- `path'.vertices = [7, 5, 9, 5]` in unary -- blocks of width `4`) maps positions `0, 1, 2` of `path` to `7, 5, 9` and is accepted. diff --git a/CslibFrontierTests/LongPathHardness.lean b/CslibFrontierTests/LongPathHardness.lean index 9c365d6ad0..08d78916ca 100644 --- a/CslibFrontierTests/LongPathHardness.lean +++ b/CslibFrontierTests/LongPathHardness.lean @@ -52,6 +52,8 @@ example {x y : LongPathInstance} (hG : x.graph = y.graph) (hk : x.target = y.tar x = y := LongPathInstance.ext hG hk +example : (default : LongPathInstance) = ⟨default, default⟩ := rfl + /-- `encLongPath` exposes polynomial-time projections and constructor to `fun_prop`. -/ example : ComputableInPolyTime LongPathInstance.graph encLongPath encGraph := by fun_prop diff --git a/CslibFrontierTests/PlanarPositiveOneInThreeSAT.lean b/CslibFrontierTests/PlanarPositiveOneInThreeSAT.lean index 2a95795506..db0f11e9e4 100644 --- a/CslibFrontierTests/PlanarPositiveOneInThreeSAT.lean +++ b/CslibFrontierTests/PlanarPositiveOneInThreeSAT.lean @@ -97,4 +97,6 @@ example : Turing.MultiTapeTM.ComputableInPolyTime encDrawnPlanarPositiveOneInExactThreeSATThree := by fun_prop +example : (default : DrawnPlanarPositiveOneInExactThreeSATThreeInstance) = ⟨default, default⟩ := rfl + end CslibFrontierTests.PlanarPositiveOneInThreeSAT diff --git a/CslibFrontierTests/PlanarThreeSATThree.lean b/CslibFrontierTests/PlanarThreeSATThree.lean index 2517d85b7a..3bc53bd525 100644 --- a/CslibFrontierTests/PlanarThreeSATThree.lean +++ b/CslibFrontierTests/PlanarThreeSATThree.lean @@ -89,6 +89,8 @@ example : Turing.MultiTapeTM.ComputableInPolyTime encDrawnPlanarThreeSATThree := by fun_prop +example : (default : DrawnPlanarThreeSATThreeInstance) = ⟨default, default⟩ := rfl + -- The paired-port variable and placed-clause encodings are computable via `FinEnum`. example : SAT.ParsimoniousPlanar.encVariable ((0, 0), .inl (0, 0)) ≠ SAT.ParsimoniousPlanar.encVariable ((0, 0), .inr .center) := by decide +kernel From e312afae23b495b0fccd1839d2a9d783fbd39f0f Mon Sep 17 00:00:00 2001 From: Samuel Schlesinger Date: Wed, 11 Nov 2026 18:11:56 -0500 Subject: [PATCH 7/8] refactor(Problems/Graph): eliminate duplicate projection wrappers and revert baseline inflation (#4385) --- .../Complexity/Problems/FeedbackArcSet.lean | 4 ++-- .../Complexity/Problems/FeedbackVertexSet.lean | 4 ++-- .../Complexity/Problems/Graph/VertexColors.lean | 15 --------------- scripts/projection_chains_baseline.json | 2 -- 4 files changed, 4 insertions(+), 21 deletions(-) diff --git a/CslibFrontier/Computability/Complexity/Problems/FeedbackArcSet.lean b/CslibFrontier/Computability/Complexity/Problems/FeedbackArcSet.lean index 73614ccc81..682820b828 100644 --- a/CslibFrontier/Computability/Complexity/Problems/FeedbackArcSet.lean +++ b/CslibFrontier/Computability/Complexity/Problems/FeedbackArcSet.lean @@ -135,8 +135,8 @@ private theorem computableInPolyTime_backArcs : ComputableInPolyTime (fun x : FeedbackArcSetInstance × List ℕ => backArcs x.1.graph x.2) (encPair encFeedbackArcSet (encList encNat)) (encList (encPair encNat encNat)) := by unfold backArcs - exact filterWithInput (Graph.computableInPolyTime_edges (by fun_prop)) - (by fun_prop [Graph.computableInPolyTime_vertices]) + exact filterWithInput (encGraph_edges_polyTime (by fun_prop)) + (by fun_prop [encGraph_vertices_polyTime]) private theorem computableInPolyTime_check : ComputableInPolyTime check (encPair encFeedbackArcSet (encList encNat)) encBool := by diff --git a/CslibFrontier/Computability/Complexity/Problems/FeedbackVertexSet.lean b/CslibFrontier/Computability/Complexity/Problems/FeedbackVertexSet.lean index 6371ec2b62..7334d4bc67 100644 --- a/CslibFrontier/Computability/Complexity/Problems/FeedbackVertexSet.lean +++ b/CslibFrontier/Computability/Complexity/Problems/FeedbackVertexSet.lean @@ -137,7 +137,7 @@ private theorem computableInPolyTime_arcOK : (encPair (encPair encFeedbackVertexSet (encList encNat)) (encPair encNat encNat)) encBool := by unfold arcOK - fun_prop [Graph.computableInPolyTime_vertices] + fun_prop [encGraph_vertices_polyTime] private theorem computableInPolyTime_check : ComputableInPolyTime check (encPair encFeedbackVertexSet (encList encNat)) encBool := by @@ -146,7 +146,7 @@ private theorem computableInPolyTime_check : unfold zeroCount exact countPWithInput (by fun_prop) (by fun_prop) exact bool_and.comp ((binary_le' (unary_to_binary.comp hz) (by fun_prop)).prod - (allWithInput (Graph.computableInPolyTime_edges (by fun_prop)) computableInPolyTime_arcOK)) + (allWithInput (encGraph_edges_polyTime (by fun_prop)) computableInPolyTime_arcOK)) /-- The distinct declared vertices numbered `0` are counted by `zeroCount`. -/ private theorem zeroCount_eq (x : FeedbackVertexSetInstance × List ℕ) : diff --git a/CslibFrontier/Computability/Complexity/Problems/Graph/VertexColors.lean b/CslibFrontier/Computability/Complexity/Problems/Graph/VertexColors.lean index ca30aed156..36de6bcaf7 100644 --- a/CslibFrontier/Computability/Complexity/Problems/Graph/VertexColors.lean +++ b/CslibFrontier/Computability/Complexity/Problems/Graph/VertexColors.lean @@ -29,9 +29,6 @@ witnesses read them in this form (`Cslib.Complexity.mem_NP_of_colorOf`). ## Main results -* `Cslib.Complexity.Graph.computableInPolyTime_vertices`, - `Cslib.Complexity.Graph.computableInPolyTime_edges`: projecting the declared vertex list or edge - list from `encGraph` is polynomial-time. * `Cslib.Complexity.colorOf_map`: listing the colors of the declared vertices gives every declared vertex its color. * `Cslib.Complexity.computableInPolyTime_colorOf`, @@ -48,18 +45,6 @@ namespace Cslib.Complexity open Turing.MultiTapeTM -/-- Projecting the declared vertex list of a computed graph is polynomial-time. -/ -theorem Graph.computableInPolyTime_vertices {γ : Type} {enc : γ ↪ List Bool} {G : γ → Graph} - (hG : ComputableInPolyTime G enc encGraph) : - ComputableInPolyTime (fun x => (G x).vertices) enc (encList encNat) := by - fun_prop - -/-- Projecting the edge list of a computed graph is polynomial-time. -/ -theorem Graph.computableInPolyTime_edges {γ : Type} {enc : γ ↪ List Bool} {G : γ → Graph} - (hG : ComputableInPolyTime G enc encGraph) : - ComputableInPolyTime (fun x => (G x).edges) enc (encList (encPair encNat encNat)) := by - fun_prop - /-- The color of the vertex `v` under a list of colors `cs` parallel to the vertex list of `G`: the entry of `cs` at the first occurrence of `v`, and `0` when there is none. -/ @[expose] diff --git a/scripts/projection_chains_baseline.json b/scripts/projection_chains_baseline.json index c6d8e522d8..2eb6a65864 100644 --- a/scripts/projection_chains_baseline.json +++ b/scripts/projection_chains_baseline.json @@ -268,7 +268,6 @@ "CslibFrontier/Computability/USTCON/Reader/Encode.lean": 13, "CslibFrontier/Computability/WordRAM/Heap.lean": 2, "CslibFrontier/Computability/WordRAM/Realization/Absorb.lean": 4, - "CslibFrontier/Computability/WordRAM/Realization/BoundedTactic.lean": 4, "CslibFrontier/Computability/WordRAM/Realization/Loop.lean": 12, "CslibFrontier/Crypto/Computational/DiscreteLog/SchnorrGroup.lean": 4, "CslibFrontier/Crypto/Computational/Encryption/CCA.lean": 12, @@ -388,7 +387,6 @@ "CslibFrontier/Logics/FirstOrder/Problems/ColoringReduction.lean": 7, "CslibFrontier/Logics/FirstOrder/Raw/Graph.lean": 6, "CslibFrontier/Logics/FirstOrder/ZeroOneLaw/Relational/ExtensionAxiom.lean": 2, - "CslibFrontier/Logics/FirstOrder/ZeroOneLaw/Relational/ExtensionProbability.lean": 2, "CslibFrontier/Logics/Propositional/CNF/Compact/Relabel.lean": 3, "CslibFrontier/Logics/Propositional/CNF/ModelCount.lean": 4, "CslibFrontier/Logics/Propositional/CNF/Occurrence.lean": 1, From cafbd599c001a27f8d8bca1ce37143d42c3b823d Mon Sep 17 00:00:00 2001 From: Samuel Schlesinger Date: Thu, 12 Nov 2026 15:28:07 -0500 Subject: [PATCH 8/8] fix(GeneralizedGeography): reuse generated Graph field certificates Address PR #4672 review comment 4544088452 at exact parent 5684ee7c0cddeebc24f585fe70ea921aa440878b. In hB and hR, use the existing encGraph_edges_polyTime hG and encGraph_vertices_polyTime hG, as this module already does in computableInPolyTime_next. Retain the surrounding length, unary addition and constant-one composition. Exact source comparison changes only these two proof terms on two lines; file line count, imports, attributes, all public statements, models, encodings, independent resource expressions and original authors/credit are unchanged. No helper, alias, registration, baseline or new test. Both manuals, Guide, results inventory and retired-abstract scope have no impact: no API meaning, quantitative claim or development procedure changes, and source-anchor line counts remain unchanged. Preparation only, created with official GitHub blob/tree/commit APIs and a leased unique preparation ref. Original PR ref remains untouched. No local Git objects, worktree/switch, Lean/Lake/MCP/cache/build/compiled probes or main merge. These are source checks, not Lean validation. Parent #4670's actual landing and later necessary reconciliation remain prerequisites; fresh hosted gates/notices/Guide/manuals and eligible independent review remain required. No author approval or queue action. AI-assisted repair by worker codex-cycles-projection-return-20261112, delegated by codex-coord-20261111-1634 under issue4385 comment6834982144. Original Samuel Schlesinger source credit retained. --- .../Classes/Space/GeneralizedGeography/Membership.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CslibFrontier/Computability/Complexity/Classes/Space/GeneralizedGeography/Membership.lean b/CslibFrontier/Computability/Complexity/Classes/Space/GeneralizedGeography/Membership.lean index 90d4b450a7..9074f0dfe3 100644 --- a/CslibFrontier/Computability/Complexity/Classes/Space/GeneralizedGeography/Membership.lean +++ b/CslibFrontier/Computability/Complexity/Classes/Space/GeneralizedGeography/Membership.lean @@ -537,11 +537,11 @@ theorem computableInPolyTime_verifier : fun_prop have hB : ComputableInPolyTime (fun x : GeneralizedGeographyInstance × List Bool => x.1.graph.edges.length) encInput - encUnary := (length_list _).comp ((snd _ _).comp hG) + encUnary := (length_list _).comp (encGraph_edges_polyTime hG) have hR : ComputableInPolyTime (fun x : GeneralizedGeographyInstance × List Bool => x.1.graph.vertices.length + 1) encInput encUnary := - unary_add.comp (((length_list _).comp ((fst _ _).comp hG)).prod (const 1)) + unary_add.comp (((length_list _).comp (encGraph_vertices_polyTime hG)).prod (const 1)) have hblocks : ComputableInPolyTime (fun x : GeneralizedGeographyInstance × List Bool => blocks x.1.graph.edges.length (x.1.graph.vertices.length + 1) x.2)