def test_to_graphviz(self): dfa = SimpleDFA( {"q0", "q1", "q2", "q3", "q4", "q5"}, MapAlphabet({"a", "b"}), "q0", {"q0"}, {}, ) dfa.to_graphviz() dfa = SimpleDFA( {"q0", "q1", "q2", "q3", "q4", "q5"}, MapAlphabet({"a", "b"}), "q0", {"q3"}, { "q0": {"a": "q0", "b": "q1"}, "q1": {"a": "q0", "b": "q2"}, "q2": {"a": "q3", "b": "q4"}, "q3": {"a": "q3", "b": "q4"}, "q4": {"a": "q3", "b": "q5"}, }, ) dfa.to_graphviz()
def test_complete_when_dfa_is_not_complete(self): """Test that when we try to make complete a non-complete SimpleDFA then the returned SimpleDFA is complete.""" dfa = SimpleDFA( {"q0", "q1"}, MapAlphabet({"a", "b"}), "q0", set(), {"q0": {"a": "q0", "b": "q1"}}, ) expected_dfa = SimpleDFA( {"q0", "q1", "sink"}, MapAlphabet({"a", "b"}), "q0", set(), { "q0": {"a": "q0", "b": "q1"}, "q1": {"a": "sink", "b": "sink"}, "sink": {"a": "sink", "b": "sink"}, }, ) actual_dfa = dfa.complete() assert actual_dfa == expected_dfa
def test_reachable_no_transitions(self): """Test that the reachable SimpleDFA of a SimpleDFA without transitions is the SimpleDFA with only the initial state.""" dfa = SimpleDFA({"q0", "q1", "q2"}, MapAlphabet({"a1", "a2"}), "q0", {"q1"}, {}) actual_reachable_dfa = dfa.reachable() expected_reachable_dfa = SimpleDFA( {"q0"}, MapAlphabet({"a1", "a2"}), "q0", set(), {} ) assert actual_reachable_dfa == expected_reachable_dfa
def test_coreachable_no_accepting_states_gives_empty_dfa(self): dfa = SimpleDFA( {"q0", "q1", "q2"}, MapAlphabet({"a1", "a2"}), "q0", set(), {"q0": {"a1": "q0", "a2": "q1"}}, ) actual_coreachable = dfa.coreachable() expected_coreachable = EmptyDFA(MapAlphabet({"a1", "a2"})) assert actual_coreachable == expected_coreachable
def test_determinize(self): nfa = SimpleNFA( {"q0", "q1", "q2", "q3"}, MapAlphabet({"a", "b"}), "q0", {"q3"}, { "q0": { "a": {"q1"} }, "q1": { "a": {"q0"}, "b": {"q2", "q1"} }, "q2": { "a": {"q2"}, "b": {"q3"} }, }, ) actual_dfa = nfa.determinize().minimize().trim() assert not actual_dfa.accepts([]) assert not actual_dfa.accepts(["a"]) assert not actual_dfa.accepts(["b"]) assert not actual_dfa.accepts(["a", "a"]) assert not actual_dfa.accepts(["a", "b", "a"]) assert not actual_dfa.accepts(["a", "a", "a", "b"]) assert actual_dfa.accepts(["a", "a", "a", "b", "b", "a", "b"])
def __init__( self, states: Set[StateType], alphabet: AlphabetLike[SymbolType], initial_state: StateType, accepting_states: Set[StateType], transition_function: Dict[StateType, Dict[SymbolType, Set[StateType]]], ): """ Initialize a NFA. :param states: the set of states. :param alphabet: the alphabet :param initial_state: the initial state :param accepting_states: the set of accepting states :param transition_function: the transition function """ super().__init__() alphabet = (MapAlphabet(alphabet) if not isinstance(alphabet, Alphabet) else alphabet) self._check_input(states, alphabet, initial_state, accepting_states, transition_function) self._states = frozenset(states) # type: FrozenSet[StateType] self._alphabet = alphabet # type: Alphabet[SymbolType] self._initial_state = initial_state # type: StateType self._accepting_states = frozenset( accepting_states) # type: FrozenSet[StateType] self._transition_function = ( transition_function ) # type: Dict[StateType, Dict[SymbolType, Set[StateType]]] self._build_indexes()
def test_some_accepting_states_not_in_states_raises_error(self): """Test that if some accepting states are not in the set of states we raise an error.""" with pytest.raises( ValueError, match="Accepting states .* not in the set of states."): nfa = SimpleNFA({"q0", "q1"}, MapAlphabet({"a"}), "q0", {"q2", "q3"}, {})
def test_coreachable_simple_case(self): dfa = SimpleDFA( {"q0", "q1", "q2"}, MapAlphabet({"a1", "a2"}), "q0", {"q0"}, {"q0": {"a1": "q0", "a2": "q1"}}, ) actual_coreachable = dfa.coreachable() expected_coreachable = SimpleDFA( {"q0"}, MapAlphabet({"a1", "a2"}), "q0", {"q0"}, {"q0": {"a1": "q0"}} ) assert actual_coreachable == expected_coreachable
def setup_class(cls): """Set the test up.""" cls.dfa = SimpleDFA( {0, 1, 2}, MapAlphabet(["a", "b", "c"]), 0, {2}, {0: {"a": 0, "b": 1}, 1: {"b": 1, "c": 2}, 2: {"c": 2}}, )
def test_is_complete_when_dfa_is_not_complete(self): """Test that the is_complete method return False if the SimpleDFA is not complete.""" dfa = SimpleDFA( {"q0", "q1"}, MapAlphabet({"a", "b"}), "q0", set(), {"q0": {"a": "q0", "b": "q1"}}, ) assert not dfa.is_complete()
def test_complete_when_dfa_is_already_complete(self): """Test that when we try to make complete an already complete SimpleDFA then the returned SimpleDFA is equal to the previous one.""" complete_dfa = SimpleDFA( {"q"}, MapAlphabet({"a"}), "q", set(), {"q": {"a": "q"}} ) new_dfa = complete_dfa.complete() assert complete_dfa == new_dfa
def _extract_states_from_transition_function( transition_function: Dict, ) -> Tuple[Set[StateType], Alphabet]: """Extract states from a transition function.""" states, symbols = set(), set() for start_state in transition_function: states.add(start_state) for symbol in transition_function[start_state]: end_state = transition_function[start_state][symbol] states.add(end_state) symbols.add(symbol) return states, MapAlphabet(symbols)
def _extract_states_from_nondet_transition_function(transition_function): # type: (Dict) -> Tuple[Set[StateType], Alphabet] """Extract states from a non-deterministic transition function.""" states, symbols = set(), set() for start_state in transition_function: states.add(start_state) for symbol in transition_function[start_state]: end_states = transition_function[start_state][symbol] states = states.union(end_states) symbols.add(symbol) return states, MapAlphabet(symbols)
def test_transition_function_with_invalid_symbols_raises_error(self): """Test that if a symbol of some transitions is invalid we raise an error.""" with pytest.raises( ValueError, match="Transition function not valid: symbols .* are not in the alphabet.", ): SimpleDFA( {"q0", "q1"}, MapAlphabet({"a"}), "q0", set(), {"q0": {"a": "q1"}, "q1": {"b": "q1"}}, )
def test_trim_simple_case(self): dfa = SimpleDFA( {"q0", "q1", "q2", "sink"}, MapAlphabet({"a", "b"}), "q0", {"q1"}, { "q0": {"a": "q0", "b": "q1"}, "q1": {"a": "sink", "b": "sink"}, "sink": {"a": "sink", "b": "sink"}, }, ) actual_trimmed_dfa = dfa.trim() expected_trimmed_dfa = SimpleDFA( {"q0", "q1"}, MapAlphabet({"a", "b"}), "q0", {"q1"}, {"q0": {"a": "q0", "b": "q1"}}, ) assert actual_trimmed_dfa == expected_trimmed_dfa
def test_transition_function_with_invalid_end_states_raises_error(self): """Test that if some of the ending states of the transitions is not in the set of states we raise an error.""" with pytest.raises( ValueError, match="Transition function not valid: states .* " "are not in the set of states.", ): SimpleDFA( {"q0", "q1"}, MapAlphabet({"a"}), "q0", set(), {"q0": {"a": "q1"}, "q1": {"a": "q2"}}, )
def test_equality(self): """Test that the equality between two SimpleDFA works correctly.""" assert self.dfa == self.dfa another_dfa = SimpleDFA( {0, 1, 2}, MapAlphabet(["a", "b", "c"]), 0, {2}, { 0: {"a": 0, "b": 1, "c": 2}, 1: {"a": 0, "b": 1, "c": 2}, 2: {"a": 0, "b": 1, "c": 2}, }, ) assert self.dfa == another_dfa
def test_accepts(self): dfa = SimpleDFA( {"q0", "q1"}, MapAlphabet({"a", "b"}), "q0", {"q1"}, {"q0": {"a": "q0", "b": "q1"}}, ) assert not dfa.accepts([]) assert not dfa.accepts(["a"]) assert not dfa.accepts(["a"]) assert dfa.accepts(["b"]) assert dfa.accepts(["a", "b"]) assert not dfa.accepts(["a", "a"]) assert not dfa.accepts(["b", "b"])
def test_dfa_from_transitions(): """Test that the constructor "from_transitions" works correctly.""" states = {"q0", "q1", "q2"} actions = MapAlphabet({"a0", "a1"}) initial_state = "q0" final_states = {"q2"} transition_function = {"q0": {"a0": "q1"}, "q1": {"a1": "q2"}} expected_dfa = SimpleDFA( states, actions, initial_state, final_states, transition_function ) actual_dfa = SimpleDFA.from_transitions( initial_state, final_states, transition_function ) assert expected_dfa == actual_dfa
def test_transition_function_with_symbols_not_in_alphabet_raises_error( self): """Test that if a symbol of some transitions is not in the alphabet we raise an error.""" with pytest.raises( ValueError, match= "Transition function not valid: some symbols are not in the alphabet.", ): nfa = SimpleNFA( {"q0", "q1"}, MapAlphabet({"a"}), "q0", set(), { "q0": { "a": {"q1"} }, "q1": { "b": {"q1"} } }, )
def test_minimize(self): dfa = SimpleDFA( {"q0", "q1", "q2", "q3", "q4"}, MapAlphabet({"a", "b", "c"}), "q0", {"q3", "q4"}, { "q0": {"a": "q1", "b": "q2"}, "q1": {"c": "q3"}, "q2": {"c": "q3"}, "q3": {"c": "q4"}, "q4": {"c": "q4"}, }, ) actual_minimized_dfa = dfa.minimize() # the renaming of the states is non deterministic, so we need to compare every substructure. assert len(actual_minimized_dfa._states) == 4 assert actual_minimized_dfa._alphabet == ArrayAlphabet(["a", "b", "c"]) assert actual_minimized_dfa.is_complete()
def test_level_to_accepting_states(self): dfa = SimpleDFA( {"q0", "q1", "q2", "q3", "q4", "q5"}, MapAlphabet({"a", "b"}), "q0", {"q3"}, { "q0": {"a": "q0", "b": "q1"}, "q1": {"a": "q0", "b": "q2"}, "q2": {"a": "q3", "b": "q4"}, "q3": {"a": "q3", "b": "q4"}, "q4": {"a": "q3", "b": "q5"}, }, ) assert dfa.levels_to_accepting_states() == { "q0": 3, "q1": 2, "q2": 1, "q3": 0, "q4": 1, "q5": -1, }
def test_empty_set_of_states_raises_error(self): """Test that when we try to instantiate a DFA with an empty set of states we raise an error.""" with pytest.raises(ValueError, match="The set of states cannot be empty."): SimpleDFA(set(), MapAlphabet({"a"}), "q0", set(), {})
def test_initial_state_not_in_states_raises_error(self): """Test that if the initial state is not in the set of states we raise an error.""" with pytest.raises( ValueError, match="Initial state .* not in the set of states." ): SimpleDFA(set("q1"), MapAlphabet({"a"}), "q0", set(), {})
def test_is_complete_when_dfa_is_complete(self): """Test that the is_complete method return True if the SimpleDFA is complete.""" dfa = SimpleDFA({"q"}, MapAlphabet({"a"}), "q", set(), {"q": {"a": "q"}}) assert dfa.is_complete()