Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit edaaaa4

Browse files
committed
feat(combinatorics/simple_graph/trails): Euler's condition for trails (#15158)
Adds theory for trails and Eulerian trails and proves that Eulerian trails imply a condition on vertex degrees.
1 parent 261c24c commit edaaaa4

2 files changed

Lines changed: 178 additions & 0 deletions

File tree

src/combinatorics/simple_graph/basic.lean

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,14 @@ lemma mem_incidence_finset [decidable_eq V] (e : sym2 V) :
780780
e ∈ G.incidence_finset v ↔ e ∈ G.incidence_set v :=
781781
set.mem_to_finset
782782

783+
lemma incidence_finset_eq_filter [decidable_eq V] [fintype G.edge_set] :
784+
G.incidence_finset v = G.edge_finset.filter (has_mem.mem v) :=
785+
begin
786+
ext e,
787+
refine sym2.ind (λ x y, _) e,
788+
simp [mk_mem_incidence_set_iff],
789+
end
790+
783791
end finite_at
784792

785793
section locally_finite
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/-
2+
Copyright (c) 2022 Kyle Miller. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Kyle Miller
5+
-/
6+
import combinatorics.simple_graph.connectivity
7+
import data.nat.parity
8+
9+
/-!
10+
11+
# Trails and Eulerian trails
12+
13+
This module contains additional theory about trails, including Eulerian trails (also known
14+
as Eulerian circuits).
15+
16+
## Main definitions
17+
18+
* `simple_graph.walk.is_eulerian` is the predicate that a trail is an Eulerian trail.
19+
* `simple_graph.walk.is_trail.even_countp_edges_iff` gives a condition on the number of edges
20+
in a trail that can be incident to a given vertex.
21+
* `simple_graph.walk.is_eulerian.even_degree_iff` gives a condition on the degrees of vertices
22+
when there exists an Eulerian trail.
23+
* `simple_graph.walk.is_eulerian.card_odd_degree` gives the possible numbers of odd-degree
24+
vertices when there exists an Eulerian trail.
25+
26+
## Todo
27+
28+
* Prove that there exists an Eulerian trail when the conclusion to
29+
`simple_graph.walk.is_eulerian.card_odd_degree` holds.
30+
31+
## Tags
32+
33+
Eulerian trails
34+
35+
-/
36+
37+
namespace simple_graph
38+
variables {V : Type*} {G : simple_graph V}
39+
40+
namespace walk
41+
42+
/-- The edges of a trail as a finset, since each edge in a trail appears exactly once. -/
43+
@[reducible] def is_trail.edges_finset {u v : V} {p : G.walk u v}
44+
(h : p.is_trail) : finset (sym2 V) :=
45+
⟨p.edges, h.edges_nodup⟩
46+
47+
variables [decidable_eq V]
48+
49+
lemma is_trail.even_countp_edges_iff {u v : V} {p : G.walk u v} (ht : p.is_trail) (x : V) :
50+
even (p.edges.countp (λ e, x ∈ e)) ↔ (u ≠ v → x ≠ u ∧ x ≠ v) :=
51+
begin
52+
induction p with u u v w huv p ih,
53+
{ simp, },
54+
{ rw [cons_is_trail_iff] at ht,
55+
specialize ih ht.1,
56+
simp only [list.countp_cons, ne.def, edges_cons, sym2.mem_iff],
57+
split_ifs with h,
58+
{ obtain (rfl | rfl) := h,
59+
{ rw [nat.even_add_one, ih],
60+
simp only [huv.ne, imp_false, ne.def, not_false_iff, true_and, not_forall, not_not,
61+
exists_prop, eq_self_iff_true, not_true, false_and, and_iff_right_iff_imp],
62+
rintro rfl rfl,
63+
exact G.loopless _ huv, },
64+
{ rw [nat.even_add_one, ih, ← not_iff_not],
65+
simp only [huv.ne.symm, ne.def, eq_self_iff_true, not_true, false_and, not_forall,
66+
not_false_iff, exists_prop, and_true, not_not, true_and, iff_and_self],
67+
rintro rfl,
68+
exact huv.ne, } },
69+
{ rw not_or_distrib at h,
70+
simp only [h.1, h.2, not_false_iff, true_and, add_zero, ne.def] at ih ⊢,
71+
rw ih,
72+
split;
73+
{ rintro h' h'' rfl,
74+
simp only [imp_false, eq_self_iff_true, not_true, not_not] at h',
75+
cases h',
76+
simpa using h } } },
77+
end
78+
79+
/-- An *Eulerian trail* (also known as an "Eulerian path") is a walk
80+
`p` that visits every edge exactly once. The lemma `simple_graph.walk.is_eulerian.is_trail` shows
81+
that these are trails.
82+
83+
Combine with `p.is_circuit` to get an Eulerian circuit (also known as an "Eulerian cycle"). -/
84+
def is_eulerian {u v : V} (p : G.walk u v) : Prop :=
85+
∀ e, e ∈ G.edge_set → p.edges.count e = 1
86+
87+
lemma is_eulerian.is_trail {u v : V} {p : G.walk u v}
88+
(h : p.is_eulerian) : p.is_trail :=
89+
begin
90+
rw [is_trail_def, list.nodup_iff_count_le_one],
91+
intro e,
92+
by_cases he : e ∈ p.edges,
93+
{ exact (h e (edges_subset_edge_set _ he)).le },
94+
{ simp [he] },
95+
end
96+
97+
lemma is_eulerian.mem_edges_iff {u v : V} {p : G.walk u v} (h : p.is_eulerian) {e : sym2 V} :
98+
e ∈ p.edges ↔ e ∈ G.edge_set :=
99+
⟨λ h, p.edges_subset_edge_set h, λ he, by simpa using (h e he).ge⟩
100+
101+
/-- The edge set of an Eulerian graph is finite. -/
102+
def is_eulerian.fintype_edge_set {u v : V} {p : G.walk u v}
103+
(h : p.is_eulerian) : fintype G.edge_set :=
104+
fintype.of_finset h.is_trail.edges_finset $ λ e,
105+
by simp only [finset.mem_mk, multiset.mem_coe, h.mem_edges_iff]
106+
107+
lemma is_trail.is_eulerian_of_forall_mem {u v : V} {p : G.walk u v}
108+
(h : p.is_trail) (hc : ∀ e, e ∈ G.edge_set → e ∈ p.edges) :
109+
p.is_eulerian :=
110+
λ e he, list.count_eq_one_of_mem h.edges_nodup (hc e he)
111+
112+
lemma is_eulerian_iff {u v : V} (p : G.walk u v) :
113+
p.is_eulerian ↔ p.is_trail ∧ ∀ e, e ∈ G.edge_set → e ∈ p.edges :=
114+
begin
115+
split,
116+
{ intro h,
117+
exact ⟨h.is_trail, λ _, h.mem_edges_iff.mpr⟩, },
118+
{ rintro ⟨h, hl⟩,
119+
exact h.is_eulerian_of_forall_mem hl, },
120+
end
121+
122+
lemma is_eulerian.edges_finset_eq [fintype G.edge_set]
123+
{u v : V} {p : G.walk u v} (h : p.is_eulerian) :
124+
h.is_trail.edges_finset = G.edge_finset :=
125+
by { ext e, simp [h.mem_edges_iff] }
126+
127+
lemma is_eulerian.even_degree_iff {x u v : V} {p : G.walk u v} (ht : p.is_eulerian)
128+
[fintype V] [decidable_rel G.adj] :
129+
even (G.degree x) ↔ (u ≠ v → x ≠ u ∧ x ≠ v) :=
130+
begin
131+
convert ht.is_trail.even_countp_edges_iff x,
132+
rw [← multiset.coe_countp, multiset.countp_eq_card_filter, ← card_incidence_finset_eq_degree],
133+
change multiset.card _ = _,
134+
congr' 1,
135+
convert_to _ = (ht.is_trail.edges_finset.filter (has_mem.mem x)).val,
136+
rw [ht.edges_finset_eq, G.incidence_finset_eq_filter x],
137+
end
138+
139+
lemma is_eulerian.card_filter_odd_degree [fintype V] [decidable_rel G.adj]
140+
{u v : V} {p : G.walk u v} (ht : p.is_eulerian)
141+
{s} (h : s = (finset.univ : finset V).filter (λ v, odd (G.degree v))) :
142+
s.card = 0 ∨ s.card = 2 :=
143+
begin
144+
subst s,
145+
simp only [nat.odd_iff_not_even, finset.card_eq_zero],
146+
simp only [ht.even_degree_iff, ne.def, not_forall, not_and, not_not, exists_prop],
147+
obtain (rfl | hn) := eq_or_ne u v,
148+
{ left,
149+
simp, },
150+
{ right,
151+
convert_to _ = ({u, v} : finset V).card,
152+
{ simp [hn], },
153+
{ congr',
154+
ext x,
155+
simp [hn, imp_iff_not_or], } },
156+
end
157+
158+
lemma is_eulerian.card_odd_degree [fintype V] [decidable_rel G.adj]
159+
{u v : V} {p : G.walk u v} (ht : p.is_eulerian) :
160+
fintype.card {v : V | odd (G.degree v)} = 0 ∨ fintype.card {v : V | odd (G.degree v)} = 2 :=
161+
begin
162+
rw ← set.to_finset_card,
163+
apply is_eulerian.card_filter_odd_degree ht,
164+
ext v,
165+
simp,
166+
end
167+
168+
end walk
169+
170+
end simple_graph

0 commit comments

Comments
 (0)