Skip to content

Commit 5a9e13e

Browse files
or4nge19ocfnash
andcommitted
feat(combinatorics): Define vertices of a Quiver.Path (leanprover-community#27332)
This PR is linked to leanprover-community#27315 and leanprover-community#27325. It's part of PRing the formalization of core PF theorem here https://leanprover.zulipchat.com/#narrow/channel/116395-maths/topic/Formalizing.20Perron-Frobenius/with/525516636 This adds: Quiver.Path.vertices: A List V representing the sequence of vertices along a path. vertices_comp: A proof that the vertices of a composed path p.comp q are p.vertices.dropLast ++ q.vertices. vertices_length: The length of the vertices list is p.length + 1. Lemmas connecting vertices to standard list operations like head?, getLast, and get. Lemmas for membership: start_mem_vertices and end_mem_vertices. This is a prerequisite for defining path simplicity and formalizing cycle extraction, as used in the formalization of the Perron-Frobenius theorem Co-authored-by: Oliver Nash <github@olivernash.org>
1 parent 67b6f4e commit 5a9e13e

3 files changed

Lines changed: 170 additions & 0 deletions

File tree

Mathlib.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2798,6 +2798,7 @@ import Mathlib.Combinatorics.Quiver.ConnectedComponent
27982798
import Mathlib.Combinatorics.Quiver.Covering
27992799
import Mathlib.Combinatorics.Quiver.Path
28002800
import Mathlib.Combinatorics.Quiver.Path.Decomposition
2801+
import Mathlib.Combinatorics.Quiver.Path.Vertices
28012802
import Mathlib.Combinatorics.Quiver.Path.Weight
28022803
import Mathlib.Combinatorics.Quiver.Prefunctor
28032804
import Mathlib.Combinatorics.Quiver.Push

Mathlib/Combinatorics/Quiver/Path.lean

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ theorem eq_nil_of_length_zero (p : Path a a) (hzero : p.length = 0) : p = nil :=
7878
· rfl
7979
· simp at hzero
8080

81+
@[simp]
82+
lemma length_toPath {a b : V} (e : a ⟶ b) : e.toPath.length = 1 := rfl
83+
8184
/-- Composition of paths. -/
8285
def comp {a b : V} : ∀ {c}, Path a b → Path b c → Path a c
8386
| _, p, nil => p
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/-
2+
Copyright (c) 2025 Matteo Cipollina. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Matteo Cipollina
5+
-/
6+
7+
import Mathlib.Algebra.Order.Group.Nat
8+
import Mathlib.Combinatorics.Quiver.Path
9+
import Mathlib.Data.Set.Insert
10+
import Mathlib.Data.List.Basic
11+
12+
/-!
13+
# Path Vertices
14+
15+
This file provides lemmas for reasoning about the vertices of a path.
16+
-/
17+
18+
namespace Quiver.Path
19+
20+
open List
21+
22+
variable {V : Type*} [Quiver V]
23+
24+
/-- The end vertex of a path. A path `p : Path a b` has `p.end = b`. -/
25+
def «end» {a : V} : ∀ {b : V}, Path a b → V
26+
| b, _ => b
27+
28+
@[simp]
29+
lemma end_cons {a b c : V} (p : Path a b) (e : b ⟶ c) : (p.cons e).end = c := rfl
30+
31+
/-- The list of vertices in a path, including the start and end vertices. -/
32+
def vertices {a : V} : ∀ {b : V}, Path a b → List V
33+
| _, nil => [a]
34+
| _, cons p e => (p.vertices).concat (p.cons e).end
35+
36+
@[simp]
37+
lemma vertices_nil (a : V) : (nil : Path a a).vertices = [a] := rfl
38+
39+
@[simp]
40+
lemma vertices_cons {a b c : V} (p : Path a b) (e : b ⟶ c) :
41+
(p.cons e).vertices = p.vertices.concat c := rfl
42+
43+
/-- The vertex list of `cons` — convenient `simp` form. -/
44+
lemma mem_vertices_cons {a b c : V} (p : Path a b)
45+
(e : b ⟶ c) {x : V} :
46+
x ∈ (p.cons e).vertices ↔ x ∈ p.vertices ∨ x = c := by
47+
simp only [vertices_cons]
48+
simp_all only [concat_eq_append, mem_append, mem_cons, not_mem_nil, or_false]
49+
50+
lemma verticesSet_nil {a : V} : {v | v ∈ (nil : Path a a).vertices} = {a} := by
51+
simp only [vertices_nil, List.mem_singleton, Set.ext_iff, Set.mem_singleton_iff]
52+
exact fun x ↦ Set.mem_setOf
53+
54+
/-- The length of vertices list equals path length plus one -/
55+
@[simp]
56+
lemma vertices_length {V : Type*} [Quiver V] {a b : V} (p : Path a b) :
57+
p.vertices.length = p.length + 1 := by
58+
induction p with
59+
| nil => simp only [vertices_nil, List.length_cons, List.length_nil, zero_add, length_nil]
60+
| cons p' e ih =>
61+
simp only [vertices_cons, length_cons, List.length_concat, ih]
62+
63+
lemma length_vertices_pos {a b : V} (p : Path a b) :
64+
0 < p.vertices.length := by simp
65+
66+
lemma vertices_ne_nil {a : V} {b : V} (p : Path a b) : p.vertices ≠ [] := by
67+
simp [← List.length_pos_iff_ne_nil]
68+
69+
/-- The head of the vertices list is the start vertex -/
70+
@[simp]
71+
lemma vertices_head? {a b : V} (p : Path a b) : p.vertices.head? = some a := by
72+
induction p with
73+
| nil => simp only [vertices_nil, List.head?_cons]
74+
| cons p' e ih => simp [ih]
75+
76+
@[simp]
77+
lemma getElem_vertices_zero {a b : V} (p : Path a b) : p.vertices[0] = a := by
78+
induction p with
79+
| nil => simp
80+
| cons p' e ih => simp [ih]
81+
82+
@[simp]
83+
lemma vertices_getLast {a b : V} (p : Path a b) (h : p.vertices ≠ [] := p.vertices_ne_nil) :
84+
p.vertices.getLast h = b := by
85+
induction p with
86+
| nil => simp only [vertices_nil, List.getLast_singleton]
87+
| cons p' e ih => simp
88+
89+
@[simp]
90+
lemma vertices_comp {a b c : V} (p : Path a b) (q : Path b c) :
91+
(p.comp q).vertices = p.vertices.dropLast ++ q.vertices := by
92+
induction q with
93+
| nil => simpa using (List.dropLast_append_getLast p.vertices_ne_nil).symm
94+
| cons q' e ih => simp [ih]
95+
96+
lemma start_mem_vertices {a b : V} (p : Path a b) : a ∈ p.vertices := by
97+
induction p with
98+
| nil => simp
99+
| cons p' e ih => simp [ih]
100+
101+
@[simp] lemma length_eq_zero_iff {a : V} (p : Path a a) :
102+
p.length = 0 ↔ p = Path.nil := by
103+
cases p <;> tauto
104+
105+
/-- The head of the vertices list is the start vertex. -/
106+
@[simp]
107+
lemma vertices_head_eq {a b : V} (p : Path a b) (h : p.vertices ≠ [] := p.vertices_ne_nil) :
108+
p.vertices.head h = a := by
109+
induction p with
110+
| nil => simp only [vertices_nil, List.head_cons]
111+
| cons p' _ ih => simp [List.head_append_of_ne_nil (vertices_ne_nil p'), ih]
112+
113+
/-- The last element of the vertices list is the end vertex. -/
114+
lemma vertices_getLast_eq {a b : V} (p : Path a b) (h : p.vertices ≠ [] := p.vertices_ne_nil) :
115+
p.vertices.getLast h = b := by
116+
exact vertices_getLast p (vertices_ne_nil p)
117+
118+
lemma vertices_comp_get_length_eq {a b c : V} (p₁ : Path a c) (p₂ : Path c b)
119+
(h : p₁.length < (p₁.comp p₂).vertices.length := by simp) :
120+
(p₁.comp p₂).vertices.get ⟨p₁.length, h⟩ = c := by
121+
simp
122+
123+
@[simp]
124+
lemma vertices_toPath {i j : V} (e : i ⟶ j) :
125+
(e.toPath).vertices = [i, j] := by
126+
change (Path.nil.cons e).vertices = [i, j]
127+
simp
128+
129+
lemma vertices_toPath_tail {i j : V} (e : i ⟶ j) :
130+
(e.toPath).vertices.tail = [j] := by
131+
simp
132+
133+
/-- If a composition is `nil`, the left component must be `nil`
134+
(proved via lengths, avoiding dependent pattern-matching). -/
135+
lemma nil_of_comp_eq_nil_left {a b : V} {p : Path a b} {q : Path b a}
136+
(h : p.comp q = Path.nil) : p.length = 0 := by
137+
have hlen : (p.comp q).length = 0 := by
138+
simpa using congrArg Path.length h
139+
have : p.length + q.length = 0 := by
140+
simpa [length_comp] using hlen
141+
exact Nat.eq_zero_of_add_eq_zero_right this
142+
143+
/-- If a composition is `nil`, the right component must be `nil` -/
144+
lemma nil_of_comp_eq_nil_right {a b : V} {p : Path a b} {q : Path b a}
145+
(h : p.comp q = Path.nil) : q.length = 0 := by
146+
have hlen : (p.comp q).length = 0 := by
147+
simpa using congrArg Path.length h
148+
have : p.length + q.length = 0 := by
149+
simpa [length_comp] using hlen
150+
exact Nat.eq_zero_of_add_eq_zero_left this
151+
152+
lemma comp_eq_nil_iff {a b : V} {p : Path a b} {q : Path b a} :
153+
p.comp q = Path.nil ↔ p.length = 0 ∧ q.length = 0 := by
154+
refine ⟨fun h ↦ ⟨nil_of_comp_eq_nil_left h, nil_of_comp_eq_nil_right h⟩, fun ⟨hp, hq⟩ ↦ ?_⟩
155+
induction p with
156+
| nil => simpa using (length_eq_zero_iff q).mp hq
157+
| cons p' _ ihp => simp at hp
158+
159+
@[simp]
160+
lemma end_mem_vertices {a b : V} (p : Path a b) : b ∈ p.vertices := by
161+
have h₁ : p.vertices.getLast (vertices_ne_nil p) = b :=
162+
vertices_getLast p (vertices_ne_nil p)
163+
have h₂ := List.getLast_mem (l := p.vertices) (vertices_ne_nil p)
164+
simpa [h₁] using h₂
165+
166+
end Quiver.Path

0 commit comments

Comments
 (0)