Skip to content

Commit 4d41532

Browse files
committed
feat: Number of edges of a tree (#5918)
Port/review of [mathlib#18638](leanprover-community/mathlib3#18638) directly to Mathlib4. Co-authored-by: Yaël Dillies <yael.dillies@gmail.com> Co-authored-by: Bhavik Mehta <bhavikmehta8@gmail.com>
1 parent 881c646 commit 4d41532

File tree

2 files changed

+140
-0
lines changed

2 files changed

+140
-0
lines changed

Mathlib/Combinatorics/SimpleGraph/Acyclic.lean

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
44
Authors: Kyle Miller
55
-/
66
import Mathlib.Combinatorics.SimpleGraph.Connectivity
7+
import Mathlib.Tactic.Linarith
78

89
#align_import combinatorics.simple_graph.acyclic from "leanprover-community/mathlib"@"b07688016d62f81d14508ff339ea3415558d6353"
910

@@ -43,6 +44,8 @@ universe u v
4344

4445
namespace SimpleGraph
4546

47+
open Walk
48+
4649
variable {V : Type u} (G : SimpleGraph V)
4750

4851
/-- A graph is *acyclic* (or a *forest*) if it has no cycles. -/
@@ -149,4 +152,57 @@ theorem isTree_iff_existsUnique_path :
149152
simp only [ExistsUnique.unique (h v w) hp hq]
150153
#align simple_graph.is_tree_iff_exists_unique_path SimpleGraph.isTree_iff_existsUnique_path
151154

155+
lemma IsTree.existsUnique_path (hG : G.IsTree) : ∀ v w, ∃! p : G.Walk v w, p.IsPath :=
156+
(isTree_iff_existsUnique_path.1 hG).2
157+
158+
lemma IsTree.card_edgeFinset [Fintype V] [Fintype G.edgeSet] (hG : G.IsTree) :
159+
Finset.card G.edgeFinset + 1 = Fintype.card V := by
160+
have := hG.isConnected.nonempty
161+
inhabit V
162+
classical
163+
have : Finset.card ({default} : Finset V)ᶜ + 1 = Fintype.card V := by
164+
rw [Finset.card_compl, Finset.card_singleton, Nat.sub_add_cancel Fintype.card_pos]
165+
rw [← this, add_left_inj]
166+
choose f hf hf' using (hG.existsUnique_path · default)
167+
refine Eq.symm <| Finset.card_congr
168+
(fun w hw => ((f w).firstDart <| ?notNil).edge)
169+
(fun a ha => ?memEdges) ?inj ?surj
170+
case notNil => exact not_nil_of_ne (by simpa using hw)
171+
case memEdges => simp
172+
case inj =>
173+
intros a b ha hb h
174+
wlog h' : (f a).length ≤ (f b).length generalizing a b
175+
· exact Eq.symm (this _ _ hb ha h.symm (le_of_not_le h'))
176+
rw [dart_edge_eq_iff] at h
177+
obtain (h | h) := h
178+
· exact (congrArg (·.fst) h)
179+
· have h1 : ((f a).firstDart <| not_nil_of_ne (by simpa using ha)).snd = b :=
180+
congrArg (·.snd) h
181+
have h3 := congrArg length (hf' _ (((f _).tail _).copy h1 rfl) ?_)
182+
rw [length_copy, ← add_left_inj 1, length_tail_add_one] at h3
183+
· exfalso
184+
linarith
185+
· simp only [ne_eq, eq_mp_eq_cast, id_eq, isPath_copy]
186+
exact (hf _).tail _
187+
case surj =>
188+
simp only [mem_edgeFinset, Finset.mem_compl, Finset.mem_singleton, Sym2.forall, mem_edgeSet]
189+
intros x y h
190+
wlog h' : (f x).length ≤ (f y).length generalizing x y
191+
· rw [Sym2.eq_swap]
192+
exact this y x h.symm (le_of_not_le h')
193+
refine ⟨y, ?_, dart_edge_eq_mk'_iff.2 <| Or.inr ?_⟩
194+
· rintro rfl
195+
rw [← hf' _ nil IsPath.nil, length_nil,
196+
← hf' _ (.cons h .nil) (IsPath.nil.cons <| by simpa using h.ne),
197+
length_cons, length_nil] at h'
198+
simp [le_zero_iff, Nat.one_ne_zero] at h'
199+
rw [← hf' _ (.cons h.symm (f x)) ((cons_isPath_iff _ _).2 ⟨hf _, fun hy => ?contra⟩)]
200+
rfl
201+
case contra =>
202+
suffices : (f x).takeUntil y hy = .cons h .nil
203+
· rw [← take_spec _ hy] at h'
204+
simp [this, hf' _ _ ((hf _).dropUntil hy)] at h'
205+
refine (hG.existsUnique_path _ _).unique ((hf _).takeUntil _) ?_
206+
simp [h.ne]
207+
152208
end SimpleGraph

Mathlib/Combinatorics/SimpleGraph/Connectivity.lean

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@ theorem support_reverse {u v : V} (p : G.Walk u v) : p.reverse.support = p.suppo
556556
induction p <;> simp [support_append, *]
557557
#align simple_graph.walk.support_reverse SimpleGraph.Walk.support_reverse
558558

559+
@[simp]
559560
theorem support_ne_nil {u v : V} (p : G.Walk u v) : p.support ≠ [] := by cases p <;> simp
560561
#align simple_graph.walk.support_ne_nil SimpleGraph.Walk.support_ne_nil
561562

@@ -825,6 +826,81 @@ theorem edges_nodup_of_support_nodup {u v : V} {p : G.Walk u v} (h : p.support.N
825826
exact ⟨fun h' => h.1 (fst_mem_support_of_mem_edges p' h'), ih h.2
826827
#align simple_graph.walk.edges_nodup_of_support_nodup SimpleGraph.Walk.edges_nodup_of_support_nodup
827828

829+
/-- Predicate for the empty walk.
830+
831+
Solves the dependent type problem where `p = G.Walk.nil` typechecks
832+
only if `p` has defeq endpoints. -/
833+
inductive Nil : {v w : V} → G.Walk v w → Prop
834+
| nil {u : V} : Nil (nil : G.Walk u u)
835+
836+
@[simp] lemma nil_nil : (nil : G.Walk u u).Nil := Nil.nil
837+
838+
@[simp] lemma not_nil_cons {h : G.Adj u v} {p : G.Walk v w} : ¬ (cons h p).Nil := fun.
839+
840+
instance (p : G.Walk v w) : Decidable p.Nil :=
841+
match p with
842+
| nil => isTrue .nil
843+
| cons _ _ => isFalse fun.
844+
845+
protected lemma Nil.eq {p : G.Walk v w} : p.Nil → v = w | .nil => rfl
846+
847+
lemma not_nil_of_ne {p : G.Walk v w} : v ≠ w → ¬ p.Nil := mt Nil.eq
848+
849+
lemma nil_iff_support_eq {p : G.Walk v w} : p.Nil ↔ p.support = [v] := by
850+
cases p <;> simp
851+
852+
lemma nil_iff_length_eq {p : G.Walk v w} : p.Nil ↔ p.length = 0 := by
853+
cases p <;> simp
854+
855+
lemma not_nil_iff {p : G.Walk v w} :
856+
¬ p.Nil ↔ ∃ (u : V) (h : G.Adj v u) (q : G.Walk u w), p = cons h q := by
857+
cases p <;> simp [*]
858+
859+
@[elab_as_elim]
860+
def notNilRec {motive : {u w : V} → (p : G.Walk u w) → (h : ¬ p.Nil) → Sort _}
861+
(cons : {u v w : V} → (h : G.Adj u v) → (q : G.Walk v w) → motive (cons h q) not_nil_cons)
862+
(p : G.Walk u w) : (hp : ¬ p.Nil) → motive p hp :=
863+
match p with
864+
| nil => fun hp => absurd .nil hp
865+
| .cons h q => fun _ => cons h q
866+
867+
/-- The second vertex along a non-nil walk. -/
868+
def sndOfNotNil (p : G.Walk v w) (hp : ¬ p.Nil) : V :=
869+
p.notNilRec (@fun _ u _ _ _ => u) hp
870+
871+
@[simp] lemma adj_sndOfNotNil {p : G.Walk v w} (hp : ¬ p.Nil) :
872+
G.Adj v (p.sndOfNotNil hp) :=
873+
p.notNilRec (fun h _ => h) hp
874+
875+
/-- The walk obtained by removing the first dart of a non-nil walk. -/
876+
def tail (p : G.Walk x y) (hp : ¬ p.Nil) : G.Walk (p.sndOfNotNil hp) y :=
877+
p.notNilRec (fun _ q => q) hp
878+
879+
/-- The first dart of a walk. -/
880+
@[simps]
881+
def firstDart (p : G.Walk v w) (hp : ¬ p.Nil) : G.Dart where
882+
fst := v
883+
snd := p.sndOfNotNil hp
884+
is_adj := p.adj_sndOfNotNil hp
885+
886+
lemma edge_firstDart (p : G.Walk v w) (hp : ¬ p.Nil) :
887+
(p.firstDart hp).edge = ⟦(v, p.sndOfNotNil hp)⟧ := rfl
888+
889+
@[simp] lemma cons_tail_eq (p : G.Walk x y) (hp : ¬ p.Nil) :
890+
cons (p.adj_sndOfNotNil hp) (p.tail hp) = p :=
891+
p.notNilRec (fun _ _ => rfl) hp
892+
893+
@[simp] lemma cons_support_tail (p : G.Walk x y) (hp : ¬ p.Nil) :
894+
x :: (p.tail hp).support = p.support := by
895+
rw [← support_cons, cons_tail_eq]
896+
897+
@[simp] lemma length_tail_add_one {p : G.Walk x y} (hp : ¬ p.Nil) :
898+
(p.tail hp).length + 1 = p.length := by
899+
rw [← length_cons, cons_tail_eq]
900+
901+
@[simp] lemma nil_copy {p : G.Walk x y} (hx : x = x') (hy : y = y') :
902+
(p.copy hx hy).Nil = p.Nil := by
903+
subst_vars; rfl
828904

829905
/-! ### Trails, paths, circuits, cycles -/
830906

@@ -972,6 +1048,10 @@ theorem cons_isPath_iff {u v w : V} (h : G.Adj u v) (p : G.Walk v w) :
9721048
constructor <;> simp (config := { contextual := true }) [isPath_def]
9731049
#align simple_graph.walk.cons_is_path_iff SimpleGraph.Walk.cons_isPath_iff
9741050

1051+
protected lemma IsPath.cons (hp : p.IsPath) (hu : u ∉ p.support) {h : G.Adj u v} :
1052+
(cons h p).IsPath :=
1053+
(cons_isPath_iff _ _).2 ⟨hp, hu⟩
1054+
9751055
@[simp]
9761056
theorem isPath_iff_eq_nil {u : V} (p : G.Walk u u) : p.IsPath ↔ p = nil := by
9771057
cases p <;> simp [IsPath.nil]
@@ -1011,6 +1091,10 @@ theorem cons_isCycle_iff {u v : V} (p : G.Walk v u) (h : G.Adj u v) :
10111091
tauto
10121092
#align simple_graph.walk.cons_is_cycle_iff SimpleGraph.Walk.cons_isCycle_iff
10131093

1094+
lemma IsPath.tail {p : G.Walk u v} (hp : p.IsPath) (hp' : ¬ p.Nil) : (p.tail hp').IsPath := by
1095+
rw [Walk.isPath_def] at hp ⊢
1096+
rw [← cons_support_tail _ hp', List.nodup_cons] at hp
1097+
exact hp.2
10141098

10151099
/-! ### About paths -/
10161100

0 commit comments

Comments
 (0)