Exemple #1
0
def glushkov(regular_expression: RegularExpression) -> FiniteAutomaton:
    """Implementation of Glushkov's algorithm
    """

    linearized, _ = _linearize_regular_expression(regular_expression)
    q_init_result = '0'
    result = FiniteAutomaton(alphabet=regular_expression.alphabet(),
                             states=linearized.alphabet() | {q_init_result},
                             initial_states={q_init_result},
                             accepting_states=linearized.accepting_letters(),
                             transitions={q_init_result: []})

    for letter in linearized.initial_letters():
        real_letter = letter[0]
        result.transitions[q_init_result].append((real_letter, letter))

    for letter in linearized.alphabet():
        if letter not in result.transitions:
            result.transitions[letter] = []
        for successor in linearized.successors(letter):
            real_letter = successor[0]
            result.transitions[letter].append((real_letter, successor))

    if linearized.accepts_epsilon():
        result.accepting_states |= {q_init_result}

    return result
Exemple #2
0
 def test_is_deterministic(self):
     dfa = FiniteAutomaton(alphabet={'a', 'b'},
                           states={'q0', 'q1'},
                           initial_states={'q0'},
                           accepting_states={'q1'},
                           transitions={
                               'q0': [('a', 'q1'), ('b', 'q0')],
                               'q1': [('a', 'q1'), ('b', 'q0')]
                           })
     ndfa = FiniteAutomaton(alphabet={'a', 'b'},
                            states={'q0', 'q1'},
                            initial_states={'q0'},
                            accepting_states={'q1'},
                            transitions={
                                'q0': [('a', 'q0'), ('b', 'q0'),
                                       ('a', 'q1')],
                                'q1': [('a', 'q1'), ('b', 'q1')]
                            })
     ndfae = FiniteAutomaton(alphabet={'a', 'b'},
                             states={'q0', 'q1'},
                             initial_states={'q0'},
                             accepting_states={'q1'},
                             transitions={
                                 'q0': [('ε', 'q1'), ('b', 'q0')],
                                 'q1': [('a', 'q1'), ('b', 'q0')]
                             })
     self.assertTrue(dfa.is_deterministic())
     self.assertFalse(ndfa.is_deterministic())
     self.assertFalse(ndfae.is_deterministic())
Exemple #3
0
def residual_automaton(
        regular_expression: RegularExpression) -> FiniteAutomaton:
    """From a regular expression, constructs an equivalent finite automaton
    using the residuals method
    """

    initial_state = _state_identifier(regular_expression)
    accepting_states = []
    alphabet = regular_expression.alphabet()
    transitions: Dict[State, List[Tuple[Letter, State]]] = {}
    unexplored_states = [initial_state]

    while unexplored_states:
        state = unexplored_states.pop(0)
        state_re = parse_regular_expression(state)
        if state_re.accepts_epsilon():
            accepting_states.append(state)
        transitions[state] = []
        for letter in alphabet:
            next_residual = residual(state_re, letter)
            next_state = _state_identifier(next_residual)
            if next_residual is not None:
                transitions[state].append((letter, next_state))
                # Equivalent re can have different string representations...
                if next_state not in transitions:
                    transitions[next_state] = []
                    unexplored_states.append(next_state)

    return FiniteAutomaton(alphabet=alphabet,
                           states=set(transitions.keys()),
                           initial_states={initial_state},
                           accepting_states=set(accepting_states),
                           transitions=transitions)
