@@ -16,24 +16,53 @@ import Data.IntMap (IntMap)
1616import qualified Data.IntMap as IM
1717import qualified Data.List as List
1818
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+ --
2225-- P := {{all accepting states}, {all nonaccepting states}};
2326-- Q := {{all accepting states}};
2427-- while (Q is not empty) do
2528-- choose and remove a set A from Q
2629-- for each c in ∑ do
2730-- 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
3033-- if Y is in Q
3134-- replace Y in Q by the same two sets
3235-- else
3336-- add the smaller of the two sets to Q
3437-- end;
3538-- end;
3639-- 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.
3766
3867minimizeDFA :: forall a . Ord a => DFA Int a -> DFA Int a
3968minimizeDFA dfa@ (DFA { dfa_start_states = starts,
@@ -93,7 +122,7 @@ type EquivalenceClass = IntSet
93122
94123groupEquivStates :: forall a . Ord a => DFA Int a -> [EquivalenceClass ]
95124groupEquivStates DFA { dfa_states = statemap }
96- = go init_p init_q
125+ = go init_r init_q
97126 where
98127 accepting , nonaccepting :: Map Int (State Int a )
99128 (accepting, nonaccepting) = Map. partition acc statemap
@@ -112,57 +141,61 @@ groupEquivStates DFA { dfa_states = statemap }
112141 accept_groups :: [EquivalenceClass ]
113142 accept_groups = map IS. fromList (Map. elems accept_map)
114143
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]
119148 init_q = accept_groups
120149
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
123152 -- 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) )
128157 | (from, state) <- Map. toList statemap,
129158 (i,to) <- IM. toList (state_out state) ]
130159
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 [] []
148190 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