/// <summary> /// Returns a (deterministic) automaton that accepts the complement of the language of the /// given automaton. /// </summary> /// <param name="a">The automaton.</param> /// <returns>A (deterministic) automaton that accepts the complement of the language of the /// given automaton.</returns> /// <remarks> /// Complexity: linear in number of states (if already deterministic). /// </remarks> public static Automaton Complement(Automaton a) { a = a.CloneExpandedIfRequired(); a.Determinize(); a.Totalize(); foreach (State p in a.GetStates()) { p.Accept = !p.Accept; } a.RemoveDeadTransitions(); return(a); }
public static string ToDot(Automaton a) { StringBuilder transitions = new StringBuilder(INITIAL_CAPACITY); State finalState = null; foreach (var state in a.GetStates()) { transitions.Append(BuildDotTransitions(state)); finalState = state; } string header = BuildDotHeader(a.Initial, finalState); sb.Clear(); sb.Append($"{header}\n"); sb.Append(transitions.ToString()); sb.Append("\n}"); return(sb.ToString()); }
/// <summary> /// Reverses the language of the given (non-singleton) automaton while returning the set of /// new initial states. /// </summary> /// <param name="a">The automaton.</param> /// <returns></returns> public static HashSet <State> Reverse(Automaton a) { // Reverse all edges. var m = new Dictionary <State, HashSet <Transition> >(); HashSet <State> states = a.GetStates(); HashSet <State> accept = a.GetAcceptStates(); foreach (State r in states) { m.Add(r, new HashSet <Transition>()); r.Accept = false; } foreach (State r in states) { foreach (Transition t in r.Transitions) { m[t.To].Add(new Transition(t.Min, t.Max, r)); } } foreach (State r in states) { r.Transitions = m[r].ToList(); } // Make new initial+final states. a.Initial.Accept = true; a.Initial = new State(); foreach (State r in accept) { a.Initial.AddEpsilon(r); // Ensures that all initial states are reachable. } a.IsDeterministic = false; return(accept); }
/// <summary> /// Reverses the language of the given (non-singleton) automaton while returning the set of /// new initial states. /// </summary> /// <param name="a">The automaton.</param> /// <returns></returns> public static HashSet<State> Reverse(Automaton a) { // Reverse all edges. var m = new Dictionary<State, HashSet<Transition>>(); HashSet<State> states = a.GetStates(); HashSet<State> accept = a.GetAcceptStates(); foreach (State r in states) { m.Add(r, new HashSet<Transition>()); r.Accept = false; } foreach (State r in states) { foreach (Transition t in r.Transitions) { m[t.To].Add(new Transition(t.Min, t.Max, r)); } } foreach (State r in states) { r.Transitions = m[r].ToList(); } // Make new initial+final states. a.Initial.Accept = true; a.Initial = new State(); foreach (State r in accept) { a.Initial.AddEpsilon(r); // Ensures that all initial states are reachable. } a.IsDeterministic = false; return accept; }
/// <summary> /// Returns true if the given string is accepted by the automaton. /// </summary> /// <param name="a">The automaton.</param> /// <param name="s">The string.</param> /// <returns></returns> /// <remarks> /// Complexity: linear in the length of the string. /// For full performance, use the RunAutomaton class. /// </remarks> public static bool Run(Automaton a, string s) { if (a.IsSingleton) { return s.Equals(a.IsSingleton); } if (a.IsDeterministic) { State p = a.Initial; foreach (char t in s) { State q = p.Step(t); if (q == null) { return false; } p = q; } return p.Accept; } HashSet<State> states = a.GetStates(); Automaton.SetStateNumbers(states); var pp = new LinkedList<State>(); var ppOther = new LinkedList<State>(); var bb = new BitArray(states.Count); var bbOther = new BitArray(states.Count); pp.AddLast(a.Initial); var dest = new List<State>(); bool accept = a.Initial.Accept; foreach (char c in s) { accept = false; ppOther.Clear(); bbOther.SetAll(false); foreach (State p in pp) { dest.Clear(); p.Step(c, dest); foreach (State q in dest) { if (q.Accept) { accept = true; } if (!bbOther.Get(q.Number)) { bbOther.Set(q.Number, true); ppOther.AddLast(q); } } } LinkedList<State> tp = pp; pp = ppOther; ppOther = tp; BitArray tb = bb; bb = bbOther; bbOther = tb; } return accept; }
/// <summary> /// Returns an automaton that accepts the intersection of the languages of the given automata. /// Never modifies the input automata languages. /// </summary> /// <param name="a1">The a1.</param> /// <param name="a2">The a2.</param> /// <returns></returns> public static Automaton Intersection(Automaton a1, Automaton a2) { if (a1.IsSingleton) { if (a2.Run(a1.Singleton)) { return a1.CloneIfRequired(); } return BasicAutomata.MakeEmpty(); } if (a2.IsSingleton) { if (a1.Run(a2.Singleton)) { return a2.CloneIfRequired(); } return BasicAutomata.MakeEmpty(); } if (a1 == a2) { return a1.CloneIfRequired(); } Transition[][] transitions1 = Automaton.GetSortedTransitions(a1.GetStates()); Transition[][] transitions2 = Automaton.GetSortedTransitions(a2.GetStates()); var c = new Automaton(); var worklist = new LinkedList<StatePair>(); var newstates = new Dictionary<StatePair, StatePair>(); var p = new StatePair(c.Initial, a1.Initial, a2.Initial); worklist.AddLast(p); newstates.Add(p, p); while (worklist.Count > 0) { p = worklist.RemoveAndReturnFirst(); p.S.Accept = p.FirstState.Accept && p.SecondState.Accept; Transition[] t1 = transitions1[p.FirstState.Number]; Transition[] t2 = transitions2[p.SecondState.Number]; for (int n1 = 0, b2 = 0; n1 < t1.Length; n1++) { while (b2 < t2.Length && t2[b2].Max < t1[n1].Min) { b2++; } for (int n2 = b2; n2 < t2.Length && t1[n1].Max >= t2[n2].Min; n2++) { if (t2[n2].Max >= t1[n1].Min) { var q = new StatePair(t1[n1].To, t2[n2].To); StatePair r; newstates.TryGetValue(q, out r); if (r == null) { q.S = new State(); worklist.AddLast(q); newstates.Add(q, q); r = q; } char min = t1[n1].Min > t2[n2].Min ? t1[n1].Min : t2[n2].Min; char max = t1[n1].Max < t2[n2].Max ? t1[n1].Max : t2[n2].Max; p.S.Transitions.Add(new Transition(min, max, r.S)); } } } } c.IsDeterministic = a1.IsDeterministic && a2.IsDeterministic; c.RemoveDeadTransitions(); c.CheckMinimizeAlways(); return c; }
/// <summary> /// Returns a (deterministic) automaton that accepts the complement of the language of the /// given automaton. /// </summary> /// <param name="a">The automaton.</param> /// <returns>A (deterministic) automaton that accepts the complement of the language of the /// given automaton.</returns> /// <remarks> /// Complexity: linear in number of states (if already deterministic). /// </remarks> public static Automaton Complement(Automaton a) { a = a.CloneExpandedIfRequired(); a.Determinize(); a.Totalize(); foreach (State p in a.GetStates()) { p.Accept = !p.Accept; } a.RemoveDeadTransitions(); return a; }
internal static void MinimizeHopcroft(Automaton a) { a.Determinize(); IList <Transition> tr = a.Initial.Transitions; if (tr.Count == 1) { Transition t = tr[0]; if (t.To == a.Initial && t.Min == char.MinValue && t.Max == char.MaxValue) { return; } } a.Totalize(); // Make arrays for numbered states and effective alphabet. HashSet <State> ss = a.GetStates(); var states = new State[ss.Count]; int number = 0; foreach (State q in ss) { states[number] = q; q.Number = number++; } char[] sigma = a.GetStartPoints(); // Initialize data structures. var reverse = new List <List <LinkedList <State> > >(); foreach (State s in states) { var v = new List <LinkedList <State> >(); Initialize(ref v, sigma.Length); reverse.Add(v); } var reverseNonempty = new bool[states.Length, sigma.Length]; var partition = new List <LinkedList <State> >(); Initialize(ref partition, states.Length); var block = new int[states.Length]; var active = new StateList[states.Length, sigma.Length]; var active2 = new StateListNode[states.Length, sigma.Length]; var pending = new LinkedList <IntPair>(); var pending2 = new bool[sigma.Length, states.Length]; var split = new List <State>(); var split2 = new bool[states.Length]; var refine = new List <int>(); var refine2 = new bool[states.Length]; var splitblock = new List <List <State> >(); Initialize(ref splitblock, states.Length); for (int q = 0; q < states.Length; q++) { splitblock[q] = new List <State>(); partition[q] = new LinkedList <State>(); for (int x = 0; x < sigma.Length; x++) { reverse[q][x] = new LinkedList <State>(); active[q, x] = new StateList(); } } // Find initial partition and reverse edges. foreach (State qq in states) { int j = qq.Accept ? 0 : 1; partition[j].AddLast(qq); block[qq.Number] = j; for (int x = 0; x < sigma.Length; x++) { char y = sigma[x]; State p = qq.Step(y); reverse[p.Number][x].AddLast(qq); reverseNonempty[p.Number, x] = true; } } // Initialize active sets. for (int j = 0; j <= 1; j++) { for (int x = 0; x < sigma.Length; x++) { foreach (State qq in partition[j]) { if (reverseNonempty[qq.Number, x]) { active2[qq.Number, x] = active[j, x].Add(qq); } } } } // Initialize pending. for (int x = 0; x < sigma.Length; x++) { int a0 = active[0, x].Size; int a1 = active[1, x].Size; int j = a0 <= a1 ? 0 : 1; pending.AddLast(new IntPair(j, x)); pending2[x, j] = true; } // Process pending until fixed point. int k = 2; while (pending.Count > 0) { IntPair ip = pending.RemoveAndReturnFirst(); int p = ip.N1; int x = ip.N2; pending2[x, p] = false; // Find states that need to be split off their blocks. for (StateListNode m = active[p, x].First; m != null; m = m.Next) { foreach (State s in reverse[m.State.Number][x]) { if (!split2[s.Number]) { split2[s.Number] = true; split.Add(s); int j = block[s.Number]; splitblock[j].Add(s); if (!refine2[j]) { refine2[j] = true; refine.Add(j); } } } } // Refine blocks. foreach (int j in refine) { if (splitblock[j].Count < partition[j].Count) { LinkedList <State> b1 = partition[j]; LinkedList <State> b2 = partition[k]; foreach (State s in splitblock[j]) { b1.Remove(s); b2.AddLast(s); block[s.Number] = k; for (int c = 0; c < sigma.Length; c++) { StateListNode sn = active2[s.Number, c]; if (sn != null && sn.StateList == active[j, c]) { sn.Remove(); active2[s.Number, c] = active[k, c].Add(s); } } } // Update pending. for (int c = 0; c < sigma.Length; c++) { int aj = active[j, c].Size; int ak = active[k, c].Size; if (!pending2[c, j] && 0 < aj && aj <= ak) { pending2[c, j] = true; pending.AddLast(new IntPair(j, c)); } else { pending2[c, k] = true; pending.AddLast(new IntPair(k, c)); } } k++; } foreach (State s in splitblock[j]) { split2[s.Number] = false; } refine2[j] = false; splitblock[j].Clear(); } split.Clear(); refine.Clear(); } // Make a new state for each equivalence class, set initial state. var newstates = new State[k]; for (int n = 0; n < newstates.Length; n++) { var s = new State(); newstates[n] = s; foreach (State q in partition[n]) { if (q == a.Initial) { a.Initial = s; } s.Accept = q.Accept; s.Number = q.Number; // Select representative. q.Number = n; } } // Build transitions and set acceptance. foreach (State s in newstates) { s.Accept = states[s.Number].Accept; foreach (Transition t in states[s.Number].Transitions) { s.Transitions.Add(new Transition(t.Min, t.Max, newstates[t.To.Number])); } } a.RemoveDeadTransitions(); }
/// <summary> /// Minimizes the given automaton using Huffman's algorithm. /// </summary> /// <param name="a">The automaton.</param> internal static void MinimizeHuffman(Automaton a) { a.Determinize(); a.Totalize(); HashSet <State> ss = a.GetStates(); var transitions = new Transition[ss.Count][]; State[] states = ss.ToArray(); var mark = new List <List <bool> >(); var triggers = new List <List <HashSet <IntPair> > >(); foreach (State t in states) { var v = new List <HashSet <IntPair> >(); Initialize(ref v, states.Length); triggers.Add(v); } // Initialize marks based on acceptance status and find transition arrays. for (int n1 = 0; n1 < states.Length; n1++) { states[n1].Number = n1; transitions[n1] = states[n1].GetSortedTransitions(false).ToArray(); for (int n2 = n1 + 1; n2 < states.Length; n2++) { if (states[n1].Accept != states[n2].Accept) { mark[n1][n2] = true; } } } // For all pairs, see if states agree. for (int n1 = 0; n1 < states.Length; n1++) { for (int n2 = n1 + 1; n2 < states.Length; n2++) { if (!mark[n1][n2]) { if (MinimizationOperations.StatesAgree(transitions, mark, n1, n2)) { MinimizationOperations.AddTriggers(transitions, triggers, n1, n2); } else { MinimizationOperations.MarkPair(mark, triggers, n1, n2); } } } } // Assign equivalence class numbers to states. int numclasses = 0; foreach (State t in states) { t.Number = -1; } for (int n1 = 0; n1 < states.Length; n1++) { if (states[n1].Number == -1) { states[n1].Number = numclasses; for (int n2 = n1 + 1; n2 < states.Length; n2++) { if (!mark[n1][n2]) { states[n2].Number = numclasses; } } numclasses++; } } // Make a new state for each equivalence class. var newstates = new State[numclasses]; for (int n = 0; n < numclasses; n++) { newstates[n] = new State(); } // Select a class representative for each class and find the new initial state. for (int n = 0; n < states.Length; n++) { newstates[states[n].Number].Number = n; if (states[n] == a.Initial) { a.Initial = newstates[states[n].Number]; } } // Build transitions and set acceptance. for (int n = 0; n < numclasses; n++) { State s = newstates[n]; s.Accept = states[s.Number].Accept; foreach (Transition t in states[s.Number].Transitions) { s.Transitions.Add(new Transition(t.Min, t.Max, newstates[t.To.Number])); } } a.RemoveDeadTransitions(); }
/// <summary> /// Returns true if the given string is accepted by the automaton. /// </summary> /// <param name="a">The automaton.</param> /// <param name="s">The string.</param> /// <returns></returns> /// <remarks> /// Complexity: linear in the length of the string. /// For full performance, use the RunAutomaton class. /// </remarks> public static bool Run(Automaton a, string s) { if (a.IsSingleton) { return(s.Equals(a.Singleton, System.StringComparison.CurrentCulture)); } if (a.IsDeterministic) { State p = a.Initial; foreach (char t in s) { State q = p.Step(t); if (q == null) { return(false); } p = q; } return(p.Accept); } HashSet <State> states = a.GetStates(); Automaton.SetStateNumbers(states); var pp = new LinkedList <State>(); var ppOther = new LinkedList <State>(); var bb = new BitArray(states.Count); var bbOther = new BitArray(states.Count); pp.AddLast(a.Initial); var dest = new List <State>(); bool accept = a.Initial.Accept; foreach (char c in s) { accept = false; ppOther.Clear(); bbOther.SetAll(false); foreach (State p in pp) { dest.Clear(); p.Step(c, dest); foreach (State q in dest) { if (q.Accept) { accept = true; } if (!bbOther.Get(q.Number)) { bbOther.Set(q.Number, true); ppOther.AddLast(q); } } } LinkedList <State> tp = pp; pp = ppOther; ppOther = tp; BitArray tb = bb; bb = bbOther; bbOther = tb; } return(accept); }
/// <summary> /// Returns an automaton that accepts the intersection of the languages of the given automata. /// Never modifies the input automata languages. /// </summary> /// <param name="a1">The a1.</param> /// <param name="a2">The a2.</param> /// <returns></returns> public static Automaton Intersection(Automaton a1, Automaton a2) { if (a1.IsSingleton) { if (a2.Run(a1.Singleton)) { return(a1.CloneIfRequired()); } return(BasicAutomata.MakeEmpty()); } if (a2.IsSingleton) { if (a1.Run(a2.Singleton)) { return(a2.CloneIfRequired()); } return(BasicAutomata.MakeEmpty()); } if (a1 == a2) { return(a1.CloneIfRequired()); } Transition[][] transitions1 = Automaton.GetSortedTransitions(a1.GetStates()); Transition[][] transitions2 = Automaton.GetSortedTransitions(a2.GetStates()); var c = new Automaton(); var worklist = new LinkedList <StatePair>(); var newstates = new Dictionary <StatePair, StatePair>(); var p = new StatePair(c.Initial, a1.Initial, a2.Initial); worklist.AddLast(p); newstates.Add(p, p); while (worklist.Count > 0) { p = worklist.RemoveAndReturnFirst(); p.S.Accept = p.FirstState.Accept && p.SecondState.Accept; Transition[] t1 = transitions1[p.FirstState.Number]; Transition[] t2 = transitions2[p.SecondState.Number]; for (int n1 = 0, b2 = 0; n1 < t1.Length; n1++) { while (b2 < t2.Length && t2[b2].Max < t1[n1].Min) { b2++; } for (int n2 = b2; n2 < t2.Length && t1[n1].Max >= t2[n2].Min; n2++) { if (t2[n2].Max >= t1[n1].Min) { var q = new StatePair(t1[n1].To, t2[n2].To); StatePair r; newstates.TryGetValue(q, out r); if (r == null) { q.S = new State(); worklist.AddLast(q); newstates.Add(q, q); r = q; } char min = t1[n1].Min > t2[n2].Min ? t1[n1].Min : t2[n2].Min; char max = t1[n1].Max < t2[n2].Max ? t1[n1].Max : t2[n2].Max; p.S.Transitions.Add(new Transition(min, max, r.S)); } } } } c.IsDeterministic = a1.IsDeterministic && a2.IsDeterministic; c.RemoveDeadTransitions(); c.CheckMinimizeAlways(); return(c); }