Skip to content

Commit e5686e7

Browse files
committed
feat(Combinatorics/SimpleGraph/Acyclic): acyclic and bridge theorems + golfing (leanprover-community#30640)
1 parent 7ca1685 commit e5686e7

2 files changed

Lines changed: 76 additions & 80 deletions

File tree

Mathlib/Combinatorics/SimpleGraph/Acyclic.lean

Lines changed: 60 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -357,108 +357,88 @@ lemma isTree_iff_minimal_connected : IsTree G ↔ Minimal Connected G := by
357357
simp only [edges_map, Hom.coe_ofLE, Sym2.map_id, List.map_id_fun, id_eq] at this
358358
simp [this, p.adj_of_mem_edges]
359359

360-
set_option backward.isDefEq.respectTransparency false in
361-
/--
362-
Adding an edge to an acyclic graph preserves acyclicity if the endpoints are not reachable.
363-
-/
360+
/-- Connecting two unreachable vertices by an edge preserves acyclicity. -/
361+
theorem IsAcyclic.isAcyclic_sup_fromEdgeSet_of_not_reachable {u v : V} (hnreach : ¬G.Reachable u v)
362+
(hacyc : G.IsAcyclic) : (G ⊔ fromEdgeSet {s(u, v)}).IsAcyclic := by
363+
grind [isAcyclic_iff_forall_edge_isBridge, IsBridge.sup_fromEdgeSet_of_not_reachable,
364+
edgeSet_sup, IsBridge.sup_fromEdgeSet_of_not_reachable_of_isBridge, edgeSet_fromEdgeSet]
365+
364366
theorem isAcyclic_add_edge_iff_of_not_reachable (x y : V) (hxy : ¬ G.Reachable x y) :
365-
(G ⊔ fromEdgeSet {s(x, y)}).IsAcyclic ↔ IsAcyclic G := by
366-
refine ⟨fun h ↦ h.anti le_sup_left, fun hG ↦ ?_⟩
367-
have x_ne_y : x ≠ y := fun c => (c ▸ hxy) (Reachable.refl y)
368-
have h_add_remove : (G ⊔ fromEdgeSet {s(x, y)}) \ fromEdgeSet {s(x, y)} = G := by
369-
simpa using fun h => hxy h.reachable
370-
have h_bridge : (G ⊔ fromEdgeSet {s(x, y)}).IsBridge s(x, y) := by
371-
simpa [isBridge_iff, x_ne_y, h_add_remove]
372-
rw [isBridge_iff_adj_and_forall_cycle_notMem] at h_bridge
373-
intro u c hc
374-
let c' : G.Walk u u := Walk.transfer c G (fun e he ↦ by
375-
have eneq : e ≠ s(x, y) := fun h => h_bridge.2 c hc (h ▸ he)
376-
simpa [eneq] using Walk.edges_subset_edgeSet c he)
377-
exact hG c' (Walk.IsCycle.transfer (qc := hc) ..)
367+
(G ⊔ fromEdgeSet {s(x, y)}).IsAcyclic ↔ IsAcyclic G :=
368+
⟨.anti le_sup_left, .isAcyclic_sup_fromEdgeSet_of_not_reachable hxy⟩
369+
370+
/-- Adding an edge results in an acyclic graph iff the original graph was acyclic and
371+
the edge connects vertices that previously had no path between them. -/
372+
theorem isAcyclic_sup_fromEdgeSet_iff {u v : V} :
373+
(G ⊔ fromEdgeSet {s(u, v)}).IsAcyclic ↔
374+
G.IsAcyclic ∧ (G.Reachable u v → u = v ∨ G.Adj u v) := by
375+
by_cases huv : u = v
376+
· grind [sup_eq_left, fromEdgeSet_le, Sym2.mem_diagSet, Sym2.mk_isDiag_iff]
377+
by_cases hadj : G.Adj u v
378+
· grind [sup_eq_left, fromEdgeSet_le, mem_edgeSet]
379+
refine ⟨?_, fun ⟨hacyc, hreach⟩ ↦ hacyc.isAcyclic_sup_fromEdgeSet_of_not_reachable <| by grind⟩
380+
refine fun hacyc ↦ ⟨hacyc.anti le_sup_left, fun hreach ↦ False.elim ?_⟩
381+
have := isAcyclic_iff_forall_edge_isBridge.mp (e := s(u, v)) hacyc <| by simp [huv]
382+
refine isBridge_iff.mp this |>.right <| hreach.mono <| Eq.le <| Eq.symm ?_
383+
rw [sup_sdiff_right_self]
384+
exact deleteEdges_eq_self.mpr <| Set.disjoint_singleton_right.mpr hadj
378385

