Mercurial > pub > ImplabNet
diff Implab/Automaton/DFATable.cs @ 176:0c3c69fe225b ref20160224
rewritten the text scanner
author | cin |
---|---|
date | Tue, 22 Mar 2016 18:58:40 +0300 |
parents | 92d5278d1b10 |
children | d5c5db0335ee |
line wrap: on
line diff
--- a/Implab/Automaton/DFATable.cs Mon Mar 21 18:41:45 2016 +0300 +++ b/Implab/Automaton/DFATable.cs Tue Mar 22 18:58:40 2016 +0300 @@ -1,305 +1,313 @@ -using Implab; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Implab.Automaton { - public class DFATable : IDFATableBuilder { - int m_stateCount; - int m_symbolCount; - int m_initialState; - - readonly HashSet<int> m_finalStates = new HashSet<int>(); - readonly HashSet<AutomatonTransition> m_transitions = new HashSet<AutomatonTransition>(); - - - #region IDFADefinition implementation - - public bool IsFinalState(int s) { - Safe.ArgumentInRange(s, 0, m_stateCount, "s"); - - return m_finalStates.Contains(s); - } - - public IEnumerable<int> FinalStates { - get { - return m_finalStates; - } - } - - public int StateCount { - get { return m_stateCount; } - } - - public int AlphabetSize { - get { return m_symbolCount; } - } - - public int InitialState { - get { return m_initialState; } - } - - #endregion - - public void SetInitialState(int s) { - Safe.ArgumentAssert(s >= 0, "s"); - m_initialState = s; - } - - public void MarkFinalState(int state) { - m_finalStates.Add(state); - } - - public void Add(AutomatonTransition item) { - Safe.ArgumentAssert(item.s1 >= 0, "item"); - Safe.ArgumentAssert(item.s2 >= 0, "item"); - Safe.ArgumentAssert(item.edge >= 0, "item"); - - m_stateCount = Math.Max(m_stateCount, Math.Max(item.s1, item.s2) + 1); - m_symbolCount = Math.Max(m_symbolCount, item.edge); - - m_transitions.Add(item); - } - - public void Clear() { - m_stateCount = 0; - m_symbolCount = 0; - m_finalStates.Clear(); - m_transitions.Clear(); - } - - public bool Contains(AutomatonTransition item) { - return m_transitions.Contains(item); - } - - public void CopyTo(AutomatonTransition[] array, int arrayIndex) { - m_transitions.CopyTo(array, arrayIndex); - } - - public bool Remove(AutomatonTransition item) { - m_transitions.Remove(item); - } - - public int Count { - get { - return m_transitions.Count; - } - } - - public bool IsReadOnly { - get { - return false; - } - } - - public IEnumerator<AutomatonTransition> GetEnumerator() { - return m_transitions.GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return GetEnumerator(); - } - - public DFAStateDescriptor[] CreateTransitionTable() { - var table = new DFAStateDescriptor[StateCount]; - - foreach (var t in this) { - if (table[t.s1].transitions == null) - table[t.s1] = new DFAStateDescriptor(AlphabetSize, IsFinalState(t.s1)); - if (table[t.s2].transitions == null) - table[t.s2] = new DFAStateDescriptor(AlphabetSize, IsFinalState(t.s2)); - table[t.s1].transitions[t.edge] = t.s2; - } - - return table; - } - - /// <summary>Формирует множества конечных состояний перед началом работы алгоритма минимизации.</summary> - /// <remarks> - /// В процессе построения минимального автомата требуется разделить множество состояний, - /// на два подмножества - конечные состояния и все остальные, после чего эти подмножества - /// будут резделены на более мелкие. Иногда требуется гарантировать различия конечных сосотяний, - /// для этого необходимо переопределить даннцю фукнцию, для получения множеств конечных состояний. - /// </remarks> - /// <returns>The final states.</returns> - protected virtual IEnumerable<HashSet<int>> GroupFinalStates() { - return new HashSet<int>[] { m_finalStates }; - } - - protected void Optimize( - IDFATableBuilder optimalDFA, - IDictionary<int,int> alphabetMap, - IDictionary<int,int> stateMap - ) { - Safe.ArgumentNotNull(optimalDFA, "dfa"); - Safe.ArgumentNotNull(alphabetMap, "alphabetMap"); - Safe.ArgumentNotNull(stateMap, "stateMap"); - - - var setComparer = new CustomEqualityComparer<HashSet<int>>( - (x, y) => x.SetEquals(y), - s => s.Sum(x => x.GetHashCode()) - ); - - var optimalStates = new HashSet<HashSet<int>>(setComparer); - var queue = new HashSet<HashSet<int>>(setComparer); - - // получаем конечные состояния, сгруппированные по маркерам - optimalStates.UnionWith( - GroupFinalStates() - ); - - var state = new HashSet<int>( - Enumerable - .Range(0, m_stateCount - 1) - .Where(i => !m_finalStates.Contains(i)) - ); - - optimalStates.Add(state); - queue.Add(state); - - var rmap = m_transitions - .GroupBy(t => t.s2) - .ToLookup( - g => g.Key, // s2 - g => g.ToLookup(t => t.edge, t => t.s1) - ); - - while (queue.Count > 0) { - var stateA = queue.First(); - queue.Remove(stateA); - - for (int c = 0; c < m_symbolCount; c++) { - var stateX = new HashSet<int>(); - foreach(var a in stateA) - stateX.UnionWith(rmap[a][c]); // all states from wich 'c' leads to 'a' - - foreach (var stateY in optimalStates.ToArray()) { - if (stateX.Overlaps(stateY) && !stateY.IsSubsetOf(stateX)) { - var stateR1 = new HashSet<int>(stateY); - var stateR2 = new HashSet<int>(stateY); - - stateR1.IntersectWith(stateX); - stateR2.ExceptWith(stateX); - - optimalStates.Remove(stateY); - optimalStates.Add(stateR1); - optimalStates.Add(stateR2); - - if (queue.Contains(stateY)) { - queue.Remove(stateY); - queue.Add(stateR1); - queue.Add(stateR2); - } else { - queue.Add(stateR1.Count <= stateR2.Count ? stateR1 : stateR2); - } - } - } - } - } - - // карта получения оптимального состояния по соотвествующему ему простому состоянию - var nextState = 0; - foreach (var item in optimalStates) { - var id = nextState++; - foreach (var s in item) - stateMap[s] = id; - } - - // получаем минимальный алфавит - // входные символы не различимы, если Move(s,a1) == Move(s,a2), для любого s - // для этого используем алгоритм кластеризации, сначала - // считаем, что все символы не различимы - - var minClasses = new HashSet<HashSet<int>>(setComparer); - var alphaQueue = new Queue<HashSet<int>>(); - alphaQueue.Enqueue(new HashSet<int>(Enumerable.Range(0,AlphabetSize))); - - // для всех состояний, будем проверять каждый класс на различимость, - // т.е. символы различимы, если они приводят к разным состояниям - for (int s = 0 ; s < optimalStates.Count; s++) { - var newQueue = new Queue<HashSet<int>>(); - - foreach (var A in alphaQueue) { - // классы из одного символа делить бесполезно, переводим их сразу в - // результирующий алфавит - if (A.Count == 1) { - minClasses.Add(A); - continue; - } - - // различаем классы символов, которые переводят в различные оптимальные состояния - // optimalState -> alphaClass - var classes = new Dictionary<int, HashSet<int>>(); - - foreach (var term in A) { - // ищем все переходы класса по символу term - var res = m_transitions.Where(t => stateMap[t.s1] == s && t.edge == term).Select(t => stateMap[t.s2]).ToArray(); - - var s2 = res.Length > 0 ? res[0] : -1; - - HashSet<int> a2; - if (!classes.TryGetValue(s2, out a2)) { - a2 = new HashSet<int>(); - newQueue.Enqueue(a2); - classes[s2] = a2; - } - a2.Add(term); - } - } - - if (newQueue.Count == 0) - break; - alphaQueue = newQueue; - } - - // после окончания работы алгоритма в очереди останутся минимальные различимые классы - // входных символов - foreach (var A in alphaQueue) - minClasses.Add(A); - - // построение отображения алфавитов входных символов. - // поскольку символ DFAConst.UNCLASSIFIED_INPUT может иметь - // специальное значение, тогда сохраним минимальный класс, - // содержащий этот символ на томже месте. - - var nextCls = 0; - foreach (var item in minClasses) { - if (nextCls == DFAConst.UNCLASSIFIED_INPUT) - nextCls++; - - // сохраняем DFAConst.UNCLASSIFIED_INPUT - var cls = item.Contains(DFAConst.UNCLASSIFIED_INPUT) ? DFAConst.UNCLASSIFIED_INPUT : nextCls; - - foreach (var a in item) - alphabetMap[a] = cls; - - nextCls++; - } - - // построение автомата - optimalDFA.SetInitialState(stateMap[m_initialState]); - - foreach (var sf in m_finalStates.Select(s => stateMap[s]).Distinct()) - optimalDFA.MarkFinalState(sf); - - foreach (var t in m_transitions.Select(t => new AutomatonTransition(stateMap[t.s1],stateMap[t.s2],alphabetMap[t.edge])).Distinct()) - optimalDFA.Add(t); - } - - protected void PrintDFA<TInput, TState>(IAlphabet<TInput> inputAlphabet, IAlphabet<TState> stateAlphabet) { - Safe.ArgumentNotNull(inputAlphabet, "inputAlphabet"); - Safe.ArgumentNotNull(stateAlphabet, "stateAlphabet"); - - foreach(var t in m_transitions) - Console.WriteLine( - "[{0}] -{{{1}}}-> [{2}]{3}", - String.Join(",", stateAlphabet.GetSymbols(t.s1)), - String.Join("", inputAlphabet.GetSymbols(t.edge)), - String.Join(",", stateAlphabet.GetSymbols(t.s2)), - m_finalStates.Contains(t.s2) ? "$" : "" - ); - } - - } -} +using Implab; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Implab.Automaton { + public class DFATable : IDFATableBuilder { + int m_stateCount; + int m_symbolCount; + int m_initialState; + + readonly HashSet<int> m_finalStates = new HashSet<int>(); + readonly HashSet<AutomatonTransition> m_transitions = new HashSet<AutomatonTransition>(); + + + #region IDFADefinition implementation + + public bool IsFinalState(int s) { + Safe.ArgumentInRange(s, 0, m_stateCount, "s"); + + return m_finalStates.Contains(s); + } + + public IEnumerable<int> FinalStates { + get { + return m_finalStates; + } + } + + public int StateCount { + get { return m_stateCount; } + } + + public int AlphabetSize { + get { return m_symbolCount; } + } + + public int InitialState { + get { return m_initialState; } + } + + #endregion + + public void SetInitialState(int s) { + Safe.ArgumentAssert(s >= 0, "s"); + m_initialState = s; + } + + public void MarkFinalState(int state) { + m_finalStates.Add(state); + } + + public void Add(AutomatonTransition item) { + Safe.ArgumentAssert(item.s1 >= 0, "item"); + Safe.ArgumentAssert(item.s2 >= 0, "item"); + Safe.ArgumentAssert(item.edge >= 0, "item"); + + m_stateCount = Math.Max(m_stateCount, Math.Max(item.s1, item.s2) + 1); + m_symbolCount = Math.Max(m_symbolCount, item.edge); + + m_transitions.Add(item); + } + + public void Clear() { + m_stateCount = 0; + m_symbolCount = 0; + m_finalStates.Clear(); + m_transitions.Clear(); + } + + public bool Contains(AutomatonTransition item) { + return m_transitions.Contains(item); + } + + public void CopyTo(AutomatonTransition[] array, int arrayIndex) { + m_transitions.CopyTo(array, arrayIndex); + } + + public bool Remove(AutomatonTransition item) { + m_transitions.Remove(item); + } + + public int Count { + get { + return m_transitions.Count; + } + } + + public bool IsReadOnly { + get { + return false; + } + } + + public IEnumerator<AutomatonTransition> GetEnumerator() { + return m_transitions.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + public int[,] CreateTransitionTable() { + var table = new int[StateCount,AlphabetSize]; + + for (int i = 0; i < StateCount; i++) + for (int j = 0; i < AlphabetSize; j++) + table[i, j] = DFAConst.UNREACHABLE_STATE; + + foreach (var t in this) + table[t.s1,t.edge] = t.s2; + + return table; + } + + public bool[] CreateFinalStateTable() { + var table = new bool[StateCount]; + + foreach (var s in FinalStates) + table[s] = true; + + return table; + } + + /// <summary>Формирует множества конечных состояний перед началом работы алгоритма минимизации.</summary> + /// <remarks> + /// В процессе построения минимального автомата требуется разделить множество состояний, + /// на два подмножества - конечные состояния и все остальные, после чего эти подмножества + /// будут резделены на более мелкие. Иногда требуется гарантировать различия конечных сосотяний, + /// для этого необходимо переопределить даннцю фукнцию, для получения множеств конечных состояний. + /// </remarks> + /// <returns>The final states.</returns> + protected virtual IEnumerable<HashSet<int>> GroupFinalStates() { + return new HashSet<int>[] { m_finalStates }; + } + + protected void Optimize( + IDFATableBuilder optimalDFA, + IDictionary<int,int> alphabetMap, + IDictionary<int,int> stateMap + ) { + Safe.ArgumentNotNull(optimalDFA, "dfa"); + Safe.ArgumentNotNull(alphabetMap, "alphabetMap"); + Safe.ArgumentNotNull(stateMap, "stateMap"); + + + var setComparer = new CustomEqualityComparer<HashSet<int>>( + (x, y) => x.SetEquals(y), + s => s.Sum(x => x.GetHashCode()) + ); + + var optimalStates = new HashSet<HashSet<int>>(setComparer); + var queue = new HashSet<HashSet<int>>(setComparer); + + // получаем конечные состояния, сгруппированные по маркерам + optimalStates.UnionWith( + GroupFinalStates() + ); + + var state = new HashSet<int>( + Enumerable + .Range(0, m_stateCount - 1) + .Where(i => !m_finalStates.Contains(i)) + ); + + optimalStates.Add(state); + queue.Add(state); + + var rmap = m_transitions + .GroupBy(t => t.s2) + .ToLookup( + g => g.Key, // s2 + g => g.ToLookup(t => t.edge, t => t.s1) + ); + + while (queue.Count > 0) { + var stateA = queue.First(); + queue.Remove(stateA); + + for (int c = 0; c < m_symbolCount; c++) { + var stateX = new HashSet<int>(); + foreach(var a in stateA) + stateX.UnionWith(rmap[a][c]); // all states from wich 'c' leads to 'a' + + foreach (var stateY in optimalStates.ToArray()) { + if (stateX.Overlaps(stateY) && !stateY.IsSubsetOf(stateX)) { + var stateR1 = new HashSet<int>(stateY); + var stateR2 = new HashSet<int>(stateY); + + stateR1.IntersectWith(stateX); + stateR2.ExceptWith(stateX); + + optimalStates.Remove(stateY); + optimalStates.Add(stateR1); + optimalStates.Add(stateR2); + + if (queue.Contains(stateY)) { + queue.Remove(stateY); + queue.Add(stateR1); + queue.Add(stateR2); + } else { + queue.Add(stateR1.Count <= stateR2.Count ? stateR1 : stateR2); + } + } + } + } + } + + // карта получения оптимального состояния по соотвествующему ему простому состоянию + var nextState = 0; + foreach (var item in optimalStates) { + var id = nextState++; + foreach (var s in item) + stateMap[s] = id; + } + + // получаем минимальный алфавит + // входные символы не различимы, если Move(s,a1) == Move(s,a2), для любого s + // для этого используем алгоритм кластеризации, сначала + // считаем, что все символы не различимы + + var minClasses = new HashSet<HashSet<int>>(setComparer); + var alphaQueue = new Queue<HashSet<int>>(); + alphaQueue.Enqueue(new HashSet<int>(Enumerable.Range(0,AlphabetSize))); + + // для всех состояний, будем проверять каждый класс на различимость, + // т.е. символы различимы, если они приводят к разным состояниям + for (int s = 0 ; s < optimalStates.Count; s++) { + var newQueue = new Queue<HashSet<int>>(); + + foreach (var A in alphaQueue) { + // классы из одного символа делить бесполезно, переводим их сразу в + // результирующий алфавит + if (A.Count == 1) { + minClasses.Add(A); + continue; + } + + // различаем классы символов, которые переводят в различные оптимальные состояния + // optimalState -> alphaClass + var classes = new Dictionary<int, HashSet<int>>(); + + foreach (var term in A) { + // ищем все переходы класса по символу term + var res = m_transitions.Where(t => stateMap[t.s1] == s && t.edge == term).Select(t => stateMap[t.s2]).ToArray(); + + var s2 = res.Length > 0 ? res[0] : -1; + + HashSet<int> a2; + if (!classes.TryGetValue(s2, out a2)) { + a2 = new HashSet<int>(); + newQueue.Enqueue(a2); + classes[s2] = a2; + } + a2.Add(term); + } + } + + if (newQueue.Count == 0) + break; + alphaQueue = newQueue; + } + + // после окончания работы алгоритма в очереди останутся минимальные различимые классы + // входных символов + foreach (var A in alphaQueue) + minClasses.Add(A); + + // построение отображения алфавитов входных символов. + // поскольку символ DFAConst.UNCLASSIFIED_INPUT может иметь + // специальное значение, тогда сохраним минимальный класс, + // содержащий этот символ на томже месте. + + var nextCls = 0; + foreach (var item in minClasses) { + if (nextCls == DFAConst.UNCLASSIFIED_INPUT) + nextCls++; + + // сохраняем DFAConst.UNCLASSIFIED_INPUT + var cls = item.Contains(DFAConst.UNCLASSIFIED_INPUT) ? DFAConst.UNCLASSIFIED_INPUT : nextCls; + + foreach (var a in item) + alphabetMap[a] = cls; + + nextCls++; + } + + // построение автомата + optimalDFA.SetInitialState(stateMap[m_initialState]); + + foreach (var sf in m_finalStates.Select(s => stateMap[s]).Distinct()) + optimalDFA.MarkFinalState(sf); + + foreach (var t in m_transitions.Select(t => new AutomatonTransition(stateMap[t.s1],stateMap[t.s2],alphabetMap[t.edge])).Distinct()) + optimalDFA.Add(t); + } + + protected void PrintDFA<TInput, TState>(IAlphabet<TInput> inputAlphabet, IAlphabet<TState> stateAlphabet) { + Safe.ArgumentNotNull(inputAlphabet, "inputAlphabet"); + Safe.ArgumentNotNull(stateAlphabet, "stateAlphabet"); + + foreach(var t in m_transitions) + Console.WriteLine( + "[{0}] -{{{1}}}-> [{2}]{3}", + String.Join(",", stateAlphabet.GetSymbols(t.s1)), + String.Join("", inputAlphabet.GetSymbols(t.edge)), + String.Join(",", stateAlphabet.GetSymbols(t.s2)), + m_finalStates.Contains(t.s2) ? "$" : "" + ); + } + + } +}