Skip to content

Commit ad8efb3

Browse files
committed
feat(Combinatorics/SimpleGraph/Acyclic): IsAcyclic -> IsBipartite (#32568)
This generalizes the existing `IsTree` -> `IsBipartite` result.
1 parent bf0f847 commit ad8efb3

File tree

2 files changed

+63
-14
lines changed

2 files changed

+63
-14
lines changed

Mathlib/Combinatorics/SimpleGraph/Acyclic.lean

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -454,27 +454,36 @@ lemma Connected.exists_preconnected_induce_compl_singleton_of_finite [Finite V]
454454
obtain ⟨v, hv⟩ := hconn.exists_connected_induce_compl_singleton_of_finite_nontrivial
455455
exact ⟨v, hv.preconnected⟩
456456

457-
lemma IsTree.dist_ne_of_adj (hG : G.IsTree) (u : V) {v w : V} (hadj : G.Adj v w) :
458-
G.dist u v ≠ G.dist u w := by
459-
obtain ⟨p, hp, hp'⟩ := hG.isConnected.exists_path_of_dist u v
460-
obtain ⟨q, hq, hq'⟩ := hG.isConnected.exists_path_of_dist u w
457+
lemma IsAcyclic.dist_ne_of_adj (hG : G.IsAcyclic) {u v w : V} (hadj : G.Adj v w)
458+
(hreach : G.Reachable u v) : G.dist u v ≠ G.dist u w := by
459+
obtain ⟨p, hp, hp'⟩ := hreach.exists_path_of_dist
460+
obtain ⟨q, hq, hq'⟩ := hreach.trans hadj.reachable |>.exists_path_of_dist
461461
rw [← hp', ← hq']
462462
by_cases hw : w ∈ p.support
463-
· rw [hG.IsAcyclic.path_concat hq hp hadj.symm hw, q.length_concat]
463+
· rw [hG.path_concat hq hp hadj.symm hw, q.length_concat]
464464
exact q.length.ne_add_one.symm
465-
· have hv : v ∈ q.support := hG.IsAcyclic.mem_support_of_ne_mem_support_of_adj_of_isPath hq hp
465+
· have hv : v ∈ q.support := hG.mem_support_of_ne_mem_support_of_adj_of_isPath hq hp
466466
hadj.symm hw
467-
rw [hG.IsAcyclic.path_concat hp hq hadj hv, p.length_concat]
467+
rw [hG.path_concat hp hq hadj hv, p.length_concat]
468468
exact p.length.ne_add_one
469469

470-
lemma IsTree.diff_dist_adj (hG : G.IsTree) (u : V) {v w : V} (hadj : SimpleGraph.Adj G v w) :
471-
G.dist u v = G.dist u w + 1 ∨ G.dist u v + 1 = G.dist u w := by
472-
grind [dist_ne_of_adj, Connected.diff_dist_adj, IsTree]
470+
lemma IsTree.dist_ne_of_adj (hG : G.IsTree) (u : V) {v w : V} (hadj : G.Adj v w) :
471+
G.dist u v ≠ G.dist u w :=
472+
hG.IsAcyclic.dist_ne_of_adj hadj <| hG.isConnected u v
473+
474+
lemma IsAcyclic.dist_eq_dist_add_one_of_adj_of_reachable
475+
(hG : G.IsAcyclic) (u : V) {v w : V} (hadj : G.Adj v w) (hreach : G.Reachable u v) :
476+
G.dist u v = G.dist u w + 1 ∨ G.dist u w = G.dist u v + 1 := by
477+
grind [dist_ne_of_adj, Adj.diff_dist_adj]
478+
479+
lemma IsTree.dist_eq_dist_add_one_of_adj (hG : G.IsTree) (u : V) {v w : V} (hadj : G.Adj v w) :
480+
G.dist u v = G.dist u w + 1 ∨ G.dist u w = G.dist u v + 1 := by
481+
grind [dist_ne_of_adj, Adj.diff_dist_adj]
473482

474483
/-- The unique two-coloring of a tree that colors the given vertex with zero -/
475484
noncomputable def IsTree.coloringTwoOfVert (hG : G.IsTree) (u : V) : G.Coloring (Fin 2) :=
476485
Coloring.mk (fun v ↦ ⟨G.dist u v % 2, Nat.mod_lt (G.dist u v) Nat.zero_lt_two⟩) <| by
477-
grind [diff_dist_adj]
486+
grind [dist_eq_dist_add_one_of_adj]
478487

479488
/-- Arbitrary coloring with two colors for a tree -/
480489
noncomputable def IsTree.coloringTwo (hG : G.IsTree) : G.Coloring (Fin 2) :=
@@ -483,4 +492,23 @@ noncomputable def IsTree.coloringTwo (hG : G.IsTree) : G.Coloring (Fin 2) :=
483492
lemma IsTree.isBipartite (hG : G.IsTree) : G.IsBipartite :=
484493
⟨hG.coloringTwo⟩
485494

495+
/-- The unique two-coloring of a forest that colors the given vertices with zero -/
496+
noncomputable def IsAcyclic.coloringTwoOfVerts (hG : G.IsAcyclic) (verts : G.ConnectedComponent → V)
497+
(h : ∀ C, verts C ∈ C) : G.Coloring (Fin 2) where
498+
toFun v :=
499+
let u := verts <| G.connectedComponentMk v
500+
⟨G.dist u v % 2, Nat.mod_lt (G.dist u v) Nat.zero_lt_two⟩
501+
map_rel' := by
502+
intro u v hadj
503+
have := ConnectedComponent.sound hadj.reachable
504+
have := hG.dist_eq_dist_add_one_of_adj_of_reachable _ hadj <| ConnectedComponent.exact <| h _
505+
grind [top_adj]
506+
507+
/-- Arbitrary coloring with two colors for a forest -/
508+
noncomputable def IsAcyclic.coloringTwo (hG : G.IsAcyclic) : G.Coloring (Fin 2) :=
509+
hG.coloringTwoOfVerts (·.nonempty_supp.some) (·.nonempty_supp.some_mem)
510+
511+
lemma IsAcyclic.isBipartite (hG : G.IsAcyclic) : G.IsBipartite :=
512+
⟨hG.coloringTwo⟩
513+
486514
end SimpleGraph

Mathlib/Combinatorics/SimpleGraph/Metric.lean

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ variable {G} {u v w : V}
172172
theorem dist_eq_sInf : G.dist u v = sInf (Set.range (Walk.length : G.Walk u v → ℕ)) :=
173173
ENat.iInf_toNat
174174

175+
@[grind =]
176+
lemma Reachable.coe_dist_eq_edist (h : G.Reachable u v) : G.dist u v = G.edist u v :=
177+
ENat.coe_toNat <| edist_ne_top_iff_reachable.mpr h
178+
175179
protected theorem Reachable.exists_walk_length_eq_dist (hr : G.Reachable u v) :
176180
∃ p : G.Walk u v, p.length = G.dist u v :=
177181
dist_eq_sInf ▸ Nat.sInf_mem (Set.range_nonempty_iff_nonempty.mpr hr)
@@ -187,6 +191,7 @@ theorem dist_le (p : G.Walk u v) : G.dist u v ≤ p.length :=
187191
theorem dist_eq_zero_iff_eq_or_not_reachable :
188192
G.dist u v = 0 ↔ u = v ∨ ¬G.Reachable u v := by simp [dist_eq_sInf, Nat.sInf_eq_zero, Reachable]
189193

194+
@[simp, grind =]
190195
theorem dist_self : dist G v v = 0 := by simp
191196

192197
protected theorem Reachable.dist_eq_zero_iff (hr : G.Reachable u v) :
@@ -230,6 +235,20 @@ protected theorem Connected.dist_triangle (hconn : G.Connected) :
230235
rw [← hp, ← hq, ← Walk.length_append]
231236
apply dist_le
232237

238+
lemma Reachable.dist_triangle_left (h : G.Reachable u v) (w) :
239+
G.dist u w ≤ G.dist u v + G.dist v w := by
240+
by_cases! h' : ¬G.Reachable u w
241+
· grind [dist_eq_zero_iff_eq_or_not_reachable]
242+
rw [← ENat.coe_le_coe, ENat.coe_add]
243+
grind [SimpleGraph.edist_triangle, Reachable.trans, Reachable.symm]
244+
245+
lemma Reachable.dist_triangle_right (h : G.Reachable v w) (u) :
246+
G.dist u w ≤ G.dist u v + G.dist v w := by
247+
by_cases! h' : ¬G.Reachable u w
248+
· grind [dist_eq_zero_iff_eq_or_not_reachable]
249+
rw [← ENat.coe_le_coe, ENat.coe_add]
250+
grind [SimpleGraph.edist_triangle, Reachable.trans, Reachable.symm]
251+
233252
theorem dist_comm : G.dist u v = G.dist v u := by
234253
rw [dist, dist, edist_comm]
235254

@@ -251,12 +270,14 @@ theorem dist_eq_one_iff_adj : G.dist u v = 1 ↔ G.Adj u v := by
251270
rw [dist, ENat.toNat_eq_iff, ENat.coe_one, edist_eq_one_iff_adj]
252271
decide
253272

254-
theorem Connected.diff_dist_adj (hG : G.Connected) (hadj : G.Adj v w) :
273+
theorem Adj.diff_dist_adj (hadj : G.Adj v w) :
255274
G.dist u w = G.dist u v ∨ G.dist u w = G.dist u v + 1 ∨ G.dist u w = G.dist u v - 1 := by
275+
by_cases! huw : ¬G.Reachable u w
276+
· grind [dist_eq_zero_iff_eq_or_not_reachable, Reachable.trans, Adj.reachable]
256277
have : G.dist v w = 1 := dist_eq_one_iff_adj.mpr hadj
257278
have : G.dist w v = 1 := dist_eq_one_iff_adj.mpr hadj.symm
258-
have : G.dist u w ≤ G.dist u v + G.dist v w := hG.dist_triangle
259-
have : G.dist u v ≤ G.dist u w + G.dist w v := hG.dist_triangle
279+
have : G.dist u w ≤ G.dist u v + G.dist v w := hadj.reachable.dist_triangle_right u
280+
have : G.dist u v ≤ G.dist u w + G.dist w v := huw.dist_triangle_left v
260281
lia
261282

262283
theorem Walk.isPath_of_length_eq_dist (p : G.Walk u v) (hp : p.length = G.dist u v) :

0 commit comments

Comments
 (0)