Zulip Chat Archive

Stream: graph theory

Topic: Every connected graph has a spanning tree


Thomas Waring (Nov 20 2025 at 16:00):

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:

import Mathlib.Combinatorics.SimpleGraph.Acyclic

namespace SimpleGraph

variable {V : Type _} {G : SimpleGraph V}

-- 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`).
theorem Connected.subset_connected_complement (h : G.Connected) (s : Set V) (hs : s.Nonempty) :
    s = Set.univ   (u v : V), u  s  v  s  G.Adj u v := sorry

/-- Given a walk in `sSup Hs`, where `Hs` is directed, find `H ∈ Hs` which already contains the
walk. -/
theorem Walk.shrink_of_directed_sSup (Hs : Set <| SimpleGraph V) (H₀ : Hs)
    (h_dir : DirectedOn (·  ·) Hs) {u v : V} (p : (sSup Hs).Walk u v) :
     H  Hs,  (p' : H.Walk u v),
      p'.edges = p.edges  p'.support = p.support := sorry

/-- The directed (in particular increasing) union of acyclic graphs is acylic. -/
theorem sSup_acyclic_of_directed_of_acyclic (Hs : Set <| SimpleGraph V) (H₀ : Hs)
    (h_acyc :  H  Hs, H.IsAcyclic) (h_dir : DirectedOn (·  ·) Hs) : IsAcyclic (sSup Hs) := by sorry

/-- Adding an edge between two unreachable vertices to an acyclic graph produces
an acyclic graph. -/
theorem add_edge_acyclic [DecidableEq V] {G : SimpleGraph V} (hG : IsAcyclic G) (x y : V)
    (hxy : ¬ Reachable G x y) : IsAcyclic <| G  fromEdgeSet {s(x,y)} := by sorry

/-- A maximal acyclic subgraph of an acyclic graph is acylic. -/
theorem Connected.connected_of_maximal_acyclic [Inhabited V] (T : SimpleGraph V) (hG : G.Connected)
    (h : Maximal (fun H => H  G  H.IsAcyclic) T) : T.Connected := by sorry

/-- A connected graph has a spanning tree. -/
theorem Connected.has_spanning_tree [Inhabited V] (hG : G.Connected) :  T  G, T.IsTree := by sorry

end SimpleGraph

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

Aaron Liu (Nov 20 2025 at 16:59):

Can you do the extends version, like what we have with "every vector space has a basis" in docs#LinearIndepOn.extend

Aaron Liu (Nov 20 2025 at 17:00):

any acyclic subgraph supported on a set s can be extended to an acyclic subgraph maximal among those supported on a set t if s ⊆ t

Aaron Liu (Nov 20 2025 at 17:01):

actually probably we want s and t to be subgraphs

Thomas Waring (Nov 20 2025 at 17:04):

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₀

Thomas Waring (Nov 20 2025 at 17:07):

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

Aaron Liu (Nov 20 2025 at 18:05):

oh yes that's a more consise way to say that

Thomas Waring (Nov 20 2025 at 19:14):

done :) the full file is here but the new statements look like this:

import Mathlib.Combinatorics.SimpleGraph.Acyclic

namespace SimpleGraph

variable {V : Type _} {G : SimpleGraph V}

theorem exists_maximal_acyclic_extension {H : SimpleGraph V} (hHG : H  G) (hH : H.IsAcyclic) :
     H' : SimpleGraph V, H  H'  Maximal (fun H => H  G  H.IsAcyclic) H' := sorry

theorem Connected.has_spanning_tree (hG : G.Connected) {H : SimpleGraph V} (hHG : H  G)
  (hH : H.IsAcyclic) :  T  G, H  T  T.IsTree := sorry

end SimpleGraph

John Talbot (Nov 20 2025 at 21:56):

This looks great!

One small suggestion: you can useWalk.exists_boundary_dart to help with your first lemma above.

theorem Connected.subset_connected_complement (h : G.Connected) (s : Set V) (hs : s.Nonempty) :
    s = Set.univ   (u v : V), u  s  v  s  G.Adj u v := by
  simp_rw [Classical.or_iff_not_imp_left, Set.ne_univ_iff_exists_notMem]
  intro v, hv
  obtain u, hu := hs
  obtain p := h u v
  obtain d, _, h1, h2 := p.exists_boundary_dart _ hu hv
  exact ⟨_, _, h1, h2, d.adj

Thomas Waring (Nov 20 2025 at 22:00):

Ahh I thought there must be a better way thank you so much (in fact with that the whole lemma can be inlined for the case I use it)

Thomas Waring (Nov 21 2025 at 11:16):

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:

import Mathlib.Combinatorics.SimpleGraph.Acyclic

namespace SimpleGraph

variable {V : Type _} {G : SimpleGraph V}

theorem exists_maximal_acyclic_extension {H : SimpleGraph V} (hHG : H  G) (hH : H.IsAcyclic) :
     H' : SimpleGraph V, H  H'  Maximal (fun H => H  G  H.IsAcyclic) H' := sorry

theorem reachable_eq_of_maximal_acyclic (F : SimpleGraph V)
    (h : Maximal (fun H => H  G  H.IsAcyclic) F) : F.Reachable = G.Reachable := sorry

/-- Every graph has a spanning forest. -/
theorem exists_extension_acyclic_reachable_eq {H : SimpleGraph V} (hHG : H  G)
    (hH : H.IsAcyclic) :  F  G, H  F  F.IsAcyclic  F.Reachable = G.Reachable := sorry

theorem Connected.connected_of_maximal_acyclic (T : SimpleGraph V) (hG : G.Connected)
    (h : Maximal (fun H => H  G  H.IsAcyclic) T) : T.Connected := sorry

theorem Connected.exists_extension_isTree_le (hG : G.Connected) {H : SimpleGraph V} (hHG : H  G)
  (hH : H.IsAcyclic) :  T  G, H  T  T.IsTree := sorry

end SimpleGraph

The full file is still at the same link or (now I learnt this trick) here:

every graph has a spanning forest

Snir Broshi (Nov 24 2025 at 18:55):

Incredible! For the past few weeks I have been trying to prove the same thing with Zorn's lemma, which seems to take a lot of work:

Code with sorries

(and like you I figured this is a special case of finding an acyclic subgraph with F.Reachable = G.Reachable)

Thomas Waring (Nov 25 2025 at 10:58):

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

Thomas Waring (Nov 25 2025 at 11:00):

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


Last updated: Dec 20 2025 at 21:32 UTC