1-- Connectivity through an edge set: two vertices are joined when a path of2-- edges of F links them, the component of a vertex collects the vertices3-- joined to it, and the components of F partition the vertex set.4-- Part of Cumulus, the Grace17 mathematics library.5-- Copyright (c) 2026 Grant Passmore.6-- Released under the Apache License 2.0. See LICENSE in the repository root.7-- Authors: Grant Passmore89module Grace.Cumulus.GraphTheory.Multigraph.Connectivity10import Cuts1112-- A vertex set that no edge of F leaves.13predicate ClosedUnder (leftEnd rightEnd F A : Set) :=14∀ e ∈ F, (leftEnd(e) ∈ A ↔ rightEnd(e) ∈ A)1516-- v is reached from u along edges of F: every closed set containing u17-- contains v.18predicate Joined (leftEnd rightEnd F u v : Set) :=19∀ A, u ∈ A → ClosedUnder(leftEnd, rightEnd, F, A) → v ∈ A2021predicate Connects (V leftEnd rightEnd F : Set) :=22∀ u ∈ V, ∀ v ∈ V, Joined(leftEnd, rightEnd, F, u, v)2324-- The component of u: the vertices joined to u along F.25def componentBy (V leftEnd rightEnd F u : Set) : Set:=26 { v ∈ V | Joined(leftEnd, rightEnd, F, u, v) }2728def componentsBy (V leftEnd rightEnd F : Set) : Set:=29 { componentBy(V, leftEnd, rightEnd, F, u) | u ∈ V }3031theorem Joined.reflexive (leftEnd rightEnd F u : Set)32shows Joined(leftEnd, rightEnd, F, u, u)33proof34intros A start closed35exact start3637theorem Joined.of_edge (leftEnd rightEnd F e : Set)38requires edge : e ∈ F39shows Joined(leftEnd, rightEnd, F, leftEnd(e), rightEnd(e))40proof41intros A start closed42have crossed := closed with e, edge43exact crossed.forward with start4445theorem Joined.of_edge_reverse (leftEnd rightEnd F e : Set)46requires edge : e ∈ F47shows Joined(leftEnd, rightEnd, F, rightEnd(e), leftEnd(e))48proof49intros A start closed50have crossed := closed with e, edge51exact crossed.backward with start5253theorem Joined.transitive (leftEnd rightEnd F u v w : Set)54requires first : Joined(leftEnd, rightEnd, F, u, v)55requires second : Joined(leftEnd, rightEnd, F, v, w)56shows Joined(leftEnd, rightEnd, F, u, w)57proof58intros A start closed59have middle := first with A, start, closed60exact second with A, middle, closed6162theorem Joined.monotone (leftEnd rightEnd F G u v : Set)63requires subset : F ⊆ G64requires joined : Joined(leftEnd, rightEnd, F, u, v)65shows Joined(leftEnd, rightEnd, G, u, v)66proof67intros A start closed68have closedF : ClosedUnder(leftEnd, rightEnd, F, A)69intros e edge70exact closed with e, (subset with e, edge)71exact joined with A, start, closedF7273-- Symmetry: the vertices joined to u form a closed set containing u, so it74-- contains every vertex that u is joined to.75def joinedTo (V leftEnd rightEnd F u : Set) : Set:=76 { w ∈ V | Joined(leftEnd, rightEnd, F, w, u) }7778theorem joinedTo.closed (V leftEnd rightEnd F u : Set)79requires ends : ∀ e ∈ F, leftEnd(e) ∈ V ∧ rightEnd(e) ∈ V80shows ClosedUnder(leftEnd, rightEnd, F, joinedTo(V, leftEnd, rightEnd, F, u))81proof82intros e edge83obtain⟨leftV, rightV⟩:= ends with e, edge84constructor85 . intro member86exact⟨rightV, (Joined.transitive87with (Joined.of_edge_reverse with edge), member.right)⟩88 . intro member89exact⟨leftV, (Joined.transitive90with (Joined.of_edge with edge), member.right)⟩9192theorem Joined.symmetric (V leftEnd rightEnd F u v : Set)93requires ends : ∀ e ∈ F, leftEnd(e) ∈ V ∧ rightEnd(e) ∈ V94requires first : u ∈ V95requires second : v ∈ V96requires joined : Joined(leftEnd, rightEnd, F, u, v)97shows Joined(leftEnd, rightEnd, F, v, u)98proof99have reached := joined with joinedTo(V, leftEnd, rightEnd, F, u),100⟨first, Joined.reflexive⟩, (joinedTo.closed with ends)101exact reached.right102103-- A vertex outside V is a closed set on its own, so it is joined to nothing104-- else.105theorem Joined.start_in (V leftEnd rightEnd F u v : Set)106requires ends : ∀ e ∈ F, leftEnd(e) ∈ V ∧ rightEnd(e) ∈ V107requires distinct : u ≠ v108requires joined : Joined(leftEnd, rightEnd, F, u, v)109shows u ∈ V110proof111by_contra outside112have closed : ClosedUnder(leftEnd, rightEnd, F, {u})113intros e edge114have bounds := ends with e, edge115simp116have reached := joined with {u}, singleton.self, closed117simpat reached118cc119120-- A single non-loop edge has odd degree exactly at its two ends.121theorem degreeIn.single_parity (leftEnd rightEnd e : Set)122requires distinct : leftEnd(e) ≠ rightEnd(e)123shows∀ x, (Even(degreeIn(leftEnd, rightEnd, {e}, x)) ↔124 (x ≠ leftEnd(e) ∧ x ≠ rightEnd(e)))125proof126intro x127 by_cases hit : leftEnd(e) = x ∨ rightEnd(e) = x128 . rw degreeIn.end with distinct, hit129simp [Even.not_one]130 . have notLeft : leftEnd(e) ≠ x bytauto131have notRight : rightEnd(e) ≠ x bytauto132rw degreeIn.absent with notLeft, notRight133simp [Even.zero]134135-- The vertices reached from u by a path along F, with u itself: w ≠ u is136-- reached when some P ⊆ F has odd degree exactly at u and w.137def pathReached (V leftEnd rightEnd F u : Set) : Set:=138 { w ∈ V | w = u ∨∃ P ⊆ F, ∀ x ∈ V,139 (Even(degreeIn(leftEnd, rightEnd, P, x)) ↔ (x ≠ u ∧ x ≠ w)) }140141-- An edge with ends a and b carries a path from u to a on to b: the edge142-- alone when a = u, and otherwise the symmetric difference with the edge.143theorem pathReached.step (V leftEnd rightEnd F u e a b : Set)144requires finite : Finite(F)145requires edge : e ∈ F146requires distinct : a ≠ b147requires vertex : b ∈ V148requires parity : ∀ x, (Even(degreeIn(leftEnd, rightEnd, {e}, x)) ↔149 (x ≠ a ∧ x ≠ b))150requires reached : a ∈ pathReached(V, leftEnd, rightEnd, F, u)151shows b ∈ pathReached(V, leftEnd, rightEnd, F, u)152proof153have single : {e} ⊆ F := singleton.subset_iff.backward with edge154refine⟨vertex, _⟩155 by_cases start : a = u156 . right157witness {e}158refine⟨single, _⟩159intros x inV160rw← start161exact parity with x162rcases reached with⟨_, same |⟨P, subset, parityP⟩⟩163 . contradiction164 by_cases finish : b = u165 . left166exact finish167right168witness P △ {e}169refine⟨(symmetricDifference.subset with subset, single), _⟩170intros x inV171have finiteP := Finite.subset with finite, subset172have atP := parityP with x, inV173have atEdge := parity with x174-- P and {e} are both odd at a, so their sum is odd exactly at u and b.175rw degreeIn.symmetricDifference_parity with finiteP, (Finite.singleton at e)176simp [atP, atEdge]177178theorem pathReached.closed (V leftEnd rightEnd F u : Set)179requires finite : Finite(F)180requires ends : ∀ e ∈ F, leftEnd(e) ∈ V ∧ rightEnd(e) ∈ V181shows ClosedUnder(leftEnd, rightEnd, F,182 pathReached(V, leftEnd, rightEnd, F, u))183proof184intros e edge185have bounds := ends with e, edge186 by_cases loop : leftEnd(e) = rightEnd(e)187 . simp [loop]188have parity := degreeIn.single_parity with loop189have flipped : ∀ x, (Even(degreeIn(leftEnd, rightEnd, {e}, x)) ↔190 (x ≠ rightEnd(e) ∧ x ≠ leftEnd(e)))191intro x192have direct := parity with x193tauto194have reverse : rightEnd(e) ≠ leftEnd(e) bycc195constructor196 . intro reached197exact pathReached.step198with finite, edge, loop, bounds.right, parity, reached199 . intro reached200exact pathReached.step201with finite, edge, reverse, bounds.left, flipped, reached202203-- Along edges of F, a vertex joined to u is reached by a path from u: an204-- edge set P ⊆ F whose degrees are odd exactly at u and v when u ≠ v.205theorem Joined.path (V leftEnd rightEnd F u v : Set)206requires finite : Finite(F)207requires ends : ∀ e ∈ F, leftEnd(e) ∈ V ∧ rightEnd(e) ∈ V208requires distinct : u ≠ v209requires joined : Joined(leftEnd, rightEnd, F, u, v)210shows∃ P ⊆ F, ∀ w ∈ V,211 (Even(degreeIn(leftEnd, rightEnd, P, w)) ↔ (w ≠ u ∧ w ≠ v))212proof213have start := Joined.start_in with ends, distinct, joined214haveself : u ∈ pathReached(V, leftEnd, rightEnd, F, u)215bysimp [pathReached, start]216have reached := joined with pathReached(V, leftEnd, rightEnd, F, u), self,217 (pathReached.closed with finite, ends)218rcases reached with⟨_, same | path⟩219 . cc220 . exact path221222theorem componentBy.closed (V leftEnd rightEnd F u : Set)223requires ends : ∀ e ∈ F, leftEnd(e) ∈ V ∧ rightEnd(e) ∈ V224shows ClosedUnder(leftEnd, rightEnd, F,225 componentBy(V, leftEnd, rightEnd, F, u))226proof227intros e edge228obtain⟨leftV, rightV⟩:= ends with e, edge229constructor230 . intro member231exact⟨rightV, (Joined.transitive232with member.right, (Joined.of_edge with edge))⟩233 . intro member234exact⟨leftV, (Joined.transitive235with member.right, (Joined.of_edge_reverse with edge))⟩236237theorem componentBy.eq_of_joined (V leftEnd rightEnd F u w : Set)238requires ends : ∀ e ∈ F, leftEnd(e) ∈ V ∧ rightEnd(e) ∈ V239requires first : u ∈ V240requires second : w ∈ V241requires joined : Joined(leftEnd, rightEnd, F, u, w)242shows componentBy(V, leftEnd, rightEnd, F, u) =243 componentBy(V, leftEnd, rightEnd, F, w)244proof245have back := Joined.symmetric with ends, first, second, joined246ext z247constructor248 . intro member249exact⟨member.left, (Joined.transitive with back, member.right)⟩250 . intro member251exact⟨member.left, (Joined.transitive with joined, member.right)⟩252253-- The edges of F with both ends in X.254def inside (leftEnd rightEnd F X : Set) : Set:=255 { e ∈ F | leftEnd(e) ∈ X ∧ rightEnd(e) ∈ X }256257-- A partition of V: nonempty classes, pairwise disjoint, covering V.258predicate Partition (V P : Set) :=259 (∀ X ∈ P, X ⊆ V ∧ X ≠∅) ∧260 (∀ X ∈ P, ∀ Y ∈ P, X ≠ Y → Disjoint(X, Y)) ∧261 (∀ v ∈ V, ∃ X ∈ P, v ∈ X)262263theorem componentsBy.partition (V leftEnd rightEnd F : Set)264requires ends : ∀ e ∈ F, leftEnd(e) ∈ V ∧ rightEnd(e) ∈ V265shows Partition(V, componentsBy(V, leftEnd, rightEnd, F))266proof267constructor268-- A component is a subset of V containing its own vertex.269 . intros X inFamily270obtain⟨u, inV, rfl⟩:= inFamily271constructor272 . intros w member273exact member.left274 . apply Nonempty.ne_empty275witness u276exact⟨inV, Joined.reflexive⟩277constructor278-- Two components sharing a vertex are one component.279 . intros X inFamilyX Y inFamilyY different280obtain⟨u, uInV, rfl⟩:= inFamilyX281obtain⟨w, wInV, rfl⟩:= inFamilyY282apply Disjoint.iff.backward283intros x inU inW284obtain⟨xInV, fromU⟩:= inU285obtain⟨_, fromW⟩:= inW286have back := Joined.symmetric with ends, wInV, xInV, fromW287have joined := Joined.transitive with fromU, back288apply different289exact componentBy.eq_of_joined with ends, uInV, wInV, joined290 . intros v inV291witness componentBy(V, leftEnd, rightEnd, F, v)292exact⟨⟨v, inV, rfl⟩, ⟨inV, Joined.reflexive⟩⟩293