Пример #1
0
        /// <summary>
        /// Returns a new automaton which is the result of intersection with another DFA.
        /// Both Automaton must be DFA.
        /// </summary>
        public Automaton IntersectionWithDFA(Automaton automaton)
        {
            // Check they have the same alphabet.
            if (automaton.Alphabet.Count != this.Alphabet.Count)
            {
                throw new InvalidOperationException("Could not calculate the intersection as the alphabets were distinct");
            }
            foreach (var symbol in automaton.Alphabet)
            {
                if (!_alphabet.Contains(symbol))
                {
                    throw new InvalidOperationException("Could not calculate the intersection as the alphabets were distinct");
                }
            }

            var newAutomaton = new Automaton(_alphabet);

            var stateLookup = new Dictionary <(int, int), int>();
            // Only one start state per dfa
            var stateStart = (this.StartStates.First(), automaton.StartStates.First());
            var stateQueue = new Queue <(int firstState, int secondState)>();
            Func <(int, int), bool, int> AddState = ((int firstState, int secondState)stateTuple, bool isStartState) =>
            {
                bool isFinalState = false;
                if (this.FinalStates.Contains(stateTuple.firstState) && automaton.FinalStates.Contains(stateTuple.secondState))
                {
                    isFinalState = true;
                }
                var stateId = newAutomaton.AddState(isFinalState: isFinalState, isStartState: isStartState);
                stateLookup[stateTuple] = stateId;
                stateQueue.Enqueue(stateTuple);
                return(stateId);
            };

            AddState(stateStart, true);
            while (stateQueue.TryDequeue(out var stateTuple))
            {
                var fromStateId = stateLookup[stateTuple];
                foreach (var symbol in _alphabet)
                {
                    var reachableStates1 = this.TransitionMatrix.GetStates(stateTuple.firstState, symbol);
                    if (reachableStates1.Count == 0)
                    {
                        continue;
                    }
                    var reachableStates2 = automaton.TransitionMatrix.GetStates(stateTuple.secondState, symbol);
                    if (reachableStates2.Count == 0)
                    {
                        continue;
                    }
                    // DFA, so must only be one more state.
                    var newStateTuple = (reachableStates1.First(), reachableStates2.First());
                    if (!stateLookup.TryGetValue(newStateTuple, out var stateId))
                    {
                        stateId = AddState(newStateTuple, false);
                    }
                    newAutomaton.AddTransition(fromStateId, stateId, symbol);
                }
            }
            return(newAutomaton);
        }
