Skip to content

Commit c87cc97

Browse files
committed
feat(Combinatorics/SimpleGraph/Walk): make p.Nil the simpNF of p = nil and p.length = 0 (#39218)
These are equivalent, and `p.Nil` works even when the endpoints of the walk aren't defeq. [#graph theory > Deprecate `p = nil` in favor of `p.Nil`?](https://leanprover.zulipchat.com/#narrow/channel/252551-graph-theory/topic/Deprecate.20.60p.20.3D.20nil.60.20in.20favor.20of.20.60p.2ENil.60.3F/with/538017334) The main event is `Walk/Basic.lean`: - Swapped sides of `nil_iff_eq_nil` to create `eq_nil_iff_nil`, tagged `@[simp]` and deprecated the original - Changed the RHS of `length_eq_zero_iff` from `p = nil` to `p.Nil`, and generalized to non-closed walks - Deprecated `nil_iff_length_eq` which is the symmetric iff - Added `Nil.length_eq_zero` alias - Untagged `exists_length_eq_zero_iff` with `@[simp]` and added `exists_nil_iff` to replace it The rest is fixing everything that broke (or uses of now-deprecated lemmas), and: - [`Walk/Maps`] Added `nil_map_iff` to replace `map_eq_nil_iff` which is the same but with `.Nil` instead of `= nil` - [`Walk/Decomp`] Added `nil_rotate` to replace `rotate_eq_nil` which is the same but with `.Nil` instead of `= nil`
1 parent 4f97290 commit c87cc97

10 files changed

Lines changed: 70 additions & 54 deletions

File tree

Mathlib/Combinatorics/SimpleGraph/Acyclic.lean

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@ theorem IsTree.coe_singletonSubgraph (G : SimpleGraph V) (v : V) :
160160

161161
theorem IsTree.coe_subgraphOfAdj {u v : V} (h : G.Adj u v) : G.subgraphOfAdj h |>.coe.IsTree := by
162162
refine ⟨Subgraph.subgraphOfAdj_connected h, fun w p hp ↦ ?_⟩
163-
have : _ = _ := p.adj_snd <| nil_iff_eq_nil.not.mpr hp.ne_nil
164-
have : _ = _ := p.adj_penultimate <| nil_iff_eq_nil.not.mpr hp.ne_nil
163+
have : _ = _ := p.adj_snd hp.not_nil
164+
have : _ = _ := p.adj_penultimate hp.not_nil
165165
#adaptation_note /-- Before https://github.com/leanprover/lean4/pull/13166
166166
(replacing grind's canonicalizer with a type-directed normalizer), `grind` closed this goal.
167167
It is not yet clear whether this is due to defeq abuse in Mathlib or a problem in the new
@@ -301,7 +301,7 @@ theorem IsAcyclic.isPath_iff_isChain (hG : G.IsAcyclic) {v w : V} (p : G.Walk v
301301
have hcc := List.isChain_cons.mp (edges_cons _ _ ▸ h)
302302
refine cons_isPath_iff head tail |>.mpr ⟨ih hcc.2, ?_⟩
303303
rcases tail.length.eq_zero_or_pos with h' | h'
304-
· simp [nil_iff_support_eq.mp (nil_iff_length_eq.mpr h'), head.ne]
304+
· simp [nil_iff_support_eq.mp (length_eq_zero_iff.mp h'), head.ne]
305305
· by_contra hh
306306
apply hG <| cons head (tail.takeUntil u' hh)
307307
simp only [isCycle_def, isTrail_def, edges_cons, List.nodup_cons, ne_eq, reduceCtorEq,

Mathlib/Combinatorics/SimpleGraph/Connectivity/EdgeConnectivity.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ lemma IsEdgeReachable.le_degree [Fintype (G.neighborSet u)] (h : G.IsEdgeReachab
9898
by_contra! hh
9999
obtain ⟨w, _⟩ :=
100100
@h (G.incidenceSet u) (by simpa [← Set.coe_fintypeCard, ENat.coe_lt_coe]) |>.exists_isPath
101-
simpa using w.adj_snd <| by grind [Walk.nil_iff_length_eq, Walk.eq_of_length_eq_zero]
101+
simpa using w.adj_snd <| mt Walk.Nil.eq huv
102102

103103
lemma IsEdgeConnected.le_degree [Fintype (G.neighborSet u)] [Nontrivial V]
104104
(h : G.IsEdgeConnected k) : k ≤ G.degree u := by

Mathlib/Combinatorics/SimpleGraph/Metric.lean

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,8 @@ theorem edist_le (p : G.Walk u v) :
7373
protected alias Walk.edist_le := edist_le
7474

7575
@[simp]
76-
theorem edist_eq_zero_iff :
77-
G.edist u v = 0 ↔ u = v := by
78-
apply Iff.intro <;> simp [edist, ENat.iInf_eq_zero]
76+
theorem edist_eq_zero_iff : G.edist u v = 0 ↔ u = v := by
77+
simp [edist]
7978

8079
@[simp]
8180
theorem edist_self : edist G v v = 0 :=
@@ -380,7 +379,7 @@ of length at least two: the first and third nodes are different and not connecte
380379
lemma Walk.exists_adj_adj_not_adj_ne {p : G.Walk v w} (hp : p.length = G.dist v w)
381380
(hl : 1 < G.dist v w) : ∃ (x a b : V), G.Adj x a ∧ G.Adj a b ∧ ¬ G.Adj x b ∧ x ≠ b := by
382381
use v, p.getVert 1, p.getVert 2
383-
have hnp : ¬p.Nil := by simpa [nil_iff_length_eq, hp] using Nat.ne_zero_of_lt hl
382+
have hnp : ¬p.Nil := by grind [Nil.length_eq_zero]
384383
have : p.tail.tail.length < p.tail.length := by
385384
rw [← p.tail.length_tail_add_one (by
386385
simp only [not_nil_iff_lt_length, ← p.length_tail_add_one hnp] at hp ⊢

Mathlib/Combinatorics/SimpleGraph/Paths.lean

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -415,9 +415,9 @@ lemma IsPath.getVert_eq_start_iff_of_not_nil {i : ℕ} {p : G.Walk u w} (hp : p.
415415

416416
lemma IsPath.getVert_eq_start_iff {i : ℕ} {p : G.Walk u w} (hp : p.IsPath) (hi : i ≤ p.length) :
417417
p.getVert i = u ↔ i = 0 := by
418-
by_cases h' : p.Nil
419-
· simp_all [nil_iff_length_eq.mp h']
420-
· exact hp.getVert_eq_start_iff_of_not_nil h'
418+
cases p
419+
· simpa using hi
420+
· exact hp.getVert_eq_start_iff_of_not_nil not_nil_cons
421421

422422
lemma IsPath.getVert_eq_end_iff {i : ℕ} {p : G.Walk u w} (hp : p.IsPath) (hi : i ≤ p.length) :
423423
p.getVert i = w ↔ i = p.length := by
@@ -854,8 +854,8 @@ alias ⟨_, map_isTrail_of_injective⟩ := map_isTrail_iff_of_injective
854854

855855
theorem map_isCycle_iff_of_injective {p : G.Walk u u} (hinj : Function.Injective f) :
856856
(p.map f).IsCycle ↔ p.IsCycle := by
857-
rw [isCycle_def, isCycle_def, map_isTrail_iff_of_injective hinj, Ne, map_eq_nil_iff,
858-
support_map, ← List.map_tail, List.nodup_map_iff hinj]
857+
rw [isCycle_def, isCycle_def, map_isTrail_iff_of_injective hinj, ne_eq, ne_eq, eq_nil_iff_nil,
858+
eq_nil_iff_nil, nil_map_iff, support_map, ← List.map_tail, List.nodup_map_iff hinj]
859859

860860
alias ⟨_, IsCycle.map⟩ := map_isCycle_iff_of_injective
861861

Mathlib/Combinatorics/SimpleGraph/Walk/Basic.lean

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,17 +101,13 @@ theorem eq_of_length_eq_zero {u v : V} : ∀ {p : G.Walk u v}, p.length = 0 →
101101
theorem adj_of_length_eq_one {u v : V} : ∀ {p : G.Walk u v}, p.length = 1 → G.Adj u v
102102
| cons h nil, _ => h
103103

104-
@[simp]
105104
theorem exists_length_eq_zero_iff {u v : V} : (∃ p : G.Walk u v, p.length = 0) ↔ u = v :=
106105
fun ⟨_, h⟩ ↦ (eq_of_length_eq_zero h), (· ▸ ⟨nil, rfl⟩)⟩
107106

108107
@[simp]
109108
lemma exists_length_eq_one_iff {u v : V} : (∃ (p : G.Walk u v), p.length = 1) ↔ G.Adj u v :=
110109
fun ⟨_, hp⟩ ↦ adj_of_length_eq_one hp, (⟨·.toWalk, by simp⟩)⟩
111110

112-
@[simp]
113-
theorem length_eq_zero_iff {u : V} {p : G.Walk u u} : p.length = 0 ↔ p = nil := by cases p <;> simp
114-
115111
/-- The `support` of a walk is the list of vertices it visits in order. -/
116112
def support {u v : V} : G.Walk u v → List V
117113
| nil => [u]
@@ -367,9 +363,16 @@ lemma darts_eq_nil {p : G.Walk v w} : p.darts = [] ↔ p.Nil := by
367363
lemma edges_eq_nil {p : G.Walk v w} : p.edges = [] ↔ p.Nil := by
368364
cases p <;> simp
369365

370-
lemma nil_iff_length_eq {p : G.Walk v w} : p.Nil ↔ p.length = 0 := by
366+
@[simp]
367+
theorem length_eq_zero_iff {p : G.Walk u v} : p.length = 0 ↔ p.Nil := by
371368
cases p <;> simp
372369

370+
alias ⟨_, Nil.length_eq_zero⟩ := length_eq_zero_iff
371+
372+
@[deprecated length_eq_zero_iff (since := "2026-05-11")]
373+
lemma nil_iff_length_eq {p : G.Walk v w} : p.Nil ↔ p.length = 0 :=
374+
length_eq_zero_iff.symm
375+
373376
lemma not_nil_iff_lt_length {p : G.Walk v w} : ¬ p.Nil ↔ 0 < p.length := by
374377
cases p <;> simp
375378

@@ -378,16 +381,25 @@ lemma not_nil_iff {p : G.Walk v w} :
378381
cases p <;> simp [*]
379382

380383
/-- A walk with its endpoints defeq is `Nil` if and only if it is equal to `nil`. -/
381-
lemma nil_iff_eq_nil : ∀ {p : G.Walk v v}, p.Nil ↔ p = nil
382-
| .nil | .cons _ _ => by simp
384+
@[simp]
385+
theorem eq_nil_iff_nil {p : G.Walk v v} : p = nil ↔ p.Nil := by
386+
cases p <;> simp
387+
388+
alias ⟨_, Nil.eq_nil⟩ := eq_nil_iff_nil
383389

384-
alias ⟨Nil.eq_nil, _⟩ := nil_iff_eq_nil
390+
@[deprecated eq_nil_iff_nil (since := "2026-05-11")]
391+
lemma nil_iff_eq_nil : ∀ {p : G.Walk v v}, p.Nil ↔ p = nil :=
392+
eq_nil_iff_nil.symm
385393

386394
lemma nil_of_subsingleton [Subsingleton V] (p : G.Walk v w) : p.Nil :=
387395
match p with
388396
| nil => Nil.nil
389397
| cons h w => Unique.eq_default G ▸ h |>.elim
390398

399+
@[simp]
400+
theorem exists_nil_iff {u v : V} : (∃ p : G.Walk u v, p.Nil) ↔ u = v :=
401+
fun ⟨_, h⟩ ↦ h.eq, (· ▸ ⟨nil, .nil⟩)⟩
402+
391403
/-- The recursion principle for nonempty walks -/
392404
@[elab_as_elim]
393405
def notNilRec {motive : {u w : V} → (p : G.Walk u w) → (h : ¬ p.Nil) → Sort*}

Mathlib/Combinatorics/SimpleGraph/Walk/Counting.lean

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,25 +32,28 @@ namespace SimpleGraph
3232

3333
variable {V : Type u} (G : SimpleGraph V)
3434

35-
theorem set_walk_self_length_zero_eq (u : V) : {p : G.Walk u u | p.length = 0} = {Walk.nil} := by
36-
simp
35+
theorem Walk.setOf_length_eq_zero (u : V) : {p : G.Walk u u | p.length = 0} = {.nil} := by
36+
simp [Walk.length_eq_zero_iff, ← Walk.eq_nil_iff_nil]
3737

38-
theorem set_walk_length_zero_eq_of_ne {u v : V} (h : u ≠ v) :
39-
{p : G.Walk u v | p.length = 0} = ∅ := by
40-
ext p
41-
simp only [Set.mem_setOf_eq, Set.mem_empty_iff_false, iff_false]
42-
exact fun h' => absurd (Walk.eq_of_length_eq_zero h') h
38+
@[deprecated (since := "2026-05-12")]
39+
alias set_walk_self_length_zero_eq := Walk.setOf_length_eq_zero
40+
41+
theorem Walk.setOf_length_eq_zero_of_ne {u v : V} (h : u ≠ v) :
42+
{p : G.Walk u v | p.length = 0} = ∅ :=
43+
Set.eq_empty_of_forall_notMem (h <| ·.eq_of_length_eq_zero ·)
44+
45+
@[deprecated (since := "2026-05-12")]
46+
alias set_walk_length_zero_eq_of_ne := Walk.setOf_length_eq_zero_of_ne
4347

44-
theorem set_walk_length_succ_eq (u v : V) (n : ℕ) :
45-
{p : G.Walk u v | p.length = n.succ} =
48+
theorem Walk.setOf_length_eq_add_one (u v : V) (n : ℕ) :
49+
{p : G.Walk u v | p.length = n + 1} =
4650
⋃ (w : V) (h : G.Adj u w), Walk.cons h '' {p' : G.Walk w v | p'.length = n} := by
4751
ext p
4852
cases p with
4953
| nil => simp [eq_comm]
50-
| cons huw pwv =>
51-
simp only [Nat.succ_eq_add_one, Set.mem_setOf_eq, Walk.length_cons, add_left_inj,
52-
Set.mem_iUnion, Set.mem_image]
53-
grind
54+
| cons huw pwv => grind [length_cons, Set.mem_iUnion]
55+
56+
@[deprecated (since := "2026-05-12")] alias set_walk_length_succ_eq := Walk.setOf_length_eq_add_one
5457

5558
/-- Walks of length two from `u` to `v` correspond bijectively to common neighbours of `u` and `v`.
5659
Note that `u` and `v` may be the same. -/
@@ -86,10 +89,9 @@ def finsetWalkLength (n : ℕ) (u v : V) : Finset (G.Walk u v) :=
8689
theorem coe_finsetWalkLength_eq (n : ℕ) (u v : V) :
8790
(G.finsetWalkLength n u v : Set (G.Walk u v)) = {p : G.Walk u v | p.length = n} := by
8891
induction n generalizing u v with
89-
| zero =>
90-
obtain rfl | huv := eq_or_ne u v <;> simp [finsetWalkLength, set_walk_length_zero_eq_of_ne, *]
92+
| zero => grind [finsetWalkLength, Walk.length_eq_zero_iff, Walk.eq_nil_iff_nil, Walk.Nil.eq]
9193
| succ n ih =>
92-
simp only [finsetWalkLength, set_walk_length_succ_eq, Finset.coe_biUnion, Finset.mem_coe,
94+
simp only [finsetWalkLength, Walk.setOf_length_eq_add_one, Finset.coe_biUnion, Finset.mem_coe,
9395
Finset.mem_univ, Set.iUnion_true, Finset.coe_map, Set.iUnion_coe_set]
9496
congr!
9597
grind

Mathlib/Combinatorics/SimpleGraph/Walk/Decomp.lean

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,9 @@ lemma getVert_takeUntil {u v : V} {n : ℕ} {p : G.Walk u v} (hw : w ∈ p.suppo
239239
lemma snd_takeUntil (hsu : w ≠ u) (p : G.Walk u v) (h : w ∈ p.support) :
240240
(p.takeUntil w h).snd = p.snd := by
241241
apply p.getVert_takeUntil h
242-
by_contra! hc
243-
simp only [Nat.lt_one_iff, ← nil_iff_length_eq, nil_takeUntil] at hc
244-
exact hsu hc.symm
242+
contrapose hsu
243+
symm
244+
simpa [length_eq_zero_iff] using hsu
245245

246246
lemma getVert_length_takeUntil {p : G.Walk v w} (h : u ∈ p.support) :
247247
p.getVert (p.takeUntil _ h).length = u := by
@@ -313,9 +313,13 @@ theorem rotate_edges (c : G.Walk v v) (u : V) (h) : (c.rotate u h).edges ~r c.ed
313313
@[simp] lemma length_rotate (c : G.Walk v v) (u : V) (h) : (c.rotate u h).length = c.length := by
314314
simpa using (rotate_edges c u h).perm.length_eq
315315

316-
@[simp] lemma rotate_eq_nil {c : G.Walk v v} {u : V} (h) : c.rotate u h = nil ↔ c = nil := by
316+
@[simp]
317+
theorem nil_rotate {c : G.Walk v v} (h) : (c.rotate u h).Nil ↔ c.Nil := by
317318
simp [← length_eq_zero_iff]
318319

320+
@[deprecated nil_rotate (since := "2026-05-11")]
321+
lemma rotate_eq_nil {c : G.Walk v v} (h) : c.rotate u h = nil ↔ c = nil := by simp
322+
319323
end WalkDecomp
320324

321325
/-- Given a walk `p` and a node in the support, there exists a natural `n`, such that given node

Mathlib/Combinatorics/SimpleGraph/Walk/Maps.lean

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,12 @@ theorem map_eq_of_eq {f : G →g G'} (f' : G →g G') (h : f = f') :
7575
subst_vars
7676
rfl
7777

78+
variable {p} in
7879
@[simp]
80+
theorem nil_map_iff : (p.map f).Nil ↔ p.Nil := by
81+
cases p <;> simp
82+
83+
@[deprecated nil_map_iff (since := "2026-05-12")]
7984
theorem map_eq_nil_iff {p : G.Walk u u} : p.map f = nil ↔ p = nil := by cases p <;> simp
8085

8186
@[simp]

Mathlib/Combinatorics/SimpleGraph/Walk/Operations.lean

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ lemma drop_zero {u v} (p : G.Walk u v) :
653653

654654
lemma nil_drop_of_length_le {u v n} {p : G.Walk u v} (h : p.length ≤ n) :
655655
(p.drop n).Nil := by
656-
rw [nil_iff_length_eq, drop_length, Nat.sub_eq_zero_of_le h]
656+
rw [← length_eq_zero_iff, drop_length, Nat.sub_eq_zero_of_le h]
657657

658658
lemma drop_support_eq_support_drop_min {u v} (p : G.Walk u v) (n : ℕ) :
659659
(p.drop n).support = p.support.drop (n ⊓ p.length) := by
@@ -702,8 +702,8 @@ lemma dropLast_concat {t u v} (p : G.Walk u v) (h : G.Adj v t) :
702702
(p.concat h).dropLast = p.copy rfl (by simp) := by
703703
induction p
704704
· rfl
705-
· simp_rw [concat_cons]
706-
rw [dropLast_cons_of_not_nil] <;> simp [*, nil_iff_length_eq]
705+
· rw! [concat_cons, dropLast_cons_of_not_nil] <;>
706+
simp [*, ← length_eq_zero_iff]
707707

708708
lemma cons_tail_eq (p : G.Walk u v) (hp : ¬ p.Nil) :
709709
cons (p.adj_snd hp) p.tail = p := by
@@ -756,11 +756,8 @@ protected lemma Nil.dropLast {p : G.Walk v w} (hp : p.Nil) : p.dropLast.Nil := b
756756
subst_vars
757757
rfl
758758

759-
lemma Nil.eq_copy_nil {p : G.Walk u v} (h : p.Nil) :
760-
p = Walk.nil.copy rfl h.eq := by
761-
have := h.eq
762-
subst this
763-
simp [nil_iff_eq_nil.mp h]
759+
lemma Nil.eq_copy_nil {p : G.Walk u v} (h : p.Nil) : p = Walk.nil.copy rfl h.eq := by
760+
grind [eq_nil_iff_nil, copy_rfl_rfl]
764761

765762
lemma drop_of_length_le {u v n} {p : G.Walk u v} (h : p.length ≤ n) :
766763
p.drop n = nil.copy rfl (p.getVert_of_length_le h) :=

Mathlib/Combinatorics/SimpleGraph/Walk/Traversal.lean

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,8 @@ lemma penultimate_cons_of_not_nil (h : G.Adj u v) (p : G.Walk v w) (hp : ¬ p.Ni
178178
p.notNilRec (by simp) hp h
179179

180180
@[simp]
181-
lemma adj_penultimate {p : G.Walk v w} (hp : ¬ p.Nil) :
182-
G.Adj p.penultimate w := by
183-
conv => rhs; rw [← getVert_length p]
184-
rw [nil_iff_length_eq] at hp
185-
convert adj_getVert_succ _ _ <;> lia
181+
lemma adj_penultimate {p : G.Walk v w} (hp : ¬ p.Nil) : G.Adj p.penultimate w := by
182+
grind [getVert_length, length_eq_zero_iff, adj_getVert_succ]
186183

187184
lemma penultimate_mem_dropLast_support {p : G.Walk u v} (h : ¬p.Nil) :
188185
p.penultimate ∈ p.support.dropLast := by

0 commit comments

Comments
 (0)