Hopcroft's algorithm
DefinitionPreimage of a set under a letter#✓fully proved
For a DFA \(M\) with transition function \(\delta\), a set \(C\) of states and a letter \(a\), the preimage of \(C\) under \(a\) is the set of states \(q\) with \(\delta(q, a) \in C\).
def preimage (C : Finset σ) (a : α) : Finset σ :=
Finset.univ.filter fun q => M.step q a ∈ C
Used by 14
DefinitionA set of states split by another#✓fully proved
A set \(D\) of states is split by a set \(P\) of states when \(D\) contains both a state in \(P\) and a state not in \(P\), that is, when \(D \cap P\) and \(D \setminus P\) are both nonempty.
def Splits (pre D : Finset σ) : Prop :=
(D.filter (· ∈ pre)).Nonempty ∧ (D.filter (· ∉ pre)).Nonempty
Used by 11
DefinitionSplitting a block by a set#✓fully proved
For sets \(P\) and \(D\) of states, splitting \(D\) by \(P\) gives the two-element list \([D \cap P,\ D \setminus P]\) if both of these sets are nonempty, and the one-element list \([D]\) otherwise.
def splitBlock (pre D : Finset σ) : List (Finset σ) :=
if Splits pre D then [D.filter (· ∈ pre), D.filter (· ∉ pre)] else [D]
def Splits (pre D : Finset σ) : Prop :=
(D.filter (· ∈ pre)).Nonempty ∧ (D.filter (· ∉ pre)).Nonempty
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:84
Uses 1 · Used by 7
DefinitionSmaller of two finite sets of states#✓fully proved
For finite sets \(A\) and \(B\) of states, the smaller one is \(A\) if \(|A| \le |B|\) and \(B\) otherwise; on a tie in cardinality it is \(A\).
Used by 18
DefinitionNew splitters for a split block#✓fully proved
Given a waiting list \(W\) of pairs of a set of states and a letter and sets \(S\) and \(D\) of states, write \(D_1 = D \cap S\) and \(D_2 = D \setminus S\). The new waiting entries for \(D\) consist, for each letter \(b\) in the enumeration order of the alphabet, of both \((D_1, b)\) and \((D_2, b)\) if \((D, b)\) is in \(W\), and otherwise of the single entry \((E, b)\), where \(E\) is the smaller of \(D_1\) and \(D_2\) by cardinality (\(D_1\) on a tie).
def newSplitters (W : List (Finset σ × α)) (pre D : Finset σ) : List (Finset σ × α) :=
(FinEnum.toList α).flatMap fun b =>
if (D, b) ∈ W then [(D.filter (· ∈ pre), b), (D.filter (· ∉ pre), b)]
else [(smaller (D.filter (· ∈ pre)) (D.filter (· ∉ pre)), b)]
def smaller (A B : Finset σ) : Finset σ := if A.card ≤ B.card then A else B
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:96
Uses 1 · Used by 7
DefinitionOne refinement round of the partition#✓fully proved
For a DFA \(M\) with transition function \(\delta\), a list \(P\) of blocks of states, a set \(C\) of states and a letter \(a\), let \(S = \{q : \delta(q, a) \in C\}\) be the preimage of \(C\) under \(a\). The partition after the splitter \((C, a)\) replaces each block \(D\) of \(P\), in order, by the two blocks \(D \cap S\) and \(D \setminus S\) when both are nonempty, and keeps \(D\) otherwise.
def roundP (P : List (Finset σ)) (C : Finset σ) (a : α) : List (Finset σ) :=
P.flatMap (splitBlock (preimage M C a))
def splitBlock (pre D : Finset σ) : List (Finset σ) :=
if Splits pre D then [D.filter (· ∈ pre), D.filter (· ∉ pre)] else [D]
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:92
def preimage (C : Finset σ) (a : α) : Finset σ :=
Finset.univ.filter fun q => M.step q a ∈ C
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:75
Uses 2 · Used by 15
DefinitionOne update of the waiting list#✓fully proved
For a DFA \(M\) with transition function \(\delta\), a list \(P\) of blocks of states, a list \(W\) of pairs of a set of states and a letter, a set \(C\) of states and a letter \(a\), let \(S = \{q : \delta(q, a) \in C\}\) be the preimage of \(C\) under \(a\), and say that \(S\) splits a set \(D\) when \(D\) meets both \(S\) and its complement. The waiting list after the splitter \((C, a)\), from the rest \(W\) of the waiting list, consists of the entries of \(W\) whose set is not split by \(S\), followed, for each block \(D\) of \(P\) split by \(S\) in order, by the new splitters for \(D\): for each letter \(b\) in the enumeration order of the alphabet, both \((D \cap S, b)\) and \((D \setminus S, b)\) if \((D, b)\) is in \(W\), and otherwise the single entry \((E, b)\), where \(E\) is the smaller of \(D \cap S\) and \(D \setminus S\) by cardinality (\(D \cap S\) on a tie).
def roundW (P : List (Finset σ)) (W : List (Finset σ × α)) (C : Finset σ) (a : α) :
List (Finset σ × α) :=
W.filter (fun cb => !decide (Splits (preimage M C a) cb.1)) ++
(P.filter fun D => decide (Splits (preimage M C a) D)).flatMap
(newSplitters W (preimage M C a))
def Splits (pre D : Finset σ) : Prop :=
(D.filter (· ∈ pre)).Nonempty ∧ (D.filter (· ∉ pre)).Nonempty
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:84
def preimage (C : Finset σ) (a : α) : Finset σ :=
Finset.univ.filter fun q => M.step q a ∈ C
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:75
def newSplitters (W : List (Finset σ × α)) (pre D : Finset σ) : List (Finset σ × α) :=
(FinEnum.toList α).flatMap fun b =>
if (D, b) ∈ W then [(D.filter (· ∈ pre), b), (D.filter (· ∉ pre), b)]
else [(smaller (D.filter (· ∈ pre)) (D.filter (· ∉ pre)), b)]
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:100
Uses 3 · Used by 14
DefinitionBlock of accepting states#✓fully proved
For a DFA \(M\) with finite state set \(Q\), this is the set \(F \subseteq Q\) of accepting states of \(M\), as a finite set.
def accBlock : Finset σ := Finset.univ.filter (· ∈ M.accept)
Used by 9
DefinitionRejecting states#✓fully proved
For a DFA \(M\) with state set \(Q\) and set of accepting states \(F\), the rejecting block is the set \(Q \setminus F\) of states of \(M\) that are not accepting.
def rejBlock : Finset σ := Finset.univ.filter (· ∉ M.accept)
Used by 9
DefinitionHopcroft's initial partition#✓fully proved
For a DFA \(M\) with finite state set \(Q\) and set of accepting states \(F\), the initial partition is the list of blocks \(F\), \(Q \setminus F\), in this order, with any empty block removed.
def initP : List (Finset σ) := [accBlock M, rejBlock M].filter (·.Nonempty)
def accBlock : Finset σ := Finset.univ.filter (· ∈ M.accept)
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:118
def rejBlock : Finset σ := Finset.univ.filter (· ∉ M.accept)
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:121
Uses 2 · Used by 14
DefinitionHopcroft's initial waiting list#✓fully proved
Let \(M\) be a DFA with finite state set \(Q\), set of accepting states \(F\) and finite alphabet \(\Sigma\). If \(F\) and \(Q \setminus F\) are both nonempty, the initial waiting list consists of the splitters \((S, b)\) for every letter \(b \in \Sigma\), where \(S = F\) if \(|F| \le |Q \setminus F|\) and \(S = Q \setminus F\) otherwise; if one of \(F\) and \(Q \setminus F\) is empty, the initial waiting list is empty.
def initW : List (Finset σ × α) :=
if (accBlock M).Nonempty ∧ (rejBlock M).Nonempty then
(FinEnum.toList α).map fun b => (smaller (accBlock M) (rejBlock M), b)
else []
def accBlock : Finset σ := Finset.univ.filter (· ∈ M.accept)
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:118
def rejBlock : Finset σ := Finset.univ.filter (· ∉ M.accept)
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:121
def smaller (A B : Finset σ) : Finset σ := if A.card ≤ B.card then A else B
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:96
Uses 3 · Used by 16
DefinitionHopcroft's refinement loop#✓fully proved
Let \(M\) be a DFA. Given a round budget \(k\), a partition \(P\) of the states of \(M\) (a list of blocks) and a waiting list \(W\) of splitters \((C, a)\), each a block \(C\) with a letter \(a\), the loop returns the pair \((P, W)\) unchanged if \(k = 0\) or \(W\) is empty. Otherwise it removes the first splitter \((C, a)\) from \(W\), replaces \(P\) by the partition after the splitter \((C, a)\) and the remaining waiting list by the waiting list after the splitter \((C, a)\), and continues with budget \(k - 1\).
def hopcroftLoop : ℕ → List (Finset σ) → List (Finset σ × α) →
List (Finset σ) × List (Finset σ × α)
| 0, P, W => (P, W)
| _ + 1, P, [] => (P, [])
| k + 1, P, (C, a) :: W => hopcroftLoop k (roundP M P C a) (roundW M P W C a)
def roundP (P : List (Finset σ)) (C : Finset σ) (a : α) : List (Finset σ) :=
P.flatMap (splitBlock (preimage M C a))
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:106
def roundW (P : List (Finset σ)) (W : List (Finset σ × α)) (C : Finset σ) (a : α) :
List (Finset σ × α) :=
W.filter (fun cb => !decide (Splits (preimage M C a) cb.1)) ++
(P.filter fun D => decide (Splits (preimage M C a) D)).flatMap
(newSplitters W (preimage M C a))
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:111
Uses 2 · Used by 9
DefinitionHopcroft's partition#✓fully proved
Let \(M\) be a DFA with finite state set \(Q\) over a finite alphabet \(\Sigma\). Hopcroft's partition of \(M\) is the partition returned by Hopcroft's loop run from the initial partition and the initial waiting list with a budget of \(|\Sigma| \cdot (|Q| + 1)\) rounds.
def hopcroftPartition : List (Finset σ) :=
(M.hopcroftLoop (Fintype.card α * (Fintype.card σ + 1)) (initP M) (initW M)).1
def initP : List (Finset σ) := [accBlock M, rejBlock M].filter (·.Nonempty)
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:124
def initW : List (Finset σ × α) :=
if (accBlock M).Nonempty ∧ (rejBlock M).Nonempty then
(FinEnum.toList α).map fun b => (smaller (accBlock M) (rejBlock M), b)
else []
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:128
Uses 3 · Used by 5
DefinitionHopcroft's relation#✓fully proved
For a DFA \(M\) with finitely many states, Hopcroft's relation is the set of pairs \((p, q)\) of states of \(M\) such that \(p\) and \(q\) lie in a common block of Hopcroft's partition.
def hopcroftRel : Finset (σ × σ) :=
Finset.univ.filter fun pq => ∃ D ∈ M.hopcroftPartition, pq.1 ∈ D ∧ pq.2 ∈ D
Uses 1 · Used by 3
StructurePartitions of the states#✓fully proved
A list \(P\) of sets of states is a partition into nonempty blocks when entries of \(P\) at different positions are disjoint, every entry of \(P\) is nonempty, and every state lies in some entry of \(P\).
structure IsPartition (P : List (Finset σ)) : Prop where
disjoint : P.Pairwise Disjoint
nonempty : ∀ D ∈ P, D.Nonempty
cover : ∀ q, ∃ D ∈ P, q ∈ D
Used by 13
DefinitionLying in a common block#✓fully proved
For a list \(P\) of sets of states, two states \(p\) and \(q\) are related by \(P\) when some block \(D\) of \(P\) contains both \(p\) and \(q\).
Used by 9
TheoremA block contains both or neither of two related states#✓fully proved
Let \(P\) be a list of sets of states that is a partition into nonempty blocks, let \(C\) be an entry of \(P\), and let \(p\) and \(q\) be states lying in a common entry of \(P\). Then \(p \in C\) if and only if \(q \in C\).
theorem IsPartition.mem_iff_of_rel {P : List (Finset σ)} (hP : IsPartition P) {C : Finset σ}
(hC : C ∈ P) {p q : σ} (h : Rel P p q) : p ∈ C ↔ q ∈ C
structure IsPartition (P : List (Finset σ)) : Prop where
disjoint : P.Pairwise Disjoint
nonempty : ∀ D ∈ P, D.Nonempty
cover : ∀ q, ∃ D ∈ P, q ∈ D
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:160
def Rel (P : List (Finset σ)) (p q : σ) : Prop := ∃ D ∈ P, p ∈ D ∧ q ∈ D
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:166
Uses 2 · Used by 2
TheoremA partition has at most as many blocks as there are states#✓fully proved
If a list \(P\) of sets of states is a partition into nonempty blocks, then the length of \(P\) is at most the number \(|Q|\) of states.
theorem IsPartition.length_le {P : List (Finset σ)} (hP : IsPartition P) :
P.length ≤ Fintype.card σ
structure IsPartition (P : List (Finset σ)) : Prop where
disjoint : P.Pairwise Disjoint
nonempty : ∀ D ∈ P, D.Nonempty
cover : ∀ q, ∃ D ∈ P, q ∈ D
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:160
Uses 1 · Used by 1
TheoremPieces of a split block lie on one side#✓fully proved
Let \(S\) and \(D\) be sets of states. Every piece of \(D\) split by \(S\) (the two sets \(D \cap S\) and \(D \setminus S\) if \(D\) meets both \(S\) and its complement, and \(D\) itself otherwise) is contained in \(S\) or disjoint from \(S\): any two states \(x\) and \(y\) of the piece satisfy \(x \in S \iff y \in S\).
theorem side_of_mem_splitBlock {pre D D' : Finset σ} (h : D' ∈ splitBlock pre D) {x y : σ}
(hx : x ∈ D') (hy : y ∈ D') : x ∈ pre ↔ y ∈ pre
def splitBlock (pre D : Finset σ) : List (Finset σ) :=
if Splits pre D then [D.filter (· ∈ pre), D.filter (· ∉ pre)] else [D]
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:92
Uses 2 · Used by 1
TheoremEffect of one splitter on the common-block relation#✓fully proved
Let \(M\) be a DFA with transition function \(\delta\), \(P\) a list of blocks of states, \(C\) a set of states and \(a\) a letter. Two states \(p\) and \(q\) lie in a common block after the splitter \((C, a)\) is applied to \(P\) if and only if they lie in a common block of \(P\) and \(\delta(p, a) \in C \iff \delta(q, a) \in C\).
theorem rel_roundP_iff {P : List (Finset σ)} {C : Finset σ} {a : α} {p q : σ} :
Rel (roundP M P C a) p q ↔ Rel P p q ∧ (p ∈ preimage M C a ↔ q ∈ preimage M C a)
def Rel (P : List (Finset σ)) (p q : σ) : Prop := ∃ D ∈ P, p ∈ D ∧ q ∈ D
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:166
def roundP (P : List (Finset σ)) (C : Finset σ) (a : α) : List (Finset σ) :=
P.flatMap (splitBlock (preimage M C a))
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:106
def preimage (C : Finset σ) (a : α) : Finset σ :=
Finset.univ.filter fun q => M.step q a ∈ C
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:75
Uses 6 · Used by 2
TheoremNumber of blocks after one round#✓fully proved
Let \(M\) be a DFA with transition function \(\delta\), let \(P\) be a list of sets of states, \(C\) a set of states and \(a\) a letter, and let \(\mathrm{pre} = \{q : \delta(q, a) \in C\}\). The partition after the splitter \((C, a)\), which replaces each entry \(D\) of \(P\) meeting both \(\mathrm{pre}\) and its complement by \(D \cap \mathrm{pre}\) and \(D \setminus \mathrm{pre}\), has length equal to the length of \(P\) plus the number of entries of \(P\) (counted with repetition) that meet both \(\mathrm{pre}\) and its complement.
theorem length_roundP {P : List (Finset σ)} {C : Finset σ} {a : α} :
(roundP M P C a).length =
P.length + (P.filter fun D => decide (Splits (preimage M C a) D)).length
def roundP (P : List (Finset σ)) (C : Finset σ) (a : α) : List (Finset σ) :=
P.flatMap (splitBlock (preimage M C a))
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:106
def Splits (pre D : Finset σ) : Prop :=
(D.filter (· ∈ pre)).Nonempty ∧ (D.filter (· ∉ pre)).Nonempty
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:84
def preimage (C : Finset σ) (a : α) : Finset σ :=
Finset.univ.filter fun q => M.step q a ∈ C
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:75
Uses 4 · Used by 1
TheoremGrowth of the waiting list in one round#✓fully proved
Let \(M\) be a DFA with transition function \(\delta\) over a finite alphabet \(\Sigma\), let \(P\) be a list of sets of states with no repeated entries, let \(W\) be a list of pairs of a set of states and a letter, let \(C\) be a set of states and \(a\) a letter, and let \(S = \{q : \delta(q, a) \in C\}\) be the preimage of \(C\) under \(a\). Then the waiting list after the splitter \((C, a)\), computed from the blocks \(P\) and the rest \(W\) of the waiting list, has length at most \(|W| + |\Sigma| \cdot k\), where \(k\) is the number of entries of \(P\) split by \(S\), that is, meeting both \(S\) and its complement.
theorem length_roundW_le {P : List (Finset σ)} (hnd : P.Nodup) {W : List (Finset σ × α)}
{C : Finset σ} {a : α} :
(roundW M P W C a).length ≤
W.length +
Fintype.card α * (P.filter fun D => decide (Splits (preimage M C a) D)).length
def roundW (P : List (Finset σ)) (W : List (Finset σ × α)) (C : Finset σ) (a : α) :
List (Finset σ × α) :=
W.filter (fun cb => !decide (Splits (preimage M C a) cb.1)) ++
(P.filter fun D => decide (Splits (preimage M C a) D)).flatMap
(newSplitters W (preimage M C a))
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:111
def Splits (pre D : Finset σ) : Prop :=
(D.filter (· ∈ pre)).Nonempty ∧ (D.filter (· ∉ pre)).Nonempty
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:84
def preimage (C : Finset σ) (a : α) : Finset σ :=
Finset.univ.filter fun q => M.step q a ∈ C
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:75
Uses 5 · Used by 1
StructureInvariants of Hopcroft's loop#✓fully proved
Let \(M\) be a DFA with transition function \(\delta\). A list \(P\) of sets of states and a waiting list \(W\) of pairs \((C, b)\) of a set of states \(C\) and a letter \(b\) satisfy the invariants when: (i) \(P\) is a partition of the states into nonempty blocks; (ii) for every \((C, b)\) in \(W\), \(C\) is an entry of \(P\); (iii) for every letter \(b\) and all states \(p\), \(q\) lying in a common entry of \(P\), if for every \(C\) with \((C, b)\) in \(W\) the states \(\delta(p, b)\) and \(\delta(q, b)\) are both in \(C\) or both outside \(C\), then \(\delta(p, b)\) and \(\delta(q, b)\) lie in a common entry of \(P\); (iv) any two states lying in a common entry of \(P\) are both accepting or both rejecting; and (v) any two states from which \(M\) accepts the same language lie in a common entry of \(P\).
structure Inv (P : List (Finset σ)) (W : List (Finset σ × α)) : Prop where
/-- `P` is a partition into nonempty blocks. -/
part : IsPartition P
/-- Every waiting set is a block. -/
mem : ∀ cb ∈ W, cb.1 ∈ P
/-- Two states in one block whose `b`-successors agree on every set waiting with `b` have
`b`-successors in one block. -/
stable : ∀ b p q, Rel P p q → (∀ C, (C, b) ∈ W → (M.step p b ∈ C ↔ M.step q b ∈ C)) →
Rel P (M.step p b) (M.step q b)
/-- Blocks agree on acceptance. -/
accept : ∀ p q, Rel P p q → (p ∈ M.accept ↔ q ∈ M.accept)
/-- States with one continuation language share a block. -/
coarse : ∀ p q, M.acceptsFrom p = M.acceptsFrom q → Rel P p q
structure IsPartition (P : List (Finset σ)) : Prop where
disjoint : P.Pairwise Disjoint
nonempty : ∀ D ∈ P, D.Nonempty
cover : ∀ q, ∃ D ∈ P, q ∈ D
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:160
def Rel (P : List (Finset σ)) (p q : σ) : Prop := ∃ D ∈ P, p ∈ D ∧ q ∈ D
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:166
Uses 2 · Used by 11
TheoremOne round preserves the invariants#✓fully proved
Let \(M\) be a DFA, \(P\) a list of blocks of states, \(W\) a list of pairs of a set of states and a letter, \(C\) a set of states and \(a\) a letter. If \(P\) and the waiting list obtained by putting \((C, a)\) in front of \(W\) satisfy the invariants of Hopcroft's loop, then so do the partition after the splitter \((C, a)\) and the waiting list after the splitter \((C, a)\) from the rest \(W\).
theorem Inv.round {P : List (Finset σ)} {C : Finset σ} {a : α} {W : List (Finset σ × α)}
(h : Inv M P ((C, a) :: W)) : Inv M (roundP M P C a) (roundW M P W C a)
structure Inv (P : List (Finset σ)) (W : List (Finset σ × α)) : Prop where
/-- `P` is a partition into nonempty blocks. -/
part : IsPartition P
/-- Every waiting set is a block. -/
mem : ∀ cb ∈ W, cb.1 ∈ P
/-- Two states in one block whose `b`-successors agree on every set waiting with `b` have
`b`-successors in one block. -/
stable : ∀ b p q, Rel P p q → (∀ C, (C, b) ∈ W → (M.step p b ∈ C ↔ M.step q b ∈ C)) →
Rel P (M.step p b) (M.step q b)
/-- Blocks agree on acceptance. -/
accept : ∀ p q, Rel P p q → (p ∈ M.accept ↔ q ∈ M.accept)
/-- States with one continuation language share a block. -/
⋯
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:395
def roundP (P : List (Finset σ)) (C : Finset σ) (a : α) : List (Finset σ) :=
P.flatMap (splitBlock (preimage M C a))
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:106
def roundW (P : List (Finset σ)) (W : List (Finset σ × α)) (C : Finset σ) (a : α) :
List (Finset σ × α) :=
W.filter (fun cb => !decide (Splits (preimage M C a) cb.1)) ++
(P.filter fun D => decide (Splits (preimage M C a) D)).flatMap
(newSplitters W (preimage M C a))
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:111
Uses 12 · Used by 3
TheoremWith an empty waiting list, blocks are the classes of equal continuation languages#✓fully proved
Let \(M\) be a DFA and \(P\) a list of blocks of states such that \(P\) with the empty waiting list satisfies the invariants of Hopcroft's loop. Then two states \(p\) and \(q\) lie in a common block of \(P\) if and only if they accept the same continuation languages, that is, \(M\) accepts a word from \(p\) exactly when it accepts it from \(q\).
With an empty waiting list the invariants say that \(P\) is a right congruence refining acceptance.
theorem Inv.rel_iff {P : List (Finset σ)} (h : Inv M P []) {p q : σ} :
Rel P p q ↔ M.acceptsFrom p = M.acceptsFrom q
structure Inv (P : List (Finset σ)) (W : List (Finset σ × α)) : Prop where
/-- `P` is a partition into nonempty blocks. -/
part : IsPartition P
/-- Every waiting set is a block. -/
mem : ∀ cb ∈ W, cb.1 ∈ P
/-- Two states in one block whose `b`-successors agree on every set waiting with `b` have
`b`-successors in one block. -/
stable : ∀ b p q, Rel P p q → (∀ C, (C, b) ∈ W → (M.step p b ∈ C ↔ M.step q b ∈ C)) →
Rel P (M.step p b) (M.step q b)
/-- Blocks agree on acceptance. -/
accept : ∀ p q, Rel P p q → (p ∈ M.accept ↔ q ∈ M.accept)
/-- States with one continuation language share a block. -/
⋯
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:395
def Rel (P : List (Finset σ)) (p q : σ) : Prop := ∃ D ∈ P, p ∈ D ∧ q ∈ D
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:166
Uses 2 · Used by 2
TheoremThe invariants hold initially#✓fully proved
For every DFA \(M\) with finitely many states over a finite alphabet, Hopcroft's initial partition and initial waiting list satisfy the invariants of Hopcroft's loop.
theorem inv_init : Inv M (initP M) (initW M)
structure Inv (P : List (Finset σ)) (W : List (Finset σ × α)) : Prop where
/-- `P` is a partition into nonempty blocks. -/
part : IsPartition P
/-- Every waiting set is a block. -/
mem : ∀ cb ∈ W, cb.1 ∈ P
/-- Two states in one block whose `b`-successors agree on every set waiting with `b` have
`b`-successors in one block. -/
stable : ∀ b p q, Rel P p q → (∀ C, (C, b) ∈ W → (M.step p b ∈ C ↔ M.step q b ∈ C)) →
Rel P (M.step p b) (M.step q b)
/-- Blocks agree on acceptance. -/
accept : ∀ p q, Rel P p q → (p ∈ M.accept ↔ q ∈ M.accept)
/-- States with one continuation language share a block. -/
⋯
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:395
def initP : List (Finset σ) := [accBlock M, rejBlock M].filter (·.Nonempty)
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:124
def initW : List (Finset σ × α) :=
if (accBlock M).Nonempty ∧ (rejBlock M).Nonempty then
(FinEnum.toList α).map fun b => (smaller (accBlock M) (rejBlock M), b)
else []
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:128
Uses 8 · Used by 6
TheoremTermination of Hopcroft's loop#✓fully proved
Let \(M\) be a DFA with finite state set \(Q\) over a finite alphabet \(\Sigma\), let \(P\) be a partition of the states given as a list of blocks, let \(W\) be a waiting list of splitters, and let \(k\) be a natural number. If \(P\) and \(W\) satisfy the invariants of Hopcroft's loop and \(|W| + |\Sigma| \cdot (|Q| - |P|) \le k\), where \(|P|\) and \(|W|\) are the lengths of the lists, then Hopcroft's loop run from \((P, W)\) with a budget of \(k\) rounds ends with an empty waiting list, and the partition and waiting list it returns again satisfy the invariants.
The invariants make \(P\) a partition into nonempty blocks, so \(|P| \le |Q|\) and the difference \(|Q| - |P|\) is not truncated.
theorem hopcroftLoop_spec : ∀ (k : ℕ) (P : List (Finset σ)) (W : List (Finset σ × α)),
Inv M P W → W.length + Fintype.card α * (Fintype.card σ - P.length) ≤ k →
(M.hopcroftLoop k P W).2 = [] ∧ Inv M (M.hopcroftLoop k P W).1 (M.hopcroftLoop k P W).2
structure Inv (P : List (Finset σ)) (W : List (Finset σ × α)) : Prop where
/-- `P` is a partition into nonempty blocks. -/
part : IsPartition P
/-- Every waiting set is a block. -/
mem : ∀ cb ∈ W, cb.1 ∈ P
/-- Two states in one block whose `b`-successors agree on every set waiting with `b` have
`b`-successors in one block. -/
stable : ∀ b p q, Rel P p q → (∀ C, (C, b) ∈ W → (M.step p b ∈ C ↔ M.step q b ∈ C)) →
Rel P (M.step p b) (M.step q b)
/-- Blocks agree on acceptance. -/
accept : ∀ p q, Rel P p q → (p ∈ M.accept ↔ q ∈ M.accept)
/-- States with one continuation language share a block. -/
⋯
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:395
Uses 11 · Used by 4
TheoremHopcroft's waiting list is exhausted#✓fully proved
Let \(M\) be a DFA with finite state set \(Q\) over a finite alphabet \(\Sigma\). Hopcroft's loop, run from the initial partition and the initial waiting list with a budget of \(|\Sigma| \cdot (|Q| + 1)\) rounds, ends with an empty waiting list.
theorem hopcroftLoop_waiting_eq_nil :
(M.hopcroftLoop (Fintype.card α * (Fintype.card σ + 1)) (initP M) (initW M)).2 = []
def initP : List (Finset σ) := [accBlock M, rejBlock M].filter (·.Nonempty)
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:124
def initW : List (Finset σ × α) :=
if (accBlock M).Nonempty ∧ (rejBlock M).Nonempty then
(FinEnum.toList α).map fun b => (smaller (accBlock M) (rejBlock M), b)
else []
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:128
Uses 9 · Used by 1
TheoremHopcroft's final partition is a partition#✓fully proved
For a DFA \(M\), Hopcroft's final partition of \(M\) is a partition of the states of \(M\) into nonempty blocks.
theorem isPartition_hopcroftPartition : IsPartition M.hopcroftPartition
structure IsPartition (P : List (Finset σ)) : Prop where
disjoint : P.Pairwise Disjoint
nonempty : ∀ D ∈ P, D.Nonempty
cover : ∀ q, ∃ D ∈ P, q ∈ D
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:160
Uses 11 · Used by 1
TheoremCorrectness of Hopcroft's algorithm#✓fully proved
For a DFA \(M\), two states \(p\) and \(q\) share a block of Hopcroft's final partition of \(M\) exactly when the language accepted by \(M\) from \(p\) equals the language accepted by \(M\) from \(q\).
theorem mem_hopcroftRel_iff {p q : σ} :
(p, q) ∈ M.hopcroftRel ↔ M.acceptsFrom p = M.acceptsFrom q
Uses 13 · Used by 2
TheoremHopcroft's partition is Moore's relation#✓fully proved
For every DFA \(M\) with finitely many states over a finite alphabet, Hopcroft's relation of \(M\), the set of pairs of states lying in a common block of Hopcroft's partition, equals Moore's state equivalence of \(M\) as a set of pairs of states.
theorem hopcroftRel_eq_mooreRel : M.hopcroftRel = M.mooreRel
Uses 4
TheoremMoore classes are Hopcroft blocks#✓fully proved
For a DFA \(M\) and reachable states \(p\) and \(q\) of \(M\), the classes of \(p\) and \(q\) under Moore's equivalence on reachable states (the states of Moore's minimal automaton) are equal if and only if \(p\) and \(q\) share a block of Hopcroft's final partition of \(M\).
theorem mooreClass_eq_iff_mem_hopcroftRel {p q : {s // s ∈ M.reachable}} :
M.mooreClass p = M.mooreClass q ↔ (p.1, q.1) ∈ M.hopcroftRel
TheoremBlocks of Hopcroft's partition are language classes#✓fully proved
Let \(M\) be a DFA, \(D\) a block of Hopcroft's final partition of \(M\) and \(p\) a state in \(D\). Then a state \(q\) lies in \(D\) if and only if the language accepted by \(M\) from \(q\) equals the language accepted by \(M\) from \(p\).
theorem mem_block_iff {D : Finset σ} (hD : D ∈ M.hopcroftPartition) {p : σ} (hp : p ∈ D)
{q : σ} : q ∈ D ↔ M.acceptsFrom q = M.acceptsFrom p
Uses 14
TheoremThe smaller of two sets has at most half their total size#✓fully proved
The smaller of two sets has at most half their combined size: it is one of the two sets, so twice its size is at most the sum of the two sizes.
theorem two_mul_smaller_card_le {σ : Type*} (A B : Finset σ) :
2 * (smaller A B).card ≤ A.card + B.card
def smaller (A B : Finset σ) : Finset σ := if A.card ≤ B.card then A else B
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:96
Uses 1 · Used by 1
TheoremThe smaller part of a split has at most half the size#✓fully proved
If two disjoint sets union to D, the smaller of them has at most half the size of
D.
theorem two_mul_smaller_card_le_of_split {σ : Type*} [DecidableEq σ] {D A B : Finset σ}
(hun : A ∪ B = D) (hdis : Disjoint A B) : 2 * (smaller A B).card ≤ D.card
def smaller (A B : Finset σ) : Finset σ := if A.card ≤ B.card then A else B
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:96
Uses 2 · Used by 2
TheoremHalving chains shrink blocks by a factor of at least \(2^k\)#✓fully proved
After k halving steps, the initial block is at least 2 ^ k times the final block.
The halving hypothesis is stated for the cardinalities only; no subset hypothesis is
needed.
theorem mul_pow_le_card_of_halving_chain {σ : Type*} {f : ℕ → Finset σ} {k : ℕ}
(hstep : ∀ i, i < k → 2 * (f (i + 1)).card ≤ (f i).card) :
2 ^ k * (f k).card ≤ (f 0).card
Used by 1
TheoremThe smaller-half counting lemma#✓fully proved
In a chain of blocks where each step at least
halves the size, a block containing a given state can occur at most Nat.log 2 n times
past the start, where n is the number of states. In Hopcroft's algorithm, the successive
smaller halves containing one fixed state form such a chain: each of them is the smaller
half of a split of the previous block containing that state.
theorem smaller_half_chain_le_log {σ : Type*} [Fintype σ] {f : ℕ → Finset σ} {q : σ}
{k : ℕ} (hmem : ∀ i, i ≤ k → q ∈ f i)
(hstep : ∀ i, i < k → 2 * (f (i + 1)).card ≤ (f i).card) :
k ≤ Nat.log 2 (Fintype.card σ)
Sources
- J. E. Hopcroft, "An
n log nalgorithm for minimizing states in a finite automaton", Theory of Machines and Computations, 1971; the counting argument is presented in J. Berstel, L. Boasson, O. Carton and I. Fagnot, "Minimization of automata", 2010.
Uses 1
TheoremNew splitters are halves of the split block#✓fully proved
Every entry newSplitters adds for a block D is a half of D (one of the two sides
of the split by pre), or the smaller half.
theorem mem_newSplitters {W : List (Finset σ × α)} {pre D : Finset σ} {X : Finset σ}
{b : α} (h : (X, b) ∈ newSplitters W pre D) :
X ⊆ D ∧ (X = D.filter (· ∈ pre) ∨ X = D.filter (· ∉ pre) ∨
X = smaller (D.filter (· ∈ pre)) (D.filter (· ∉ pre)))
def newSplitters (W : List (Finset σ × α)) (pre D : Finset σ) : List (Finset σ × α) :=
(FinEnum.toList α).flatMap fun b =>
if (D, b) ∈ W then [(D.filter (· ∈ pre), b), (D.filter (· ∉ pre), b)]
else [(smaller (D.filter (· ∈ pre)) (D.filter (· ∉ pre)), b)]
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:100
def smaller (A B : Finset σ) : Finset σ := if A.card ≤ B.card then A else B
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:96
Uses 2 · Used by 1
TheoremSmaller-half splitters have at most half the block size#✓fully proved
A smaller-half entry added by newSplitters for a block D has at most half the size
of D: the two halves are disjoint and union to D. No split hypothesis is needed: the
halves of any block partition it.
theorem two_mul_card_le_of_mem_newSplitters_smaller {W : List (Finset σ × α)}
{pre D : Finset σ} {X : Finset σ} {b : α} (h : (X, b) ∈ newSplitters W pre D)
(hDb : (D, b) ∉ W) :
2 * X.card ≤ D.card
def newSplitters (W : List (Finset σ × α)) (pre D : Finset σ) : List (Finset σ × α) :=
(FinEnum.toList α).flatMap fun b =>
if (D, b) ∈ W then [(D.filter (· ∈ pre), b), (D.filter (· ∉ pre), b)]
else [(smaller (D.filter (· ∈ pre)) (D.filter (· ∉ pre)), b)]
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:100
Uses 3
DefinitionTrace of splitters popped by Hopcroft's algorithm#✓fully proved
The splitters popped by Hopcroft's loop, in order: the same recursion as
DFA.hopcroftLoop, collecting each popped splitter. The cost of processing one popped
splitter (C, a) is (preimage M C a).card + 1: one predecessor visit per state whose
a-successor lies in C, plus O(1) per-splitter overhead. See hopcroftCost.
hopcroftLoop_eq_hopcroftLoop_of_length proves this trace records exactly the
splitters the loop pops, so the trace cannot drift from the loop.
def hopcroftSplitters : ℕ → List (Finset σ) → List (Finset σ × α) → List (Finset σ × α)
| 0, _, _ => []
| _ + 1, _, [] => []
| k + 1, P, (C, a) :: W =>
(C, a) :: hopcroftSplitters k (roundP M P C a) (roundW M P W C a)
def roundP (P : List (Finset σ)) (C : Finset σ) (a : α) : List (Finset σ) :=
P.flatMap (splitBlock (preimage M C a))
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:106
def roundW (P : List (Finset σ)) (W : List (Finset σ × α)) (C : Finset σ) (a : α) :
List (Finset σ × α) :=
W.filter (fun cb => !decide (Splits (preimage M C a) cb.1)) ++
(P.filter fun D => decide (Splits (preimage M C a) D)).flatMap
(newSplitters W (preimage M C a))
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:111
Uses 2 · Used by 12
DefinitionPredecessor cost of a Hopcroft splitter trace#✓fully proved
The predecessor cost of a splitter trace: for each popped splitter (C, a), one
predecessor visit per state in the preimage preimage M C a, plus 1 per-splitter
overhead. This is the quantity the total-work bound constrains
to O(|Σ| n log n). Charging splitter size C.card instead
would undercount: if all n states transition on a to one state q, processing the
singleton splitter ({q}, a) costs n predecessor visits while C.card charges
only 1.
def hopcroftCost (trace : List (Finset σ × α)) : ℕ :=
(trace.map fun cb => (preimage M cb.1 cb.2).card + 1).sum
def preimage (C : Finset σ) (a : α) : Finset σ :=
Finset.univ.filter fun q => M.step q a ∈ C
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:75
Uses 1 · Used by 8
TheoremThe splitter trace records exactly the splitters the loop pops#✓fully proved
The loop and the
trace recurse on the same state, so they take the same number of steps. The two
theorems below make the correspondence usable: the trace length is at most k, and
running the loop for exactly the trace's length gives the full loop result, so the trace
contains every splitter the loop pops and no others.
theorem hopcroftSplitters_length_le {α σ : Type*} [FinEnum α] [Fintype σ]
[DecidableEq σ] (M : DFA α σ) (k : ℕ)
(P : List (Finset σ))
(W : List (Finset σ × α)) :
(hopcroftSplitters M k P W).length ≤ k
def hopcroftSplitters : ℕ → List (Finset σ) → List (Finset σ × α) → List (Finset σ × α)
| 0, _, _ => []
| _ + 1, _, [] => []
| k + 1, P, (C, a) :: W =>
(C, a) :: hopcroftSplitters k (roundP M P C a) (roundW M P W C a)
CSLibExt/Computability/Languages/Congruences/HopcroftCost.lean:168
Uses 3
TheoremHopcroft splitter trace length equals loop fuel#✓fully proved
The trace and the loop take the same number of steps: the trace length is the fuel the loop consumed, i.e. the loop would stop after exactly that many rounds if run with only that much fuel.
theorem hopcroftLoop_eq_hopcroftLoop_of_length {α σ : Type*} [FinEnum α]
[Fintype σ] [DecidableEq σ] (M : DFA α σ) (k : ℕ)
(P : List (Finset σ))
(W : List (Finset σ × α)) :
M.hopcroftLoop ((hopcroftSplitters M k P W).length) P W =
M.hopcroftLoop k P W
def hopcroftSplitters : ℕ → List (Finset σ) → List (Finset σ × α) → List (Finset σ × α)
| 0, _, _ => []
| _ + 1, _, [] => []
| k + 1, P, (C, a) :: W =>
(C, a) :: hopcroftSplitters k (roundP M P C a) (roundW M P W C a)
CSLibExt/Computability/Languages/Congruences/HopcroftCost.lean:168
Uses 4
DefinitionSplitter count of a state and letter#✓fully proved
Number of splitters with letter b containing q, with multiplicity.
This applies both to the waiting list and to the actual popped trace.
def splitterCount (q : σ) (b : α) (W : List (Finset σ × α)) : ℕ :=
W.countP fun cb => decide (cb.2 = b ∧ q ∈ cb.1)
Used by 10
TheoremSplitter count of newly added splitters#✓fully proved
Replacement contributes one token at a state in the parent block; otherwise only membership in the smaller half contributes a token.
theorem splitterCount_newSplitters (q : σ) (b : α)
(W : List (Finset σ × α)) (pre D : Finset σ) :
splitterCount q b (newSplitters W pre D) =
if (D, b) ∈ W then (if q ∈ D then 1 else 0)
else if q ∈ smaller (D.filter (· ∈ pre))
(D.filter (· ∉ pre)) then 1 else 0
def splitterCount (q : σ) (b : α) (W : List (Finset σ × α)) : ℕ :=
W.countP fun cb => decide (cb.2 = b ∧ q ∈ cb.1)
CSLibExt/Computability/Languages/Congruences/HopcroftWorklist.lean:30
def newSplitters (W : List (Finset σ × α)) (pre D : Finset σ) : List (Finset σ × α) :=
(FinEnum.toList α).flatMap fun b =>
if (D, b) ∈ W then [(D.filter (· ∈ pre), b), (D.filter (· ∉ pre), b)]
else [(smaller (D.filter (· ∈ pre)) (D.filter (· ∉ pre)), b)]
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:100
def smaller (A B : Finset σ) : Finset σ := if A.card ≤ B.card then A else B
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:96
Uses 3 · Used by 1
TheoremSplitter count update after one Hopcroft round#✓fully proved
The exact token update at a state. Only its unique parent block can contribute a new token, even when other blocks split in the same round.
theorem splitterCount_roundW (M : DFA α σ) {P : List (Finset σ)}
(hP : IsPartition P) (W : List (Finset σ × α))
(hW : ∀ cb ∈ W, cb.1 ∈ P) (q : σ) (b : α)
(C D : Finset σ) (a : α) (hD : D ∈ P) (hq : q ∈ D) :
splitterCount q b (roundW M P W C a) =
if Splits (preimage M C a) D then
if (D, b) ∈ W then 1
else if q ∈ smaller (D.filter (· ∈ preimage M C a))
(D.filter (· ∉ preimage M C a)) then 1 else 0
else splitterCount q b W
structure IsPartition (P : List (Finset σ)) : Prop where
disjoint : P.Pairwise Disjoint
nonempty : ∀ D ∈ P, D.Nonempty
cover : ∀ q, ∃ D ∈ P, q ∈ D
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:160
def splitterCount (q : σ) (b : α) (W : List (Finset σ × α)) : ℕ :=
W.countP fun cb => decide (cb.2 = b ∧ q ∈ cb.1)
CSLibExt/Computability/Languages/Congruences/HopcroftWorklist.lean:30
def roundW (P : List (Finset σ)) (W : List (Finset σ × α)) (C : Finset σ) (a : α) :
List (Finset σ × α) :=
W.filter (fun cb => !decide (Splits (preimage M C a) cb.1)) ++
(P.filter fun D => decide (Splits (preimage M C a) D)).flatMap
(newSplitters W (preimage M C a))
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:111
def Splits (pre D : Finset σ) : Prop :=
(D.filter (· ∈ pre)).Nonempty ∧ (D.filter (· ∉ pre)).Nonempty
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:84
def preimage (C : Finset σ) (a : α) : Finset σ :=
Finset.univ.filter fun q => M.step q a ∈ C
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:75
def smaller (A B : Finset σ) : Finset σ := if A.card ≤ B.card then A else B
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:96
Uses 9 · Used by 1
TheoremWaiting entries give positive splitter count#✓fully proved
A waiting entry supplies a token; duplicate entries remain counted.
theorem splitterCount_pos_of_mem (q : σ) (b : α)
{W : List (Finset σ × α)} {D : Finset σ}
(hW : (D, b) ∈ W) (hq : q ∈ D) : 0 < splitterCount q b W
def splitterCount (q : σ) (b : α) (W : List (Finset σ × α)) : ℕ :=
W.countP fun cb => decide (cb.2 = b ∧ q ∈ cb.1)
CSLibExt/Computability/Languages/Congruences/HopcroftWorklist.lean:30
Uses 1 · Used by 1
TheoremRefined Hopcroft blocks lie inside their old blocks#✓fully proved
A refined block containing q is a subset of the old block
containing q. Both partitions are the canonical Hopcroft partitions.
theorem IsPartition.subset_of_mem_roundP (M : DFA α σ)
{P : List (Finset σ)} (hP : IsPartition P)
{C D E : Finset σ} {a : α} {q : σ}
(hD : D ∈ P) (hqD : q ∈ D)
(hE : E ∈ Hopcroft.roundP M P C a) (hqE : q ∈ E) : E ⊆ D
structure IsPartition (P : List (Finset σ)) : Prop where
disjoint : P.Pairwise Disjoint
nonempty : ∀ D ∈ P, D.Nonempty
cover : ∀ q, ∃ D ∈ P, q ∈ D
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:160
def roundP (P : List (Finset σ)) (C : Finset σ) (a : α) : List (Finset σ) :=
P.flatMap (splitBlock (preimage M C a))
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:106
Uses 5 · Used by 1
TheoremPotential inequality for one Hopcroft refinement round#✓fully proved
One refinement cannot increase logarithmic block-size credit plus pending-token credit. Replacing a pending splitter spends no new credit; adding a nonpending smaller half spends one logarithmic unit.
theorem splitterCount_round_potential (M : DFA α σ)
{P : List (Finset σ)} (hP : IsPartition P)
(W : List (Finset σ × α)) (hW : ∀ cb ∈ W, cb.1 ∈ P)
(q : σ) (b : α) {C D E : Finset σ} {a : α}
(hD : D ∈ P) (hqD : q ∈ D)
(hE : E ∈ roundP M P C a) (hqE : q ∈ E) :
splitterCount q b (roundW M P W C a) + Nat.log 2 E.card ≤
splitterCount q b W + Nat.log 2 D.card
structure IsPartition (P : List (Finset σ)) : Prop where
disjoint : P.Pairwise Disjoint
nonempty : ∀ D ∈ P, D.Nonempty
cover : ∀ q, ∃ D ∈ P, q ∈ D
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:160
def roundP (P : List (Finset σ)) (C : Finset σ) (a : α) : List (Finset σ) :=
P.flatMap (splitBlock (preimage M C a))
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:106
def splitterCount (q : σ) (b : α) (W : List (Finset σ × α)) : ℕ :=
W.countP fun cb => decide (cb.2 = b ∧ q ∈ cb.1)
CSLibExt/Computability/Languages/Congruences/HopcroftWorklist.lean:30
def roundW (P : List (Finset σ)) (W : List (Finset σ × α)) (C : Finset σ) (a : α) :
List (Finset σ × α) :=
W.filter (fun cb => !decide (Splits (preimage M C a) cb.1)) ++
(P.filter fun D => decide (Splits (preimage M C a) D)).flatMap
(newSplitters W (preimage M C a))
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:111
Uses 14 · Used by 1
TheoremSplitter count bound along a Hopcroft execution#✓fully proved
Total state/letter charging along the actual execution. The bound includes initial tokens and tokens still pending through replacements. No amortized bound is assumed in the execution invariant.
theorem splitterCount_hopcroftSplitters_le (M : DFA α σ) (k : ℕ)
{P : List (Finset σ)} {W : List (Finset σ × α)} (hI : Inv M P W)
(q : σ) (b : α) {D : Finset σ} (hD : D ∈ P) (hqD : q ∈ D) :
splitterCount q b (hopcroftSplitters M k P W) ≤
splitterCount q b W + Nat.log 2 D.card
structure Inv (P : List (Finset σ)) (W : List (Finset σ × α)) : Prop where
/-- `P` is a partition into nonempty blocks. -/
part : IsPartition P
/-- Every waiting set is a block. -/
mem : ∀ cb ∈ W, cb.1 ∈ P
/-- Two states in one block whose `b`-successors agree on every set waiting with `b` have
`b`-successors in one block. -/
stable : ∀ b p q, Rel P p q → (∀ C, (C, b) ∈ W → (M.step p b ∈ C ↔ M.step q b ∈ C)) →
Rel P (M.step p b) (M.step q b)
/-- Blocks agree on acceptance. -/
accept : ∀ p q, Rel P p q → (p ∈ M.accept ↔ q ∈ M.accept)
/-- States with one continuation language share a block. -/
⋯
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:395
def splitterCount (q : σ) (b : α) (W : List (Finset σ × α)) : ℕ :=
W.countP fun cb => decide (cb.2 = b ∧ q ∈ cb.1)
CSLibExt/Computability/Languages/Congruences/HopcroftWorklist.lean:30
def hopcroftSplitters : ℕ → List (Finset σ) → List (Finset σ × α) → List (Finset σ × α)
| 0, _, _ => []
| _ + 1, _, [] => []
| k + 1, P, (C, a) :: W =>
(C, a) :: hopcroftSplitters k (roundP M P C a) (roundW M P W C a)
CSLibExt/Computability/Languages/Congruences/HopcroftCost.lean:168
Uses 8 · Used by 1
TheoremInitial splitter count is at most one#✓fully proved
Initially there is at most one token for each state and letter.
theorem splitterCount_initW_le (M : DFA α σ)
[DecidablePred (· ∈ M.accept)] (q : σ) (b : α) :
splitterCount q b (initW M) ≤ 1
def splitterCount (q : σ) (b : α) (W : List (Finset σ × α)) : ℕ :=
W.countP fun cb => decide (cb.2 = b ∧ q ∈ cb.1)
CSLibExt/Computability/Languages/Congruences/HopcroftWorklist.lean:30
def initW : List (Finset σ × α) :=
if (accBlock M).Nonempty ∧ (rejBlock M).Nonempty then
(FinEnum.toList α).map fun b => (smaller (accBlock M) (rejBlock M), b)
else []
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:128
Uses 5 · Used by 1
TheoremHopcroft visits each of \(n\) states at most \(\log_2 n + 1\) times per letter#✓fully proved
Each state is processed with a fixed letter at most log₂ n + 1
times during any prefix of the actual initialized Hopcroft execution.
theorem splitterCount_init_trace_le (M : DFA α σ)
[DecidablePred (· ∈ M.accept)] (k : ℕ) (q : σ) (b : α) :
splitterCount q b (hopcroftSplitters M k (initP M) (initW M)) ≤
Nat.log 2 (Fintype.card σ) + 1
def splitterCount (q : σ) (b : α) (W : List (Finset σ × α)) : ℕ :=
W.countP fun cb => decide (cb.2 = b ∧ q ∈ cb.1)
CSLibExt/Computability/Languages/Congruences/HopcroftWorklist.lean:30
def hopcroftSplitters : ℕ → List (Finset σ) → List (Finset σ × α) → List (Finset σ × α)
| 0, _, _ => []
| _ + 1, _, [] => []
| k + 1, P, (C, a) :: W =>
(C, a) :: hopcroftSplitters k (roundP M P C a) (roundW M P W C a)
CSLibExt/Computability/Languages/Congruences/HopcroftCost.lean:168
def initP : List (Finset σ) := [accBlock M, rejBlock M].filter (·.Nonempty)
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:124
def initW : List (Finset σ × α) :=
if (accBlock M).Nonempty ∧ (rejBlock M).Nonempty then
(FinEnum.toList α).map fun b => (smaller (accBlock M) (rejBlock M), b)
else []
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:128
Uses 9 · Used by 1
TheoremPopped Hopcroft splitters are nonempty#✓fully proved
Every popped splitter has a nonempty block, including splitters whose predecessor set is empty.
theorem nonempty_of_mem_hopcroftSplitters (M : DFA α σ) (k : ℕ)
{P : List (Finset σ)} {W : List (Finset σ × α)} (hI : Inv M P W)
{cb : Finset σ × α} (hcb : cb ∈ hopcroftSplitters M k P W) :
cb.1.Nonempty
structure Inv (P : List (Finset σ)) (W : List (Finset σ × α)) : Prop where
/-- `P` is a partition into nonempty blocks. -/
part : IsPartition P
/-- Every waiting set is a block. -/
mem : ∀ cb ∈ W, cb.1 ∈ P
/-- Two states in one block whose `b`-successors agree on every set waiting with `b` have
`b`-successors in one block. -/
stable : ∀ b p q, Rel P p q → (∀ C, (C, b) ∈ W → (M.step p b ∈ C ↔ M.step q b ∈ C)) →
Rel P (M.step p b) (M.step q b)
/-- Blocks agree on acceptance. -/
accept : ∀ p q, Rel P p q → (p ∈ M.accept ↔ q ∈ M.accept)
/-- States with one continuation language share a block. -/
⋯
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:395
def hopcroftSplitters : ℕ → List (Finset σ) → List (Finset σ × α) → List (Finset σ × α)
| 0, _, _ => []
| _ + 1, _, [] => []
| k + 1, P, (C, a) :: W =>
(C, a) :: hopcroftSplitters k (roundP M P C a) (roundW M P W C a)
CSLibExt/Computability/Languages/Congruences/HopcroftCost.lean:168
Uses 6 · Used by 1
TheoremHopcroft predecessor-work as splitter counts plus pops#✓fully proved
Double counting visits by source state and letter, retaining every incoming transition even when many sources have the same successor.
theorem hopcroftCost_eq_sum_splitterCount (M : DFA α σ)
(trace : List (Finset σ × α)) :
hopcroftCost M trace =
(∑ b : α, ∑ q : σ, splitterCount (M.step q b) b trace) +
trace.length
def hopcroftCost (trace : List (Finset σ × α)) : ℕ :=
(trace.map fun cb => (preimage M cb.1 cb.2).card + 1).sum
CSLibExt/Computability/Languages/Congruences/HopcroftCost.lean:181
def splitterCount (q : σ) (b : α) (W : List (Finset σ × α)) : ℕ :=
W.countP fun cb => decide (cb.2 = b ∧ q ∈ cb.1)
CSLibExt/Computability/Languages/Congruences/HopcroftWorklist.lean:30
Uses 3 · Used by 1
TheoremNumber of pops bounded by the total splitter count#✓fully proved
A nonempty popped block pays for its pop, even if its preimage is
empty. This bounds the +1 term independently of predecessor visits.
theorem length_le_sum_splitterCount (trace : List (Finset σ × α))
(hne : ∀ cb ∈ trace, cb.1.Nonempty) :
trace.length ≤ ∑ b : α, ∑ q : σ, splitterCount q b trace
def splitterCount (q : σ) (b : α) (W : List (Finset σ × α)) : ℕ :=
W.countP fun cb => decide (cb.2 = b ∧ q ∈ cb.1)
CSLibExt/Computability/Languages/Congruences/HopcroftWorklist.lean:30
Uses 1 · Used by 1
TheoremTotal predecessor-work bound#✓fully proved
Total predecessor-work bound, including one unit for every pop, for every prefix of the initialized execution. No runtime claim is made for the scanning implementation or for initialization.
theorem hopcroftCost_init_trace_le (M : DFA α σ)
[DecidablePred (· ∈ M.accept)] (k : ℕ) :
hopcroftCost M (hopcroftSplitters M k (initP M) (initW M)) ≤
2 * Fintype.card α * Fintype.card σ *
(Nat.log 2 (Fintype.card σ) + 1)
def hopcroftCost (trace : List (Finset σ × α)) : ℕ :=
(trace.map fun cb => (preimage M cb.1 cb.2).card + 1).sum
CSLibExt/Computability/Languages/Congruences/HopcroftCost.lean:181
def hopcroftSplitters : ℕ → List (Finset σ) → List (Finset σ × α) → List (Finset σ × α)
| 0, _, _ => []
| _ + 1, _, [] => []
| k + 1, P, (C, a) :: W =>
(C, a) :: hopcroftSplitters k (roundP M P C a) (roundW M P W C a)
CSLibExt/Computability/Languages/Congruences/HopcroftCost.lean:168
def initP : List (Finset σ) := [accBlock M, rejBlock M].filter (·.Nonempty)
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:124
def initW : List (Finset σ × α) :=
if (accBlock M).Nonempty ∧ (rejBlock M).Nonempty then
(FinEnum.toList α).map fun b => (smaller (accBlock M) (rejBlock M), b)
else []
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:128
Uses 10 · Used by 4
Theorem\(O(|\Sigma| n \log n)\) bound on Hopcroft predecessor-work#✓fully proved
A uniform O(|Σ| n log n) bound, with the explicit constant 4.
The condition n ≥ 2 is essential when absorbing the additive unit
into the logarithm. This concerns predecessor-work, not scan runtime.
theorem hopcroftCost_init_trace_le_log (M : DFA α σ)
[DecidablePred (· ∈ M.accept)] (k : ℕ)
(hn : 2 ≤ Fintype.card σ) :
hopcroftCost M (hopcroftSplitters M k (initP M) (initW M)) ≤
4 * Fintype.card α * Fintype.card σ *
Nat.log 2 (Fintype.card σ)
def hopcroftCost (trace : List (Finset σ × α)) : ℕ :=
(trace.map fun cb => (preimage M cb.1 cb.2).card + 1).sum
CSLibExt/Computability/Languages/Congruences/HopcroftCost.lean:181
def hopcroftSplitters : ℕ → List (Finset σ) → List (Finset σ × α) → List (Finset σ × α)
| 0, _, _ => []
| _ + 1, _, [] => []
| k + 1, P, (C, a) :: W =>
(C, a) :: hopcroftSplitters k (roundP M P C a) (roundW M P W C a)
CSLibExt/Computability/Languages/Congruences/HopcroftCost.lean:168
def initP : List (Finset σ) := [accBlock M, rejBlock M].filter (·.Nonempty)
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:124
def initW : List (Finset σ × α) :=
if (accBlock M).Nonempty ∧ (rejBlock M).Nonempty then
(FinEnum.toList α).map fun b => (smaller (accBlock M) (rejBlock M), b)
else []
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:128
TheoremPredecessor-work bound for the complete Hopcroft run#✓fully proved
The bound for the complete terminating minimization execution.
DFA.hopcroftLoop_waiting_eq_nil proves this fuel exhausts the queue;
hopcroftLoop_eq_hopcroftLoop_of_length ties the trace to its result.
theorem hopcroftCost_complete_le (M : DFA α σ)
[DecidablePred (· ∈ M.accept)] :
hopcroftCost M (hopcroftSplitters M
(Fintype.card α * (Fintype.card σ + 1)) (initP M) (initW M)) ≤
2 * Fintype.card α * Fintype.card σ *
(Nat.log 2 (Fintype.card σ) + 1)
def hopcroftCost (trace : List (Finset σ × α)) : ℕ :=
(trace.map fun cb => (preimage M cb.1 cb.2).card + 1).sum
CSLibExt/Computability/Languages/Congruences/HopcroftCost.lean:181
def hopcroftSplitters : ℕ → List (Finset σ) → List (Finset σ × α) → List (Finset σ × α)
| 0, _, _ => []
| _ + 1, _, [] => []
| k + 1, P, (C, a) :: W =>
(C, a) :: hopcroftSplitters k (roundP M P C a) (roundW M P W C a)
CSLibExt/Computability/Languages/Congruences/HopcroftCost.lean:168
def initP : List (Finset σ) := [accBlock M, rejBlock M].filter (·.Nonempty)
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:124
def initW : List (Finset σ × α) :=
if (accBlock M).Nonempty ∧ (rejBlock M).Nonempty then
(FinEnum.toList α).map fun b => (smaller (accBlock M) (rejBlock M), b)
else []
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:128
TheoremNo initial Hopcroft splitters with at most one state#✓fully proved
With at most one state, the accepting/rejecting split has an empty side, so the algorithm has no initial splitters.
theorem initW_eq_nil_of_card_le_one (M : DFA α σ)
[DecidablePred (· ∈ M.accept)] (hn : Fintype.card σ ≤ 1) :
initW M = []
def initW : List (Finset σ × α) :=
if (accBlock M).Nonempty ∧ (rejBlock M).Nonempty then
(FinEnum.toList α).map fun b => (smaller (accBlock M) (rejBlock M), b)
else []
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:128
Uses 4 · Used by 1
TheoremZero Hopcroft predecessor-work with at most one state#✓fully proved
Zero predecessor-work for zero/one-state boundary parameters. This does not assert that constructing the initial partition costs zero.
theorem hopcroftCost_eq_zero_of_card_le_one (M : DFA α σ)
[DecidablePred (· ∈ M.accept)] (k : ℕ) (hn : Fintype.card σ ≤ 1) :
hopcroftCost M (hopcroftSplitters M k (initP M) (initW M)) = 0
def hopcroftCost (trace : List (Finset σ × α)) : ℕ :=
(trace.map fun cb => (preimage M cb.1 cb.2).card + 1).sum
CSLibExt/Computability/Languages/Congruences/HopcroftCost.lean:181
def hopcroftSplitters : ℕ → List (Finset σ) → List (Finset σ × α) → List (Finset σ × α)
| 0, _, _ => []
| _ + 1, _, [] => []
| k + 1, P, (C, a) :: W =>
(C, a) :: hopcroftSplitters k (roundP M P C a) (roundW M P W C a)
CSLibExt/Computability/Languages/Congruences/HopcroftCost.lean:168
def initP : List (Finset σ) := [accBlock M, rejBlock M].filter (·.Nonempty)
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:124
def initW : List (Finset σ × α) :=
if (accBlock M).Nonempty ∧ (rejBlock M).Nonempty then
(FinEnum.toList α).map fun b => (smaller (accBlock M) (rejBlock M), b)
else []
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:128
TheoremZero Hopcroft predecessor-work over an empty alphabet#✓fully proved
An empty alphabet causes no predecessor-work, for any state count.
theorem hopcroftCost_eq_zero_of_alphabet_card_eq_zero (M : DFA α σ)
[DecidablePred (· ∈ M.accept)] (k : ℕ) (ha : Fintype.card α = 0) :
hopcroftCost M (hopcroftSplitters M k (initP M) (initW M)) = 0
def hopcroftCost (trace : List (Finset σ × α)) : ℕ :=
(trace.map fun cb => (preimage M cb.1 cb.2).card + 1).sum
CSLibExt/Computability/Languages/Congruences/HopcroftCost.lean:181
def hopcroftSplitters : ℕ → List (Finset σ) → List (Finset σ × α) → List (Finset σ × α)
| 0, _, _ => []
| _ + 1, _, [] => []
| k + 1, P, (C, a) :: W =>
(C, a) :: hopcroftSplitters k (roundP M P C a) (roundW M P W C a)
CSLibExt/Computability/Languages/Congruences/HopcroftCost.lean:168
def initP : List (Finset σ) := [accBlock M, rejBlock M].filter (·.Nonempty)
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:124
def initW : List (Finset σ × α) :=
if (accBlock M).Nonempty ∧ (rejBlock M).Nonempty then
(FinEnum.toList α).map fun b => (smaller (accBlock M) (rejBlock M), b)
else []
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:128
DefinitionHopcroft loop with recorded predecessor-work#✓fully proved
The actual loop with predecessor-visit and pop events recorded in
CSLib's TimeM. Pure scans are not charged in this event model.
def hopcroftLoopWork (M : DFA α σ) : ℕ → List (Finset σ) →
List (Finset σ × α) →
TimeM ℕ (List (Finset σ) × List (Finset σ × α))
| 0, P, W => pure (P, W)
| _ + 1, P, [] => pure (P, [])
| k + 1, P, (C, a) :: W => do
TimeM.tick ((preimage M C a).card + 1)
hopcroftLoopWork M k (roundP M P C a) (roundW M P W C a)
def preimage (C : Finset σ) (a : α) : Finset σ :=
Finset.univ.filter fun q => M.step q a ∈ C
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:75
def roundP (P : List (Finset σ)) (C : Finset σ) (a : α) : List (Finset σ) :=
P.flatMap (splitBlock (preimage M C a))
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:106
def roundW (P : List (Finset σ)) (W : List (Finset σ × α)) (C : Finset σ) (a : α) :
List (Finset σ × α) :=
W.filter (fun cb => !decide (Splits (preimage M C a) cb.1)) ++
(P.filter fun D => decide (Splits (preimage M C a) D)).flatMap
(newSplitters W (preimage M C a))
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:111
Uses 3 · Used by 4
TheoremInstrumented Hopcroft loop returns the original result#✓fully proved
Instrumentation preserves the exact loop result.
@[simp]
theorem hopcroftLoopWork_ret (M : DFA α σ) (k : ℕ)
(P : List (Finset σ)) (W : List (Finset σ × α)) :
(hopcroftLoopWork M k P W).ret = M.hopcroftLoop k P W
def hopcroftLoopWork (M : DFA α σ) : ℕ → List (Finset σ) →
List (Finset σ × α) →
TimeM ℕ (List (Finset σ) × List (Finset σ × α))
| 0, P, W => pure (P, W)
| _ + 1, P, [] => pure (P, [])
| k + 1, P, (C, a) :: W => do
TimeM.tick ((preimage M C a).card + 1)
hopcroftLoopWork M k (roundP M P C a) (roundW M P W C a)
CSLibExt/Computability/Languages/Congruences/HopcroftWorkTimeM.lean:40
Uses 4 · Used by 1
TheoremRecorded Hopcroft cost equals the work of the popped trace#✓fully proved
The recorded cost is exactly the work of the actual popped trace.
@[simp]
theorem hopcroftLoopWork_time (M : DFA α σ) (k : ℕ)
(P : List (Finset σ)) (W : List (Finset σ × α)) :
(hopcroftLoopWork M k P W).time =
hopcroftCost M (hopcroftSplitters M k P W)
def hopcroftLoopWork (M : DFA α σ) : ℕ → List (Finset σ) →
List (Finset σ × α) →
TimeM ℕ (List (Finset σ) × List (Finset σ × α))
| 0, P, W => pure (P, W)
| _ + 1, P, [] => pure (P, [])
| k + 1, P, (C, a) :: W => do
TimeM.tick ((preimage M C a).card + 1)
hopcroftLoopWork M k (roundP M P C a) (roundW M P W C a)
CSLibExt/Computability/Languages/Congruences/HopcroftWorkTimeM.lean:40
def hopcroftCost (trace : List (Finset σ × α)) : ℕ :=
(trace.map fun cb => (preimage M cb.1 cb.2).card + 1).sum
CSLibExt/Computability/Languages/Congruences/HopcroftCost.lean:181
def hopcroftSplitters : ℕ → List (Finset σ) → List (Finset σ × α) → List (Finset σ × α)
| 0, _, _ => []
| _ + 1, _, [] => []
| k + 1, P, (C, a) :: W =>
(C, a) :: hopcroftSplitters k (roundP M P C a) (roundW M P W C a)
CSLibExt/Computability/Languages/Congruences/HopcroftCost.lean:168
TheoremRecorded Hopcroft cost obeys the predecessor-work bound#✓fully proved
The canonical cost monad records the proved total predecessor-work bound for every initialized execution prefix.
theorem hopcroftLoopWork_init_le [Fintype α] (M : DFA α σ)
[DecidablePred (· ∈ M.accept)] (k : ℕ) :
(hopcroftLoopWork M k (initP M) (initW M)).time ≤
2 * Fintype.card α * Fintype.card σ *
(Nat.log 2 (Fintype.card σ) + 1)
def hopcroftLoopWork (M : DFA α σ) : ℕ → List (Finset σ) →
List (Finset σ × α) →
TimeM ℕ (List (Finset σ) × List (Finset σ × α))
| 0, P, W => pure (P, W)
| _ + 1, P, [] => pure (P, [])
| k + 1, P, (C, a) :: W => do
TimeM.tick ((preimage M C a).card + 1)
hopcroftLoopWork M k (roundP M P C a) (roundW M P W C a)
CSLibExt/Computability/Languages/Congruences/HopcroftWorkTimeM.lean:40
def initP : List (Finset σ) := [accBlock M, rejBlock M].filter (·.Nonempty)
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:124
def initW : List (Finset σ × α) :=
if (accBlock M).Nonempty ∧ (rejBlock M).Nonempty then
(FinEnum.toList α).map fun b => (smaller (accBlock M) (rejBlock M), b)
else []
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:128
Uses 7
TheoremInstrumented complete Hopcroft run returns the minimization partition#✓fully proved
At the proved terminating fuel, instrumentation returns the original correct minimization partition and an empty waiting list.
theorem hopcroftLoopWork_complete_ret [Fintype α] (M : DFA α σ)
[DecidablePred (· ∈ M.accept)] :
(hopcroftLoopWork M (Fintype.card α * (Fintype.card σ + 1))
(initP M) (initW M)).ret = (M.hopcroftPartition, [])
def hopcroftLoopWork (M : DFA α σ) : ℕ → List (Finset σ) →
List (Finset σ × α) →
TimeM ℕ (List (Finset σ) × List (Finset σ × α))
| 0, P, W => pure (P, W)
| _ + 1, P, [] => pure (P, [])
| k + 1, P, (C, a) :: W => do
TimeM.tick ((preimage M C a).card + 1)
hopcroftLoopWork M k (roundP M P C a) (roundW M P W C a)
CSLibExt/Computability/Languages/Congruences/HopcroftWorkTimeM.lean:40
def initP : List (Finset σ) := [accBlock M, rejBlock M].filter (·.Nonempty)
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:124
def initW : List (Finset σ × α) :=
if (accBlock M).Nonempty ∧ (rejBlock M).Nonempty then
(FinEnum.toList α).map fun b => (smaller (accBlock M) (rejBlock M), b)
else []
CSLibExt/Computability/Languages/Congruences/Hopcroft.lean:128
Loading dependency explorer…
The dependency explorer needs JavaScript and its graph data.