def powerset_determinize(automaton: FiniteAutomaton) -> FiniteAutomaton:
    """Implementation of the powerset determinization method
    """

    initial_state = automaton.epsilon_closure(automaton.initial_states)
    initial_state_identifier = _state_identifier(initial_state)
    state_dict: Dict[str, Set[State]] = {
        initial_state_identifier: initial_state
    }

    unexplored_state_identifiers: List[str] = [initial_state_identifier]

    transitions: Dict[State, List[Tuple[Letter, State]]] = {}

    while unexplored_state_identifiers:
        state_identifier = unexplored_state_identifiers.pop(0)
        transitions[state_identifier] = []
        arrows: Dict[Letter, Set[State]] = {}
        for state in state_dict[state_identifier]:
            for letter, next_state in automaton.transitions.get(state, []):
                if letter == 'ε':
                    continue
                if letter not in arrows:
                    arrows[letter] = set()
                arrows[letter].add(next_state)
        for letter in arrows:
            arrows[letter] = automaton.epsilon_closure(arrows[letter])
            next_state_identifier = _state_identifier(arrows[letter])
            if next_state_identifier not in state_dict:
                state_dict[next_state_identifier] = arrows[letter]
                unexplored_state_identifiers.append(next_state_identifier)
            transitions[state_identifier].append(
                (letter, next_state_identifier))

    states = set(state_dict.keys())
    accepting_states: Set[State] = set()
    for state_identifier in state_dict:
        if automaton.accepting_states.intersection(
                state_dict[state_identifier]):
            accepting_states.add(state_identifier)

    return FiniteAutomaton(alphabet=deepcopy(automaton.alphabet),
                           states=states,
                           initial_states={initial_state_identifier},
                           accepting_states=accepting_states,
                           transitions=transitions)
Exemple #5
0
 def test_epsilon_closure(self):
     automaton = FiniteAutomaton(
         alphabet={'a', 'b'},
         states={'q0', 'q1', 'q2', 'q3', 'q4', 'q5'},
         initial_states={'q0'},
         accepting_states={'q0'},
         transitions={
             'q0': [('a', 'q4'), ('ε', 'q1')],
             'q2': [('ε', 'q3')],
             'q3': [('ε', 'q4')],
             'q5': [('ε', 'q4')]
         })
     self.assertEqual(automaton.epsilon_closure({'q0'}), {'q0', 'q1'})
     self.assertEqual(automaton.epsilon_closure({'q1'}), {'q1'})
     self.assertEqual(automaton.epsilon_closure({'q2'}), {'q2', 'q3', 'q4'})
     self.assertEqual(automaton.epsilon_closure({'q3'}), {'q3', 'q4'})
     self.assertEqual(automaton.epsilon_closure({'q4'}), {'q4'})
     self.assertEqual(automaton.epsilon_closure({'q5'}), {'q4', 'q5'})
Exemple #6
0
 def test_powerset_determinize(self):
     ndfa = FiniteAutomaton(
         alphabet={'a', 'b'},
         states={'q-1', 'q0', 'q1', 'q2'},
         initial_states={'q-1'},
         accepting_states={'q2'},
         transitions={
             'q-1': [('ε', 'q0')],
             'q0': [('a', 'q0'), ('b', 'q0'), ('a', 'q1')],
             'q1': [('a', 'q1'), ('b', 'q1'), ('ε', 'q2')]
         }
     )
     det_ndfa = powerset_determinize(ndfa)
     self.assertTrue(det_ndfa.is_deterministic())
     ndfa.draw(
         name='PowerSetDeterminizeTest.test_powerset_determinize.ndfa'
     ).render(directory='out/', format='pdf')
     det_ndfa.draw(
         name='PowerSetDeterminizeTest.test_powerset_determinize.det_ndfa'
     ).render(directory='out/', format='pdf')
 def test_transpose(self):
     automaton1 = FiniteAutomaton(
         alphabet={'a', 'b'},
         states={'q0', 'q1', 'q2'},
         initial_states={'q0'},
         accepting_states={'q2'},
         transitions={
             'q0':[('a', 'q1')],
             'q1':[('b', 'q2')]
         }
     )
     transposed_automaton1 = transpose(automaton1)
     self.assertTrue(transposed_automaton1.read('ba'))
     self.assertFalse(transposed_automaton1.read(''))
     self.assertFalse(transposed_automaton1.read('a'))
     self.assertFalse(transposed_automaton1.read('b'))
     self.assertFalse(transposed_automaton1.read('aa'))
     self.assertFalse(transposed_automaton1.read('ab'))
     self.assertFalse(transposed_automaton1.read('bb'))
