Coverage for /home/marcofavorito/workfolder/pythomata/pythomata/impl/simple.py : 19%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2"""This package contains naive implementations of DFA and NFA."""
3import itertools
4import pprint
5import queue
6from copy import deepcopy, copy
7from typing import Set, Dict, Tuple, FrozenSet, Iterable, cast, Optional
9import graphviz
11from pythomata._internal_utils import greatest_fixpoint, least_fixpoint
12from pythomata.alphabets import MapAlphabet
13from pythomata.core import StateType, SymbolType, Alphabet, DFA, FiniteAutomaton
14from pythomata.utils import powerset
17class SimpleDFA(DFA[StateType, SymbolType]):
18 """
19 Implementation of a simple DFA.
21 It is a naive implementation where all the components of the DFA are
22 stored explicitly.
24 If the DFA is not complete (i.e. the transition function is partial),
25 the successor state is None in those cases where the transition is not specified.
26 """
28 def __init__(
29 self,
30 states: Set[StateType],
31 alphabet: Alphabet,
32 initial_state: StateType,
33 final_states: Set[StateType],
34 transition_function: Dict[StateType, Dict[SymbolType, StateType]],
35 ):
36 """
37 Initialize a DFA.
39 :param states: the set of states.
40 :param alphabet: the alphabet
41 :param initial_state: the initial state
42 :param final_states: the set of accepting states
43 :param transition_function: the transition function
44 """
45 self._check_input(
46 states, alphabet, initial_state, final_states, transition_function
47 )
49 self._states = states
50 self._alphabet = alphabet
51 self._initial_state = initial_state
52 self._final_states = final_states
53 self._transition_function = transition_function
55 self._build_indexes()
57 @property
58 def alphabet(self) -> Alphabet:
59 """Get the alphabet."""
60 return self._alphabet
62 @property
63 def transition_function(self) -> Dict:
64 """Get the transition function."""
65 return self._transition_function
67 @property
68 def initial_state(self) -> StateType:
69 """Get the initial state."""
70 return self._initial_state
72 def get_successor(self, state: StateType, symbol: SymbolType) -> StateType:
73 """Get the successor."""
74 return self.transition_function.get(state, {}).get(symbol, None)
76 @property
77 def states(self) -> Set[StateType]:
78 """Get the set of states."""
79 return self._states
81 @property
82 def final_states(self) -> Set[StateType]:
83 """Get the set of final states."""
84 return self._final_states
86 @classmethod
87 def _check_input(
88 cls,
89 states: Set[StateType],
90 alphabet: Alphabet,
91 initial_state: StateType,
92 accepting_states: Set[StateType],
93 transition_function: Dict,
94 ):
95 """
96 Check the consistency of the constructor parameters.
98 :return: None
99 :raises ValueError: if some consistency check fails.
100 """
101 _check_at_least_one_state(states)
102 _check_no_none_states(states)
103 _check_initial_state_in_states(initial_state, states)
104 _check_accepting_states_in_states(accepting_states, states)
105 _check_transition_function_is_valid_wrt_states_and_alphabet(
106 transition_function, states, alphabet
107 )
109 def _build_indexes(self):
110 """Build indexes for several components of the object."""
111 self._idx_to_state = list(self._states)
112 self._state_to_idx = dict(map(reversed, enumerate(self._idx_to_state)))
113 self._idx_to_symbol = list(self._alphabet)
114 self._symbol_to_idx = dict(map(reversed, enumerate(self._idx_to_symbol)))
116 # state -> action -> state
117 self._idx_transition_function = {
118 self._state_to_idx[state]: {
119 self._symbol_to_idx[symbol]: self._state_to_idx[
120 self._transition_function[state][symbol]
121 ]
122 for symbol in self._transition_function.get(state, {})
123 }
124 for state in self._states
125 }
127 # state -> (action, state)
128 self._idx_delta_by_state = {}
129 for s in self._idx_transition_function:
130 self._idx_delta_by_state[s] = set(
131 list(self._idx_transition_function[s].items())
132 )
134 self._idx_initial_state = self._state_to_idx[self._initial_state]
135 self._idx_accepting_states = frozenset(
136 self._state_to_idx[s] for s in self.final_states
137 )
139 def __eq__(self, other):
140 """Check equality with another object."""
141 if not isinstance(other, SimpleDFA):
142 return False
144 return (
145 self._states == other.states
146 and self._alphabet == other.alphabet
147 and self._initial_state == other.initial_state
148 and self.final_states == other.final_states
149 and self._transition_function == other.transition_function
150 )
152 @staticmethod
153 def from_transitions(initial_state, accepting_states, transition_function):
154 # type: (StateType, Set[StateType], Dict[StateType, Dict[SymbolType, StateType]]) -> SimpleDFA
155 """
156 Initialize a DFA without explicitly specifying the set of states and the alphabet.
158 :param initial_state: the initial state.
159 :param accepting_states: the accepting state.
160 :param transition_function: the transition function.
161 :return: the DFA.
162 """
163 states, alphabet = _extract_states_from_transition_function(
164 transition_function) # type: Set[StateType], Alphabet[SymbolType]
166 return SimpleDFA(
167 states, alphabet, initial_state, accepting_states, transition_function
168 )
170 def is_complete(self) -> bool:
171 """
172 Check whether the automaton is complete.
174 :return: True if the automaton is complete, False otherwise.
175 """
176 complete_number_of_transitions = len(self.states) * len(self.alphabet)
177 current_number_of_transitions = sum(
178 len(self._transition_function[state]) for state in self._transition_function
179 )
180 return complete_number_of_transitions == current_number_of_transitions
182 def complete(self) -> "SimpleDFA":
183 """
184 Complete the DFA.
186 :return: the completed DFA, if it's not already complete.
187 | Otherwise, return the caller instance.
188 """
189 if self.is_complete():
190 return self
191 else:
192 return self._complete()
194 def _complete(self) -> "SimpleDFA":
195 """
196 Complete the DFA.
198 :return: the completed DFA.
199 """
200 sink_state = _generate_sink_name(self._states)
201 transitions = deepcopy(self._transition_function)
203 # for every missing transition, add a transition towards the sink state.
204 for state in self._states:
205 for action in self._alphabet:
206 cur_transitions = self._transition_function.get(state, {})
207 end_state = cur_transitions.get(action, None) # type: ignore
208 if end_state is None:
209 transitions.setdefault(state, {})[action] = sink_state
211 # for every action, add a transition from the sink state to the sink state
212 for action in self._alphabet:
213 transitions.setdefault(sink_state, {})[action] = sink_state
215 return SimpleDFA(
216 self.states.union({sink_state}),
217 self.alphabet,
218 self.initial_state,
219 self.final_states,
220 transitions,
221 )
223 def minimize(self) -> 'SimpleDFA':
224 """
225 Minimize the DFA.
227 :return: the minimized DFA.
228 """
229 dfa = self
230 dfa = dfa.complete()
232 def greatest_fixpoint_condition(el: Tuple[int, int], current_set: Set):
233 """Condition to say whether the pair must be removed from the bisimulation relation."""
234 s, t = el
235 s_is_final = s in dfa._idx_accepting_states
236 t_is_final = t in dfa._idx_accepting_states
237 if s_is_final and not t_is_final or not s_is_final and t_is_final:
238 return True
240 s_transitions = dfa._idx_transition_function.get(s, {})
241 t_transitions = dfa._idx_transition_function.get(t, {})
243 for a, s_prime in s_transitions.items():
244 t_prime = t_transitions.get(a, None)
245 if t_prime is not None and (s_prime, t_prime) in current_set:
246 continue
247 else:
248 return True
250 for a, t_prime in t_transitions.items():
251 s_prime = s_transitions.get(a, None)
252 if s_prime is not None and (s_prime, t_prime) in current_set:
253 continue
254 else:
255 return True
257 return False
259 result = greatest_fixpoint(
260 set(
261 itertools.product(
262 range(len(dfa._idx_to_state)), range(len(dfa._idx_to_state))
263 )
264 ),
265 condition=greatest_fixpoint_condition,
266 )
268 state2equiv_class = {} # type: Dict[int, FrozenSet[int]]
269 for (s, t) in result:
270 state2equiv_class.setdefault(s, frozenset())
271 state2equiv_class[s] = state2equiv_class[s].union(frozenset({t}))
272 equivalence_classes = set(map(lambda x: frozenset(x), state2equiv_class.values()))
273 equiv_class2new_state = dict(
274 (ec, i) for i, ec in enumerate(equivalence_classes)
275 )
277 new_transition_function = {} # type: Dict[int, Dict[SymbolType, int]]
278 for state in dfa._idx_delta_by_state:
279 new_state = equiv_class2new_state[state2equiv_class[state]]
280 for action, next_state in dfa._idx_delta_by_state[state]:
281 new_next_state = equiv_class2new_state[state2equiv_class[next_state]]
283 new_transition_function.setdefault(new_state, {})[
284 dfa._idx_to_symbol[action]
285 ] = new_next_state
287 new_states = frozenset(equiv_class2new_state.values())
288 new_initial_state = equiv_class2new_state[
289 state2equiv_class[dfa._idx_initial_state]
290 ]
291 new_final_states = frozenset(
292 s
293 for s in set(
294 equiv_class2new_state[state2equiv_class[old_state]]
295 for old_state in dfa._idx_accepting_states
296 )
297 )
299 new_dfa = SimpleDFA(
300 set(new_states),
301 dfa.alphabet,
302 new_initial_state,
303 set(new_final_states),
304 new_transition_function,
305 )
306 return new_dfa
308 def reachable(self):
309 """
310 Get the equivalent reachable automaton.
312 :return: the reachable DFA.
313 """
315 def reachable_fixpoint_rule(current_set: Set) -> Iterable:
316 result = set()
317 for el in current_set:
318 for a in self._idx_transition_function.get(el, {}):
319 result.add(self._idx_transition_function[el][a])
320 return result
322 result = least_fixpoint({self._idx_initial_state}, reachable_fixpoint_rule)
324 idx_new_states = result
325 new_transition_function = {}
326 for s in idx_new_states:
327 for a in self._idx_transition_function.get(s, {}):
328 next_state = self._idx_transition_function[s][a]
329 if next_state in idx_new_states:
330 new_transition_function.setdefault(self._idx_to_state[s], {})
331 state = self._idx_to_state[s]
332 new_transition_function[state][
333 self._idx_to_symbol[a]
334 ] = self._idx_to_state[next_state]
336 new_states = set(map(lambda x: self._idx_to_state[x], idx_new_states))
337 new_final_states = new_states.intersection(self._final_states)
339 return SimpleDFA(
340 new_states,
341 self.alphabet,
342 self._initial_state,
343 new_final_states,
344 new_transition_function,
345 )
347 def coreachable(self) -> 'SimpleDFA':
348 """
349 Get the equivalent co-reachable automaton.
351 :return: the co-reachable DFA.
352 """
353 # least fixpoint
355 def coreachable_fixpoint_rule(current_set: Set) -> Iterable:
356 result = set()
357 for s in range(len(self._states)):
358 for a in self._idx_transition_function.get(s, {}):
359 next_state = self._idx_transition_function[s][a]
360 if next_state in current_set:
361 result.add(s)
362 break
363 return result
365 result = least_fixpoint(
366 set(self._idx_accepting_states), coreachable_fixpoint_rule
367 )
369 idx_new_states = result
370 if self._idx_initial_state not in idx_new_states:
371 return EmptyDFA(alphabet=self.alphabet)
373 new_states = set(map(lambda x: self._idx_to_state[x], idx_new_states))
374 new_transition_function = {} # type: Dict[StateType, Dict[SymbolType, StateType]]
375 for s in idx_new_states:
376 for a in self._idx_transition_function.get(s, {}):
377 next_state = self._idx_transition_function[s][a]
378 if next_state in idx_new_states:
379 new_transition_function.setdefault(self._idx_to_state[s], {})
380 state = self._idx_to_state[s]
381 new_transition_function[state][
382 self._idx_to_symbol[a]
383 ] = self._idx_to_state[next_state]
385 return SimpleDFA(
386 new_states,
387 self.alphabet,
388 self.initial_state,
389 set(self._final_states),
390 new_transition_function,
391 )
393 def trim(self) -> 'SimpleDFA':
394 """
395 Trim the automaton.
397 :return: the trimmed DFA.
398 """
399 dfa = self
400 dfa = dfa.complete()
401 dfa = dfa.reachable()
402 dfa = dfa.coreachable()
403 return dfa
405 def levels_to_accepting_states(self) -> dict:
406 """
407 Return a dict from states to level.
409 i.e. the number of steps to reach any accepting state.
410 level = -1 if the state cannot reach any accepting state
411 """
412 res = {accepting_state: 0 for accepting_state in self.final_states}
413 level = 0
415 # least fixpoint
416 z_current = set() # type: Set[StateType]
417 z_next = set(self.final_states)
419 while z_current != z_next:
420 level += 1
421 z_current = z_next
422 z_next = copy(z_current)
423 for state in self._transition_function:
424 for action in self._transition_function[state]:
425 if state in z_current:
426 continue
427 next_state = self._transition_function[state][action]
428 if next_state in z_current:
429 z_next.add(state)
430 res[state] = level
431 break
433 z_current = z_next
434 for failure_state in filter(lambda x: x not in z_current, self._states):
435 res[failure_state] = -1
437 return res
439 def renumbering(self) -> "SimpleDFA":
440 """Deterministically renumber all the states.
442 :raises ValueError: if the symbols of the transitions
443 | cannot be sorted uniquely
444 """
445 idx = 0
446 visited_states = {self._idx_initial_state}
447 q = queue.Queue() # type: queue.Queue
449 old_state_to_number = {}
451 q.put(self._idx_initial_state)
452 while not q.empty():
453 current_state = q.get()
454 old_state_to_number[current_state] = idx
455 idx += 1
457 try:
458 next_actions = sorted(
459 self._idx_transition_function[current_state],
460 key=lambda x: self._idx_to_symbol[x],
461 )
462 except TypeError:
463 raise TypeError("Cannot sort the transition symbols.")
465 for action in next_actions:
466 cur_tf = self._idx_transition_function[current_state]
467 next_state = cur_tf[action]
468 if next_state not in visited_states:
469 visited_states.add(next_state)
470 q.put(next_state)
472 new_states = set(range(len(old_state_to_number)))
473 new_initial_state = old_state_to_number[self._idx_initial_state]
474 new_accepting_states = {
475 old_state_to_number[x] for x in self._idx_accepting_states
476 }
477 new_transition_function = {
478 old_state_to_number[start]: {
479 self._idx_to_symbol[symbol]: old_state_to_number[end]
480 for symbol, end in self._idx_transition_function[start].items()
481 }
482 for start in self._idx_transition_function
483 }
485 return SimpleDFA(
486 cast(Set[StateType], new_states),
487 self.alphabet,
488 new_initial_state,
489 cast(Set[SimpleDFA], new_accepting_states),
490 cast(Dict[StateType, Dict[SymbolType, StateType]], new_transition_function),
491 )
493 def to_graphviz(self, title: Optional[str] = None) -> graphviz.Digraph:
494 """Convert to graphviz.Digraph object."""
495 g = graphviz.Digraph(format="svg")
496 g.node("fake", style="invisible")
497 for state in self.states:
498 if state in self.initial_states:
499 if state in self.final_states:
500 g.node(str(state), root="true", shape="doublecircle")
501 else:
502 g.node(str(state), root="true")
503 elif state in self.final_states:
504 g.node(str(state), shape="doublecircle")
505 else:
506 g.node(str(state))
508 for i in self.initial_states:
509 g.edge("fake", str(i), style="bold")
510 for start in self.transition_function:
511 for symbol, end in self._transition_function[start].items():
512 g.edge(str(start), str(end), label=str(symbol))
514 if title is not None:
515 g.attr(label=title)
516 g.attr(fontsize="20")
518 return g
521class EmptyDFA(SimpleDFA):
522 """Implementation of an empty DFA."""
524 def __init__(self, alphabet: Alphabet):
525 """Initialize an empty DFA."""
526 super().__init__({"0"}, alphabet, "0", set(), {})
528 def __eq__(self, other):
529 """Check equality with another object."""
530 return type(self) == type(other) == EmptyDFA
533class SimpleNFA(FiniteAutomaton[StateType, SymbolType]):
534 """This class implements a NFA."""
536 def __init__(
537 self,
538 states: Set[StateType],
539 alphabet: Alphabet[SymbolType],
540 initial_state: StateType,
541 accepting_states: Set[StateType],
542 transition_function: Dict[StateType, Dict[SymbolType, Set[StateType]]],
543 ):
544 """
545 Initialize a NFA.
547 :param states: the set of states.
548 :param alphabet: the alphabet
549 :param initial_state: the initial state
550 :param accepting_states: the set of accepting states
551 :param transition_function: the transition function
552 """
553 self._check_input(
554 states, alphabet, initial_state, accepting_states, transition_function
555 )
557 self._states = frozenset(states) # type: FrozenSet[StateType]
558 self._alphabet = alphabet # type: Alphabet[SymbolType]
559 self._initial_state = initial_state # type: StateType
560 self._accepting_states = frozenset(accepting_states) # type: FrozenSet[StateType]
561 self._transition_function = (
562 transition_function
563 ) # type: Dict[StateType, Dict[SymbolType, Set[StateType]]]
565 self._build_indexes()
567 def _build_indexes(self):
568 self._idx_to_state = sorted(self._states)
569 self._state_to_idx = dict(map(reversed, enumerate(self._idx_to_state)))
570 self._idx_to_symbol = sorted(self._alphabet)
571 self._symbol_to_idx = dict(map(reversed, enumerate(self._idx_to_symbol)))
573 # state -> action -> state
574 self._idx_transition_function = {
575 self._state_to_idx[state]: {
576 self._symbol_to_idx[symbol]: set(
577 map(
578 lambda x: self._state_to_idx[x],
579 self._transition_function[state][symbol],
580 )
581 )
582 for symbol in self._transition_function.get(state, {})
583 }
584 for state in self._states
585 }
587 self._idx_initial_state = self._state_to_idx[self._initial_state]
588 self._idx_accepting_states = frozenset(
589 self._state_to_idx[s] for s in self._accepting_states
590 )
592 @classmethod
593 def _check_input(
594 cls,
595 states: Set[StateType],
596 alphabet: Alphabet[SymbolType],
597 initial_state: StateType,
598 accepting_states: Set[StateType],
599 transition_function: Dict[StateType, Dict[SymbolType, Set[StateType]]],
600 ):
601 _check_at_least_one_state(states)
602 _check_initial_state_in_states(initial_state, states)
603 _check_accepting_states_in_states(accepting_states, states)
604 _check_nondet_transition_function_is_valid_wrt_states_and_alphabet(
605 transition_function, states, alphabet
606 )
608 @property
609 def alphabet(self) -> Alphabet:
610 """Get the alphabet."""
611 return self._alphabet
613 @property
614 def states(self) -> Set[StateType]:
615 """Get the states."""
616 return set(self._states)
618 @property
619 def initial_states(self) -> Set[StateType]:
620 """Get the initial states."""
621 return {self._initial_state}
623 @property
624 def final_states(self) -> Set[StateType]:
625 """Get the final states."""
626 return set(self._accepting_states)
628 @property
629 def transition_function(self) -> Dict[StateType, Dict[SymbolType, Set[StateType]]]:
630 """Get the transition function."""
631 return self._transition_function
633 def get_successors(self, state: StateType, symbol: SymbolType) -> Set[StateType]:
634 """Get the successors states."""
635 return self._transition_function.get(state, {}).get(symbol, set())
637 def to_graphviz(self, title: Optional[str] = None) -> graphviz.Digraph:
638 """Convert to graphviz.Digraph object."""
639 g = graphviz.Digraph(format="svg")
640 g.node("fake", style="invisible")
641 for state in self.states:
642 if state in self.initial_states:
643 if state in self.final_states:
644 g.node(str(state), root="true", shape="doublecircle")
645 else:
646 g.node(str(state), root="true")
647 elif state in self.final_states:
648 g.node(str(state), shape="doublecircle")
649 else:
650 g.node(str(state))
652 for i in self.initial_states:
653 g.edge("fake", str(i), style="bold")
654 for start in self.transition_function:
655 for symbol, states in self._transition_function[start].items():
656 for end in states:
657 g.edge(str(start), str(end), label=str(symbol))
659 if title is not None:
660 g.attr(label=title)
661 g.attr(fontsize="20")
663 return g
665 def determinize(self) -> DFA:
666 """
667 Do determinize the NFA.
669 :return: the DFA equivalent to the DFA.
670 """
671 nfa = self
673 new_states = {macro_state for macro_state in powerset(nfa._states)}
674 initial_state = frozenset([nfa._initial_state])
675 final_states = {
676 q for q in new_states if len(q.intersection(nfa._accepting_states)) != 0
677 }
678 transition_function = {} # type: Dict[FrozenSet[StateType], Dict[SymbolType, FrozenSet[StateType]]]
680 for state_set in new_states:
681 for action in nfa.alphabet:
683 next_macrostate = set()
684 for s in state_set:
685 for next_state in nfa._transition_function.get(s, {}).get(
686 action, set()
687 ):
688 next_macrostate.add(next_state)
690 transition_function.setdefault(state_set, {})[action] = frozenset(next_macrostate)
692 return SimpleDFA(
693 new_states,
694 nfa.alphabet,
695 initial_state,
696 set(final_states),
697 transition_function,
698 )
700 @classmethod
701 def from_transitions(cls, initial_state, accepting_states, transition_function):
702 # type: (StateType, Set[StateType], Dict[StateType, Dict[SymbolType, Set[StateType]]]) -> SimpleNFA
703 """
704 Initialize a DFA without explicitly specifying the set of states and the alphabet.
706 :param initial_state: the initial state.
707 :param accepting_states: the accepting state.
708 :param transition_function: the (nondeterministic) transition function.
709 :return: the NFA.
710 """
711 states, alphabet = _extract_states_from_nondet_transition_function(
712 transition_function
713 ) # type: Set[StateType], Alphabet[SymbolType]
715 return SimpleNFA(
716 states, alphabet, initial_state, accepting_states, transition_function
717 )
719 def __eq__(self, other):
720 """Check the equality with another object."""
721 if not isinstance(other, SimpleNFA):
722 return False
723 return (
724 self.states == other.states
725 and self.alphabet == other.alphabet
726 and self.initial_states == other.initial_states
727 and self.final_states == other.final_states
728 and self.transition_function == other.transition_function
729 )
732def _check_at_least_one_state(states: Set[StateType]):
733 """Check that the set of states is not empty."""
734 if len(states) == 0:
735 raise ValueError(
736 "The set of states cannot be empty. Found {} instead.".format(pprint.pformat(states))
737 )
740def _check_no_none_states(states: Set[StateType]):
741 """Check that the set of states does not contain a None."""
742 if any(s is None for s in states):
743 raise ValueError("A state cannot be 'None'.")
746def _check_initial_state_in_states(initial_state: StateType, states: Set[StateType]):
747 """Check that the initial state is in the set of states."""
748 if initial_state not in states:
749 raise ValueError(
750 "Initial state {} not in the set of states.".format(
751 pprint.pformat(initial_state)
752 )
753 )
756def _check_accepting_states_in_states(accepting_states: Set[StateType], states: Set[StateType]):
757 """Check that all the accepting states are in the set of states."""
758 if not states.issuperset(accepting_states):
759 wrong_accepting_states = accepting_states.difference(states)
760 raise ValueError(
761 "Accepting states {} not in the set of states.".format(
762 pprint.pformat(wrong_accepting_states)
763 )
764 )
767def _check_transition_function_is_valid_wrt_states_and_alphabet(
768 transition_function: Dict, states: Set[StateType], alphabet: Alphabet
769):
770 """Check that a transition function is compatible with the set of states and the alphabet."""
771 if len(transition_function) == 0:
772 return
774 extracted_states, extracted_alphabet = _extract_states_from_transition_function(
775 transition_function
776 ) # type: Set[StateType], Alphabet
777 if not all(s in states for s in extracted_states):
778 raise ValueError(
779 "Transition function not valid: "
780 "states {} are not in the set of states.".format(
781 extracted_states.difference(states)
782 )
783 )
784 if not all(s in alphabet for s in extracted_alphabet):
785 raise ValueError(
786 "Transition function not valid: "
787 "symbols {} are not in the alphabet.".format(
788 set(extracted_alphabet).difference(alphabet)
789 )
790 )
793def _check_nondet_transition_function_is_valid_wrt_states_and_alphabet(
794 transition_function: Dict,
795 states: Set[StateType],
796 alphabet: Alphabet[SymbolType],
797):
798 """Check that a non-det tr. function is compatible wrt the set of states and the alphabet."""
799 if len(transition_function) == 0:
800 return
802 extracted_states, extracted_alphabet = _extract_states_from_nondet_transition_function(
803 transition_function
804 ) # type: Set[StateType], Alphabet[SymbolType]
805 if not all(s in states for s in extracted_states):
806 raise ValueError(
807 "Transition function not valid: "
808 "states {} are not in the set of states.".format(
809 extracted_states.difference(states)
810 )
811 )
812 if not all(s in alphabet for s in extracted_alphabet):
813 raise ValueError(
814 "Transition function not valid: "
815 "some symbols are not in the alphabet."
816 )
819def _extract_states_from_nondet_transition_function(transition_function):
820 # type: (Dict) -> Tuple[Set[StateType], Alphabet]
821 """Extract states from a non-deterministic transition function."""
822 states, symbols = set(), set()
823 for start_state in transition_function:
824 states.add(start_state)
825 for symbol in transition_function[start_state]:
826 end_states = transition_function[start_state][symbol]
827 states = states.union(end_states)
828 symbols.add(symbol)
830 return states, MapAlphabet(symbols)
833def _extract_states_from_transition_function(
834 transition_function: Dict
835) -> Tuple[Set[StateType], Alphabet]:
836 """Extract states from a transition function."""
837 states, symbols = set(), set()
838 for start_state in transition_function:
839 states.add(start_state)
840 for symbol in transition_function[start_state]:
841 end_state = transition_function[start_state][symbol]
842 states.add(end_state)
843 symbols.add(symbol)
845 return states, MapAlphabet(symbols)
848def _generate_sink_name(states: Set[StateType]):
849 """Generate a sink name."""
850 sink_name = "sink"
851 while True:
852 if sink_name not in states:
853 return sink_name
854 sink_name = "_" + sink_name