@@ -16,24 +16,53 @@ import Data.IntMap (IntMap)
16
16
import qualified Data.IntMap as IM
17
17
import qualified Data.List as List
18
18
19
-
20
- -- Hopcroft's Algorithm for DFA minimization (cut/pasted from Wikipedia):
21
-
19
+ -- % Hopcroft's Algorithm for DFA minimization (cut/pasted from Wikipedia):
20
+ -- % X refines Y into Y1 and Y2 means
21
+ -- % Y1 := Y ∩ X
22
+ -- % Y2 := Y \ X
23
+ -- % where both Y1 and Y2 are nonempty
24
+ --
22
25
-- P := {{all accepting states}, {all nonaccepting states}};
23
26
-- Q := {{all accepting states}};
24
27
-- while (Q is not empty) do
25
28
-- choose and remove a set A from Q
26
29
-- for each c in ∑ do
27
30
-- let X be the set of states for which a transition on c leads to a state in A
28
- -- for each set Y in P for which X ∩ Y is nonempty do
29
- -- replace Y in P by the two sets X ∩ Y and Y \ X
31
+ -- for each set Y in P for which X refines Y into Y1 and Y2 do
32
+ -- replace Y in P by the two sets Y1 and Y2
30
33
-- if Y is in Q
31
34
-- replace Y in Q by the same two sets
32
35
-- else
33
36
-- add the smaller of the two sets to Q
34
37
-- end;
35
38
-- end;
36
39
-- end;
40
+ --
41
+ -- % X is a preimage of A under transition function.
42
+
43
+ -- % observation : Q is always subset of P
44
+ -- % let R = P \ Q. then following algorithm is the equivalent of the Hopcroft's Algorithm
45
+ --
46
+ -- R := {{all nonaccepting states}};
47
+ -- Q := {{all accepting states}};
48
+ -- while (Q is not empty) do
49
+ -- choose a set A from Q
50
+ -- remove A from Q and add it to R
51
+ -- for each c in ∑ do
52
+ -- let X be the set of states for which a transition on c leads to a state in A
53
+ -- for each set Y in R for which X refines Y into Y1 and Y2 do
54
+ -- replace Y in R by the greater of the two sets Y1 and Y2
55
+ -- add the smaller of the two sets to Q
56
+ -- end;
57
+ -- for each set Y in Q for which X refines Y into Y1 and Y2 do
58
+ -- replace Y in Q by the two sets Y1 and Y2
59
+ -- end;
60
+ -- end;
61
+ -- end;
62
+ --
63
+ -- % The second for loop that iterates over R mutates Q,
64
+ -- % but it does not affect the third for loop that iterates over Q.
65
+ -- % Because once X refines Y into Y1 and Y2, Y1 and Y2 can't be more refined by X.
37
66
38
67
minimizeDFA :: forall a . Ord a => DFA Int a -> DFA Int a
39
68
minimizeDFA dfa@ (DFA { dfa_start_states = starts,
@@ -93,7 +122,7 @@ type EquivalenceClass = IntSet
93
122
94
123
groupEquivStates :: forall a . Ord a => DFA Int a -> [EquivalenceClass ]
95
124
groupEquivStates DFA { dfa_states = statemap }
96
- = go init_p init_q
125
+ = go init_r init_q
97
126
where
98
127
accepting , nonaccepting :: Map Int (State Int a )
99
128
(accepting, nonaccepting) = Map. partition acc statemap
@@ -112,57 +141,61 @@ groupEquivStates DFA { dfa_states = statemap }
112
141
accept_groups :: [EquivalenceClass ]
113
142
accept_groups = map IS. fromList (Map. elems accept_map)
114
143
115
- init_p , init_q :: [EquivalenceClass ]
116
- init_p -- Issue #71: each EquivalenceClass needs to be a non-empty set
117
- | IS. null nonaccepting_states = accept_groups
118
- | otherwise = nonaccepting_states : accept_groups
144
+ init_r , init_q :: [EquivalenceClass ]
145
+ init_r -- Issue #71: each EquivalenceClass needs to be a non-empty set
146
+ | IS. null nonaccepting_states = []
147
+ | otherwise = [ nonaccepting_states]
119
148
init_q = accept_groups
120
149
121
- -- map token T to
122
- -- a map from state S to the list of states that transition to
150
+ -- a map from token T to
151
+ -- a map from state S to the set of states that transition to
123
152
-- S on token T
124
- -- This is a cache of the information needed to compute x below
125
- bigmap :: IntMap (IntMap [ SNum ] )
126
- bigmap = IM. fromListWith (IM. unionWith (++) )
127
- [ (i, IM. singleton to [ from] )
153
+ -- This is a cache of the information needed to compute xs below
154
+ bigmap :: IntMap (IntMap EquivalenceClass )
155
+ bigmap = IM. fromListWith (IM. unionWith IS. union )
156
+ [ (i, IM. singleton to ( IS. singleton from) )
128
157
| (from, state) <- Map. toList statemap,
129
158
(i,to) <- IM. toList (state_out state) ]
130
159
131
- -- incoming I A = the set of states that transition to a state in
132
- -- A on token I.
133
- incoming :: Int -> IntSet -> IntSet
134
- incoming i a = IS. fromList (concat ss)
135
- where
136
- map1 = IM. findWithDefault IM. empty i bigmap
137
- ss = [ IM. findWithDefault [] s map1
138
- | s <- IS. toList a ]
139
-
140
- -- The outer loop: recurse on each set in Q
141
- go :: [EquivalenceClass ] -> [EquivalenceClass ] -> [EquivalenceClass ]
142
- go p [] = p
143
- go p (a: q) = go1 0 p q
144
- where
145
- -- recurse on each token (0..255)
146
- go1 256 p q = go p q
147
- go1 i p q = go1 (i+ 1 ) p' q'
160
+ -- The outer loop: recurse on each set in R and Q
161
+ go :: [EquivalenceClass ] -> [EquivalenceClass ] -> [EquivalenceClass ]
162
+ go r [] = r
163
+ go r (a: q) = uncurry go $ List. foldl' go0 (a: r,q) xs
164
+ where
165
+ xs :: [EquivalenceClass ]
166
+ xs =
167
+ [ x
168
+ | preimageMap <- IM. elems bigmap
169
+ #if MIN_VERSION_containers(0, 6, 0)
170
+ , let x = IS. unions (IM. restrictKeys preimageMap a)
171
+ #else
172
+ , let x = IS. unions [IM. findWithDefault IS. empty s preimageMap | s <- IS. toList a]
173
+ #endif
174
+ , not (IS. null x)
175
+ ]
176
+
177
+ refineWith
178
+ :: IntSet -- preimage set that bisects the input equivalence class
179
+ -> IntSet -- initial equivalence class
180
+ -> Maybe (IntSet , IntSet )
181
+ refineWith x y =
182
+ if IS. null y1 || IS. null y2
183
+ then Nothing
184
+ else Just (y1, y2)
185
+ where
186
+ y1 = IS. intersection y x
187
+ y2 = IS. difference y x
188
+
189
+ go0 (r,q) x = go1 r [] []
148
190
where
149
- (p',q') = go2 p [] q
150
-
151
- x = incoming i a
152
-
153
- -- recurse on each set in P
154
- go2 [] p' q = (p',q)
155
- go2 (y: p) p' q
156
- | IS. null i || IS. null d = go2 p (y: p') q
157
- | otherwise = go2 p (i: d: p') q1
158
- where
159
- i = IS. intersection x y
160
- d = IS. difference y x
161
-
162
- q1 = replaceyin q
163
- where
164
- replaceyin [] =
165
- if IS. size i < IS. size d then [i] else [d]
166
- replaceyin (z: zs)
167
- | z == y = i : d : zs
168
- | otherwise = z : replaceyin zs
191
+ go1 [] r' q' = (r', go2 q q')
192
+ go1 (y: r) r' q' = case refineWith x y of
193
+ Nothing -> go1 r (y: r') q'
194
+ Just (y1, y2)
195
+ | IS. size y1 <= IS. size y2 -> go1 r (y2: r') (y1: q')
196
+ | otherwise -> go1 r (y1: r') (y2: q')
197
+
198
+ go2 [] q' = q'
199
+ go2 (y: q) q' = case refineWith x y of
200
+ Nothing -> go2 q (y: q')
201
+ Just (y1, y2) -> go2 q (y1: y2: q')
0 commit comments