Mercurial > pub > ImplabNet
comparison Implab/Automaton/DFATable.cs @ 171:0f70905b4652 ref20160224
Working on regular DFA
| author | cin |
|---|---|
| date | Thu, 10 Mar 2016 01:19:33 +0300 |
| parents | 54270c2f29f2 |
| children | 92d5278d1b10 |
comparison
equal
deleted
inserted
replaced
| 170:181119ef3b39 | 171:0f70905b4652 |
|---|---|
| 3 using System.Collections.Generic; | 3 using System.Collections.Generic; |
| 4 using System.Linq; | 4 using System.Linq; |
| 5 | 5 |
| 6 namespace Implab.Automaton { | 6 namespace Implab.Automaton { |
| 7 public class DFATable : IDFATableBuilder { | 7 public class DFATable : IDFATableBuilder { |
| 8 DFAStateDescriptior[] m_dfaTable; | |
| 9 | |
| 10 int m_stateCount; | 8 int m_stateCount; |
| 11 int m_symbolCount; | 9 int m_symbolCount; |
| 12 int m_initialState; | 10 int m_initialState; |
| 13 | 11 |
| 14 readonly HashSet<int> m_finalStates = new HashSet<int>(); | 12 readonly HashSet<int> m_finalStates = new HashSet<int>(); |
| 15 readonly HashSet<AutomatonTransition> m_transitions = new HashSet<AutomatonTransition>(); | 13 readonly HashSet<AutomatonTransition> m_transitions = new HashSet<AutomatonTransition>(); |
| 16 | 14 |
| 17 void AssertNotReadOnly() { | |
| 18 if (m_dfaTable != null) | |
| 19 throw new InvalidOperationException("The object is readonly"); | |
| 20 } | |
| 21 | |
| 22 | 15 |
| 23 #region IDFADefinition implementation | 16 #region IDFADefinition implementation |
| 24 | |
| 25 public DFAStateDescriptior[] GetTransitionTable() { | |
| 26 if (m_dfaTable == null) { | |
| 27 if (m_stateCount <= 0) | |
| 28 throw new InvalidOperationException("Invalid automaton definition: states count = {0}", m_stateCount); | |
| 29 if (m_symbolCount <= 0) | |
| 30 throw new InvalidOperationException("Invalid automaton definition: symbols count = {0}", m_symbolCount); | |
| 31 | |
| 32 m_dfaTable = ConstructTransitionTable(); | |
| 33 } | |
| 34 return m_dfaTable; | |
| 35 } | |
| 36 | 17 |
| 37 public bool IsFinalState(int s) { | 18 public bool IsFinalState(int s) { |
| 38 Safe.ArgumentInRange(s, 0, m_stateCount, "s"); | 19 Safe.ArgumentInRange(s, 0, m_stateCount, "s"); |
| 39 | 20 |
| 40 return m_dfaTable != null ? m_dfaTable[s].final : m_finalStates.Contains(s); | 21 return m_finalStates.Contains(s); |
| 41 } | 22 } |
| 42 | 23 |
| 43 public IEnumerable<int> FinalStates { | 24 public IEnumerable<int> FinalStates { |
| 44 get { | 25 get { |
| 45 return m_finalStates; | 26 return m_finalStates; |
| 58 get { return m_initialState; } | 39 get { return m_initialState; } |
| 59 } | 40 } |
| 60 | 41 |
| 61 #endregion | 42 #endregion |
| 62 | 43 |
| 63 protected virtual DFAStateDescriptior[] ConstructTransitionTable() { | |
| 64 var dfaTable = new DFAStateDescriptior[m_stateCount]; | |
| 65 | |
| 66 | |
| 67 foreach (var t in m_transitions) { | |
| 68 if (dfaTable[t.s1].transitions == null) | |
| 69 dfaTable[t.s1] = new DFAStateDescriptior(m_symbolCount, m_finalStates.Contains(t.s1)); | |
| 70 | |
| 71 dfaTable[t.s1].transitions[t.edge] = t.s2; | |
| 72 } | |
| 73 | |
| 74 foreach (var s in m_finalStates) | |
| 75 if (!dfaTable[s].final) | |
| 76 m_dfaTable[s] = new DFAStateDescriptior(m_symbolCount, true); | |
| 77 | |
| 78 } | |
| 79 | |
| 80 public void SetInitialState(int s) { | 44 public void SetInitialState(int s) { |
| 81 Safe.ArgumentAssert(s >= 0, "s"); | 45 Safe.ArgumentAssert(s >= 0, "s"); |
| 82 m_initialState = s; | 46 m_initialState = s; |
| 83 } | 47 } |
| 84 | 48 |
| 85 public void MarkFinalState(int state) { | 49 public void MarkFinalState(int state) { |
| 86 AssertNotReadOnly(); | |
| 87 m_finalStates.Add(state); | 50 m_finalStates.Add(state); |
| 88 } | 51 } |
| 89 | 52 |
| 90 public void Add(AutomatonTransition item) { | 53 public void Add(AutomatonTransition item) { |
| 91 AssertNotReadOnly(); | |
| 92 Safe.ArgumentAssert(item.s1 >= 0, "item"); | 54 Safe.ArgumentAssert(item.s1 >= 0, "item"); |
| 93 Safe.ArgumentAssert(item.s2 >= 0, "item"); | 55 Safe.ArgumentAssert(item.s2 >= 0, "item"); |
| 94 Safe.ArgumentAssert(item.edge >= 0, "item"); | 56 Safe.ArgumentAssert(item.edge >= 0, "item"); |
| 95 | 57 |
| 96 m_stateCount = Math.Max(m_stateCount, Math.Max(item.s1, item.s2) + 1); | 58 m_stateCount = Math.Max(m_stateCount, Math.Max(item.s1, item.s2) + 1); |
| 98 | 60 |
| 99 m_transitions.Add(item); | 61 m_transitions.Add(item); |
| 100 } | 62 } |
| 101 | 63 |
| 102 public void Clear() { | 64 public void Clear() { |
| 103 AssertNotReadOnly(); | |
| 104 | |
| 105 m_stateCount = 0; | 65 m_stateCount = 0; |
| 106 m_symbolCount = 0; | 66 m_symbolCount = 0; |
| 107 m_finalStates.Clear(); | 67 m_finalStates.Clear(); |
| 108 m_transitions.Clear(); | 68 m_transitions.Clear(); |
| 109 } | 69 } |
| 115 public void CopyTo(AutomatonTransition[] array, int arrayIndex) { | 75 public void CopyTo(AutomatonTransition[] array, int arrayIndex) { |
| 116 m_transitions.CopyTo(array, arrayIndex); | 76 m_transitions.CopyTo(array, arrayIndex); |
| 117 } | 77 } |
| 118 | 78 |
| 119 public bool Remove(AutomatonTransition item) { | 79 public bool Remove(AutomatonTransition item) { |
| 120 AssertNotReadOnly(); | |
| 121 m_transitions.Remove(item); | 80 m_transitions.Remove(item); |
| 122 } | 81 } |
| 123 | 82 |
| 124 public int Count { | 83 public int Count { |
| 125 get { | 84 get { |
| 127 } | 86 } |
| 128 } | 87 } |
| 129 | 88 |
| 130 public bool IsReadOnly { | 89 public bool IsReadOnly { |
| 131 get { | 90 get { |
| 132 return m_dfaTable != null; | 91 return false; |
| 133 } | 92 } |
| 134 } | 93 } |
| 135 | 94 |
| 136 public IEnumerator<AutomatonTransition> GetEnumerator() { | 95 public IEnumerator<AutomatonTransition> GetEnumerator() { |
| 137 return m_transitions.GetEnumerator(); | 96 return m_transitions.GetEnumerator(); |
| 151 /// <returns>The final states.</returns> | 110 /// <returns>The final states.</returns> |
| 152 protected virtual IEnumerable<HashSet<int>> GroupFinalStates() { | 111 protected virtual IEnumerable<HashSet<int>> GroupFinalStates() { |
| 153 return new HashSet<int>[] { m_finalStates }; | 112 return new HashSet<int>[] { m_finalStates }; |
| 154 } | 113 } |
| 155 | 114 |
| 156 protected void Optimize<TInput, TState>( | 115 protected void Optimize( |
| 157 IDFATableBuilder optimalDFA, | 116 IDFATableBuilder optimalDFA, |
| 158 IAlphabet<TInput> inputAlphabet, | 117 IDictionary<int,int> alphabetMap, |
| 159 IAlphabetBuilder<TInput> optimalInputAlphabet, | 118 IDictionary<int,int> stateMap |
| 160 IAlphabet<TState> stateAlphabet, | |
| 161 IAlphabetBuilder<TState> optimalStateAlphabet | |
| 162 ) { | 119 ) { |
| 163 Safe.ArgumentNotNull(optimalDFA, "dfa"); | 120 Safe.ArgumentNotNull(optimalDFA, "dfa"); |
| 164 Safe.ArgumentNotNull(optimalInputAlphabet, "optimalInputAlphabet"); | 121 Safe.ArgumentNotNull(alphabetMap, "alphabetMap"); |
| 165 Safe.ArgumentNotNull(optimalStateAlphabet, "optimalStateAlphabet"); | 122 Safe.ArgumentNotNull(stateMap, "stateMap"); |
| 166 Safe.ArgumentNotNull(inputAlphabet, "inputAlphabet"); | 123 |
| 167 Safe.ArgumentNotNull(stateAlphabet, "stateAlphabet"); | |
| 168 | |
| 169 if (inputAlphabet.Count != m_symbolCount) | |
| 170 throw new InvalidOperationException("The input symbols aphabet mismatch"); | |
| 171 if (stateAlphabet.Count != m_stateCount) | |
| 172 throw new InvalidOperationException("The states alphabet mismatch"); | |
| 173 | 124 |
| 174 var setComparer = new CustomEqualityComparer<HashSet<int>>( | 125 var setComparer = new CustomEqualityComparer<HashSet<int>>( |
| 175 (x, y) => x.SetEquals(y), | 126 (x, y) => x.SetEquals(y), |
| 176 s => s.Sum(x => x.GetHashCode()) | 127 s => s.Sum(x => x.GetHashCode()) |
| 177 ); | 128 ); |
| 232 } | 183 } |
| 233 } | 184 } |
| 234 } | 185 } |
| 235 | 186 |
| 236 // карта получения оптимального состояния по соотвествующему ему простому состоянию | 187 // карта получения оптимального состояния по соотвествующему ему простому состоянию |
| 237 var statesMap = stateAlphabet.Reclassify(optimalStateAlphabet, optimalStates); | 188 var nextState = 0; |
| 189 foreach (var item in optimalStates) { | |
| 190 var id = nextState++; | |
| 191 foreach (var s in item) | |
| 192 stateMap[s] = id; | |
| 193 } | |
| 238 | 194 |
| 239 // получаем минимальный алфавит | 195 // получаем минимальный алфавит |
| 240 // входные символы не различимы, если Move(s,a1) == Move(s,a2) | 196 // входные символы не различимы, если Move(s,a1) == Move(s,a2), для любого s |
| 241 var optimalAlphabet = m_transitions | 197 // для этого используем алгоритм кластеризации, сначала |
| 242 .GroupBy(t => Tuple.Create(statesMap[t.s1], statesMap[t.s2]), t => t.edge); | 198 // считаем, что все символы не различимы |
| 243 | 199 |
| 244 var alphabetMap = inputAlphabet.Reclassify(optimalInputAlphabet, optimalAlphabet); | 200 var minClasses = new HashSet<HashSet<int>>(setComparer); |
| 201 var alphaQueue = new Queue<HashSet<int>>(); | |
| 202 alphaQueue.Enqueue(new HashSet<int>(Enumerable.Range(0,AlphabetSize))); | |
| 203 | |
| 204 // для всех состояний, будем проверять каждый класс на различимость, | |
| 205 // т.е. символы различимы, если они приводят к разным состояниям | |
| 206 for (int s = 0 ; s < optimalStates.Count; s++) { | |
| 207 var newQueue = new Queue<HashSet<int>>(); | |
| 208 | |
| 209 foreach (var A in alphaQueue) { | |
| 210 // классы из одного символа делить бесполезно, переводим их сразу в | |
| 211 // результирующий алфавит | |
| 212 if (A.Count == 1) { | |
| 213 minClasses.Add(A); | |
| 214 continue; | |
| 215 } | |
| 216 | |
| 217 // различаем классы символов, которые переводят в различные оптимальные состояния | |
| 218 // optimalState -> alphaClass | |
| 219 var classes = new Dictionary<int, HashSet<int>>(); | |
| 220 | |
| 221 foreach (var term in A) { | |
| 222 // ищем все переходы класса по символу term | |
| 223 var res = m_transitions.Where(t => stateMap[t.s1] == s && t.edge == term).Select(t => stateMap[t.s2]).ToArray(); | |
| 224 | |
| 225 var s2 = res.Length > 0 ? res[0] : -1; | |
| 226 | |
| 227 HashSet<int> a2; | |
| 228 if (!classes.TryGetValue(s2, out a2)) { | |
| 229 a2 = new HashSet<int>(); | |
| 230 newQueue.Enqueue(a2); | |
| 231 classes[s2] = a2; | |
| 232 } | |
| 233 a2.Add(term); | |
| 234 } | |
| 235 } | |
| 236 | |
| 237 if (newQueue.Count == 0) | |
| 238 break; | |
| 239 alphaQueue = newQueue; | |
| 240 } | |
| 241 | |
| 242 // после окончания работы алгоритма в очереди останутся минимальные различимые классы | |
| 243 // входных символов | |
| 244 foreach (var A in alphaQueue) | |
| 245 minClasses.Add(A); | |
| 246 | |
| 247 // построение отображения алфавитов входных символов. | |
| 248 // поскольку символ DFAConst.UNCLASSIFIED_INPUT может иметь | |
| 249 // специальное значение, тогда сохраним минимальный класс, | |
| 250 // содержащий этот символ на томже месте. | |
| 251 | |
| 252 var nextCls = 0; | |
| 253 foreach (var item in minClasses) { | |
| 254 if (nextCls == DFAConst.UNCLASSIFIED_INPUT) | |
| 255 nextCls++; | |
| 256 | |
| 257 // сохраняем DFAConst.UNCLASSIFIED_INPUT | |
| 258 var cls = item.Contains(DFAConst.UNCLASSIFIED_INPUT) ? DFAConst.UNCLASSIFIED_INPUT : nextCls; | |
| 259 | |
| 260 foreach (var a in item) | |
| 261 alphabetMap[a] = cls; | |
| 262 | |
| 263 nextCls++; | |
| 264 } | |
| 245 | 265 |
| 246 // построение автомата | 266 // построение автомата |
| 247 optimalDFA.SetInitialState(statesMap[m_initialState]); | 267 optimalDFA.SetInitialState(stateMap[m_initialState]); |
| 248 | 268 |
| 249 foreach (var sf in m_finalStates.GroupBy(s => statesMap[s])) | 269 foreach (var sf in m_finalStates.Select(s => stateMap[s]).Distinct()) |
| 250 optimalDFA.MarkFinalState(sf.Key); | 270 optimalDFA.MarkFinalState(sf); |
| 251 | 271 |
| 252 foreach (var t in m_transitions.Select(t => new AutomatonTransition(statesMap[t.s1],statesMap[t.s2],alphabetMap[t.edge])).Distinct()) | 272 foreach (var t in m_transitions.Select(t => new AutomatonTransition(stateMap[t.s1],stateMap[t.s2],alphabetMap[t.edge])).Distinct()) |
| 253 optimalDFA.Add(t); | 273 optimalDFA.Add(t); |
| 254 | |
| 255 } | 274 } |
| 256 | 275 |
| 257 protected void PrintDFA<TInput, TState>(IAlphabet<TInput> inputAlphabet, IAlphabet<TState> stateAlphabet) { | 276 protected void PrintDFA<TInput, TState>(IAlphabet<TInput> inputAlphabet, IAlphabet<TState> stateAlphabet) { |
| 258 Safe.ArgumentNotNull(inputAlphabet, "inputAlphabet"); | 277 Safe.ArgumentNotNull(inputAlphabet, "inputAlphabet"); |
| 259 Safe.ArgumentNotNull(stateAlphabet, "stateAlphabet"); | 278 Safe.ArgumentNotNull(stateAlphabet, "stateAlphabet"); |
| 260 | 279 |
| 261 var inputMap = inputAlphabet.CreateReverseMap(); | |
| 262 var stateMap = stateAlphabet.CreateReverseMap(); | |
| 263 | |
| 264 for (int i = 0; i < inputMap.Length; i++) | |
| 265 Console.WriteLine("C{0}: {1}", i, String.Join(",", inputMap[i])); | |
| 266 | |
| 267 | |
| 268 foreach(var t in m_transitions) | 280 foreach(var t in m_transitions) |
| 269 Console.WriteLine( | 281 Console.WriteLine( |
| 270 "[{0}] -{{{1}}}-> [{2}]{3}", | 282 "[{0}] -{{{1}}}-> [{2}]{3}", |
| 271 stateMap[t.s1], | 283 String.Join(",", stateAlphabet.GetSymbols(t.s1)), |
| 272 String.Join(",", inputMap[t.edge]), | 284 String.Join("", inputAlphabet.GetSymbols(t.edge)), |
| 273 stateMap[t.s2], | 285 String.Join(",", stateAlphabet.GetSymbols(t.s2)), |
| 274 m_finalStates.Contains(t.s2) ? "$" : "" | 286 m_finalStates.Contains(t.s2) ? "$" : "" |
| 275 ); | 287 ); |
| 276 | |
| 277 } | 288 } |
| 278 | 289 |
| 279 } | 290 } |
| 280 } | 291 } |
