Documentation

CslibFrontier.Algorithms.Lean.Kruskal.Basic

Kruskal's Minimum Spanning Forest and Tree Algorithm #

This module formalizes Kruskal's greedy algorithm for minimum-weight spanning forests and minimum spanning trees on finite multigraphs over Fin n, together with its functional correctness, global optimality, distinct-weight uniqueness, Cslib.DataStructure disjoint-set client specification, and TimeM comparison and disjoint-set step bounds.

Mathematical model and source mapping #

An input multigraph on n vertices is represented as a finite list es : List (WeightedEdge n W) of undirected weighted edges (u, v, weight) with u v : Fin n (allowing parallel edges, self-loops, disconnected graphs, and negative or duplicate weights). Connectivity is tracked via Cslib.DataStructure.DisjointSet.Partition n:

Primary-source correspondence:

Cost model and limitations #

kruskalM executes Kruskal's algorithm in TimeM Cost with two separate operation counters:

DisjointSet.UnionFind.init n (O(n) MAKE-SET initialization in [CLRS2009], MST-KRUSKAL lines 2–3, pp. 631, 633), list splitting/allocation in List.mergeSortM and kruskalClient, and pure Lean array updates inside Batteries.UnionFind carry 0 TimeM Cost charge (so Cost records only sorting weight comparisons and post-initialization UnionFind find/union steps, not Word-RAM instructions; if a Batteries.UnionFind value were shared non-linearly, array updates could copy, whereas DisjointSet.UnionFind.run threads its state single-threadedly).

Main definitions #

Main results #

References #

Weighted edges and edge-induced partitions #

A weighted undirected edge on Fin n (allowing parallel edges and self-loops).

  • u : Fin n

    First endpoint.

  • v : Fin n

    Second endpoint.

  • weight : W

    Edge weight.

