-
Notifications
You must be signed in to change notification settings - Fork 38
Expand file tree
/
Copy pathDfa.py
More file actions
114 lines (84 loc) · 3.89 KB
/
Dfa.py
File metadata and controls
114 lines (84 loc) · 3.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
from typing import Generic, Dict
from aalpy.base import AutomatonState, DeterministicAutomaton
from aalpy.base.Automaton import InputType
class DfaState(AutomatonState, Generic[InputType]):
"""
Single state of a deterministic finite automaton.
"""
def __init__(self, state_id, is_accepting=False):
super().__init__(state_id)
self.transitions : Dict[InputType, DfaState] = dict()
self.is_accepting = is_accepting
@property
def output(self):
return self.is_accepting
class Dfa(DeterministicAutomaton[DfaState[InputType]]):
"""
Deterministic finite automaton.
"""
def __init__(self, initial_state: DfaState, states):
super().__init__(initial_state, states)
def step(self, letter):
"""
Args:
letter: single input that is looked up in the transition table of the DfaState
Returns:
True if the reached state is an accepting state, False otherwise
"""
if letter is not None:
self.current_state = self.current_state.transitions[letter]
return self.current_state.is_accepting
def compute_characterization_set(self, char_set_init=None, online_suffix_closure=True, split_all_blocks=True,
return_same_states=False, raise_warning=True):
return super(Dfa, self).compute_characterization_set(char_set_init if char_set_init else [()],
online_suffix_closure, split_all_blocks,
return_same_states, raise_warning)
def compute_output_seq(self, state, sequence):
if not sequence:
return [state.is_accepting]
return super(Dfa, self).compute_output_seq(state, sequence)
def execute_sequence(self, origin_state, seq):
if not seq:
self.current_state = origin_state
return self.current_state.output
return super(Dfa, self).execute_sequence(origin_state, seq)
def to_state_setup(self):
state_setup_dict = {}
# ensure prefixes are computed
self.compute_prefixes()
sorted_states = sorted(self.states, key=lambda x: len(x.prefix) if x.prefix is not None else len(self.states))
for s in sorted_states:
state_setup_dict[s.state_id] = (s.is_accepting, {k: v.state_id for k, v in s.transitions.items()})
return state_setup_dict
@staticmethod
def from_state_setup(state_setup : dict, **kwargs):
"""
First state in the state setup is the initial state.
Example state setup:
state_setup = {
"a": (True, {"x": "b1", "y": "a"}),
"b1": (False, {"x": "b2", "y": "a"}),
"b2": (True, {"x": "b3", "y": "a"}),
"b3": (False, {"x": "b4", "y": "a"}),
"b4": (False, {"x": "c", "y": "a"}),
"c": (True, {"x": "a", "y": "a"}),
}
Args:
state_setup: map from state_id to tuple(output and transitions_dict)
Returns:
DFA
"""
# state_setup should map from state_id to tuple(is_accepting and transitions_dict)
# build states with state_id and output
states = {key: DfaState(key, val[0]) for key, val in state_setup.items()}
# add transitions to states
for state_id, state in states.items():
for _input, target_state_id in state_setup[state_id][1].items():
state.transitions[_input] = states[target_state_id]
# states to list
states = [state for state in states.values()]
# build moore machine with first state as starting state
dfa = Dfa(states[0], states)
for state in states:
state.prefix = dfa.get_shortest_path(dfa.initial_state, state)
return dfa