|
| 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