Skip to content

Commit 4a93d1f

Browse files
committed
feat: should do the validness, determinism and completeness, along with the test cases
1 parent f3f9b6c commit 4a93d1f

5 files changed

Lines changed: 199 additions & 0 deletions

File tree

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from collections import defaultdict
2+
from .fsa import FSA
3+
4+
def is_valid_fsa(fsa: FSA) -> bool:
5+
states = set(fsa.states)
6+
alphabet = set(fsa.alphabet)
7+
8+
if fsa.initial_state not in states:
9+
return False
10+
11+
if not set(fsa.accept_states).issubset(states):
12+
return False
13+
14+
for t in fsa.transitions:
15+
if t.from_state not in states:
16+
return False
17+
if t.to_state not in states:
18+
return False
19+
if t.symbol not in alphabet:
20+
return False
21+
22+
return True
23+
24+
def is_deterministic(fsa: FSA) -> bool:
25+
if not is_valid_fsa(fsa):
26+
return False
27+
28+
seen = set()
29+
30+
for t in fsa.transitions:
31+
key = (t.from_state, t.symbol)
32+
if key in seen:
33+
return False
34+
seen.add(key)
35+
36+
return True
37+
38+
def is_complete(fsa: FSA) -> bool:
39+
if not is_deterministic(fsa):
40+
return False
41+
42+
states = set(fsa.states)
43+
alphabet = set(fsa.alphabet)
44+
45+
seen = {(t.from_state, t.symbol) for t in fsa.transitions}
46+
47+
for state in states:
48+
for symbol in alphabet:
49+
if (state, symbol) not in seen:
50+
return False
51+
52+
return True
53+
54+
def classify_fsa(fsa: FSA) -> dict:
55+
return {
56+
"valid": is_valid_fsa(fsa),
57+
"deterministic": is_deterministic(fsa),
58+
"complete": is_complete(fsa),
59+
}
60+
61+
62+
# is_nfa()
63+
# make_complete()
64+
# add_sink_state()
65+

tests/__init__.py

Whitespace-only changes.

tests/validation/__init__.py

Whitespace-only changes.
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import unittest
2+
3+
from evaluation_function.schemas.validation import (
4+
is_valid_fsa,
5+
is_deterministic,
6+
is_complete,
7+
)
8+
9+
from evaluation_function.schemas.fsa import FSA, Transition
10+
from .utils import make_fsa
11+
12+
13+
class TestFSAValidation(unittest.TestCase):
14+
15+
# a valid fsa should be identified as valid
16+
def test_valid_fsa_basic(self):
17+
fsa = make_fsa(
18+
states=["q0", "q1"],
19+
alphabet=["a"],
20+
transitions=[
21+
{"from_state": "q0", "to_state": "q1", "symbol": "a"}
22+
],
23+
initial="q0",
24+
accept=["q1"],
25+
)
26+
27+
self.assertTrue(is_valid_fsa(fsa))
28+
29+
# an invalid fsa should be identified as invalid
30+
def test_invalid_initial_state(self):
31+
fsa = make_fsa(
32+
states=["q1"],
33+
alphabet=["a"],
34+
transitions=[],
35+
initial="q0",
36+
accept=[],
37+
)
38+
39+
self.assertFalse(is_valid_fsa(fsa))
40+
41+
# an fsa with unknown transition states is not a valid fsa
42+
def test_invalid_transition_state(self):
43+
fsa = make_fsa(
44+
states=["q0"],
45+
alphabet=["a"],
46+
transitions=[
47+
{"from_state": "q0", "to_state": "q1", "symbol": "a"}
48+
],
49+
initial="q0",
50+
accept=[],
51+
)
52+
53+
self.assertFalse(is_valid_fsa(fsa))
54+
55+
# deterministic but incomplete
56+
def test_deterministic_but_incomplete(self):
57+
fsa = make_fsa(
58+
states=["q0", "q1"],
59+
alphabet=["a", "b"],
60+
transitions=[
61+
{"from_state": "q0", "to_state": "q1", "symbol": "a"}
62+
],
63+
initial="q0",
64+
accept=["q1"],
65+
)
66+
67+
self.assertTrue(is_deterministic(fsa))
68+
self.assertFalse(is_complete(fsa))
69+
70+
# nondeterministic fsa
71+
def test_nondeterministic_fsa(self):
72+
fsa = make_fsa(
73+
states=["q0", "q1", "q2"],
74+
alphabet=["a"],
75+
transitions=[
76+
{"from_state": "q0", "to_state": "q1", "symbol": "a"},
77+
{"from_state": "q0", "to_state": "q2", "symbol": "a"},
78+
],
79+
initial="q0",
80+
accept=["q2"],
81+
)
82+
83+
self.assertFalse(is_deterministic(fsa))
84+
self.assertFalse(is_complete(fsa))
85+
86+
# complete dfa
87+
def test_complete_dfa(self):
88+
fsa = make_fsa(
89+
states=["q0", "q1"],
90+
alphabet=["a", "b"],
91+
transitions=[
92+
{"from_state": "q0", "to_state": "q1", "symbol": "a"},
93+
{"from_state": "q0", "to_state": "q0", "symbol": "b"},
94+
{"from_state": "q1", "to_state": "q1", "symbol": "a"},
95+
{"from_state": "q1", "to_state": "q0", "symbol": "b"},
96+
],
97+
initial="q0",
98+
accept=["q1"],
99+
)
100+
101+
self.assertTrue(is_valid_fsa(fsa))
102+
self.assertTrue(is_deterministic(fsa))
103+
self.assertTrue(is_complete(fsa))
104+
105+
# edge case: single state complete fsa
106+
def test_single_state_complete_fsa(self):
107+
fsa = make_fsa(
108+
states=["q0"],
109+
alphabet=["a"],
110+
transitions=[
111+
{"from_state": "q0", "to_state": "q0", "symbol": "a"}
112+
],
113+
initial="q0",
114+
accept=["q0"],
115+
)
116+
117+
self.assertTrue(is_complete(fsa))
118+
119+
120+
if __name__ == "__main__":
121+
unittest.main()

tests/validation/utils.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from evaluation_function.schemas.fsa import FSA, Transition
2+
3+
4+
def make_fsa(states, alphabet, transitions, initial, accept):
5+
return FSA(
6+
states=states,
7+
alphabet=alphabet,
8+
transitions=[
9+
Transition(**t) for t in transitions
10+
],
11+
initial_state=initial,
12+
accept_states=accept,
13+
)

0 commit comments

Comments
 (0)