Skip to content

Commit 35a1d50

Browse files
committed
feat: turn a closed walk into a cycle (#31217)
From the ProofBench workshop Closes #23637, which does the same thing under a stronger hypothesis on the starting closed walk.
1 parent 9cac091 commit 35a1d50

File tree

4 files changed

+50
-18
lines changed

4 files changed

+50
-18
lines changed

Mathlib/Combinatorics/SimpleGraph/Acyclic.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ theorem isAcyclic_of_path_unique (h : ∀ (v w : V) (p q : G.Path v w), p = q) :
157157
cases c with
158158
| nil => cases hc.2.1 rfl
159159
| cons ha c' =>
160-
simp only [Walk.cons_isTrail_iff, Walk.support_cons, List.tail_cons] at hc
160+
simp only [Walk.isTrail_cons, Walk.support_cons, List.tail_cons] at hc
161161
specialize h _ _ ⟨c', by simp only [Walk.isPath_def, hc.2]⟩ (Path.singleton ha.symm)
162162
rw [Path.singleton, Subtype.mk.injEq] at h
163163
simp [h] at hc

Mathlib/Combinatorics/SimpleGraph/Paths.lean

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ variable (G : SimpleGraph V) (G' : SimpleGraph V')
5858

5959
namespace Walk
6060

61-
variable {G} {u v w : V}
61+
variable {G} {u u' v w : V}
6262

6363
/-! ### Trails, paths, circuits, cycles -/
6464

@@ -136,9 +136,14 @@ theorem IsTrail.of_cons {u v w : V} {h : G.Adj u v} {p : G.Walk v w} :
136136
(cons h p).IsTrail → p.IsTrail := by simp [isTrail_def]
137137

138138
@[simp]
139-
theorem cons_isTrail_iff {u v w : V} (h : G.Adj u v) (p : G.Walk v w) :
139+
theorem isTrail_cons {u v w : V} (h : G.Adj u v) (p : G.Walk v w) :
140140
(cons h p).IsTrail ↔ p.IsTrail ∧ s(u, v) ∉ p.edges := by simp [isTrail_def, and_comm]
141141

142+
@[deprecated (since := "2025-11-03")] alias cons_isTrail_iff := isTrail_cons
143+
144+
protected lemma IsTrail.cons {w : G.Walk u' v} (hw : w.IsTrail) (hu : G.Adj u u')
145+
(hu' : s(u, u') ∉ w.edges) : (w.cons hu).IsTrail := by simp [*]
146+
142147
theorem IsTrail.reverse {u v : V} (p : G.Walk u v) (h : p.IsTrail) : p.reverse.IsTrail := by
143148
simpa [isTrail_def] using h
144149

@@ -581,7 +586,7 @@ theorem notMem_edges_of_loop {v : V} {e : Sym2 V} {p : G.Path v v} :
581586

582587
theorem cons_isCycle {u v : V} (p : G.Path v u) (h : G.Adj u v)
583588
(he : s(u, v) ∉ (p : G.Walk v u).edges) : (Walk.cons h ↑p).IsCycle := by
584-
simp [Walk.isCycle_def, Walk.cons_isTrail_iff, he]
589+
simp [Walk.isCycle_def, Walk.isTrail_cons, he]
585590

586591
end Path
587592

@@ -590,7 +595,7 @@ end Path
590595

591596
namespace Walk
592597

593-
variable {G} [DecidableEq V]
598+
variable {G} [DecidableEq V] {u u' v v' : V}
594599

595600
/-- Given a walk, produces a walk from it by bypassing subwalks between repeated vertices.
596601
The result is a path, as shown in `SimpleGraph.Walk.bypass_isPath`.
@@ -605,12 +610,12 @@ def bypass {u v : V} : G.Walk u v → G.Walk u v
605610
cons ha p'
606611

607612
@[simp]
608-
theorem bypass_copy {u v u' v'} (p : G.Walk u v) (hu : u = u') (hv : v = v') :
613+
theorem bypass_copy (p : G.Walk u v) (hu : u = u') (hv : v = v') :
609614
(p.copy hu hv).bypass = p.bypass.copy hu hv := by
610615
subst_vars
611616
rfl
612617

613-
theorem bypass_isPath {u v : V} (p : G.Walk u v) : p.bypass.IsPath := by
618+
theorem bypass_isPath (p : G.Walk u v) : p.bypass.IsPath := by
614619
induction p with
615620
| nil => simp!
616621
| cons _ p' ih =>
@@ -619,7 +624,7 @@ theorem bypass_isPath {u v : V} (p : G.Walk u v) : p.bypass.IsPath := by
619624
· exact ih.dropUntil hs
620625
· simp [*, cons_isPath_iff]
621626

622-
theorem length_bypass_le {u v : V} (p : G.Walk u v) : p.bypass.length ≤ p.length := by
627+
theorem length_bypass_le (p : G.Walk u v) : p.bypass.length ≤ p.length := by
623628
induction p with
624629
| nil => rfl
625630
| cons _ _ ih =>
@@ -632,7 +637,7 @@ theorem length_bypass_le {u v : V} (p : G.Walk u v) : p.bypass.length ≤ p.leng
632637
· rw [length_cons, length_cons]
633638
exact Nat.add_le_add_right ih 1
634639

635-
lemma bypass_eq_self_of_length_le {u v : V} (p : G.Walk u v) (h : p.length ≤ p.bypass.length) :
640+
lemma bypass_eq_self_of_length_le (p : G.Walk u v) (h : p.length ≤ p.bypass.length) :
636641
p.bypass = p := by
637642
induction p with
638643
| nil => rfl
@@ -651,10 +656,10 @@ lemma bypass_eq_self_of_length_le {u v : V} (p : G.Walk u v) (h : p.length ≤ p
651656
rw [ih h]
652657

653658
/-- Given a walk, produces a path with the same endpoints using `SimpleGraph.Walk.bypass`. -/
654-
def toPath {u v : V} (p : G.Walk u v) : G.Path u v :=
659+
def toPath (p : G.Walk u v) : G.Path u v :=
655660
⟨p.bypass, p.bypass_isPath⟩
656661

657-
theorem support_bypass_subset {u v : V} (p : G.Walk u v) : p.bypass.support ⊆ p.support := by
662+
theorem support_bypass_subset (p : G.Walk u v) : p.bypass.support ⊆ p.support := by
658663
induction p with
659664
| nil => simp!
660665
| cons _ _ ih =>
@@ -667,11 +672,11 @@ theorem support_bypass_subset {u v : V} (p : G.Walk u v) : p.bypass.support ⊆
667672
apply List.cons_subset_cons
668673
assumption
669674

670-
theorem support_toPath_subset {u v : V} (p : G.Walk u v) :
675+
theorem support_toPath_subset (p : G.Walk u v) :
671676
(p.toPath : G.Walk u v).support ⊆ p.support :=
672677
support_bypass_subset _
673678

674-
theorem darts_bypass_subset {u v : V} (p : G.Walk u v) : p.bypass.darts ⊆ p.darts := by
679+
theorem darts_bypass_subset (p : G.Walk u v) : p.bypass.darts ⊆ p.darts := by
675680
induction p with
676681
| nil => simp!
677682
| cons _ _ ih =>
@@ -682,15 +687,41 @@ theorem darts_bypass_subset {u v : V} (p : G.Walk u v) : p.bypass.darts ⊆ p.da
682687
· rw [darts_cons]
683688
exact List.cons_subset_cons _ ih
684689

685-
theorem edges_bypass_subset {u v : V} (p : G.Walk u v) : p.bypass.edges ⊆ p.edges :=
690+
theorem edges_bypass_subset (p : G.Walk u v) : p.bypass.edges ⊆ p.edges :=
686691
List.map_subset _ p.darts_bypass_subset
687692

688-
theorem darts_toPath_subset {u v : V} (p : G.Walk u v) : (p.toPath : G.Walk u v).darts ⊆ p.darts :=
693+
theorem darts_toPath_subset (p : G.Walk u v) : (p.toPath : G.Walk u v).darts ⊆ p.darts :=
689694
darts_bypass_subset _
690695

691-
theorem edges_toPath_subset {u v : V} (p : G.Walk u v) : (p.toPath : G.Walk u v).edges ⊆ p.edges :=
696+
theorem edges_toPath_subset (p : G.Walk u v) : (p.toPath : G.Walk u v).edges ⊆ p.edges :=
692697
edges_bypass_subset _
693698

699+
/-- Bypass repeated vertices like `Walk.bypass`, except the starting vertex.
700+
701+
This is intended to be used for closed walks, for which `Walk.bypass` unhelpfully returns the empty
702+
walk. -/
703+
def cycleBypass : G.Walk v v → G.Walk v v
704+
| .nil => .nil
705+
| .cons hvv' w => .cons hvv' w.bypass
706+
707+
@[simp] lemma cycleBypass_nil : (.nil : G.Walk v v).cycleBypass = .nil := rfl
708+
709+
lemma edges_cycleBypass_subset : ∀ {w : G.Walk v v}, w.cycleBypass.edges ⊆ w.edges
710+
| .nil => by simp
711+
| .cons (v := v') hvv' w => by
712+
classical
713+
dsimp only [cycleBypass, edges_cons]
714+
gcongr
715+
exact edges_bypass_subset _
716+
717+
lemma IsTrail.isCycle_cycleBypass : ∀ {w : G.Walk v v}, w ≠ .nil → w.IsTrail → w.cycleBypass.IsCycle
718+
| .cons (v := v') hvv' w, _, hw => by
719+
dsimp [cycleBypass]
720+
refine ⟨⟨(bypass_isPath _).isTrail.cons _ fun hvv' ↦ ?_, by simp⟩, ?_⟩
721+
· simp only [isTrail_cons] at hw
722+
exact hw.2 <| edges_bypass_subset _ hvv'
723+
· simpa using (bypass_isPath _).support_nodup
724+
694725
end Walk
695726

696727
/-! ### Mapping paths -/
@@ -726,7 +757,7 @@ theorem map_isTrail_iff_of_injective (hinj : Function.Injective f) :
726757
induction p with
727758
| nil => simp
728759
| cons _ _ ih =>
729-
rw [map_cons, cons_isTrail_iff, ih, cons_isTrail_iff]
760+
rw [map_cons, isTrail_cons, ih, isTrail_cons]
730761
apply and_congr_right'
731762
rw [← Sym2.map_pair_eq, edges_map, ← List.mem_map_of_injective (Sym2.map.injective hinj)]
732763

Mathlib/Combinatorics/SimpleGraph/Trails.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ theorem IsTrail.even_countP_edges_iff {u v : V} {p : G.Walk u v} (ht : p.IsTrail
5656
induction p with
5757
| nil => simp
5858
| cons huv p ih =>
59-
rw [cons_isTrail_iff] at ht
59+
rw [isTrail_cons] at ht
6060
specialize ih ht.1
6161
simp only [List.countP_cons, Ne, edges_cons, Sym2.mem_iff]
6262
split_ifs with h

Mathlib/Tactic/GCongr/CoreAttrs.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ attribute [gcongr] mt
3636
List.Sublist.append List.Sublist.append_left List.Sublist.append_right
3737
List.Sublist.reverse List.drop_sublist_drop_left List.Sublist.drop
3838
List.Perm.append_left List.Perm.append_right List.Perm.append List.Perm.map
39+
List.cons_subset_cons
3940
Nat.sub_le_sub_left Nat.sub_le_sub_right Nat.sub_lt_sub_left Nat.sub_lt_sub_right
4041

4142
end Mathlib.Tactic.GCongr

0 commit comments

Comments
 (0)