Пример #1
0
        /// <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);
        }
Пример #2
0
        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());
        }
Пример #3
0
        /// <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);
        }
Пример #4
0
        /// <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;
        }
Пример #5
0
        /// <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;
        }
Пример #6
0
        /// <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;
        }
Пример #7
0
        /// <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();
        }
Пример #10
0
        /// <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);
        }
Пример #11
0
        /// <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);
        }