Skip to content

Commit 117b22d

Browse files
committed
chore: Add pytest-cov dependency for improved test coverage reporting
1 parent 68e173c commit 117b22d

12 files changed

Lines changed: 2155 additions & 0 deletions
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""
2+
FSA Algorithms
3+
4+
Implementation of core finite state automata algorithms:
5+
- ε-closure computation for ε-NFA support
6+
- Subset construction for NFA→DFA conversion
7+
- Hopcroft's algorithm for DFA minimization
8+
"""
9+
10+
from .epsilon_closure import epsilon_closure, epsilon_closure_set
11+
from .nfa_to_dfa import nfa_to_dfa, subset_construction
12+
from .minimization import minimize_dfa, hopcroft_minimization
13+
14+
__all__ = [
15+
# ε-closure
16+
"epsilon_closure",
17+
"epsilon_closure_set",
18+
# NFA to DFA conversion
19+
"nfa_to_dfa",
20+
"subset_construction",
21+
# DFA minimization
22+
"minimize_dfa",
23+
"hopcroft_minimization",
24+
]
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
"""
2+
ε-Closure Computation
3+
4+
Implements ε-closure computation for ε-NFA support.
5+
The ε-closure of a state is the set of states reachable from that state
6+
using only ε-transitions (including the state itself).
7+
"""
8+
9+
from typing import Set, Dict, List
10+
from ..schemas import FSA, Transition
11+
12+
13+
def epsilon_closure(state: str, epsilon_transitions: Dict[str, Set[str]]) -> Set[str]:
14+
"""
15+
Compute the ε-closure of a single state.
16+
17+
The ε-closure of a state q is the set of all states reachable from q
18+
using only ε-transitions, including q itself.
19+
20+
Algorithm:
21+
1. Initialize closure with the state itself
22+
2. Use DFS/BFS to follow all ε-transitions
23+
3. Return the complete set of reachable states
24+
25+
Args:
26+
state: The state to compute ε-closure for
27+
epsilon_transitions: Dict mapping state -> set of states reachable via ε
28+
29+
Returns:
30+
Set of states in the ε-closure
31+
32+
Example:
33+
>>> eps_trans = {"q0": {"q1", "q2"}, "q1": {"q3"}}
34+
>>> epsilon_closure("q0", eps_trans)
35+
{"q0", "q1", "q2", "q3"}
36+
"""
37+
closure = {state}
38+
stack = [state]
39+
40+
while stack:
41+
current = stack.pop()
42+
43+
# Get all states reachable via ε from current
44+
if current in epsilon_transitions:
45+
for next_state in epsilon_transitions[current]:
46+
if next_state not in closure:
47+
closure.add(next_state)
48+
stack.append(next_state)
49+
50+
return closure
51+
52+
53+
def epsilon_closure_set(states: Set[str], epsilon_transitions: Dict[str, Set[str]]) -> Set[str]:
54+
"""
55+
Compute the ε-closure of a set of states.
56+
57+
This is the union of ε-closures of all states in the set.
58+
59+
Args:
60+
states: Set of states to compute ε-closure for
61+
epsilon_transitions: Dict mapping state -> set of states reachable via ε
62+
63+
Returns:
64+
Set of all states in the combined ε-closure
65+
66+
Example:
67+
>>> eps_trans = {"q0": {"q1"}, "q2": {"q3"}}
68+
>>> epsilon_closure_set({"q0", "q2"}, eps_trans)
69+
{"q0", "q1", "q2", "q3"}
70+
"""
71+
closure = set()
72+
73+
for state in states:
74+
closure |= epsilon_closure(state, epsilon_transitions)
75+
76+
return closure
77+
78+
79+
def build_epsilon_transition_map(transitions: List[Transition]) -> Dict[str, Set[str]]:
80+
"""
81+
Build a mapping of ε-transitions from a list of transitions.
82+
83+
Args:
84+
transitions: List of all transitions in the FSA
85+
86+
Returns:
87+
Dict mapping each state to the set of states reachable via ε-transitions
88+
89+
Example:
90+
>>> transitions = [
91+
... Transition(from_state="q0", to_state="q1", symbol="ε"),
92+
... Transition(from_state="q0", to_state="q2", symbol="ε"),
93+
... Transition(from_state="q1", to_state="q3", symbol="a")
94+
... ]
95+
>>> build_epsilon_transition_map(transitions)
96+
{"q0": {"q1", "q2"}}
97+
"""
98+
epsilon_map: Dict[str, Set[str]] = {}
99+
100+
for trans in transitions:
101+
# Check for ε-transitions (various representations)
102+
if trans.symbol in ("ε", "epsilon", ""):
103+
if trans.from_state not in epsilon_map:
104+
epsilon_map[trans.from_state] = set()
105+
epsilon_map[trans.from_state].add(trans.to_state)
106+
107+
return epsilon_map
108+
109+
110+
def compute_all_epsilon_closures(fsa: FSA) -> Dict[str, Set[str]]:
111+
"""
112+
Compute ε-closures for all states in an FSA.
113+
114+
This is useful for preprocessing before NFA to DFA conversion.
115+
116+
Args:
117+
fsa: The finite state automaton
118+
119+
Returns:
120+
Dict mapping each state to its ε-closure
121+
122+
Example:
123+
>>> fsa = FSA(
124+
... states=["q0", "q1", "q2"],
125+
... alphabet=["a"],
126+
... transitions=[
127+
... Transition(from_state="q0", to_state="q1", symbol="ε"),
128+
... Transition(from_state="q1", to_state="q2", symbol="a")
129+
... ],
130+
... initial_state="q0",
131+
... accept_states=["q2"]
132+
... )
133+
>>> compute_all_epsilon_closures(fsa)
134+
{"q0": {"q0", "q1"}, "q1": {"q1"}, "q2": {"q2"}}
135+
"""
136+
epsilon_trans = build_epsilon_transition_map(fsa.transitions)
137+
closures = {}
138+
139+
for state in fsa.states:
140+
closures[state] = epsilon_closure(state, epsilon_trans)
141+
142+
return closures

0 commit comments

Comments
 (0)