Mercurial > pub > ImplabNet
diff Implab/Automaton/DFATable.cs @ 169:54270c2f29f2 ref20160224
DFA refactoring
author | cin |
---|---|
date | Thu, 03 Mar 2016 08:41:02 +0300 |
parents | |
children | 0f70905b4652 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Automaton/DFATable.cs Thu Mar 03 08:41:02 2016 +0300 @@ -0,0 +1,280 @@ +using Implab; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Implab.Automaton { + public class DFATable : IDFATableBuilder { + DFAStateDescriptior[] m_dfaTable; + + 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>(); + + void AssertNotReadOnly() { + if (m_dfaTable != null) + throw new InvalidOperationException("The object is readonly"); + } + + + #region IDFADefinition implementation + + public DFAStateDescriptior[] GetTransitionTable() { + if (m_dfaTable == null) { + if (m_stateCount <= 0) + throw new InvalidOperationException("Invalid automaton definition: states count = {0}", m_stateCount); + if (m_symbolCount <= 0) + throw new InvalidOperationException("Invalid automaton definition: symbols count = {0}", m_symbolCount); + + m_dfaTable = ConstructTransitionTable(); + } + return m_dfaTable; + } + + public bool IsFinalState(int s) { + Safe.ArgumentInRange(s, 0, m_stateCount, "s"); + + return m_dfaTable != null ? m_dfaTable[s].final : 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 + + protected virtual DFAStateDescriptior[] ConstructTransitionTable() { + var dfaTable = new DFAStateDescriptior[m_stateCount]; + + + foreach (var t in m_transitions) { + if (dfaTable[t.s1].transitions == null) + dfaTable[t.s1] = new DFAStateDescriptior(m_symbolCount, m_finalStates.Contains(t.s1)); + + dfaTable[t.s1].transitions[t.edge] = t.s2; + } + + foreach (var s in m_finalStates) + if (!dfaTable[s].final) + m_dfaTable[s] = new DFAStateDescriptior(m_symbolCount, true); + + } + + public void SetInitialState(int s) { + Safe.ArgumentAssert(s >= 0, "s"); + m_initialState = s; + } + + public void MarkFinalState(int state) { + AssertNotReadOnly(); + m_finalStates.Add(state); + } + + public void Add(AutomatonTransition item) { + AssertNotReadOnly(); + 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() { + AssertNotReadOnly(); + + 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) { + AssertNotReadOnly(); + m_transitions.Remove(item); + } + + public int Count { + get { + return m_transitions.Count; + } + } + + public bool IsReadOnly { + get { + return m_dfaTable != null; + } + } + + public IEnumerator<AutomatonTransition> GetEnumerator() { + return m_transitions.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + /// <summary>Формирует множества конечных состояний перед началом работы алгоритма минимизации.</summary> + /// <remarks> + /// В процессе построения минимального автомата требуется разделить множество состояний, + /// на два подмножества - конечные состояния и все остальные, после чего эти подмножества + /// будут резделены на более мелкие. Иногда требуется гарантировать различия конечных сосотяний, + /// для этого необходимо переопределить даннцю фукнцию, для получения множеств конечных состояний. + /// </remarks> + /// <returns>The final states.</returns> + protected virtual IEnumerable<HashSet<int>> GroupFinalStates() { + return new HashSet<int>[] { m_finalStates }; + } + + protected void Optimize<TInput, TState>( + IDFATableBuilder optimalDFA, + IAlphabet<TInput> inputAlphabet, + IAlphabetBuilder<TInput> optimalInputAlphabet, + IAlphabet<TState> stateAlphabet, + IAlphabetBuilder<TState> optimalStateAlphabet + ) { + Safe.ArgumentNotNull(optimalDFA, "dfa"); + Safe.ArgumentNotNull(optimalInputAlphabet, "optimalInputAlphabet"); + Safe.ArgumentNotNull(optimalStateAlphabet, "optimalStateAlphabet"); + Safe.ArgumentNotNull(inputAlphabet, "inputAlphabet"); + Safe.ArgumentNotNull(stateAlphabet, "stateAlphabet"); + + if (inputAlphabet.Count != m_symbolCount) + throw new InvalidOperationException("The input symbols aphabet mismatch"); + if (stateAlphabet.Count != m_stateCount) + throw new InvalidOperationException("The states alphabet mismatch"); + + 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 statesMap = stateAlphabet.Reclassify(optimalStateAlphabet, optimalStates); + + // получаем минимальный алфавит + // входные символы не различимы, если Move(s,a1) == Move(s,a2) + var optimalAlphabet = m_transitions + .GroupBy(t => Tuple.Create(statesMap[t.s1], statesMap[t.s2]), t => t.edge); + + var alphabetMap = inputAlphabet.Reclassify(optimalInputAlphabet, optimalAlphabet); + + // построение автомата + optimalDFA.SetInitialState(statesMap[m_initialState]); + + foreach (var sf in m_finalStates.GroupBy(s => statesMap[s])) + optimalDFA.MarkFinalState(sf.Key); + + foreach (var t in m_transitions.Select(t => new AutomatonTransition(statesMap[t.s1],statesMap[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"); + + var inputMap = inputAlphabet.CreateReverseMap(); + var stateMap = stateAlphabet.CreateReverseMap(); + + for (int i = 0; i < inputMap.Length; i++) + Console.WriteLine("C{0}: {1}", i, String.Join(",", inputMap[i])); + + + foreach(var t in m_transitions) + Console.WriteLine( + "[{0}] -{{{1}}}-> [{2}]{3}", + stateMap[t.s1], + String.Join(",", inputMap[t.edge]), + stateMap[t.s2], + m_finalStates.Contains(t.s2) ? "$" : "" + ); + + } + + } +}