Exemple #8
0
 def test_draw(self):
     dfa = FiniteAutomaton(alphabet={'a', 'b'},
                           states={'q0', 'q1'},
                           initial_states={'q0'},
                           accepting_states={'q1'},
                           transitions={
                               'q0': [('a', 'q1'), ('b', 'q0')],
                               'q1': [('a', 'q1'), ('b', 'q0')]
                           })
     ndfa = FiniteAutomaton(alphabet={'a', 'b'},
                            states={'q0', 'q1'},
                            initial_states={'q0'},
                            accepting_states={'q1'},
                            transitions={
                                'q0': [('a', 'q0'), ('b', 'q0'),
                                       ('a', 'q1')],
                                'q1': [('a', 'q1'), ('b', 'q1')]
                            })
     dfa.draw(name='FiniteAutomatonTest.test_draw.dfa').render(
         directory='out/', format='pdf')
     ndfa.draw(name='FiniteAutomatonTest.test_draw.ndfa').render(
         directory='out/', format='pdf')
Exemple #9
0
def transpose(automaton: FiniteAutomaton) -> FiniteAutomaton:
    """Transposes a finite automaton

    Let :math:`\\mathcal{A} = ( A, Q, Q_{\\mathrm{init}}, Q_{\\mathrm{acc}},
    \\delta )` be a finite automaton. Its **transpose** :math:`\\mathcal{A}^t`
    is given by :math:`\\mathcal{A}^t = ( A, Q, Q_{\\mathrm{acc}},
    Q_{\\mathrm{init}}, \\delta^t )` (note that initial and accepting states
    have been swaped), where for :math:`a \\in A` and :math:`q \\in Q`,
    :math:`\\delta^t (q, a) = \\{ r \\in Q \\mid \\delta (r, a) = q \\}`.
    """
    transposed_transitions: Dict[State, List[Tuple[Letter, State]]] = {}
    for state in automaton.states:
        transposed_transitions[state] = []
    for state_left in automaton.transitions:
        for letter, state_right in automaton.transitions[state_left]:
            transposed_transitions[state_right].append((letter, state_left))

    return FiniteAutomaton(alphabet=automaton.alphabet,
                           states=automaton.states,
                           initial_states=automaton.accepting_states,
                           accepting_states=automaton.initial_states,
                           transitions=transposed_transitions)
Exemple #10
0
 def test___init__(self):
     with self.assertRaises(ValueError):
         FiniteAutomaton(alphabet={},
                         states={'q0'},
                         initial_states={},
                         accepting_states={},
                         transitions=[])
     with self.assertRaises(ValueError):
         FiniteAutomaton(alphabet={'a'},
                         states={'q0'},
                         initial_states={'INVALID'},
                         accepting_states={},
                         transitions=[])
     with self.assertRaises(ValueError):
         FiniteAutomaton(alphabet={'a'},
                         states={'q0'},
                         initial_states={'q0'},
                         accepting_states={'INVALID'},
                         transitions=[])
     with self.assertRaises(ValueError):
         FiniteAutomaton(alphabet={'a'},
                         states={'q0'},
                         initial_states={'q0'},
                         accepting_states={'q0'},
                         transitions={'q0': [('INVALID', 'q0')]})
     with self.assertRaises(ValueError):
         FiniteAutomaton(alphabet={'a'},
                         states={'q0'},
                         initial_states={'q0'},
                         accepting_states={'q0'},
                         transitions={'q0': [('a', 'INVALID')]})
     FiniteAutomaton(alphabet={'a', 'b'},
                     states={'q0', 'q1'},
                     initial_states={'q0'},
                     accepting_states={'q1'},
                     transitions={
                         'q0': [('a', 'q1'), ('b', 'q0')],
                         'q1': [('a', 'q1'), ('b', 'q0')]
                     })