Instances For
    Instances For
      @[instance_reducible]
      instance Cslib.Algorithms.Lean.Kruskal.instReprWeightedEdge {n✝ : ℕ} {W✝ : Type} [Repr W✝] :
      Repr (WeightedEdge n✝ W✝)

      Endpoint pair (e.u, e.v) forgetting the weight, for interoperability with unweighted DisjointSet.spanningForestEdges.

      Instances For

        Total weight of an edge list.

        Instances For
          @[simp]
          theorem Cslib.Algorithms.Lean.Kruskal.totalWeight_append {n : ℕ} {W : Type} [AddCommMonoid W] (es₁ es₂ : List (WeightedEdge n W)) :
          totalWeight (es₁ ++ es₂) = totalWeight es₁ + totalWeight es₂
          theorem Cslib.Algorithms.Lean.Kruskal.Perm.totalWeight_eq {n : ℕ} {W : Type} [AddCommMonoid W] {es₁ es₂ : List (WeightedEdge n W)} (h : es₁.Perm es₂) :

          Permuting an edge list preserves its total weight.

          theorem Cslib.Algorithms.Lean.Kruskal.Sublist.totalWeight_le {n : ℕ} {W : Type} [AddCommMonoid W] [Preorder W] [IsOrderedAddMonoid W] {es₁ es₂ : List (WeightedEdge n W)} (h : es₁.Sublist es₂) (hnonneg : ∀ e ∈ es₂, 0 ≤ e.weight) :

          Deleting edges of nonnegative weight does not increase the total weight.

          Partition obtained by merging the endpoints of every edge in es starting from p.

          Instances For

            Connected-component partition of Fin n induced by es.

            Instances For
              theorem Cslib.Algorithms.Lean.Kruskal.rel_edgePartitionFrom_iff_eqvGen {n : ℕ} {W : Type} (p : DataStructure.DisjointSet.Partition n) (es : List (WeightedEdge n W)) (a b : Fin n) :
              (edgePartitionFrom p es).rel a b = true ↔ Relation.EqvGen (fun (x y : Fin n) => p.rel x y = true ∨ ∃ e ∈ es, x = e.u ∧ y = e.v) a b

              Exact equivalence-closure characterization of edgePartitionFrom p es.

              theorem Cslib.Algorithms.Lean.Kruskal.rel_edgePartition_iff_eqvGen {n : ℕ} {W : Type} (es : List (WeightedEdge n W)) (a b : Fin n) :
              (edgePartition es).rel a b = true ↔ Relation.EqvGen (fun (x y : Fin n) => ∃ e ∈ es, x = e.u ∧ y = e.v) a b

              Two vertices are in the same component of edgePartition es iff they are connected by the equivalence closure of the edges in es.

              Every edge in es relates its endpoints in edgePartitionFrom p es.

              edgePartitionFrom is invariant under edge-list permutations.

              theorem Cslib.Algorithms.Lean.Kruskal.Perm.edgePartition_eq {n : ℕ} {W : Type} {es₁ es₂ : List (WeightedEdge n W)} (h : es₁.Perm es₂) :

              edgePartition is invariant under edge-list permutations.

              Merging (x, y) before folding es is equal to folding es and then merging (x, y).

              Each edge reduces the number of connected components by at most 1.

              Relative acyclicity, spanning forests, and spanning trees #

              F is acyclic relative to an initial partition p when every edge in F merges two distinct components, decreasing the component count by F.length.

              Instances For

                An edge list F on Fin n is acyclic (a forest) when each edge merges two distinct connected components starting from Partition.discrete n, i.e., (edgePartition F).numComponents + F.length = n.

                Instances For

                  Inductive characterization of relative acyclicity: e :: F is acyclic over p iff e's endpoints lie in distinct classes of p and F is acyclic over p.merge e.u e.v.

                  Relative acyclicity is invariant under edge-list permutations.

                  theorem Cslib.Algorithms.Lean.Kruskal.Perm.isAcyclic_iff {n : ℕ} {W : Type} {F₁ F₂ : List (WeightedEdge n W)} (h : F₁.Perm F₂) :

                  Acyclicity is invariant under edge-list permutations.

                  Every acyclic edge list on Fin n has at most n edges (and at most n - 1 when 0 < n).

                  theorem Cslib.Algorithms.Lean.Kruskal.IsAcyclic.ne_of_mem {n : ℕ} {W : Type} {F : List (WeightedEdge n W)} (hF : IsAcyclic F) {e : WeightedEdge n W} (he : e ∈ F) :
                  e.u ≠ e.v

                  No self-loop belongs to an acyclic edge list.

                  Relative spanning forest of es starting from partition p.

                  Instances For

                    F is a spanning forest of the multigraph es when F is a sub-multiset (Subperm) of es, F is acyclic, and F induces the exact same connected components as es.

                    Instances For

                      T is a spanning tree of es when T is a sub-multiset of es, T is acyclic, and T connects Fin n into a single component (numComponents = 1).

                      Instances For

                        F is a minimum-weight spanning forest of es when F is a spanning forest of es of minimum totalWeight.

                        Instances For

                          T is a minimum-weight spanning tree of es when T is a spanning tree of es of minimum totalWeight.

                          Instances For

                            Every spanning tree on a connected multigraph (edgePartition es).numComponents = 1 is a spanning forest, and vice versa.

                            Every spanning tree on Fin n has n - 1 edges (T.length + 1 = n).

                            theorem Cslib.Algorithms.Lean.Kruskal.IsSpanningForest.length_eq {n : ℕ} {W : Type} {es F₁ F₂ : List (WeightedEdge n W)} (h₁ : IsSpanningForest es F₁) (h₂ : IsSpanningForest es F₂) :
                            F₁.length = F₂.length

                            All spanning forests of the same multigraph es have the exact same cardinality n - (edgePartition es).numComponents.

                            Pure Kruskal scan, merge-sort, optimality, and uniqueness #

                            Greedy Kruskal scan starting from partition p: inspect edges in order, accepting an edge iff its endpoints currently lie in distinct classes of p.

                            Instances For

                              kruskalScan p es is an order-preserving sublist of es.

                              Core exchange-inductive optimality of kruskalScan on weight-sorted inputs: for any initial partition p and any relative spanning forest H of sorted over p, kruskalScan p sorted has total weight at most totalWeight H.

                              Uniqueness of the minimum relative spanning forest on weight-sorted inputs with distinct weights.

                              Sort an edge list by nondecreasing weight using List.mergeSort.

                              Instances For
                                theorem Cslib.Algorithms.Lean.Kruskal.sortEdges_cons_cons {n : ℕ} {W : Type} [LE W] [DecidableLE W] (a b : WeightedEdge n W) (es : List (WeightedEdge n W)) :
                                sortEdges (a :: b :: es) = (sortEdges (List.take ((es.length + 3) / 2) (a :: b :: es))).merge (sortEdges (List.drop ((es.length + 3) / 2) (a :: b :: es))) fun (x y : WeightedEdge n W) => decide (x.weight ≤ y.weight)

                                Balanced-split unfolding step for sortEdges on lists of length at least 2.

                                sortEdges es is a permutation of es.

                                Over a linear order, sortEdges es is sorted in nondecreasing weight order.

                                Kruskal's algorithm: merge-sort edges in nondecreasing weight order and scan from the discrete partition Partition.discrete n, retaining each edge whose endpoints lie in distinct components.

                                Instances For

                                  kruskalEdges es is sorted in nondecreasing weight order.

                                  kruskalEdges es is a sub-multiset (Subperm) of es.

                                  kruskalEdges es induces the exact same connected-component partition as es.

                                  Kruskal's minimum spanning forest theorem (generalizing Cormen et al., Section 23.2, MST-KRUSKAL, p. 631 from connected simple real-weighted graphs to arbitrary finite multigraphs): for any weighted multigraph es on Fin n with weights in an ordered additive commutative monoid, kruskalEdges es is a minimum-weight spanning forest of es.

                                  With nonnegative weights, Kruskal's forest weighs at most its whole input.

                                  Kruskal's minimum spanning tree theorem (Cormen et al., Section 23.2, p. 631): when es connects Fin n into a single component ((edgePartition es).numComponents = 1), kruskalEdges es is a minimum-weight spanning tree of es.

                                  Distinct-weight uniqueness of Kruskal's forest (generalizing Kruskal, Opening paragraph, Construction A, and uniqueness proof, pp. 48–50): when all edges in es have pairwise distinct weights, every minimum-weight spanning forest of es is a permutation of kruskalEdges es (i.e., selects the exact same multiset of edges).

                                  Distinct-weight uniqueness specialized to connected multigraphs: when edge weights in es are pairwise distinct and es is connected, every minimum spanning tree of es is a permutation of kruskalEdges es.

                                  DisjointSet.spec client and backend-parametric realization #

                                  DisjointSet.spec n client for the Kruskal scan, defined as the WeightedEdge.endpoints specialization of DisjointSet.spanningForestEdges.

                                  Instances For

                                    Exact mathematical evaluation and operation history of kruskalClient: it computes kruskalScan p es, reaches edgePartitionFrom p es, and issues one Op.union e.u e.v per edge.

                                    Backend-parametric Kruskal execution: for any Cslib.DataStructure.StateTransitionImpl and Cslib.DataStructure.Bound for DisjointSet.spec n, from a state representing Partition.discrete n there is an execution of kruskalClient (sortEdges es) that returns kruskalEdges es, ends in a state representing edgePartition es, and has total cost at most the total charge of its m Op.union calls.

                                    TimeM complexity with sorting comparisons and UnionFind steps #

                                    Two-component cost profile for Kruskal's algorithm: edge-weight comparisons during merge sort and disjoint-set forest steps during the cycle-filtering scan.

                                    • comparisons : ℕ

                                      Number of edge-weight comparisons performed while sorting edges.

                                    • ufSteps : ℕ

                                      Number of disjoint-set forest steps (find parent-pointer inspections plus union root links) performed during the Kruskal scan.

                                    Instances For
                                      theorem Cslib.Algorithms.Lean.Kruskal.Cost.ext {x y : Cost} (comparisons : x.comparisons = y.comparisons) (ufSteps : x.ufSteps = y.ufSteps) :
                                      x = y

                                      Combined operation count comparisons + ufSteps.

                                      Instances For

                                        Concrete output of kruskalM: the selected forest edges together with the final DisjointSet.UnionFind.State n representing the graph's connected components.

                                        Instances For

                                          Lift a comparison-counting TimeM ℕ computation into TimeM Cost.

                                          Instances For
                                            @[simp]
                                            theorem Cslib.Algorithms.Lean.Kruskal.time_liftComparisons {α : Type} (m : TimeM ℕ α) :
                                            (liftComparisons m).time = { comparisons := m.time, ufSteps := 0 }

                                            Merge-sort edges by nondecreasing weight in TimeM ℕ, charging 1 per weight comparison.

                                            Instances For

                                              sortEdgesM es performs at most es.length * Nat.clog 2 es.length weight comparisons.

                                              Timed Kruskal scan starting from a concrete DisjointSet.UnionFind.State n, charging exact union-find forest steps to Cost.ufSteps.

                                              Instances For

                                                Kruskal's minimum-spanning-forest algorithm in TimeM Cost (Cormen et al., Section 23.2, MST-KRUSKAL, pp. 631–633): merge-sort es by weight (charging comparisons to Cost.comparisons) and scan the sorted edges from DisjointSet.UnionFind.init n (charging post-initialization parent-pointer inspections and root links to Cost.ufSteps, with DisjointSet.UnionFind.init n uncharged).

                                                Instances For

                                                  Pure return value of kruskalM es in terms of kernel-reducible sortEdges and kruskalScanM.

                                                  Over an ordered additive commutative monoid, (kruskalM es).ret.forest is a minimum-weight spanning forest of es.

                                                  Final union-find state invariant: (kruskalM es).ret.ufState represents edgePartition es.

                                                  The maintained component counter of (kruskalM es).ret.ufState equals the number of connected components (edgePartition es).numComponents.

                                                  Sorting comparison bound for kruskalM: at most m * ⌈log₂ m⌉ weight comparisons where m = es.length.

                                                  Disjoint-set forest step bound for kruskalM: at most m * (2 * ⌊log₂ n⌋ + 3) union-find steps where m = es.length.

                                                  Combined O(m log m + m log n) bound on total operations (kruskalM es).time.total.