1-- The cycle double cover of an even double cover: replacing each even set2-- by the cycles of a chosen decomposition gives a family of cycles holding3-- every edge exactly twice.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.DoubleCover.CycleCover10import Cycles11import Set.Choice1213predicate CycleDoubleCover (V E leftEnd rightEnd H I : Set) :=14 Finite(I) ∧ FunctionBetween(H, I, 𝒫(E)) ∧15 (∀ i ∈ I, Cycle(V, leftEnd, rightEnd, H(i))) ∧16 (∀ e ∈ E, multiplicity(H, I, e) =2)1718-- Replacing each even set of a double cover by its cycles gives a cycle19-- double cover, indexed by the pairs (i, C) with C a cycle of the chosen20-- decomposition of H(i). An edge is held by exactly one cycle of each even21-- set holding it, so the indices holding it correspond to those of the22-- original cover.2324-- The edges of an even double cover are finitely many: every edge lies in25-- one of the finitely many even sets of the cover.26theorem EvenDoubleCover.finite_edges (V E leftEnd rightEnd H I : Set)27requires cover : EvenDoubleCover(V, E, leftEnd, rightEnd, H, I)28shows Finite(E)29proof30obtain⟨finiteI, family, evens, twice⟩:= cover31have finiteImage :=32 Finite.function_image with family, finiteI, Subset.reflexive33have members : ∀ S ∈ functionImage(H, I, 𝒫(E)), Finite(S)34intros S hS35obtain⟨_, i, hi, rfl⟩:= hS36exact (evens with i, hi).left37have finiteUnion := Finite.big_union with finiteImage, members38have inside : E ⊆⋃(functionImage(H, I, 𝒫(E)))39intros e he40have count := twice with e, he41have inhabited : holding(H, I, e) ≠∅42intro empty43unfold multiplicity at count44rw [empty, cardinality.empty] at count45arith46obtain⟨i, hi⟩:= Nonempty.of_ne_empty with inhabited47have parts := holding.membership.forward with hi48have image : H(i) ∈ functionImage(H, I, 𝒫(E)) :=49⟨(FunctionBetween.apply_closed with family, parts.left), i, parts.left,50rfl⟩51exact union.membership.backward with⟨H(i), image, parts.right⟩52exact Finite.subset with finiteUnion, inside5354-- The decompositions of an edge set into cycles, and a chosen one.55def cycleDecompositions (V leftEnd rightEnd F : Set) : Set:=56 { D ∈𝒫(𝒫(F)) | Finite(D) ∧57 (∀ C ∈ D, Cycle(V, leftEnd, rightEnd, C)) ∧58 (∀ C ∈ D, ∀ B ∈ D, C ≠ B → Disjoint(C, B)) ∧⋃(D) = F }5960def chosenCycles (V leftEnd rightEnd F : Set) : Set:=61 choose(cycleDecompositions(V, leftEnd, rightEnd, F))6263theorem chosenCycles.decomposes (V leftEnd rightEnd F : Set)64requires finiteVertices : Finite(V)65requires ends : ∀ e ∈ F, leftEnd(e) ∈ V ∧ rightEnd(e) ∈ V66requires even : EvenEdgeSet(V, leftEnd, rightEnd, F)67shows chosenCycles(V, leftEnd, rightEnd, F) ∈68 cycleDecompositions(V, leftEnd, rightEnd, F)69proof70obtain⟨D, finiteD, cycles, apart, covers⟩:=71 EvenEdgeSet.cycle_decomposition with finiteVertices, ends, even72have inside : D ∈𝒫(𝒫(F))73apply powerset.membership_iff.backward74intros C hC75apply powerset.membership_iff.backward76rw← covers77exact union.subset_of_member with hC78have member : D ∈ cycleDecompositions(V, leftEnd, rightEnd, F) :=79⟨inside, finiteD, cycles, apart, covers⟩80have inhabited : Nonempty(cycleDecompositions(V, leftEnd, rightEnd, F)) :=81⟨D, member⟩82unfold chosenCycles83exact choose.member with inhabited8485-- In an even double cover every H(i) has its chosen decomposition.86theorem chosenCycles.of_cover (V E leftEnd rightEnd H I i : Set)87requires graph : Graph(V, E, leftEnd, rightEnd)88requires finiteVertices : Finite(V)89requires cover : EvenDoubleCover(V, E, leftEnd, rightEnd, H, I)90requires index : i ∈ I91shows chosenCycles(V, leftEnd, rightEnd, H(i)) ∈92 cycleDecompositions(V, leftEnd, rightEnd, H(i))93proof94have ends := Graph.ends with graph95have edges : H(i) ⊆ E := powerset.membership_iff.forward96with (FunctionBetween.apply_closed with cover.right.left, index)97exact chosenCycles.decomposes with finiteVertices,98 (ends_in.subset with ends, edges), (cover.right.right.left with i, index)99100-- The pairs (i, C) with C a cycle of the chosen decomposition of H(i), and101-- the family sending (i, C) to C.102def cycleIndex (V E leftEnd rightEnd H I : Set) : Set:=103 { p ∈ I ×𝒫(E) |104 snd(p) ∈ chosenCycles(V, leftEnd, rightEnd, H(fst(p))) }105106theorem cycleIndex.second (V E leftEnd rightEnd H I p : Set)107requires member : p ∈ cycleIndex(V, E, leftEnd, rightEnd, H, I)108shows snd(p) ∈𝒫(E)109proof110obtain⟨_, i, hi, C, hC, rfl⟩:= product.membership.forward111with member.left112rw snd.pair113exact hC114115defgraph cycleFamily (V E leftEnd rightEnd H I : Set)116 (p ∈ cycleIndex(V, E, leftEnd, rightEnd, H, I)) : 𝒫(E) := snd(p)117proof118exact cycleIndex.second with member119120-- An index holding e is a pair (i, C) with i holding e and C a cycle of121-- the chosen decomposition of H(i) through e.122theorem cycleIndex.holding (V E leftEnd rightEnd H I e p : Set)123requires graph : Graph(V, E, leftEnd, rightEnd)124requires finiteVertices : Finite(V)125requires cover : EvenDoubleCover(V, E, leftEnd, rightEnd, H, I)126requires member : p ∈ holding(cycleFamily(V, E, leftEnd, rightEnd, H, I),127 cycleIndex(V, E, leftEnd, rightEnd, H, I), e)128shows∃ i ∈ I, ∃ C ∈ chosenCycles(V, leftEnd, rightEnd, H(i)),129 p = (i, C) ∧ e ∈ C ∧ e ∈ H(i)130proof131have inIndex := member.left132have holds := member.right133rw cycleFamily.value with inIndex at holds134obtain⟨_, i, hi, C, hC, shape⟩:= product.membership.forward135with inIndex.left136have inChosen := inIndex.right137rw [shape, snd.pair, fst.pair] at inChosen138rw [shape, snd.pair] at holds139obtain⟨_, _, _, _, covers⟩:=140 chosenCycles.of_cover with graph, finiteVertices, cover, hi141have inH : e ∈ H(i)142rw← covers143exact union.membership.backward with⟨C, inChosen, holds⟩144exact⟨i, hi, C, inChosen, shape, holds, inH⟩145146-- The first projection is a bijection from the indices (i, C) holding e to147-- the indices i holding e: the cycle C of H(i) through e is unique.148def cycleProjection (V E leftEnd rightEnd H I e : Set) : Set:=149 { r ∈ holding(cycleFamily(V, E, leftEnd, rightEnd, H, I),150 cycleIndex(V, E, leftEnd, rightEnd, H, I), e) × holding(H, I, e) |151 snd(r) = fst(fst(r)) }152153theorem cycleProjection.bijective (V E leftEnd rightEnd H I e : Set)154requires graph : Graph(V, E, leftEnd, rightEnd)155requires finiteVertices : Finite(V)156requires cover : EvenDoubleCover(V, E, leftEnd, rightEnd, H, I)157shows BijectiveBetween(cycleProjection(V, E, leftEnd, rightEnd, H, I, e),158 holding(cycleFamily(V, E, leftEnd, rightEnd, H, I),159 cycleIndex(V, E, leftEnd, rightEnd, H, I), e),160 holding(H, I, e))161proof162have family := cover.right.left163have function : FunctionBetween(cycleProjection(V, E, leftEnd, rightEnd,164 H, I, e), holding(cycleFamily(V, E, leftEnd, rightEnd, H, I),165 cycleIndex(V, E, leftEnd, rightEnd, H, I), e), holding(H, I, e))166constructor167 . intros r hr168exact hr.left169 . constructor170 . intros x y xy z xz171have valueY := xy.right172have valueZ := xz.right173simp [fst.pair, snd.pair] at valueY, valueZ174cc175 . intros p hp176obtain⟨i, hi, C, hC, shape, holds, inH⟩:=177 cycleIndex.holding with graph, finiteVertices, cover, hp178have target : i ∈ holding(H, I, e) :=⟨hi, inH⟩179refine⟨i, target, ?_⟩180apply cycleProjection.membership.backward181refine⟨(product.pair with hp, target), ?_⟩182rw shape183simp [fst.pair, snd.pair]184constructor185 . refine⟨function, ?_⟩186-- Two indices with the same first coordinate i are cycles of the187-- decomposition of H(i) both through e, hence equal.188intros x hx y hy z hz xz yz189obtain⟨i, hi, C, hC, shapeX, holdsX, _⟩:=190 cycleIndex.holding with graph, finiteVertices, cover, hx191obtain⟨j, hj, B, hB, shapeY, holdsY, _⟩:=192 cycleIndex.holding with graph, finiteVertices, cover, hy193have valueX := xz.right194have valueY := yz.right195simp [fst.pair, snd.pair] at valueX, valueY196have same : i = j bycc197rw← same at hB198obtain⟨_, _, _, apart, _⟩:=199 chosenCycles.of_cover with graph, finiteVertices, cover, hi200have equal : C = B201by_contra different202have disjoint := apart with C, hC, B, hB, different203rw Disjoint.iff at disjoint204exact disjoint with e, holdsX, holdsY205rw [shapeX, shapeY, same, equal]206 . refine⟨function, ?_⟩207-- An index i holding e: e lies in some cycle C of H(i), and (i, C)208-- holds e.209intros i hi210have inI := hi.left211have inH := hi.right212obtain⟨inPowerset, _, _, _, covers⟩:=213 chosenCycles.of_cover with graph, finiteVertices, cover, inI214have inUnion : e ∈⋃(chosenCycles(V, leftEnd, rightEnd, H(i)))215rw covers216exact inH217obtain⟨C, hC, holds⟩:= union.membership.forward with inUnion218have cyclesIn : chosenCycles(V, leftEnd, rightEnd, H(i)) ⊆𝒫(H(i)) :=219 powerset.membership_iff.forward with inPowerset220have edges : H(i) ⊆ E := powerset.membership_iff.forward221with (FunctionBetween.apply_closed with family, inI)222have inside : C ⊆ E := Subset.transitive223with (powerset.membership_iff.forward with (cyclesIn with C, hC)), edges224have inIndex : (i, C) ∈ cycleIndex(V, E, leftEnd, rightEnd, H, I)225refine⟨(product.pair with inI,226 (powerset.membership_iff.backward with inside)), ?_⟩227rw [snd.pair, fst.pair]228exact hC229have inHolding : (i, C) ∈ holding(cycleFamily(V, E, leftEnd, rightEnd,230 H, I), cycleIndex(V, E, leftEnd, rightEnd, H, I), e)231refine⟨inIndex, ?_⟩232rw [(cycleFamily.value with inIndex), snd.pair]233exact holds234refine⟨(i, C), inHolding, ?_⟩235apply cycleProjection.membership.backward236refine⟨(product.pair with inHolding, hi), ?_⟩237simp [fst.pair, snd.pair]238239theorem EvenDoubleCover.to_cycles (V E leftEnd rightEnd H I : Set)240requires graph : Graph(V, E, leftEnd, rightEnd)241requires finiteVertices : Finite(V)242requires cover : EvenDoubleCover(V, E, leftEnd, rightEnd, H, I)243shows∃ K, ∃ J, CycleDoubleCover(V, E, leftEnd, rightEnd, K, J)244proof245have finiteE := EvenDoubleCover.finite_edges with cover246obtain⟨finiteI, family, evens, twice⟩:= cover247have indexSubset : cycleIndex(V, E, leftEnd, rightEnd, H, I) ⊆ I ×𝒫(E)248intros p hp249exact hp.left250have finiteIndex := Finite.subset251with (Finite.product with finiteI, (Finite.powerset with finiteE)),252 indexSubset253witness cycleFamily(V, E, leftEnd, rightEnd, H, I)254witness cycleIndex(V, E, leftEnd, rightEnd, H, I)255refine⟨finiteIndex, cycleFamily.function, ?_, ?_⟩256 . -- Each index (i, C) names a cycle C of H(i).257intros p hp258rw cycleFamily.value with hp259obtain⟨_, i, hi, C, hC, shape⟩:= product.membership.forward260with hp.left261have inChosen := hp.right262rw [shape, snd.pair, fst.pair] at inChosen263rw [shape, snd.pair]264obtain⟨_, _, cycles, _, _⟩:=265 chosenCycles.of_cover with graph, finiteVertices, cover, hi266exact cycles with C, inChosen267 . -- The indices holding e correspond to the two indices of the cover268-- holding e.269intros e he270have count := twice with e, he271unfold multiplicity at count272unfold multiplicity273rw← count274have bijective :=275 cycleProjection.bijective at V, E, leftEnd, rightEnd, H, I, e276with graph, finiteVertices, cover277have finiteSource : Finite(holding(cycleFamily(V, E, leftEnd, rightEnd,278 H, I), cycleIndex(V, E, leftEnd, rightEnd, H, I), e)) :=279 Finite.subset with finiteIndex, holding.subset280exact cardinality.bijective with finiteSource, bijective281