379386
/--
380387
The reachability relation of a maximal acyclic subgraph agrees with that of the larger graph.
381388
-/
382389
lemma reachable_eq_of_maximal_isAcyclic (F : SimpleGraph V)
383-
(h : Maximal (fun H => H ≤ G ∧ H.IsAcyclic) F) : F.Reachable = G.Reachable := by
384-
obtain ⟨hF : F ≤ G, hF' : F.IsAcyclic⟩ := h.prop
385-
replace h : ∀ {H : SimpleGraph V}, H ≤ G ∧ H.IsAcyclic → F ≤ H → H ≤ F := h.le_of_ge
390+
(h : Maximal (fun H ↦ H ≤ G ∧ H.IsAcyclic) F) : F.Reachable = G.Reachable := by
386391
ext u v
387-
refine ⟨fun h ↦ h.mono hF, ?_⟩
388-
contrapose! h
389-
obtain ⟨⟨p : G.Walk u v⟩, h : ¬ F.Reachable u v⟩ := h
390-
let s : Set V := F.connectedComponentMk u
391-
have hus : u ∈ s := ConnectedComponent.connectedComponentMk_mem
392-
have hvs : v ∉ s := h ∘ (F.connectedComponentMk u).reachable_of_mem_supp hus
393-
obtain ⟨⟨⟨u', v'⟩, huv : G.Adj u' v'⟩, -, hu : u' ∈ s, hv : v' ∉ s⟩ :=
394-
p.exists_boundary_dart s hus hvs
395-
suffices (F ⊔ fromEdgeSet {s(u', v')}).IsAcyclic by
396-
refine ⟨F ⊔ fromEdgeSet {s(u', v')}, ⟨?_, this⟩, le_sup_left, ?_⟩
397-
· have : G.Adj v' u' := G.symm huv
398-
simp only [sup_le_iff, le_iff_adj, fromEdgeSet_adj, Set.mem_singleton_iff, Sym2.eq,
399-
Sym2.rel_iff'] at hF ⊢
400-
grind
401-
· rw [le_iff_adj]
402-
push_neg
403-
refine ⟨u', v', ?_, fun hc ↦ ?_⟩
404-
· simpa using Or.inr huv.ne
405-
· have := (F.connectedComponentMk u).mem_supp_congr_adj hc
406-
grind
407-
suffices ¬ F.Reachable u' v' by rwa [isAcyclic_add_edge_iff_of_not_reachable u' v' this]
408-
suffices F.connectedComponentMk u' = s by
409-
rw [← ConnectedComponent.eq]
410-
exact fun hc ↦ (hc ▸ this ▸ hv) ConnectedComponent.connectedComponentMk_mem
411-
simp_rw [s, SetLike.coe, ConnectedComponent.supp_inj, ← ConnectedComponent.mem_supp_iff]
412-
grind
392+
refine ⟨.mono h.prop.left, fun ⟨p⟩ ↦ ?_⟩
393+
by_contra
394+
let s : F.ConnectedComponent := .mk _ u
395+
have : v ∉ s := this ∘ s.reachable_of_mem_supp rfl
396+
have : ∃ d ∈ p.darts, d.fst ∈ s ∧ d.snd ∉ s := p.exists_boundary_dart s rfl this
397+
rcases this with ⟨⟨⟨u', v'⟩, huv⟩, _, hu, hv⟩
398+
have : ¬F.Reachable v' u' := mt ConnectedComponent.sound <| s.mem_supp_iff u' |>.mp hu ▸ hv
399+
suffices F ⊔ fromEdgeSet {s(v', u')} ≤ F by
400+
grind [Adj.reachable, sup_le_iff, le_iff_adj, fromEdgeSet_adj]
401+
refine h.le_of_ge ⟨?_, h.prop.right.isAcyclic_sup_fromEdgeSet_of_not_reachable this⟩ le_sup_left
402+
grind [Maximal, sup_le, le_iff_adj, fromEdgeSet_adj, huv.symm]
413403

414404
set_option backward.isDefEq.respectTransparency false in
415405
/-- A subgraph is maximal acyclic iff its reachability relation agrees with the larger graph. -/
416-
theorem maximal_isAcyclic_iff_reachable_eq {F : SimpleGraph V} (hF : F ≤ G) (hF' : F.IsAcyclic) :
417-
Maximal (fun H => H ≤ G ∧ H.IsAcyclic) F ↔ F.Reachable = G.Reachable := by
418-
refine ⟨reachable_eq_of_maximal_isAcyclic F, fun h => ?_⟩
419-
by_contra!
420-
obtain ⟨F', hF'⟩ := exists_gt_of_not_maximal (P := fun H => H ≤ G ∧ H.IsAcyclic) ⟨hF, hF'⟩ this
421-
obtain ⟨e, he⟩ := Set.exists_of_ssubset <| edgeSet_strict_mono hF'.1
422-
have : (F ⊔ fromEdgeSet {e}).IsAcyclic := by
423-
apply hF'.2.2.anti
424-
refine sup_le_iff.mpr ⟨by grind, ?_⟩
425-
rw [← F'.fromEdgeSet_edgeSet]
426-
grind [fromEdgeSet_mono]
427-
have F_sdiff_eq : (F ⊔ fromEdgeSet {e}) \ fromEdgeSet {e} = F := by
428-
simpa using he.2
406+
theorem maximal_isAcyclic_iff_reachable_eq {F : SimpleGraph V} (hle : F ≤ G) (hF : F.IsAcyclic) :
407+
Maximal (fun F ↦ F ≤ G ∧ F.IsAcyclic) F ↔ F.Reachable = G.Reachable := by
408+
refine ⟨reachable_eq_of_maximal_isAcyclic F, fun h ↦ ?_⟩
409+
by_contra
410+
have ⟨H, hFH, hHG, hH⟩ := exists_gt_of_not_maximal ⟨hle, hF⟩ this
411+
have ⟨e, heH, heF⟩ := Set.exists_of_ssubset <| edgeSet_strict_mono hFH
429412
have h_bridge : (F ⊔ fromEdgeSet {e}).IsBridge e := by
430-
apply isAcyclic_iff_forall_edge_isBridge.mp this
431-
simp [F'.not_isDiag_of_mem_edgeSet he.1]
432-
simp only [IsBridge, F_sdiff_eq] at h_bridge
413+
refine isAcyclic_iff_forall_edge_isBridge.mp ?_ <| by simp [H.not_isDiag_of_mem_edgeSet heH]
414+
exact hH.anti <| sup_le_iff.mpr ⟨hFH.le, H.fromEdgeSet_le.mpr <| by grind⟩
415+
have : (F ⊔ fromEdgeSet {e}) \ fromEdgeSet {e} = F := by simpa using heF
433416
cases e
434-
case h u v =>
435-
simp only [Sym2.lift_mk] at h_bridge
436-
suffices G.Reachable u v by exact (h ▸ h_bridge.2) this
437-
apply Reachable.mono hF'.2.1
438-
apply Adj.reachable
439-
simpa using he.1
417+
rw [isBridge_iff, this, h] at h_bridge
418+
exact h_bridge.right <| hHG heH |>.reachable
440419

441420
/-- A subgraph of a connected graph is maximal acyclic iff it is a tree. -/
442421
theorem Connected.maximal_le_isAcyclic_iff_isTree {T : SimpleGraph V} (hG : G.Connected)
443-
(hT : T ≤ G) : Maximal (fun H => H ≤ G ∧ H.IsAcyclic) T ↔ T.IsTree := by
444-
have : Nonempty V := hG.nonempty
422+
(hT : T ≤ G) : Maximal (fun H H ≤ G ∧ H.IsAcyclic) T ↔ T.IsTree := by
423+
have := hG.nonempty
445424
refine ⟨fun h ↦ ⟨⟨fun u v ↦ ?_⟩, h.1.2⟩, fun hT' ↦ ?_⟩
446-
· rw [G.reachable_eq_of_maximal_isAcyclic T h]
447-
exact hG.preconnected u v
448-
· rw [maximal_isAcyclic_iff_reachable_eq hT hT'.IsAcyclic]
449-
replace hT' : T.Reachable = ⊤ := by
450-
rw [← preconnected_iff_reachable_eq_top]
451-
exact hT'.isConnected.preconnected
452-
replace hG : G.Reachable = ⊤ := by
453-
rw [← preconnected_iff_reachable_eq_top]
454-
exact hG.preconnected
455-
rw [hT', hG]
425+
· exact G.reachable_eq_of_maximal_isAcyclic T h ▸ hG.preconnected u v
426+
· rw [maximal_isAcyclic_iff_reachable_eq hT hT'.IsAcyclic,
427+
T.preconnected_iff_reachable_eq_top.mp hT'.isConnected.preconnected,
428+
G.preconnected_iff_reachable_eq_top.mp hG.preconnected]
456429

457430
@[simp]
458431
theorem maximal_isAcyclic_iff_isTree [Nonempty V] {T : SimpleGraph V} :
459432
Maximal IsAcyclic T ↔ T.IsTree := by
460433
simp [← connected_top.maximal_le_isAcyclic_iff_isTree le_top]
461434

435+
/-- A maximally acyclic graph is a tree. This is similar to `maximal_isAcyclic_iff_isTree` except
436+
with `Nonempty V` as part of the iff rather than an assumption. -/
437+
theorem isTree_iff_maximal_isAcyclic : G.IsTree ↔ Nonempty V ∧ Maximal IsAcyclic G := by
438+
refine ⟨fun h ↦ ?_, fun ⟨_, h⟩ ↦ G.maximal_isAcyclic_iff_isTree.mp h⟩
439+
have := h.isConnected.nonempty
440+
exact ⟨this, G.maximal_isAcyclic_iff_isTree.mpr h⟩
441+
462442
/-- Every acyclic subgraph can be extended to a spanning forest. -/
463443
theorem exists_isAcyclic_reachable_eq_le_of_le_of_isAcyclic {H : SimpleGraph V} (hH_le : H ≤ G)
464444
(hH_isAcyclic : H.IsAcyclic) :

Mathlib/Combinatorics/SimpleGraph/Connectivity/Connected.lean

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,22 @@ theorem IsBridge.anti_of_mem_edgeSet {G' : SimpleGraph V} {e : Sym2 V} (hle : G
844844
isBridge_iff_mem_and_forall_cycle_notMem.mp h' |>.right
845845
(p.mapLe hle) (Walk.IsCycle.mapLe hle hp) (p.edges_mapLe_eq_edges hle ▸ hpe)⟩
846846

847+
/-- Connecting two unreachable vertices by an edge creates a bridge. -/
848+
theorem IsBridge.sup_fromEdgeSet_of_not_reachable {u v : V} (h : ¬G.Reachable u v) :
849+
(G ⊔ fromEdgeSet {s(u, v)}).IsBridge s(u, v) := by
850+
refine isBridge_iff.mpr ⟨.inr ⟨Set.mem_singleton _, mt (· ▸ .rfl) h⟩, ?_⟩
851+
exact fun h' ↦ h <| .mono (sdiff_le_iff'.mpr <| refl _) h'
852+
853+
/-- Connecting two unreachable vertices by an edge preserves existing bridges. -/
854+
theorem IsBridge.sup_fromEdgeSet_of_not_reachable_of_isBridge {u v : V} {e : Sym2 V}
855+
(h : ¬G.Reachable u v) (h' : G.IsBridge e) : (G ⊔ fromEdgeSet {s(u, v)}).IsBridge e := by
856+
refine isBridge_iff_mem_and_forall_cycle_notMem.mpr ⟨edgeSet_mono le_sup_left h'.left, ?_⟩
857+
refine fun _ p hp hpe ↦ isBridge_iff_mem_and_forall_cycle_notMem.mp h' |>.right
858+
(p.transfer G fun e' he' ↦ ?_) (hp.transfer _) (Walk.edges_transfer p _ ▸ hpe)
859+
refine edgeSet_sup .. ▸ Walk.edges_subset_edgeSet _ he' |>.elim id fun h' ↦ h ?_ |>.elim
860+
exact .mono (sdiff_le_iff'.mpr <| refl _) <|
861+
adj_and_reachable_delete_edges_iff_exists_cycle.mpr (by grind [edgeSet_fromEdgeSet]) |>.right
862+
847863
end BridgeEdges
848864

849865
/-!

0 commit comments

Comments
 (0)