Hi all, I proven that every connected graph has a spanning tree (without any finiteness assumption) — see here. I used the opposite idea to the proof that already exists in the finite case, the key lemma is that a maximal acyclic subgraph of a connected graph is connected. An outline is:
importMathlib.Combinatorics.SimpleGraph.AcyclicnamespaceSimpleGraphvariable{V:Type_}{G:SimpleGraphV}-- this is used to show that, given a non-connected acyclic subgraph `H`, we can find an edge in-- `G` which connected two connected components of `H`, so that `H ⊔ fromEdgeSet {s(x,y)}` is-- acyclic (see below `add_edge_acyclic`).theoremConnected.subset_connected_complement(h:G.Connected)(s:SetV)(hs:s.Nonempty):s=Set.univ∨∃(uv:V),u∈s∧v∉s∧G.Adjuv:=sorry/-- Given a walk in `sSup Hs`, where `Hs` is directed, find `H ∈ Hs` which already contains thewalk. -/theoremWalk.shrink_of_directed_sSup(Hs:Set<|SimpleGraphV)(H₀:Hs)(h_dir:DirectedOn(·≤·)Hs){uv:V}(p:(sSupHs).Walkuv):∃H∈Hs,∃(p':H.Walkuv),p'.edges=p.edges∧p'.support=p.support:=sorry/-- The directed (in particular increasing) union of acyclic graphs is acylic. -/theoremsSup_acyclic_of_directed_of_acyclic(Hs:Set<|SimpleGraphV)(H₀:Hs)(h_acyc:∀H∈Hs,H.IsAcyclic)(h_dir:DirectedOn(·≤·)Hs):IsAcyclic(sSupHs):=bysorry/-- Adding an edge between two unreachable vertices to an acyclic graph producesan acyclic graph. -/theoremadd_edge_acyclic[DecidableEqV]{G:SimpleGraphV}(hG:IsAcyclicG)(xy:V)(hxy:¬ReachableGxy):IsAcyclic<|G⊔fromEdgeSet{s(x,y)}:=bysorry/-- A maximal acyclic subgraph of an acyclic graph is acylic. -/theoremConnected.connected_of_maximal_acyclic[InhabitedV](T:SimpleGraphV)(hG:G.Connected)(h:Maximal(funH=>H≤G∧H.IsAcyclic)T):T.Connected:=bysorry/-- A connected graph has a spanning tree. -/theoremConnected.has_spanning_tree[InhabitedV](hG:G.Connected):∃T≤G,T.IsTree:=bysorryendSimpleGraph
(I did it this way because it wasn't clear to me how to prove the assumption of Zorn's lemma — that a descending intersection of connected graphs is connected — for the other way.) There's a lot of preparatory lemmas here, so I'm not sure it's appropriate for mathlib, but if it is I'd love any feedback that might help make it PR-ready!
the statement “every acyclic subgraph can be extended to a spanning tree” would be straightforward, that’s just swapping to use the version docs#zorn_le_nonempty₀
& i could certainly refactor it to be something like every acyclic H ≤ G can be extended to a maximal acyclic H’ ≤ G, if that’s what you mean? (& then derive H’ connected later, in the case that G is connected)
One more optimisation: the same argument (intuitively) works for a spanning forest, but I couldn't figure out how to formulate that. I settled on "acyclic subgraph with the same reachability relation", as the most elegant way of stating it, so now the main results are:
importMathlib.Combinatorics.SimpleGraph.AcyclicnamespaceSimpleGraphvariable{V:Type_}{G:SimpleGraphV}theoremexists_maximal_acyclic_extension{H:SimpleGraphV}(hHG:H≤G)(hH:H.IsAcyclic):∃H':SimpleGraphV,H≤H'∧Maximal(funH=>H≤G∧H.IsAcyclic)H':=sorrytheoremreachable_eq_of_maximal_acyclic(F:SimpleGraphV)(h:Maximal(funH=>H≤G∧H.IsAcyclic)F):F.Reachable=G.Reachable:=sorry/-- Every graph has a spanning forest. -/theoremexists_extension_acyclic_reachable_eq{H:SimpleGraphV}(hHG:H≤G)(hH:H.IsAcyclic):∃F≤G,H≤F∧F.IsAcyclic∧F.Reachable=G.Reachable:=sorrytheoremConnected.connected_of_maximal_acyclic(T:SimpleGraphV)(hG:G.Connected)(h:Maximal(funH=>H≤G∧H.IsAcyclic)T):T.Connected:=sorrytheoremConnected.exists_extension_isTree_le(hG:G.Connected){H:SimpleGraphV}(hHG:H≤G)(hH:H.IsAcyclic):∃T≤G,H≤T∧T.IsTree:=sorryendSimpleGraph
The full file is still at the same link or (now I learnt this trick) here:
every graph has a spanning forest
importMathlib.Combinatorics.SimpleGraph.AcyclicnamespaceSimpleGraphvariable{V:Type_}{G:SimpleGraphV}theoremWalk.shrink_of_directed_sSup(Hs:Set<|SimpleGraphV)(H₀:Hs)(h_dir:DirectedOn(·≤·)Hs){uv:V}(p:(sSupHs).Walkuv):∃H∈Hs,∃(p':H.Walkuv),p'.edges=p.edges∧p'.support=p.support:=byinductionpwith|nil=>refine⟨H₀,bygrind,Walk.nil,bysimp⟩|@consuvwh_adjpih=>obtain⟨H₁,hH₁,p',hp'⟩:=ihrw[sSup_adj]ath_adjreplace⟨H₂,hH₂,h_adj⟩:∃H₂∈Hs,H₂.Adjuv:=h_adjobtain⟨H,hH,h⟩:=h_dirH₁hH₁H₂hH₂letp'':H.Walkvw:=Walk.map(Hom.ofLEh.1)p'have:p''.edges=p'.edges∧p''.support=p'.support:=bysimp[p'']have:H.Adjuv:=bygrind[SimpleGraph.le_iff_adj]exact⟨H,hH,Walk.consthisp'',bygrind[support_cons,edges_cons]⟩theoremsSup_acyclic_of_directed_of_acyclic(Hs:Set<|SimpleGraphV)(H₀:Hs)(h_acyc:∀H∈Hs,H.IsAcyclic)(h_dir:DirectedOn(·≤·)Hs):IsAcyclic(sSupHs):=byintrouphpobtain⟨H,hH,p',hp'⟩:=p.shrink_of_directed_sSupHsH₀h_dirsufficesp'.IsCyclebyexact(h_acycH)hHp'thisrw[Walk.isCycle_def,Walk.isTrail_def]athp⊢refine⟨bygrind,?_,bygrind⟩rw[ne_eq,←Walk.nil_iff_eq_nil,Walk.nil_iff_support_eq]athp⊢grindtheoremexists_maximal_acyclic_extension{H:SimpleGraphV}(hHG:H≤G)(hH:H.IsAcyclic):∃H':SimpleGraphV,H≤H'∧Maximal(funH=>H≤G∧H.IsAcyclic)H':=bylets:Set(SimpleGraphV):={H:SimpleGraphV|H≤G∧H.IsAcyclic}applyzorn_le_nonempty₀s·introchcshcyhyrefine⟨sSupc,⟨?_,?_⟩,CompleteLattice.le_sSupc⟩·simponly[sSup_le_iff]grind·exactsSup_acyclic_of_directed_of_acyclicc⟨y,hy⟩(bygrind)hc.directedOn·grindtheoremadd_edge_acyclic[DecidableEqV]{G:SimpleGraphV}(hG:IsAcyclicG)(xy:V)(hxy:¬ReachableGxy):IsAcyclic<|G⊔fromEdgeSet{s(x,y)}:=byhavex_neq_y:x≠y:=func=>(c▸hxy)(Reachable.refly)haveh_add_remove:G=(G⊔fromEdgeSet{s(x,y)})\fromEdgeSet{s(x,y)}:=bysimponly[sup_sdiff_right_self]applyedgeSet_inj.mp;exte;simponly[edgeSet_sdiff,edgeSet_fromEdgeSet,edgeSet_sdiff_sdiff_isDiag,Set.mem_diff,Set.mem_singleton_iff,iff_self_and]introhecrw[c,mem_edgeSet]atheexacthxy<|Adj.reachablehehaveh_bridge:(G⊔fromEdgeSet{s(x,y)}).IsBridges(x,y):=bysimpa[isBridge_iff,x_neq_y,←h_add_remove]introuchcapplyisBridge_iff_adj_and_forall_cycle_notMem.mpath_bridgeletc':G.Walkuu:=Walk.transfercG(byintroehehaveeneq:e≠s(x,y):=funh=>h_bridge.2chc(h▸he)simpa[eneq]usingWalk.edges_subset_edgeSetche)exacthGc'(Walk.IsCycle.transfer(qc:=hc)..)theoremreachable_eq_of_maximal_acyclic(F:SimpleGraphV)(h:Maximal(funH=>H≤G∧H.IsAcyclic)F):F.Reachable=G.Reachable:=bysimponly[Maximal,and_imp]athobtain⟨hF,h⟩:=happlyfunext;introu;applyfunext;introvrefinepropext⟨funhr=>hr.monohF.1,?_⟩contrapose!hobtain⟨p⟩:=h.1lets:SetV:=F.connectedComponentMkuhavehus:u∈s:=ConnectedComponent.connectedComponentMk_memhavehvs:v∉s:=h.2∘(F.connectedComponentMku).reachable_of_mem_supphusobtain⟨⟨⟨u',v'⟩,huv⟩,_,hu,hv⟩:=p.exists_boundary_dartshushvsletF':=(F⊔fromEdgeSet{s(u',v')})sufficesF'.IsAcyclicbyrw[le_iff_adj]athFrefine⟨F',?_,this,le_sup_left,?_⟩·have:G.Adjv'u':=G.symmhuvsimponly[sup_le_iff,le_iff_adj,fromEdgeSet_adj,Set.mem_singleton_iff,Sym2.eq,Sym2.rel_iff',Prod.mk.injEq,Prod.swap_prod_mk,ne_eq,and_imp,F']grind·rw[le_iff_adj]push_negrefine⟨u',v',?_,?_⟩·simponly[sup_adj,fromEdgeSet_adj,Set.mem_singleton_iff,ne_eq,true_and,F']grind·introhchave:_:=ConnectedComponent.mem_supp_congr_adj(F.connectedComponentMku)hcgrindhave:DecidableEqV:=Classical.decEqVapplyadd_edge_acyclic(hG:=hF.2)introhcrw[←ConnectedComponent.eq]athcsufficesF.connectedComponentMku'=sbyexact(hc▸this▸hv)ConnectedComponent.connectedComponentMk_memsimp_rw[s,SetLike.coe,ConnectedComponent.supp_inj,←ConnectedComponent.mem_supp_iff]grind/-- Every graph has a spanning forest. -/theoremexists_extension_acyclic_reachable_eq{H:SimpleGraphV}(hHG:H≤G)(hH:H.IsAcyclic):∃F≤G,H≤F∧F.IsAcyclic∧F.Reachable=G.Reachable:=byobtain⟨F,hHF,hF⟩:=exists_maximal_acyclic_extensionhHGhHexact⟨F,hF.1.1,hHF,hF.1.2,reachable_eq_of_maximal_acyclicFhF⟩theoremConnected.connected_of_maximal_acyclic(T:SimpleGraphV)(hG:G.Connected)(h:Maximal(funH=>H≤G∧H.IsAcyclic)T):T.Connected:=byhave:NonemptyV:=hG.nonemptyrefine⟨funuv=>?_⟩rw[reachable_eq_of_maximal_acyclicTh]exacthGuvtheoremConnected.exists_extension_isTree_le(hG:G.Connected){H:SimpleGraphV}(hHG:H≤G)(hH:H.IsAcyclic):∃T≤G,H≤T∧T.IsTree:=byobtain⟨T,hHT,hT⟩:=exists_maximal_acyclic_extensionhHGhHexact⟨T,hT.1.1,hHT,hG.connected_of_maximal_acyclicThT,hT.1.2⟩endSimpleGraph
Hi thanks for taking a look! I think the main difference (& possibly why I found it easier to prove my results) is that I would with G.IsSubgraph aka ≤ whereas you used the type G.Subgraph — my way avoids a lot of coercions which possibly makes life easier, and it seems to me like the API is a bit better developed
I'll leave a review on you PRs now — I think my first one #32041 (which does all the zorny stuff) is fairly optimised and doesn't really overlap with your results, let me know if you agree? definitely I think we should carry through results from both of our other ones