Exemple #11
0
    def test_read(self):
        ndfa = FiniteAutomaton(alphabet={'a', 'b'},
                               states={'q0', 'q1'},
                               initial_states={'q0'},
                               accepting_states={'q1'},
                               transitions={
                                   'q0': [('a', 'q0'), ('b', 'q0'),
                                          ('a', 'q1')],
                                   'q1': [('a', 'q1'), ('b', 'q1')]
                               })
        ndfae = FiniteAutomaton(alphabet={'a', 'b'},
                                states={'q0', 'q1', 'q2'},
                                initial_states={'q0'},
                                accepting_states={'q2'},
                                transitions={
                                    'q0': [('a', 'q0'), ('b', 'q0'),
                                           ('a', 'q1')],
                                    'q1': [('a', 'q0'), ('b', 'q0'),
                                           ('ε', 'q2')]
                                })
        ndfae2 = FiniteAutomaton(alphabet={'a', 'b'},
                                 states={'q0', 'q1', 'q2'},
                                 initial_states={'q0'},
                                 accepting_states={'q2'},
                                 transitions={
                                     'q0': [('ε', 'q1')],
                                     'q1': [('ε', 'q2')]
                                 })

        self.assertTrue(ndfa.read('a'))
        self.assertTrue(ndfa.read('aa'))
        self.assertTrue(ndfa.read('aba'))
        self.assertTrue(ndfa.read('abb'))
        self.assertFalse(ndfa.read(''))
        self.assertFalse(ndfa.read('b'))
        self.assertFalse(ndfa.read('bb'))
        self.assertFalse(ndfa.read('bbb'))

        self.assertTrue(ndfae.read('a'))
        self.assertTrue(ndfae.read('aa'))
        self.assertTrue(ndfae.read('aaa'))
        self.assertTrue(ndfae.read('aba'))
        self.assertTrue(ndfae.read('bba'))

        self.assertFalse(ndfae.read('b'))
        self.assertTrue(ndfae2.read(''))
Exemple #12
0
    def test_brozozwski(self):

        alphabet = {'a', 'b', 'c'}

        aut1 = FiniteAutomaton(alphabet=alphabet,
                               states={'q0', 'q1'},
                               initial_states={'q0'},
                               accepting_states={'q1'},
                               transitions={'q0': [('a', 'q1')]})
        self.assertEqual(
            repr(brozozwski(aut1)).replace(' ', ''), 'a'.replace(' ', ''))

        aut2 = FiniteAutomaton(alphabet=alphabet,
                               states={'q0', 'q1', 'q2'},
                               initial_states={'q0'},
                               accepting_states={'q2'},
                               transitions={
                                   'q0': [('a', 'q1')],
                                   'q1': [('b', 'q2')]
                               })
        self.assertEqual(
            repr(brozozwski(aut2)).replace(' ', ''),
            'CONCAT(a, b)'.replace(' ', ''))

        aut2 = FiniteAutomaton(alphabet=alphabet,
                               states={'q0', 'q1', 'q2'},
                               initial_states={'q0'},
                               accepting_states={'q2'},
                               transitions={
                                   'q0': [('a', 'q1'), ('c', 'q2')],
                                   'q1': [('b', 'q2')]
                               })
        aut2_bro = brozozwski(aut2)
        aut2_rec = thompson(aut2_bro, alphabet)
        self._compare(aut2, aut2_rec, ['', 'a', 'b', 'c', 'ab', 'abc'])

        aut3 = FiniteAutomaton(alphabet=alphabet,
                               states={'q0', 'q1', 'q2'},
                               initial_states={'q0'},
                               accepting_states={'q2'},
                               transitions={
                                   'q0': [('a', 'q0'), ('b', 'q1')],
                                   'q1': [('c', 'q2')]
                               })
        aut3_bro = brozozwski(aut3)
        aut3_rec = thompson(aut3_bro, alphabet)
        self._compare(aut3, aut3_rec, ['', 'a', 'b', 'c', 'ab', 'abc', 'aaab'])

        aut4 = FiniteAutomaton(alphabet=alphabet,
                               states={'q0', 'q1', 'q2'},
                               initial_states={'q0'},
                               accepting_states={'q0'},
                               transitions={
                                   'q0': [('a', 'q1')],
                                   'q1': [('a', 'q2')],
                                   'q2': [('a', 'q0')]
                               })
        aut4_bro = brozozwski(aut4)
        aut4_rec = thompson(aut4_bro, alphabet)
        self._compare(
            aut4, aut4_rec,
            ['', 'a', 'b', 'c', 'aa', 'aab', 'aaa', 'aaaa', 'aaaaaa'])
Exemple #13
0
 def _compare(self, automaton1: FiniteAutomaton,
              automaton2: FiniteAutomaton, word_list: List[Word]):
     for word in word_list:
         self.assertEqual(automaton1.read(word), automaton2.read(word),
                          f'Failed word: "{word}"')