Пример #2
0
 static Automaton()
 {
     Canonical = new DataTypes.Automata.Automaton(RegularLanguage.Symbols);
     var stateIdLookup = new Dictionary <int, int>();
     var transitions   = new[]
Пример #3
0
        /// <summary>
        /// Returns a new <see cref="Automaton" /> which is a DFA
        /// </summary>
        public Automaton ToDFA()
        {
            // A list of states. Each list will become a new state
            var automaton          = new Automaton(_alphabet);
            var newAutomatonStates = new Dictionary <HashSet <int>, int>(HashSet <int> .CreateSetComparer());
            var stateQueue         = new Queue <HashSet <int> >();

            // Setup subprocedure
            Func <HashSet <int>, bool, int> AddNewState = (HashSet <int> set, bool isStartState) =>
            {
                bool isFinalState = false;
                foreach (var currentState in set)
                {
                    if (_finalStates.Contains(currentState))
                    {
                        isFinalState = true;
                        break;
                    }
                }
                var currentStateId = automaton.AddState(isStartState: isStartState, isFinalState: isFinalState);
                stateQueue.Enqueue(set);
                newAutomatonStates.Add(set, currentStateId);
                return(currentStateId);
            };

            // Begin DFA calculation
            var set = new HashSet <int>(_startStates);

            AddEpsilonStates(set);
            AddNewState(set, true);
            while (stateQueue.TryDequeue(out var currentStateList))
            {
                var currentStateId = newAutomatonStates[currentStateList];
                foreach (var symbol in Alphabet)
                {
                    var reachableStates = new HashSet <int>();
                    foreach (var currentState in currentStateList)
                    {
                        var transitionStates = TransitionMatrix.GetStates(currentState, symbol);
                        foreach (var transitionState in transitionStates)
                        {
                            reachableStates.Add(transitionState);
                        }
                    }
                    if (reachableStates.Count == 0)
                    {
                        // Ignore the empty state
                        continue;
                    }
                    AddEpsilonStates(reachableStates);
                    // We have all reachable states with the given symbol. If a state in the new automata
                    // already exists for this combination, then we don't need to add a new state.
                    if (!newAutomatonStates.TryGetValue(reachableStates, out var toStateId))
                    {
                        toStateId = AddNewState(reachableStates, false);
                    }
                    automaton.AddTransition(currentStateId, toStateId, symbol);
                }
            }
            return(automaton);
        }
Пример #4
0
        /// <summary>
        /// Converts a regex to an automaton
        /// Based on https://en.wikipedia.org/wiki/Thompson%27s_construction
        /// </summary>
        public static Automaton RegexToAutomaton(string regex, char[] alphabet)
        {
            // Initialise variables
            int startState = 0;
            int finalState = 0;
            var symbols    = new HashSet <char>(alphabet);

            symbols.Add(Automaton.Epsilon);
            // Create automaton
            var automaton = new Automaton(alphabet);

            // https://medium.com/swlh/visualizing-thompsons-construction-algorithm-for-nfas-step-by-step-f92ef378581b
            // Convert to postfix
            var postFixRegex = RegexToPostfix(regex);
            Stack <(int startState, int finalState)> NFAPartitions = new Stack <(int, int)>();

            foreach (var character in postFixRegex)
            {
                switch (character)
                {
                case KleeneStarOperator:
                    var partition = NFAPartitions.Pop();
                    startState = automaton.AddState();
                    finalState = automaton.AddState();
                    automaton.AddTransition(startState, partition.startState, Automaton.Epsilon);
                    automaton.AddTransition(startState, finalState, Automaton.Epsilon);
                    automaton.AddTransition(partition.finalState, partition.startState, Automaton.Epsilon);
                    automaton.AddTransition(partition.finalState, finalState, Automaton.Epsilon);
                    NFAPartitions.Push((startState, finalState));
                    break;

                case ConcatenationOperator:
                    var rightPartition = NFAPartitions.Pop();
                    var leftPartition  = NFAPartitions.Pop();
                    // Because each partition will always have 0 incoming transitions from their start states,
                    // to merge the states, it is simply a case of copying the transitions from the right partition's
                    // start state to the final state of the left partition, and then finally remove the first state.
                    int copyState     = rightPartition.startState;
                    int copiedToState = leftPartition.finalState;
                    foreach (var symbol in symbols)
                    {
                        var states = automaton.TransitionMatrix.GetStates(copyState, symbol);
                        foreach (var state in states)
                        {
                            automaton.AddTransition(copiedToState, state, symbol);
                        }
                    }
                    automaton.DeleteState(copyState, skipIncomingTransitions: true);
                    NFAPartitions.Push((leftPartition.startState, rightPartition.finalState));
                    break;

                case UnionOperator:
                    var topPartition    = NFAPartitions.Pop();
                    var bottomPartition = NFAPartitions.Pop();
                    startState = automaton.AddState();
                    finalState = automaton.AddState();
                    automaton.AddTransition(startState, topPartition.startState, Automaton.Epsilon);
                    automaton.AddTransition(startState, bottomPartition.startState, Automaton.Epsilon);
                    automaton.AddTransition(topPartition.finalState, finalState, Automaton.Epsilon);
                    automaton.AddTransition(bottomPartition.finalState, finalState, Automaton.Epsilon);
                    NFAPartitions.Push((startState, finalState));
                    break;

                default:
                    // Not an operator. It is a symbol
                    if (!symbols.Contains(character))
                    {
                        throw new ArgumentException($"Invalid regex. Character {character} not a member of the alphabet");
                    }
                    startState = automaton.AddState();
                    finalState = automaton.AddState();
                    automaton.AddTransition(startState, finalState, character);
                    NFAPartitions.Push((startState, finalState));
                    break;
                }
            }
            var automataPartition = NFAPartitions.Pop();

            automaton.SetAsStartState(automataPartition.startState);
            automaton.SetAsFinalState(automataPartition.finalState);
            return(automaton);
        }