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
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())
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)
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'})
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'))
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')
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)
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')] })
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(''))
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'])
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}"')