class FiniteStateAutomaton:
"""A finite state automaton
Parameters
----------
alphabet
states
initial_state
final states
transition_graph
"""
def __or__(self, other):
return self.union(other)
def _deepcopy(self):
= copy(self)
fsa del fsa._generator
return deepcopy(fsa)
def _relabel_fsas(self, other):
"""
append tag to the input/ouput states throughout two FSAs
Parameters
----------
other : FiniteStateAutomaton
"""
= self._deepcopy()._relabel_states(str(id(self)))
fsa1 = other._deepcopy()._relabel_states(str(id(other)))
fsa2
return fsa1, fsa2
def _relabel_states(self, tag):
"""
append tag to the input/ouput states throughout the FSA
Parameters
----------
tag : str
"""
= {s: s+'_'+tag for s in self._states}
state_map
self._states = {state_map[s] for s in self._states}
self._initial_state += '_'+tag
self._final_states = {state_map[s] for s in self._final_states}
self._transition_function = self._transition_function
self._transition_function.relabel_states(state_map)
return self
def union(self, other: 'FiniteStateAutomaton') -> 'FiniteStateAutomaton':
"""
union this FSA with another
Parameters
----------
other : FiniteStateAutomaton
Returns
-------
FiniteStateAutomaton
"""
= "you still need to implement FiniteStateAutomaton.union"
msg raise NotImplementedError(msg)
The regular operations
Union
Given NFAs \(M_1 = \langle Q_1, \Sigma_1, \delta_1, q_1, F_1 \rangle\) recognizing \(A = \mathbb{L}(M_1)\) and \(M_2 = \langle Q_2, \Sigma_2, \delta_2, q_2, F_2 \rangle\) recognizing \(B = \mathbb{L}(M_2)\), we construct \(M = \text{union}(M_1, M_2) = \langle Q, \Sigma, \delta, q_0, F \rangle\) recognizing \(A \cup B = \mathbb{L}(M) = \mathbb{L}(\text{union}(M_1, M_2))\):
- Relabel \(Q_1\) and \(Q_2\) so they are mutually exclusive and do not contain \(q_0\); \(Q = Q'_1 \cup Q'_2 \cup \{q_0\}\) and \(\Sigma = \Sigma_1 \cup \Sigma_2\)
- Define \[\delta(q, \sigma) = \begin{cases} \{q_1, q_2\} & \text{if } q=q_0 \land \sigma=\epsilon \\ \delta_1(q, \sigma) & \text{if } q\in Q_1 \\ \delta_2(q, \sigma) & \text{if } q\in Q_2 \\ \text{undefined} & \text{otherwise} \\ \end{cases}\]
- Define \(F = F_1 \cup F_2\)
Concatenation
Given NFAs \(M_1 = \langle Q_1, \Sigma_1, \delta_1, q_1, F_1 \rangle\) recognizing \(A = \mathbb{L}(M_1)\) and \(M_2 = \langle Q_2, \Sigma_2, \delta_2, q_2, F_2 \rangle\) recognizing \(B = \mathbb{L}(M_2)\), we construct \(M = \text{concatenate}(M_1, M_2) = \langle Q, \Sigma, \delta, q_1, F_2 \rangle\) recognizing \(A \circ B = \mathbb{L}(M) = \mathbb{L}(\text{concatenate}(M_1, M_2))\):
- Relabel \(Q_1\) and \(Q_2\) so they are mutually exclusive; \(Q = Q'_1 \cup Q'_2\) and \(\Sigma = \Sigma_1 \cup \Sigma_2\)
- Define \[\delta(q, \sigma) = \begin{cases} \delta_1(q, \sigma) & \text{if } q\in Q_1 \land q\not\in F_1 \\ \delta_1(q, \sigma) & \text{if } q\in F_1 \land \sigma \neq \epsilon \\ \delta_1(q, \sigma) \cup \{q_2\} & \text{if } q\in F_1 \land \sigma = \epsilon \\ \delta_2(q, \sigma)& \text{if } q\in Q_2 \\ \end{cases}\]
class FiniteStateAutomaton(FiniteStateAutomaton):
"""A finite state automaton
Parameters
----------
alphabet
states
initial_state
final states
transition_graph
"""
def __add__(self, other):
return self.concatenate(other)
def __pow__(self, k):
return self.exponentiate(k)
def concatenate(self, other: 'FiniteStateAutomaton'):
"""
concatenate this FSA with another
Parameters
----------
other : FiniteStateAutomaton
Returns
-------
FiniteStateAutomaton
"""
= "you still need to implement FiniteStateAutomaton.concatenate"
msg raise NotImplementedError(msg)
def exponentiate(self, k: int) -> 'FiniteStateAutomaton':
"""
concatenate this FSA k times
Parameters
----------
k : int
the number of times to repeat; must be >1
Returns
-------
FiniteStateAutomaton
"""
if k <= 1:
raise ValueError("must be >1")
= self
new
for i in range(1,k):
+= self
new
return new
Kleene Closure
Given an NFA \(M = \langle Q, \Sigma, \delta, q_0, F \rangle\) recognizing \(A = \mathbb{L}(M)\), the NFA \(N = \text{kleene}(M) = \langle Q \cup \{q'_0\}, \Sigma, \delta', q'_0\not\in Q, F' \rangle\) recognizing \(A^* = \mathbb{L}(N) = \mathbb{L}(\text{kleene}(M))\):
- Define \[\delta'(q, \sigma) = \begin{cases} q_0 & \text{if } (q = q'_0 \lor q \in F) \land \sigma = \epsilon \\ \delta(q, \sigma) & \text{otherwise} \\ \end{cases}\]
class FiniteStateAutomaton:
"""A finite state automaton
Parameters
----------
alphabet
states
initial_state
final states
transition_graph
"""
def kleene_star(self) -> 'FiniteStateAutomaton':
"""
construct the kleene closure machine
Returns
-------
FiniteStateAutomaton
"""
= self._deepcopy()
fsa
= fsa._transition_function
new_transition ''): fsa._initial_state
new_transition.add_transitions({(s, for s in fsa._final_states})
return FiniteStateAutomaton(fsa._alphabet, fsa._states,
fsa._initial_state, fsa._final_states, new_transition.transition_graph)