1-- Contracting an edge of a multigraph: the quotient by the partition merging2-- its two ends. The contraction of a bridgeless graph is bridgeless, its cuts3-- are the cuts of the graph not separating the ends of the edge, and the4-- degrees of its classes are read off from the graph.5-- Part of Cumulus, the Grace17 mathematics library.6-- Copyright (c) 2026 Grant Passmore.7-- Released under the Apache License 2.0. See LICENSE in the repository root.8-- Authors: Grant Passmore910module Grace.Cumulus.GraphTheory.Flows.Contraction11import EvenCovers1213-- Section: The contraction of an edge.1415-- The partition of V that merges the two ends of e: the components of {e}.16def contractionPartition (V leftEnd rightEnd e : Set) : Set:=17 componentsBy(V, leftEnd, rightEnd, {e})1819def contractedLeft (V E leftEnd rightEnd e : Set) : Set:=20 quotientEnd(E ∖ {e}, contractionPartition(V, leftEnd, rightEnd, e), leftEnd)21def contractedRight (V E leftEnd rightEnd e : Set) : Set:=22 quotientEnd(E ∖ {e}, contractionPartition(V, leftEnd, rightEnd, e),23 rightEnd)2425theorem contractionPartition.partition (V E leftEnd rightEnd e : Set)26requires graph : Graph(V, E, leftEnd, rightEnd)27requires edge : e ∈ E28shows Partition(V, contractionPartition(V, leftEnd, rightEnd, e))29proof30have ends : ∀ f ∈ {e}, leftEnd(f) ∈ V ∧ rightEnd(f) ∈ V31intros f hf32rw singleton.membership at hf33rw hf34exact Graph.ends with graph, e, edge35unfold contractionPartition36exact componentsBy.partition with ends3738-- The quotient ends of a subset of the edges, as a function on that subset.39theorem quotientEnd.function_subset (V E D P endpoint : Set)40requires function : endpoint : E → V41requires partition : Partition(V, P)42requires edges : D ⊆ E43shows quotientEnd(D, P, endpoint) : D → P44proof45constructor46 . intros p member47exact member.left48constructor49 . intros x y relatedY z relatedZ50obtain⟨_, valueY⟩:= quotientEnd.relates with relatedY51obtain⟨_, valueZ⟩:= quotientEnd.relates with relatedZ52cc53 . intros x edge54have vertex := FunctionBetween.apply_closed55with function, (edges with x, edge)56obtain⟨class, _⟩:= classOf.member with partition, vertex57witness classOf(P, endpoint(x))58refine⟨class, _⟩59exact quotientEnd.membership.backward60with⟨(product.pair with edge, class), x, edge, rfl⟩6162theorem quotientEnd.value_subset (V E D P endpoint f : Set)63requires function : endpoint : E → V64requires partition : Partition(V, P)65requires edges : D ⊆ E66requires edge : f ∈ D67shows quotientEnd(D, P, endpoint)(f) = classOf(P, endpoint(f))68proof69have vertex := FunctionBetween.apply_closed70with function, (edges with f, edge)71obtain⟨class, _⟩:= classOf.member with partition, vertex72have related :73 relates(quotientEnd(D, P, endpoint), f, classOf(P, endpoint(f))) :=74 quotientEnd.membership.backward75with⟨(product.pair with edge, class), f, edge, rfl⟩76exact FunctionBetween.apply_value77with (quotientEnd.function_subset with function, partition, edges),78 edge, related7980theorem contraction.graph (V E leftEnd rightEnd e : Set)81requires graph : Graph(V, E, leftEnd, rightEnd)82requires edge : e ∈ E83shows Graph(contractionPartition(V, leftEnd, rightEnd, e), E ∖ {e},84 contractedLeft(V, E, leftEnd, rightEnd, e),85 contractedRight(V, E, leftEnd, rightEnd, e))86proof87have partition := contractionPartition.partition with graph, edge88have edges := difference.subset_left at E, {e}89unfold contractedLeft90unfold contractedRight91exact⟨(quotientEnd.function_subset with graph.left, partition, edges),92 (quotientEnd.function_subset with graph.right, partition, edges)⟩9394-- The ends of an edge of the contraction are the classes of its ends.95theorem contraction.value (V E leftEnd rightEnd e f : Set)96requires graph : Graph(V, E, leftEnd, rightEnd)97requires edge : e ∈ E98requires other : f ∈ E ∖ {e}99shows contractedLeft(V, E, leftEnd, rightEnd, e)(f) =100 classOf(contractionPartition(V, leftEnd, rightEnd, e), leftEnd(f)) ∧101 contractedRight(V, E, leftEnd, rightEnd, e)(f) =102 classOf(contractionPartition(V, leftEnd, rightEnd, e), rightEnd(f))103proof104have partition := contractionPartition.partition with graph, edge105have edges := difference.subset_left at E, {e}106unfold contractedLeft107unfold contractedRight108exact⟨(quotientEnd.value_subset with graph.left, partition, edges, other),109 (quotientEnd.value_subset with graph.right, partition, edges, other)⟩110111-- Along no edges, distinct vertices stay apart.112theorem Joined.not_empty (leftEnd rightEnd u v : Set)113requires distinct : u ≠ v114shows¬Joined(leftEnd, rightEnd, ∅, u, v)115proof116intro joined117have same := Joined.empty with joined118cc119120theorem ends_in.empty (V leftEnd rightEnd : Set)121shows∀ f ∈∅, leftEnd(f) ∈ V ∧ rightEnd(f) ∈ V122proof123intros f hf124simpat hf125126-- Contracting a proper edge loses one vertex.127theorem contraction.vertex_count (V E leftEnd rightEnd e : Set)128requires graph : Graph(V, E, leftEnd, rightEnd)129requires finiteVertices : Finite(V)130requires edge : e ∈ E131requires proper : leftEnd(e) ≠ rightEnd(e)132shows cardinality(contractionPartition(V, leftEnd, rightEnd, e)) +1=133 cardinality(V)134proof135have edgeEnds := Graph.ends with graph, e, edge136-- Along no edges the two ends of e are apart, so e merges two singletons.137have apart : ¬Joined(leftEnd, rightEnd, ∅, leftEnd(e), rightEnd(e)) :=138 Joined.not_empty with proper139have count := componentsBy.add_edge_apart140with finiteVertices, ends_in.empty, edgeEnds, apart141rw [union2.left_empty, (componentsBy.empty_count with finiteVertices)]142at count143unfold contractionPartition144exact count145146-- The two ends of e share a class of the contraction.147theorem contraction.same_class (V E leftEnd rightEnd e : Set)148requires graph : Graph(V, E, leftEnd, rightEnd)149requires edge : e ∈ E150shows classOf(contractionPartition(V, leftEnd, rightEnd, e), leftEnd(e)) =151 classOf(contractionPartition(V, leftEnd, rightEnd, e), rightEnd(e))152proof153have partition := contractionPartition.partition with graph, edge154unfold contractionPartition at partition155unfold contractionPartition156have apart := partition.right.left157obtain⟨leftIn, rightIn⟩:= Graph.ends with graph, e, edge158have joined : Joined(leftEnd, rightEnd, {e}, leftEnd(e), rightEnd(e)) :=159 Joined.of_edge with singleton.self160haveclass : componentBy(V, leftEnd, rightEnd, {e}, leftEnd(e)) ∈161 componentsBy(V, leftEnd, rightEnd, {e}) :=162 componentsBy.contains at V, leftEnd, rightEnd, {e}, leftEnd(e) with leftIn163have leftMember :164 leftEnd(e) ∈ componentBy(V, leftEnd, rightEnd, {e}, leftEnd(e)) :=165 componentBy.self with leftIn166have rightMember :167 rightEnd(e) ∈ componentBy(V, leftEnd, rightEnd, {e}, leftEnd(e)) :=168⟨rightIn, joined⟩169rw [(classOf.eq_of_member with apart, class, leftMember),170 (classOf.eq_of_member with apart, class, rightMember)]171172-- Section: Cuts of the contraction.173174-- A cut of the contraction is a cut of the graph not separating the ends of175-- e, which e therefore does not cross.176theorem contraction.crossing (V E leftEnd rightEnd e Y : Set)177requires graph : Graph(V, E, leftEnd, rightEnd)178requires edge : e ∈ E179requires classes : Y ⊆ contractionPartition(V, leftEnd, rightEnd, e)180shows crossing(contractedLeft(V, E, leftEnd, rightEnd, e),181 contractedRight(V, E, leftEnd, rightEnd, e), E ∖ {e}, Y) =182 crossing(leftEnd, rightEnd, E, ⋃(Y))183proof184have partition := contractionPartition.partition with graph, edge185have endsE := Graph.ends with graph186ext f187 by_cases inRest : f ∈ E ∖ {e}188 . obtain⟨leftValue, rightValue⟩:=189 contraction.value with graph, edge, inRest190obtain⟨inE, _⟩:= difference.membership.forward with inRest191obtain⟨leftIn, rightIn⟩:= endsE with f, inE192have leftIff := classOf.mem_union_iff with partition, classes, leftIn193have rightIff := classOf.mem_union_iff with partition, classes, rightIn194simp [crossing, leftValue, rightValue, leftIff, rightIff, inRest, inE]195 . by_cases same : f = e196 . rw same197obtain⟨leftIn, rightIn⟩:= endsE with e, edge198have leftIff := classOf.mem_union_iff with partition, classes, leftIn199have rightIff := classOf.mem_union_iff with partition, classes, rightIn200have sameClass := contraction.same_class with graph, edge201have absent : ¬e ∈ E ∖ {e} bysimp202simp [crossing, leftIff, rightIff, sameClass, absent]203 . have notE : ¬f ∈ E204intro inE205apply inRest206simp [inE, same]207simp [crossing, inRest, notE]208209-- Every cut of the contraction is a cut of the graph, so none has one edge.210theorem contraction.bridgeless (V E leftEnd rightEnd e : Set)211requires graph : Graph(V, E, leftEnd, rightEnd)212requires finiteEdges : Finite(E)213requires edge : e ∈ E214requires bridgeless : Bridgeless(V, E, leftEnd, rightEnd)215shows Bridgeless(contractionPartition(V, leftEnd, rightEnd, e), E ∖ {e},216 contractedLeft(V, E, leftEnd, rightEnd, e),217 contractedRight(V, E, leftEnd, rightEnd, e))218proof219intros Y classes220have partition := contractionPartition.partition with graph, edge221have inV : ⋃(Y) ⊆ V222intros v hv223obtain⟨X, inY, inX⟩:= union.membership.forward with hv224exact (partition.left with X, (classes with X, inY)).left with v, inX225rw contraction.crossing with graph, edge, classes226exact bridgeless with⋃(Y), inV227228-- Section: The classes of the contraction and their degrees.229230-- The class of a vertex in the contraction is its component along {e}: a231-- vertex apart from the ends of e is a class by itself, and the ends of e232-- form the one other class.233theorem contraction.class (V E leftEnd rightEnd e v : Set)234requires graph : Graph(V, E, leftEnd, rightEnd)235requires edge : e ∈ E236requires vertex : v ∈ V237shows classOf(contractionPartition(V, leftEnd, rightEnd, e), v) =238 componentBy(V, leftEnd, rightEnd, {e}, v)239proof240have partition := contractionPartition.partition with graph, edge241unfold contractionPartition at partition242unfold contractionPartition243haveclass : componentBy(V, leftEnd, rightEnd, {e}, v) ∈244 componentsBy(V, leftEnd, rightEnd, {e}) :=245 componentsBy.contains at V, leftEnd, rightEnd, {e}, v with vertex246have member : v ∈ componentBy(V, leftEnd, rightEnd, {e}, v) :=247 componentBy.self with vertex248exact classOf.eq_of_member with partition.right.left, class, member249250theorem contraction.class_apart (V E leftEnd rightEnd e v : Set)251requires graph : Graph(V, E, leftEnd, rightEnd)252requires edge : e ∈ E253requires vertex : v ∈ V254requiresleft : v ≠ leftEnd(e)255requiresright : v ≠ rightEnd(e)256shows classOf(contractionPartition(V, leftEnd, rightEnd, e), v) = {v}257proof258rw contraction.class with graph, edge, vertex259-- Along no edges v reaches neither end of e, so e does not move v.260have apartLeft : ¬Joined(leftEnd, rightEnd, ∅, v, leftEnd(e)) :=261 Joined.not_empty withleft262have apartRight : ¬Joined(leftEnd, rightEnd, ∅, v, rightEnd(e)) :=263 Joined.not_empty withright264have unchanged := componentBy.add_edge_apart265with ends_in.empty, vertex, apartLeft, apartRight266rw union2.left_empty at unchanged267rw unchanged268ext w269constructor270 . intro hw271have same := Joined.empty with hw.right272rw same273exact singleton.self274 . intro hw275rw singleton.membership at hw276rw hw277exact componentBy.self with vertex278279theorem contraction.class_end (V E leftEnd rightEnd e : Set)280requires graph : Graph(V, E, leftEnd, rightEnd)281requires edge : e ∈ E282shows classOf(contractionPartition(V, leftEnd, rightEnd, e), leftEnd(e)) =283 {leftEnd(e), rightEnd(e)}284proof285obtain⟨leftIn, rightIn⟩:= Graph.ends with graph, e, edge286rw contraction.class with graph, edge, leftIn287ext w288constructor289 . intro hw290-- The pair of ends is closed under e, so it holds all that e reaches.291have closed : ClosedUnder(leftEnd, rightEnd, {e},292 {leftEnd(e), rightEnd(e)})293intros f hf294rw singleton.membership at hf295rw hf296simp297have start : leftEnd(e) ∈ {leftEnd(e), rightEnd(e)} := pair.left298exact hw.right with {leftEnd(e), rightEnd(e)}, start, closed299 . intro hw300rw pair.membership at hw301rcases hw withrfl|rfl302 . exact componentBy.self with leftIn303 . have joined : Joined(leftEnd, rightEnd, {e}, leftEnd(e), rightEnd(e)) :=304 Joined.of_edge with singleton.self305exact⟨rightIn, joined⟩306307-- An end (f, i) of the contraction at a class Y is an end of f at a vertex308-- of Y.309theorem incidentEnds.contraction (V E leftEnd rightEnd e F Y f i : Set)310requires graph : Graph(V, E, leftEnd, rightEnd)311requires edge : e ∈ E312requires edges : F ⊆ E ∖ {e}313requiresclass : Y ∈ contractionPartition(V, leftEnd, rightEnd, e)314shows (f, i) ∈ incidentEnds(contractedLeft(V, E, leftEnd, rightEnd, e),315 contractedRight(V, E, leftEnd, rightEnd, e), F, Y) ↔316 f ∈ F ∧ ((i =0∧ leftEnd(f) ∈ Y) ∨ (i =1∧ rightEnd(f) ∈ Y))317proof318have partition := contractionPartition.partition with graph, edge319rw incidentEnds.pair320 by_cases inF : f ∈ F321 . have other := edges with f, inF322obtain⟨leftValue, rightValue⟩:=323 contraction.value with graph, edge, other324obtain⟨inE, _⟩:= difference.membership.forward with other325obtain⟨leftIn, rightIn⟩:= Graph.ends with graph, f, inE326have leftIff := classOf.eq_iff_member with partition, class, leftIn327have rightIff := classOf.eq_iff_member with partition, class, rightIn328simp [leftValue, rightValue, leftIff, rightIff, inF]329 . simp [inF]330331-- The degree of a singleton class is the degree of its vertex, and the332-- degree of the class of the two ends of a proper edge e is the sum of333-- their degrees.334theorem degreeIn.contraction_singleton (V E leftEnd rightEnd e F v : Set)335requires graph : Graph(V, E, leftEnd, rightEnd)336requires edge : e ∈ E337requires edges : F ⊆ E ∖ {e}338requiresclass : {v} ∈ contractionPartition(V, leftEnd, rightEnd, e)339shows degreeIn(contractedLeft(V, E, leftEnd, rightEnd, e),340 contractedRight(V, E, leftEnd, rightEnd, e), F, {v}) =341 degreeIn(leftEnd, rightEnd, F, v)342proof343unfold degreeIn344have same : incidentEnds(contractedLeft(V, E, leftEnd, rightEnd, e),345 contractedRight(V, E, leftEnd, rightEnd, e), F, {v}) =346 incidentEnds(leftEnd, rightEnd, F, v)347apply incidentEnds.ext348intros f i349rw [(incidentEnds.contraction with graph, edge, edges, class),350 incidentEnds.pair]351simp [singleton.membership]352rw same353354theorem degreeIn.contraction_pair (V E leftEnd rightEnd e F : Set)355requires graph : Graph(V, E, leftEnd, rightEnd)356requires edge : e ∈ E357requires finite : Finite(F)358requires edges : F ⊆ E ∖ {e}359requires proper : leftEnd(e) ≠ rightEnd(e)360shows degreeIn(contractedLeft(V, E, leftEnd, rightEnd, e),361 contractedRight(V, E, leftEnd, rightEnd, e), F,362 {leftEnd(e), rightEnd(e)}) =363 degreeIn(leftEnd, rightEnd, F, leftEnd(e)) +364 degreeIn(leftEnd, rightEnd, F, rightEnd(e))365proof366have partition := contractionPartition.partition with graph, edge367obtain⟨leftIn, _⟩:= Graph.ends with graph, e, edge368haveclass : {leftEnd(e), rightEnd(e)} ∈369 contractionPartition(V, leftEnd, rightEnd, e)370rw← contraction.class_end with graph, edge371exact (classOf.member with partition, leftIn).left372-- The ends at the class are the ends at either vertex, and no end is at373-- both since the vertices differ.374have same : incidentEnds(contractedLeft(V, E, leftEnd, rightEnd, e),375 contractedRight(V, E, leftEnd, rightEnd, e), F,376 {leftEnd(e), rightEnd(e)}) =377 incidentEnds(leftEnd, rightEnd, F, leftEnd(e)) ∪378 incidentEnds(leftEnd, rightEnd, F, rightEnd(e))379ext p380 by_cases shape : ∃ f ∈ F, ∃ i ∈2, p = (f, i)381 . obtain⟨f, hf, i, hi, rfl⟩:= shape382rw [(incidentEnds.contraction with graph, edge, edges, class),383 union2.membership, incidentEnds.pair, incidentEnds.pair]384simp [pair.membership]385 . constructor386 . intro hp387exfalso388exact shape with (incidentEnds.shape with hp)389 . intro hp390rw union2.membership at hp391exfalso392rcases hp with hp | hp393 . exact shape with (incidentEnds.shape with hp)394 . exact shape with (incidentEnds.shape with hp)395have apart : Disjoint(incidentEnds(leftEnd, rightEnd, F, leftEnd(e)),396 incidentEnds(leftEnd, rightEnd, F, rightEnd(e)))397rw Disjoint.iff398intros p hp inRight399obtain⟨f, _, i, _, rfl⟩:= incidentEnds.shape with hp400rw incidentEnds.pair at hp, inRight401have apart01 : 0≠1bysimp402rcases hp.right with⟨_, atLeft⟩|⟨_, atLeft⟩403 . rcases inRight.right with⟨_, atRight⟩|⟨_, atRight⟩404 . cc405 . cc406 . rcases inRight.right with⟨_, atRight⟩|⟨_, atRight⟩407 . cc408 . cc409unfold degreeIn410rw same411exact cardinality.union_disjoint with (incidentEnds.finite with finite),412 (incidentEnds.finite with finite), apart413