1-- Finite reachability, connected components, and their partition laws.2-- Part of Cumulus, the Grace17 mathematics library.3-- Copyright (c) 2026 Grant Passmore.4-- Released under the Apache License 2.0. See LICENSE in the repository root.5-- Authors: Grant Passmore67module Grace.Cumulus.GraphTheory.Connectivity8import Reachability910-- Closure is relative to V; it also makes sense for directed graphs.11predicate EdgeClosed (V E A : Set) :=12∀ u ∈ A, ∀ v ∈ V, Adjacent(E, u, v) → v ∈ A1314-- One induction covers all finite lengths and arbitrary vertex sets.15theorem reachableWithin.in_closed_set (V E x A : Set, n : Nat)16requires start : x ∈ A17requires closed : EdgeClosed(V, E, A)18shows reachableWithin(V, E, x, n) ⊆ A19proof20induction n21| zero =>22rw reachableWithin.zero23intros y member24have equality := singleton.membership.forward with member25rw equality26exact start27| succ(k) using ih =>28rw reachableWithin.successor29intros y member30have alternatives := union2.membership.forward at y,31 reachableWithin(V, E, x, k), nextNeighbors(V, E, reachableWithin(V, E, x, k)) with member32cases alternatives with old | new33exact ih with y, old34 . have parts := nextNeighbors.membership.forward at V, E, reachableWithin(V, E, x, k), y with new35rcases parts with⟨hy, z, hz, edge⟩36have zInA := ih with z, hz37exact closed with z, zInA, y, hy, edge3839theorem Reachable.in_closed_set (V E x y A : Set)40requires start : x ∈ A41requires closed : EdgeClosed(V, E, A)42requires path : Reachable(V, E, x, y)43shows y ∈ A44proof45rcases path with⟨n, hn, reached⟩46have nNat := nat.omega.backward with hn47have included := reachableWithin.in_closed_set with nNat, start, closed48exact included with y, reached4950theorem Reachable.vertex (V E x y : Set)51requires hx : x ∈ V52requires path : Reachable(V, E, x, y)53shows y ∈ V54proof55have closed : EdgeClosed(V, E, V)56intros u hu v hv edge57exact hv58exact Reachable.in_closed_set with hx, closed, path5960theorem Reachable.extend (V E x y z : Set)61requires path : Reachable(V, E, x, y)62requires hz : z ∈ V63requires edge : Adjacent(E, y, z)64shows Reachable(V, E, x, z)65proof66rcases path with⟨n, hn, reached⟩67have nNat := nat.omega.backward with hn68have nextNat := Naturals.constructor.succ with nNat69have nextIn := nat.omega.forward with nextNat70have next := reachableWithin.advance with nNat, reached, hz, edge71unfold Reachable72refine⟨succ(n), nextIn, next⟩7374-- Separation gives a set even when the graph is infinite.75def component (V E x : Set) : Set:= { y ∈ V | Reachable(V, E, x, y) }7677theorem component.source (V E x : Set)78requires hx : x ∈ V79shows x ∈ component(V, E, x)80proof81apply component.membership.backward82have path := Reachable.reflexive at V, E, x83refine⟨hx, path⟩8485theorem component.closed (V E x : Set)86shows EdgeClosed(V, E, component(V, E, x))87proof88intros u hu v hv edge89obtain⟨uInV, reached⟩:= component.membership.forward with hu90have extended := Reachable.extend with reached, hv, edge91exact component.membership.backward with⟨hv, extended⟩9293theorem component.minimal (V E x A : Set)94requires start : x ∈ A95requires closed : EdgeClosed(V, E, A)96shows component(V, E, x) ⊆ A97proof98intros y member99obtain⟨hy, path⟩:= component.membership.forward with member100exact Reachable.in_closed_set with start, closed, path101102theorem Reachable.transitive (V E x y z : Set)103requires hy : y ∈ V104requires first : Reachable(V, E, x, y)105requires second : Reachable(V, E, y, z)106shows Reachable(V, E, x, z)107proof108have start : y ∈ component(V, E, x) := component.membership.backward with⟨hy, first⟩109have member := Reachable.in_closed_set with start, second110obtain⟨hz, path⟩:= component.membership.forward with member111exact path112113-- Reversal is proved at every finite length, using concatenation above.114theorem reachableWithin.reverse (V E x : Set, n : Nat)115requires graph : SimpleGraph(V, E)116requires hx : x ∈ V117shows∀ y ∈ reachableWithin(V, E, x, n), Reachable(V, E, y, x)118proof119induction n120| zero =>121intros y member122rw reachableWithin.zero at member123have equality := singleton.membership.forward with member124rw equality125exact Reachable.reflexive126| succ(k) using ih =>127intros y member128rw reachableWithin.successor at member129have alternatives := union2.membership.forward at y,130 reachableWithin(V, E, x, k), nextNeighbors(V, E, reachableWithin(V, E, x, k)) with member131cases alternatives with old | new132 . exact ih with y, old133 . have parts := nextNeighbors.membership.forward at V, E, reachableWithin(V, E, x, k), y with new134rcases parts with⟨hy, z, hz, edge⟩135have back := ih with z, hz136have reverseEdge := Adjacent.symmetric with graph, edge137have bounds := Adjacent.bounds with graph, edge138rcases bounds with⟨zInV, yInV⟩139have first := Reachable.of_edge with zInV, reverseEdge140exact Reachable.transitive with zInV, first, back141142theorem Reachable.symmetric (V E x y : Set)143requires graph : SimpleGraph(V, E)144requires hx : x ∈ V145requires path : Reachable(V, E, x, y)146shows Reachable(V, E, y, x)147proof148rcases path with⟨n, hn, reached⟩149have nNat := nat.omega.backward with hn150have reverse := reachableWithin.reverse with nNat, graph, hx151exact reverse with y, reached152153theorem component.subset_vertices (V E x : Set)154shows component(V, E, x) ⊆ V155proof156intros y member157obtain⟨hy, path⟩:= component.membership.forward with member158exact hy159160theorem component.equal_of_reachable (V E x y : Set)161requires graph : SimpleGraph(V, E)162requires hx : x ∈ V163requires hy : y ∈ V164requires path : Reachable(V, E, x, y)165shows component(V, E, x) = component(V, E, y)166proof167have back := Reachable.symmetric with graph, hx, path168ext z169constructor170 . intro member171obtain⟨hz, xz⟩:= component.membership.forward with member172have yz := Reachable.transitive with hx, back, xz173exact component.membership.backward with⟨hz, yz⟩174 . intro member175obtain⟨hz, yz⟩:= component.membership.forward with member176have xz := Reachable.transitive with hy, path, yz177exact component.membership.backward with⟨hz, xz⟩178179theorem component.equal_iff (V E x y : Set)180requires graph : SimpleGraph(V, E)181requires hx : x ∈ V182requires hy : y ∈ V183shows component(V, E, x) = component(V, E, y) ↔ Reachable(V, E, x, y)184proof185constructor186 . intro equality187have member := component.source at V, E, y with hy188rw← equality at member189obtain⟨yInV, path⟩:= component.membership.forward with member190exact path191 . intro path192exact component.equal_of_reachable with graph, hx, hy, path193194theorem component.equal_of_common_vertex (V E x y z : Set)195requires graph : SimpleGraph(V, E)196requires hx : x ∈ V197requires hy : y ∈ V198requires first : z ∈ component(V, E, x)199requires second : z ∈ component(V, E, y)200shows component(V, E, x) = component(V, E, y)201proof202have partsX := component.membership.forward with first203have partsY := component.membership.forward with second204rcases partsX with⟨hz, xz⟩205rcases partsY with⟨hz2, yz⟩206have firstEqual := component.equal_of_reachable with graph, hx, hz, xz207have secondEqual := component.equal_of_reachable with graph, hy, hz, yz208rw firstEqual209 symm210exact secondEqual211212theorem component.equal_or_disjoint (V E x y : Set)213requires graph : SimpleGraph(V, E)214requires hx : x ∈ V215requires hy : y ∈ V216shows component(V, E, x) = component(V, E, y) ∨217 Disjoint(component(V, E, x), component(V, E, y))218proof219have alternatives : component(V, E, x) = component(V, E, y) ∨ component(V, E, x) ≠ component(V, E, y) bytauto220cases alternatives with equal | different221 . left222exact equal223 . right224unfold Disjoint225ext z226constructor227 . intro member228obtain⟨first, second⟩:= intersection.membership.forward with member229exfalso230apply different231exact component.equal_of_common_vertex with graph, hx, hy, first, second232 . intro impossible233exfalso234exactset.empty with impossible235236predicate GraphConnected (V E : Set) :=237∀ x ∈ V, ∀ y ∈ V, Reachable(V, E, x, y)238239theorem component.connected (V E x : Set)240requires graph : SimpleGraph(V, E)241requires hx : x ∈ V242shows∀ u ∈ component(V, E, x), ∀ v ∈ component(V, E, x), Reachable(V, E, u, v)243proof244intros u hu v hv245have partsU := component.membership.forward with hu246have partsV := component.membership.forward with hv247rcases partsU with⟨uInV, xu⟩248rcases partsV with⟨vInV, xv⟩249have ux := Reachable.symmetric with graph, hx, xu250exact Reachable.transitive with hx, ux, xv251252theorem GraphConnected.component (V E x : Set)253requires connected : GraphConnected(V, E)254requires hx : x ∈ V255shows component(V, E, x) = V256proof257ext y258constructor259 . intro member260have included := component.subset_vertices at V, E, x261exact included with y, member262 . intro hy263have path := connected with x, hx, y, hy264exact component.membership.backward with⟨hy, path⟩265266theorem GraphConnected.of_component (V E x : Set)267requires graph : SimpleGraph(V, E)268requires hx : x ∈ V269requires entire : component(V, E, x) = V270shows GraphConnected(V, E)271proof272have connected := component.connected with graph, hx273intros u hu v hv274rw← entire at hu275rw← entire at hv276exact connected with u, hu, v, hv277278-- This set of sets is the component partition, bounded by the powerset.279def components (V E : Set) : Set:=280 { A ∈𝒫(V) |∃ x ∈ V, A = component(V, E, x) }281282theorem components.contains (V E x : Set)283requires hx : x ∈ V284shows component(V, E, x) ∈ components(V, E)285proof286have included := component.subset_vertices at V, E, x287unfold Subset at included288have inPower :=set.powerset.backward with included289exact components.membership.backward with⟨inPower, x, hx, rfl⟩290291theorem components.nonempty_members (V E A : Set)292requires member : A ∈ components(V, E)293shows∃ x ∈ A, x ∈ V294proof295obtain⟨inPower, x, hx, equality⟩:= components.membership.forward with member296have source := component.source at V, E, x with hx297rw← equality at source298refine⟨x, source, hx⟩299300theorem components.cover (V E : Set)301shows⋃(components(V, E)) = V302proof303ext x304constructor305 . intro member306obtain⟨A, inFamily, inA⟩:=set.union.forward with member307obtain⟨inPower, witness⟩:= components.membership.forward with inFamily308have included :=set.powerset.forward with inPower309exact included with x, inA310 . intro hx311have inFamily := components.contains at V, E, x with hx312have inComponent := component.source at V, E, x with hx313exact union.contains with inFamily, inComponent314315theorem components.equal_or_disjoint (V E A B : Set)316requires graph : SimpleGraph(V, E)317requires first : A ∈ components(V, E)318requires second : B ∈ components(V, E)319shows A = B ∨ Disjoint(A, B)320proof321have partsA := components.membership.forward with first322have partsB := components.membership.forward with second323rcases partsA with⟨powerA, x, hx, equalityA⟩324rcases partsB with⟨powerB, y, hy, equalityB⟩325rw [equalityA, equalityB]326exact component.equal_or_disjoint with graph, hx, hy327328-- A route starting in an edge-closed set never needs to leave that set.329theorem reachableWithin.restrict_closed (V E x A : Set, n : Nat)330requires start : x ∈ A331requires closed : EdgeClosed(V, E, A)332shows∀ y ∈ reachableWithin(V, E, x, n), Reachable(A, inducedEdges(E, A), x, y)333proof334induction n335| zero =>336intros y member337rw reachableWithin.zero at member338have equality := singleton.membership.forward with member339rw equality340exact Reachable.reflexive341| succ(k) using ih =>342intros y member343rw reachableWithin.successor at member344have alternatives := union2.membership.forward with member345cases alternatives with old | new346 . exact ih with y, old347 . have parts := nextNeighbors.membership.forward at V, E, reachableWithin(V, E, x, k), y with new348rcases parts with⟨hy, z, hz, edge⟩349have previous := ih with z, hz350have included := reachableWithin.in_closed_set with start, closed351have zInA := included with z, hz352have yInA := closed with z, zInA, y, hy, edge353have restricted : Adjacent(inducedEdges(E, A), z, y)354unfold Adjacent355exact inducedEdges.membership.backward with⟨edge, zInA, yInA⟩356exact Reachable.extend with previous, yInA, restricted357358theorem Reachable.restrict_closed (V E x y A : Set)359requires start : x ∈ A360requires closed : EdgeClosed(V, E, A)361requires path : Reachable(V, E, x, y)362shows Reachable(A, inducedEdges(E, A), x, y)363proof364rcases path with⟨n, hn, reached⟩365have nNat := nat.omega.backward with hn366have restricted := reachableWithin.restrict_closed with nNat, start, closed367exact restricted with y, reached368369theorem component.induced_connected (V E x : Set)370requires graph : SimpleGraph(V, E)371requires hx : x ∈ V372shows GraphConnected(component(V, E, x), inducedEdges(E, component(V, E, x)))373proof374intros u hu v hv375have connected := component.connected with graph, hx376have path := connected with u, hu, v, hv377exact Reachable.restrict_closed with hu, path378379theorem component.induced_graph (V E x : Set)380requires graph : SimpleGraph(V, E)381shows SimpleGraph(component(V, E, x), inducedEdges(E, component(V, E, x)))382proof383have included := component.subset_vertices at V, E, x384exact inducedEdges.graph with graph, included385386theorem component.no_crossing_edge (V E x u v : Set)387requires inside : u ∈ component(V, E, x)388requires hv : v ∈ V389requires outside : ¬(v ∈ component(V, E, x))390shows¬Adjacent(E, u, v)391proof392intro edge393have closed := component.closed at V, E, x394apply outside395exact closed with u, inside, v, hv, edge396397theorem GraphConnected.closed_set (V E A x : Set)398requires connected : GraphConnected(V, E)399requires included : A ⊆ V400requires start : x ∈ A401requires closed : EdgeClosed(V, E, A)402shows A = V403proof404have hx := included with x, start405have entire := GraphConnected.component with connected, hx406have minimal := component.minimal with start, closed407rw entire at minimal408ext y409constructor410 . intro hy411exact included with y, hy412 . intro hy413exact minimal with y, hy414415theorem component.empty_graph (V x : Set)416requires hx : x ∈ V417shows component(V, empty, x) = {x}418proof419have closed : EdgeClosed(V, empty, {x})420intros u hu v hv edge421exfalso422unfold Adjacent at edge423unfold relates at edge424exactset.empty with edge425have start := singleton.self at x426have minimal := component.minimal with start, closed427ext y428constructor429 . intro member430exact minimal with y, member431 . intro member432have equality := singleton.membership.forward with member433rw equality434exact component.source with hx435436theorem completeEdges.connected (V : Set)437shows GraphConnected(V, completeEdges(V))438proof439intros x hx y hy440have alternatives : x = y ∨ x ≠ y bytauto441cases alternatives with equal | distinct442 . rw equal443exact Reachable.reflexive444 . apply Reachable.of_edge at V, completeEdges(V), x, y445 . exact hy446 . apply completeEdges.adjacent.backward at V, x, y447refine⟨hx, hy, distinct⟩448449-- This characterization also applies to directed graphs: GraphConnected450-- means that every vertex reaches every other vertex.451theorem GraphConnected.iff_closed_sets (V E : Set)452shows GraphConnected(V, E) ↔453 (∀ A, A ⊆ V → EdgeClosed(V, E, A) → (A =∅∨ A = V))454proof455constructor456 . intros connected A included closed457have alternatives : A =∅∨ A ≠∅bytauto458cases alternatives with vacant | nonempty459 . left460exact vacant461 . right462have inhabited := Nonempty.of_ne_empty at A with*463rcases inhabited with⟨x, start⟩464exact GraphConnected.closed_set at V, E, A, x with*465 . intros closedSets x hx y hy466have included := component.subset_vertices at V, E, x467have closed := component.closed at V, E, x468have start := component.source at V, E, x with*469have alternatives := closedSets with component(V, E, x), included, closed470cases alternatives with vacant | entire471 . exfalso472rw vacant at start473exactset.empty with start474 . have member : y ∈ component(V, E, x)475 rwa entire476have parts := component.membership.forward at V, E, x, y with*477rcases parts with⟨vertex, path⟩478exact path479480theorem GraphConnected.no_closed_cut (V E A : Set)481requires included : A ⊆ V482requires inhabited : Nonempty(A)483requires proper : A ≠ V484requires closed : EdgeClosed(V, E, A)485shows¬GraphConnected(V, E)486proof487intro connected488rcases inhabited with⟨x, start⟩489have entire := GraphConnected.closed_set with*490exact proper with entire491492-- Every nontrivial cut of a connected graph has an outgoing crossing edge.493theorem GraphConnected.crossing_edge (V E A : Set)494requires connected : GraphConnected(V, E)495requires included : A ⊆ V496requires inhabited : Nonempty(A)497requires proper : A ≠ V498shows∃ u ∈ A, ∃ v ∈ V, ¬(v ∈ A) ∧ Adjacent(E, u, v)499proof500by_contra noCrossing501have closed : EdgeClosed(V, E, A)502intros u hu v hv edge503by_contra outside504apply noCrossing505refine⟨u, hu, v, hv, outside, edge⟩506have disconnected := GraphConnected.no_closed_cut with*507exact disconnected with connected508509-- Connectivity is equivalent to an outgoing edge across every nontrivial cut.510theorem GraphConnected.iff_crossing_cuts (V E : Set)511shows GraphConnected(V, E) ↔512 (∀ A, A ⊆ V → Nonempty(A) → A ≠ V →513∃ u ∈ A, ∃ v ∈ V, ¬(v ∈ A) ∧ Adjacent(E, u, v))514proof515constructor516 . intros connected A included inhabited proper517exact GraphConnected.crossing_edge with*518 . intro crossingCuts519apply GraphConnected.iff_closed_sets.backward520intros A included closed521have alternatives : A =∅∨ A ≠∅bytauto522cases alternatives with vacant | nonempty523 . left524exact vacant525 . right526by_contra proper527have inhabited := Nonempty.of_ne_empty with*528obtain⟨u, hu, v, hv, outside, edge⟩:= crossingCuts with A, included, inhabited, proper529have inside := closed with u, hu, v, hv, edge530exact outside with inside531