Documentation

CslibFrontier.DataStructure.DisjointSet.UnionFind

Union-find realization and logarithmic step bounds #

This module realizes Cslib.DataStructure.DisjointSet.spec using Batteries.UnionFind (disjoint-set forests with union by rank and path compression) together with an explicit component counter, and establishes worst-case O(log n) per-operation parent-pointer traversal bounds.

Under path compression, internal nodes can lose descendants when compressed paths bypass them, so the naive subtree-size invariant 2 ^ rank u ≤ |subtree u| is not preserved at internal nodes. Following Cormen et al., Exercise 21.4-2, ValidRank instead bounds the equivalence-class size of each root r < uf.size by 2 ^ uf.rank r ≤ (equivClass uf r).card. Path compression preserves rootD, all node ranks, and the set of roots, so ValidRank is preserved immediately across find as well as across push, root link, checkEquiv, and union. Because node ranks strictly increase along non-root parent pointers (uf.rank_lt and le_rank_root, matching Cormen et al., Lemma 21.4 and Corollary 21.5), every node satisfies uf.rank i ≤ Nat.log 2 uf.size, and every find traverses at most Nat.log 2 uf.size + 1 nodes.

Cost model and limitations #

The native trace of Exec counts parent-pointer inspections during find (1 at the root plus 1 per parent hop, matching recursive calls of Batteries.UnionFind.findAux), plus 1 link step for Op.union and 1 counter read for Op.numComponents. Because Batteries.UnionFind.rankMax is marked noncomputable, the helpers rankBound, findGo, and compressWithSteps are exposed solely so that findSteps, step, and run reduce in the Lean kernel (rfl / decide) while remaining propositionally equal to Batteries.UnionFind.find. This gives a worst-case O(log n) bound from union by rank alone (not Tarjan's inverse-Ackermann O(α(n)) amortized bound), and counts abstract forest steps in Lean rather than word-RAM instructions or array-copying overhead if a Batteries.UnionFind value is shared non-linearly.

Main definitions #

Main results #

References #

Structural lemmas for Batteries.UnionFind #

@[simp]

Path compression preserves every node's rank.

Batteries.UnionFind.checkEquiv preserves the equivalence relation.

Batteries.UnionFind.checkEquiv returns true iff x and y are equivalent.

Initialize a Batteries.UnionFind forest of n singleton classes {0}, …, {n - 1}.

Instances For

    Root rank-size invariant and logarithmic rank bound #

    The finite set of in-bounds elements of uf whose canonical root is r.

    Instances For

      Root rank-size invariant (Cormen et al., Exercise 21.4-2): every root r < uf.size has equivalence class of cardinality at least 2 ^ uf.rank r.

      Instances For

        Every in-bounds node i < uf.size satisfies 2 ^ uf.rank i ≤ uf.size.

        Logarithmic rank bound (Cormen et al., Exercise 21.4-2): every node in a rank-valid Batteries.UnionFind forest has rank at most Nat.log 2 uf.size.

        Computable path-compression wrapper and step bounds #

        Computable strict upper bound on all node ranks in uf (matching Batteries.UnionFind.rankMax without noncomputable).

        Instances For

          Structural-fuel traversal matching Batteries.UnionFind.findAux while counting visited nodes (1 at the root check plus 1 per parent hop).

          Instances For

            Number of nodes inspected by Batteries.UnionFind.find starting at x (1 at the root plus 1 per parent pointer traversed).

            Instances For

              Recursive unfolding equation for findSteps.

              Every find inspects at least 1 node.

              Because uf.rank increases strictly at every non-root parent hop, findSteps uf x is bounded by (uf.rank (uf.rootD x) - uf.rank x) + 1.

              Under ValidRank uf, every find inspects at most Nat.log 2 uf.size + 1 nodes.

              Kernel-reducible path-compression find returning the updated Batteries.UnionFind, the root in Fin uf.size, and findSteps uf x. Propositionally equal to Batteries.UnionFind.find.

              Instances For

                Stateful realization of DisjointSet.spec #

                Concrete union-find state over Fin n: a Batteries.UnionFind forest of size n paired with the maintained number of equivalence classes.

                Instances For

                  Initial concrete disjoint-set state of n singleton components.

                  Instances For

                    Execute a single DisjointSet.Op n on s, returning the updated state, answer, and step count.

                    Instances For

                      Native execution relation for DisjointSet.spec n over State n.

                      Instances For

                        Representation relation coupling an abstract Partition n with a concrete State n.

                        Instances For

                          Extract the root rank-size invariant from Rep.

                          theorem Cslib.DataStructure.DisjointSet.UnionFind.Rep.rel_iff_equiv {n : ℕ} {p : Partition n} {s : State n} (h : Rep p s) (x y : Fin n) :
                          p.rel x y = true ↔ s.uf.Equiv ↑x ↑y

                          Extract the element-equivalence coupling from Rep.

                          Extract the maintained component-count equality from Rep.

                          init n represents the discrete partition Partition.discrete n of n singletons.

                          Semantic realization of DisjointSet.spec n by Batteries.UnionFind with path compression and union by rank.

                          Instances For

                            Worst-case per-operation step budget on Fin n: at most Nat.log 2 n + 1 nodes inspected per find, plus 1 link step in Op.union and 1 counter read in Op.numComponents.

                            Instances For

                              Every disjoint-set operation has worst-case charge at most 2 * Nat.log 2 n + 3.

                              Per-operation logarithmic step certificate for impl n.

                              theorem Cslib.DataStructure.DisjointSet.UnionFind.uniformStepBound (n : ℕ) :
                              Bound (impl n) id fun (x : (spec n).State) (x_1 : (spec n).Op) => 2 * Nat.log 2 n + 3

                              Uniform per-operation logarithmic step certificate 2 * Nat.log 2 n + 3.

                              theorem Cslib.DataStructure.DisjointSet.UnionFind.realized_client {n : ℕ} {β : Type} (client : (spec n).Client β) {p : Partition n} {s : State n} (hr : Rep p s) :
                              ∃ (s' : State n) (traces : List ℕ), Executes (Exec n) client s ((spec n).runClient p client).1 s' traces ∧ Rep ((spec n).runClient p client).2.1 s' ∧ total id traces ≤ (spec n).totalCost (charge n) p ((spec n).runClient p client).2.2

                              Every adaptive disjoint-set client has a valid execution matching abstract partition semantics and satisfying the additive O(log n) per-operation step bound.

                              Execute a disjoint-set client concretely, recording the exact step count of each operation.

                              Instances For
                                theorem Cslib.DataStructure.DisjointSet.UnionFind.run_exec {n : ℕ} {β : Type} (client : (spec n).Client β) (s : State n) :
                                Executes (Exec n) client s (run s client).1 (run s client).2.1 (run s client).2.2

                                The concrete runner produces a valid Executes (Exec n) derivation.

                                theorem Cslib.DataStructure.DisjointSet.UnionFind.run_correct {n : ℕ} {β : Type} (client : (spec n).Client β) {p : Partition n} {s : State n} (hr : Rep p s) :
                                (run s client).1 = ((spec n).runClient p client).1 ∧ Rep ((spec n).runClient p client).2.1 (run s client).2.1 ∧ total id (run s client).2.2 ≤ (spec n).totalCost (charge n) p ((spec n).runClient p client).2.2 ∧ total id (run s client).2.2 ≤ ((spec n).runClient p client).2.2.length * (2 * Nat.log 2 n + 3)

                                Running the concrete union-find interpreter agrees with abstract Partition n semantics, preserves Rep, and satisfies both the exact per-operation and uniform O(log n) step bounds.