Skip to content

Commit b4911b2

Browse files
LodeVermeulenlinus-mdb-mehtadsertbasYaelDillies
committed
feat: Define Hamiltonian paths and cycles (#7102)
Co-authored-by: Bhavik Mehta <bhavikmehta8@gmail.com> Co-authored-by: Linus Sommer Co-authored-by: Lode Vermeulen Co-authored-by: Lode <lode685@gmail.com> Co-authored-by: Linus <95619282+linus-md@users.noreply.github.com> Co-authored-by: Bhavik Mehta <bhavikmehta8@gmail.com> Co-authored-by: Doga Can Sertbas <dogacan.sertbas@gmail.com> Co-authored-by: Yaël Dillies <yael.dillies@gmail.com>
1 parent 1df6b98 commit b4911b2

File tree

4 files changed

+161
-8
lines changed

4 files changed

+161
-8
lines changed

Mathlib.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,6 +1735,7 @@ import Mathlib.Combinatorics.SimpleGraph.Ends.Properties
17351735
import Mathlib.Combinatorics.SimpleGraph.Finite
17361736
import Mathlib.Combinatorics.SimpleGraph.Finsubgraph
17371737
import Mathlib.Combinatorics.SimpleGraph.Girth
1738+
import Mathlib.Combinatorics.SimpleGraph.Hamiltonian
17381739
import Mathlib.Combinatorics.SimpleGraph.Hasse
17391740
import Mathlib.Combinatorics.SimpleGraph.IncMatrix
17401741
import Mathlib.Combinatorics.SimpleGraph.Init

Mathlib/Algebra/BigOperators/Group/Finset.lean

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2407,6 +2407,11 @@ theorem prod_toFinset {M : Type*} [DecidableEq α] [CommMonoid M] (f : α → M)
24072407
#align list.prod_to_finset List.prod_toFinset
24082408
#align list.sum_to_finset List.sum_toFinset
24092409

2410+
@[simp]
2411+
theorem sum_toFinset_count_eq_length [DecidableEq α] (l : List α) :
2412+
∑ a in l.toFinset, l.count a = l.length :=
2413+
by simpa using (Finset.sum_list_map_count l fun _ => (1 : ℕ)).symm
2414+
24102415
end List
24112416

24122417
namespace Multiset
@@ -2450,11 +2455,8 @@ theorem disjoint_finset_sum_right {β : Type*} {i : Finset β} {f : β → Multi
24502455
variable [DecidableEq α]
24512456

24522457
@[simp]
2453-
theorem toFinset_sum_count_eq (s : Multiset α) : ∑ a ∈ s.toFinset, s.count a = card s :=
2454-
calc
2455-
∑ a ∈ s.toFinset, s.count a = ∑ a ∈ s.toFinset, s.count a • 1 := by simp
2456-
_ = (s.map fun _ => 1).sum := (Finset.sum_multiset_map_count _ _).symm
2457-
_ = card s := by simp
2458+
theorem toFinset_sum_count_eq (s : Multiset α) : ∑ a in s.toFinset, s.count a = card s :=
2459+
by simpa using (Finset.sum_multiset_map_count s (fun _ => (1 : ℕ))).symm
24582460
#align multiset.to_finset_sum_count_eq Multiset.toFinset_sum_count_eq
24592461

24602462
@[simp]

Mathlib/Combinatorics/SimpleGraph/Connectivity.lean

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,12 @@ lemma not_nil_iff {p : G.Walk v w} :
877877
¬ p.Nil ↔ ∃ (u : V) (h : G.Adj v u) (q : G.Walk u w), p = cons h q := by
878878
cases p <;> simp [*]
879879

880+
/-- A walk with its endpoints defeq is `Nil` if and only if it is equal to `nil`. -/
881+
lemma nil_iff_eq_nil : ∀ {p : G.Walk v v}, p.Nil ↔ p = nil
882+
| .nil | .cons _ _ => by simp
883+
884+
alias ⟨Nil.eq_nil, _⟩ := nil_iff_eq_nil
885+
880886
@[elab_as_elim]
881887
def notNilRec {motive : {u w : V} → (p : G.Walk u w) → (h : ¬ p.Nil) → Sort*}
882888
(cons : {u v w : V} → (h : G.Adj u v) → (q : G.Walk v w) → motive (cons h q) not_nil_cons)
@@ -913,7 +919,7 @@ variable {x y : V} -- TODO: rename to u, v, w instead?
913919
cons (p.adj_sndOfNotNil hp) (p.tail hp) = p :=
914920
p.notNilRec (fun _ _ => rfl) hp
915921

916-
@[simp] lemma cons_support_tail (p : G.Walk x y) (hp : ¬ p.Nil) :
922+
@[simp] lemma cons_support_tail (p : G.Walk x y) (hp : ¬p.Nil) :
917923
x :: (p.tail hp).support = p.support := by
918924
rw [← support_cons, cons_tail_eq]
919925

@@ -925,6 +931,10 @@ variable {x y : V} -- TODO: rename to u, v, w instead?
925931
(p.copy hx hy).Nil = p.Nil := by
926932
subst_vars; rfl
927933

934+
@[simp] lemma support_tail (p : G.Walk v v) (hp) :
935+
(p.tail hp).support = p.support.tail := by
936+
rw [← cons_support_tail p hp, List.tail_cons]
937+
928938
/-! ### Trails, paths, circuits, cycles -/
929939

930940
/-- A *trail* is a walk with no repeating edges. -/
@@ -994,6 +1004,8 @@ theorem isCircuit_copy {u u'} (p : G.Walk u u) (hu : u = u') :
9941004
rfl
9951005
#align simple_graph.walk.is_circuit_copy SimpleGraph.Walk.isCircuit_copy
9961006

1007+
lemma IsCircuit.not_nil {p : G.Walk v v} (hp : IsCircuit p) : ¬ p.Nil := (hp.ne_nil ·.eq_nil)
1008+
9971009
theorem isCycle_def {u : V} (p : G.Walk u u) :
9981010
p.IsCycle ↔ p.IsTrail ∧ p ≠ nil ∧ p.support.tail.Nodup :=
9991011
Iff.intro (fun h => ⟨h.1.1, h.1.2, h.2⟩) fun h => ⟨⟨h.1, h.2.1⟩, h.2.2
@@ -1006,6 +1018,8 @@ theorem isCycle_copy {u u'} (p : G.Walk u u) (hu : u = u') :
10061018
rfl
10071019
#align simple_graph.walk.is_cycle_copy SimpleGraph.Walk.isCycle_copy
10081020

1021+
lemma IsCycle.not_nil {p : G.Walk v v} (hp : IsCycle p) : ¬ p.Nil := (hp.ne_nil ·.eq_nil)
1022+
10091023
@[simp]
10101024
theorem IsTrail.nil {u : V} : (nil : G.Walk u u).IsTrail :=
10111025
by simp [edges]⟩
@@ -1710,8 +1724,8 @@ theorem map_isCycle_iff_of_injective {p : G.Walk u u} (hinj : Function.Injective
17101724
support_map, ← List.map_tail, List.nodup_map_iff hinj]
17111725
#align simple_graph.walk.map_is_cycle_iff_of_injective SimpleGraph.Walk.map_isCycle_iff_of_injective
17121726

1713-
alias ⟨_, map_isCycle_of_injective⟩ := map_isCycle_iff_of_injective
1714-
#align simple_graph.walk.map_is_cycle_of_injective SimpleGraph.Walk.map_isCycle_of_injective
1727+
alias ⟨_, IsCycle.map⟩ := map_isCycle_iff_of_injective
1728+
#align simple_graph.walk.map_is_cycle_of_injective SimpleGraph.Walk.IsCycle.map
17151729

17161730
variable (p f)
17171731

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/-
2+
Copyright (c) 2023 Bhavik Mehta, Rishi Mehta, Linus Sommer. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Bhavik Mehta, Rishi Mehta, Linus Sommer
5+
-/
6+
import Mathlib.Algebra.Order.Ring.Nat
7+
import Mathlib.Combinatorics.SimpleGraph.Connectivity
8+
9+
/-!
10+
# Hamiltonian Graphs
11+
12+
In this file we introduce hamiltonian paths, cycles and graphs.
13+
14+
## Main definitions
15+
16+
- `SimpleGraph.Walk.IsHamiltonian`: Predicate for a walk to be hamiltonian.
17+
- `SimpleGraph.Walk.IsHamiltonianCycle`: Predicate for a walk to be a hamiltonian cycle.
18+
- `SimpleGraph.IsHamiltonian`: Predicate for a graph to be hamiltonian.
19+
-/
20+
21+
open Finset Function
22+
open scoped BigOperators
23+
24+
namespace SimpleGraph
25+
variable {α β : Type*} [Fintype α] [Fintype β] [DecidableEq α] [DecidableEq β] {G : SimpleGraph α}
26+
{a b : α} {p : G.Walk a b}
27+
28+
namespace Walk
29+
30+
/-- A hamiltonian path is a walk `p` that visits every vertex exactly once. Note that while
31+
this definition doesn't contain that `p` is a path, `p.isPath` gives that. -/
32+
def IsHamiltonian (p : G.Walk a b) : Prop := ∀ a, p.support.count a = 1
33+
34+
lemma IsHamiltonian.map {H : SimpleGraph β} (f : G →g H) (hf : Bijective f) (hp : p.IsHamiltonian) :
35+
(p.map f).IsHamiltonian := by
36+
simp [IsHamiltonian, hf.surjective.forall, hf.injective, hp _]
37+
38+
/-- A hamiltonian path visits every vertex. -/
39+
@[simp] lemma IsHamiltonian.mem_support (hp : p.IsHamiltonian) (c : α) : c ∈ p.support := by
40+
simp only [← List.count_pos_iff_mem, hp _, Nat.zero_lt_one]
41+
42+
/-- The support of a hamiltonian walk is the entire vertex set. -/
43+
lemma IsHamiltonian.support_toFinset (hp : p.IsHamiltonian) : p.support.toFinset = Finset.univ := by
44+
simp [eq_univ_iff_forall, hp]
45+
46+
/-- The length of a hamiltonian path is one less than the number of vertices of the graph. -/
47+
lemma IsHamiltonian.length_eq (hp : p.IsHamiltonian) : p.length = Fintype.card α - 1 :=
48+
eq_tsub_of_add_eq $ by
49+
rw [← length_support, ← List.sum_toFinset_count_eq_length, Finset.sum_congr rfl fun _ _ ↦ hp _,
50+
← card_eq_sum_ones, hp.support_toFinset, card_univ]
51+
52+
/-- Hamiltonian paths are paths. -/
53+
lemma IsHamiltonian.isPath (hp : p.IsHamiltonian) : p.IsPath :=
54+
IsPath.mk' <| List.nodup_iff_count_le_one.2 <| (le_of_eq <| hp ·)
55+
56+
/-- A path whose support contains every vertex is hamiltonian. -/
57+
lemma IsPath.isHamiltonian_of_mem (hp : p.IsPath) (hp' : ∀ w, w ∈ p.support) :
58+
p.IsHamiltonian := fun _ ↦
59+
le_antisymm (List.nodup_iff_count_le_one.1 hp.support_nodup _) (List.count_pos_iff_mem.2 (hp' _))
60+
61+
lemma IsPath.isHamiltonian_iff (hp : p.IsPath) : p.IsHamiltonian ↔ ∀ w, w ∈ p.support :=
62+
⟨(·.mem_support), hp.isHamiltonian_of_mem⟩
63+
64+
/-- A hamiltonian cycle is a cycle that visits every vertex once. -/
65+
structure IsHamiltonianCycle (p : G.Walk a a) extends p.IsCycle : Prop :=
66+
isHamiltonian_tail : (p.tail toIsCycle.not_nil).IsHamiltonian
67+
68+
variable {p : G.Walk a a}
69+
70+
lemma IsHamiltonianCycle.isCycle (hp : p.IsHamiltonianCycle) : p.IsCycle :=
71+
hp.toIsCycle
72+
73+
lemma IsHamiltonianCycle.map {H : SimpleGraph β} (f : G →g H) (hf : Bijective f)
74+
(hp : p.IsHamiltonianCycle) : (p.map f).IsHamiltonianCycle where
75+
toIsCycle := hp.isCycle.map hf.injective
76+
isHamiltonian_tail := by
77+
simp [IsHamiltonian, support_tail, hf.surjective.forall, List.count_tail, hf.injective]
78+
intro x
79+
rcases p with (_ | ⟨y, p⟩)
80+
· cases hp.ne_nil rfl
81+
simp only [support_cons, List.count_cons, List.map_cons, List.head_cons, hf.injective.eq_iff,
82+
add_tsub_cancel_right]
83+
exact hp.isHamiltonian_tail _
84+
85+
lemma isHamiltonianCycle_isCycle_and_isHamiltonian_tail :
86+
p.IsHamiltonianCycle ↔ ∃ h : p.IsCycle, (p.tail h.not_nil).IsHamiltonian :=
87+
fun ⟨h, h'⟩ ↦ ⟨h, h'⟩, fun ⟨h, h'⟩ ↦ ⟨h, h'⟩⟩
88+
89+
lemma isHamiltonianCycle_iff_isCycle_and_support_count_tail_eq_one :
90+
p.IsHamiltonianCycle ↔ p.IsCycle ∧ ∀ a, (support p).tail.count a = 1 := by
91+
simp only [isHamiltonianCycle_isCycle_and_isHamiltonian_tail , IsHamiltonian, support_tail,
92+
exists_prop]
93+
94+
/-- A hamiltonian cycle visits every vertex. -/
95+
lemma IsHamiltonianCycle.mem_support (hp : p.IsHamiltonianCycle) (b : α) :
96+
b ∈ p.support := List.mem_of_mem_tail <| support_tail p _ ▸ hp.isHamiltonian_tail.mem_support _
97+
98+
/-- The length of a hamiltonian cycle is the number of vertices. -/
99+
lemma IsHamiltonianCycle.length_eq (hp : p.IsHamiltonianCycle) :
100+
p.length = Fintype.card α := by
101+
rw [← length_tail_add_one hp.not_nil, hp.isHamiltonian_tail.length_eq, Nat.sub_add_cancel]
102+
rw [Nat.succ_le, Fintype.card_pos_iff]
103+
exact ⟨a⟩
104+
105+
lemma IsHamiltonianCycle.count_support_self (hp : p.IsHamiltonianCycle) :
106+
p.support.count a = 2 := by
107+
rw [support_eq_cons, List.count_cons_self, ← support_tail, hp.isHamiltonian_tail]
108+
109+
lemma IsHamiltonianCycle.support_count_of_ne (hp : p.IsHamiltonianCycle) (h : a ≠ b) :
110+
p.support.count b = 1 := by
111+
rw [← cons_support_tail p, List.count_cons_of_ne h.symm, hp.isHamiltonian_tail]
112+
113+
end Walk
114+
115+
/-- A hamiltonian graph is a graph that contains a hamiltonian cycle.
116+
117+
By convention, the singleton graph is considered to be hamiltonian. -/
118+
def IsHamiltonian (G : SimpleGraph α) : Prop :=
119+
Fintype.card α ≠ 1 → ∃ a, ∃ p : G.Walk a a, p.IsHamiltonianCycle
120+
121+
lemma IsHamiltonian.mono {H : SimpleGraph α} (hGH : G ≤ H) (hG : G.IsHamiltonian) :
122+
H.IsHamiltonian :=
123+
fun hα ↦ let ⟨_, p, hp⟩ := hG hα; ⟨_, p.map $ .ofLE hGH, hp.map _ bijective_id⟩
124+
125+
lemma IsHamiltonian.connected (hG : G.IsHamiltonian) : G.Connected where
126+
preconnected a b := by
127+
obtain rfl | hab := eq_or_ne a b
128+
· rfl
129+
have : Nontrivial α := ⟨a, b, hab⟩
130+
obtain ⟨_, p, hp⟩ := hG Fintype.one_lt_card.ne'
131+
have a_mem := hp.mem_support a
132+
have b_mem := hp.mem_support b
133+
exact ((p.takeUntil a a_mem).reverse.append $ p.takeUntil b b_mem).reachable
134+
nonempty := not_isEmpty_iff.1 fun _ ↦ by simpa using hG $ by simp [@Fintype.card_eq_zero]
135+
136+
end SimpleGraph

0 commit comments

Comments
 (0)