Documentation

CslibFrontier.DataStructure.DisjointSet.Spec

Representation-independent disjoint-set specification #

This module specifies a dynamic partition (disjoint-set / union-find abstract state machine) over a finite universe Fin n in the Cslib.DataStructure framework.

Mathematical states are equivalence relations Partition n with decidable Boolean membership, starting from the discrete partition Partition.discrete n of n singleton classes. The interface (Op, spec) exposes:

Unlike concrete union-find forests, spec intentionally does not expose raw tree-root indices: under union by rank, two histories reaching the same mathematical partition can choose different canonical roots, so exposing root indices would leak internal forest history rather than abstract partition state. Returning the effective-merge Boolean from Op.union gives single-pass edge filters such as spanningForestEdges (the unweighted Kruskal / cycle-filtering loop, whose returned edge list depends on observed answers while its issued operation sequence is fixed at one Op.union per input edge), while clients such as connectIfNew branch on same so that the issued operation sequence itself adapts to the current partition.

Main definitions #

Main results #

References #

A decidable partition (equivalence relation) on Fin n.

Instances For
    theorem Cslib.DataStructure.DisjointSet.Partition.ext {n : ℕ} {p q : Partition n} (h : ∀ (x y : Fin n), p.rel x y = q.rel x y) :
    p = q
    theorem Cslib.DataStructure.DisjointSet.Partition.ext_iff {n : ℕ} {p q : Partition n} :
    p = q ↔ ∀ (x y : Fin n), p.rel x y = q.rel x y
    theorem Cslib.DataStructure.DisjointSet.Partition.rel_comm {n : ℕ} (p : Partition n) (x y : Fin n) :
    p.rel x y = p.rel y x

    Boolean symmetry of p.rel.

    View a Partition n as a Setoid (Fin n).

    Instances For

      The discrete partition of Fin n into n singleton classes.

      Instances For

        The equivalence class of x in p.

        Instances For

          Two elements have the same equivalence class iff they are related in p.

          Unrelated elements have disjoint equivalence classes.

          The finite set of equivalence classes (connected components) of p.

          Instances For

            The number of equivalence classes of p.

            Instances For

              Every partition of Fin n has at most n components.

              Every partition of a nonempty Fin n has at least one component.

              @[simp]

              The discrete partition on Fin n has n components.

              Merge the equivalence classes of x and y in p.

              Instances For
                @[simp]
                theorem Cslib.DataStructure.DisjointSet.Partition.rel_merge {n : ℕ} (p : Partition n) (x y a b : Fin n) :
                (p.merge x y).rel a b = (p.rel a b || p.rel a x && p.rel y b || p.rel a y && p.rel x b)
                theorem Cslib.DataStructure.DisjointSet.Partition.rel_merge_eq_true {n : ℕ} {p : Partition n} {x y a b : Fin n} :
                (p.merge x y).rel a b = true ↔ p.rel a b = true ∨ p.rel a x = true ∧ p.rel y b = true ∨ p.rel a y = true ∧ p.rel x b = true
                theorem Cslib.DataStructure.DisjointSet.Partition.rel_merge_iff_eqvGen {n : ℕ} {p : Partition n} {x y a b : Fin n} :
                (p.merge x y).rel a b = true ↔ Relation.EqvGen (fun (u v : Fin n) => p.rel u v = true ∨ u = x ∧ v = y) a b

                p.merge x y is the exact equivalence closure Relation.EqvGen of p together with (x, y).

                theorem Cslib.DataStructure.DisjointSet.Partition.merge_of_rel {n : ℕ} {p : Partition n} {x y : Fin n} (h : p.rel x y = true) :
                p.merge x y = p

                Merging two already-equivalent elements leaves the partition unchanged.

                Redundant merges preserve the number of components.

                Merging two distinct equivalence classes decreases the number of components by exactly 1.

                Typed operations on a disjoint-set partition over Fin n.

                • same {n : ℕ} (x y : Fin n) : Op n

                  Test whether x and y belong to the same equivalence class.

                • union {n : ℕ} (x y : Fin n) : Op n

                  Merge the classes of x and y, returning true iff two distinct classes merged.

                • numComponents {n : ℕ} : Op n

                  Query the current number of equivalence classes.

                Instances For
                  def Cslib.DataStructure.DisjointSet.instDecidableEqOp.decEq {n✝ : ℕ} (x✝ x✝¹ : Op n✝) :
                  Decidable (x✝ = x✝¹)
                  Instances For
                    @[instance_reducible]
                    @[instance_reducible]
                    @[reducible, inline]

                    Operation return types for Op n.

                    Instances For
                      @[reducible, inline]

                      Total stateful specification of a disjoint-set structure on Fin n.

                      Instances For

                        Client helper: query whether x and y are in the same equivalence class.

                        Instances For

                          Client helper: merge the classes of x and y, returning true iff two distinct classes merged.

                          Instances For

                            Client helper: query the current number of equivalence classes.

                            Instances For

                              History-adaptive connection client: query same x y first, issuing union x y only when x and y belong to distinct equivalence classes. Its operation history has length 1 when x and y are already connected and 2 when they merge.

                              Instances For

                                Exact semantics and state-dependent operation history of connectIfNew.

                                def Cslib.DataStructure.DisjointSet.spanningForestEdges {n : ℕ} {E : Type} (endpts : E → Fin n × Fin n) :
                                List E → (spec n).Client (List E)

                                Single-pass spanning-forest client (the cycle-filtering loop shared with Kruskal's algorithm) parameterized over an arbitrary edge type E with endpoint map endpts : E → Fin n × Fin n: scan edges in order, retaining an edge e iff union (endpts e).1 (endpts e).2 reports that its endpoints lay in distinct components.

                                Instances For
                                  @[simp]
                                  @[simp]
                                  theorem Cslib.DataStructure.DisjointSet.eval_spanningForestEdges_cons {n : ℕ} {E : Type} (endpts : E → Fin n × Fin n) (e : E) (es : List E) (p : Partition n) :
                                  eval (spec n) (spanningForestEdges endpts (e :: es)) p = have rest := eval (spec n) (spanningForestEdges endpts es) (p.merge (endpts e).1 (endpts e).2); (if (!p.rel (endpts e).1 (endpts e).2) = true then e :: rest.1 else rest.1, rest.2)
                                  theorem Cslib.DataStructure.DisjointSet.runClient_spanningForestEdges_history {n : ℕ} {E : Type} (endpts : E → Fin n × Fin n) (es : List E) (p : Partition n) :
                                  ((spec n).runClient p (spanningForestEdges endpts es)).2.2 = List.map (fun (e : E) => Op.union (endpts e).1 (endpts e).2) es

                                  Exact operation history issued by spanningForestEdges endpts es: one Op.union call per input edge in order.

                                  theorem Cslib.DataStructure.DisjointSet.runClient_spanningForestEdges_length {n : ℕ} {E : Type} (endpts : E → Fin n × Fin n) (es : List E) (p : Partition n) :
                                  ((spec n).runClient p (spanningForestEdges endpts es)).2.2.length = es.length

                                  spanningForestEdges endpts es issues es.length operations.

                                  theorem Cslib.DataStructure.DisjointSet.spanningForestEdges_sublist {n : ℕ} {E : Type} (endpts : E → Fin n × Fin n) (es : List E) (p : Partition n) :
                                  (eval (spec n) (spanningForestEdges endpts es) p).1.Sublist es

                                  Every retained edge belongs to the input edge list in order.

                                  Exact conservation law: the final component count plus the number of accepted forest edges equals the initial component count.

                                  theorem Cslib.DataStructure.DisjointSet.spanningForestEdges_rel_iff_eqvGen {n : ℕ} {E : Type} (endpts : E → Fin n × Fin n) (es : List E) (p : Partition n) (a b : Fin n) :
                                  (eval (spec n) (spanningForestEdges endpts es) p).2.rel a b = true ↔ Relation.EqvGen (fun (u v : Fin n) => p.rel u v = true ∨ ∃ e ∈ es, u = (endpts e).1 ∧ v = (endpts e).2) a b

                                  The final partition after spanningForestEdges endpts es is the exact equivalence closure of p together with all endpoint pairs in es.

                                  theorem Cslib.DataStructure.DisjointSet.spanningForestEdges_idempotent_partition {n : ℕ} {E : Type} (endpts : E → Fin n × Fin n) (es : List E) (p : Partition n) :
                                  (eval (spec n) (spanningForestEdges endpts (eval (spec n) (spanningForestEdges endpts es) p).1) p).2 = (eval (spec n) (spanningForestEdges endpts es) p).2

                                  Discarding cycle edges preserves connectivity: running spanningForestEdges on the retained forest edges reaches the exact same partition as running it on es.