Skip to content

Commit dea149e

Browse files
committed
refactor: remove draw_fsa function and related imports to simplify utils.py
1 parent 4156d51 commit dea149e

1 file changed

Lines changed: 20 additions & 108 deletions

File tree

Lines changed: 20 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,6 @@
11
from .fsa import FSA, Transition
22

3-
import matplotlib.pyplot as plt
4-
import networkx as nx
53

6-
# plt is not thread safe, but graphviz is harder to install for cross-env compiles
7-
# this is sequentially safe anyway
8-
# notice that if relative path, it is relative to where you execute the code,
9-
# and invalid fsa always look weird as nx+plt cannot find a equilibrium
10-
def draw_fsa(fsa:FSA, show=False, save=True, filename="./fsa.png"):
11-
# 1. Initialize Graph and collect all possible states
12-
G = nx.MultiDiGraph()
13-
14-
# Start with declared states, but use a set to allow for "orphans"
15-
all_states = set(fsa.states)
16-
17-
# Always include initial and accept states in the set (even if missing from fsa.states)
18-
if fsa.initial_state:
19-
all_states.add(fsa.initial_state)
20-
all_states.update(fsa.accept_states)
21-
22-
# 2. Add Transitions and discover states used in transitions
23-
for t in fsa.transitions:
24-
all_states.add(t.from_state)
25-
all_states.add(t.to_state)
26-
G.add_edge(t.from_state, t.to_state, label=t.symbol)
27-
28-
# Ensure all discovered states are in the graph
29-
G.add_nodes_from(all_states)
30-
31-
# 3. Layout and Figure
32-
# Kamada-Kawai is more stable than Spring for state machines
33-
pos = nx.kamada_kawai_layout(G)
34-
plt.figure(figsize=(10, 7))
35-
36-
# 4. Draw Nodes with specific styling
37-
for node in G.nodes():
38-
# Default Style
39-
color = 'skyblue'
40-
linewidth = 1.0
41-
edgecolor = 'black'
42-
label_suffix = ""
43-
44-
# Initial State Styling (Green fill)
45-
if node == fsa.initial_state:
46-
color = '#90ee90' # Light green
47-
label_suffix = "\n(start)"
48-
49-
# Accept State Styling (Double-circle effect/Thick border)
50-
if node in fsa.accept_states:
51-
linewidth = 4.0
52-
edgecolor = '#ff4500' # Orange-red border for visibility
53-
54-
nx.draw_networkx_nodes(
55-
G, pos,
56-
nodelist=[node],
57-
node_color=color,
58-
edgecolors=edgecolor,
59-
linewidths=linewidth,
60-
node_size=2500
61-
)
62-
63-
# 5. Draw Edges (Arcing handles multi-edges/non-determinism)
64-
nx.draw_networkx_edges(
65-
G, pos,
66-
arrowstyle='->',
67-
arrowsize=25,
68-
connectionstyle='arc3,rad=0.15',
69-
edge_color='gray'
70-
)
71-
72-
# 6. Edge and Node Labels
73-
# Use bracket notation for the 'label' key in the data dict
74-
edge_labels = {(u, v): d['label'] for u, v, k, d in G.edges(data=True, keys=True)}
75-
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=10)
76-
77-
node_labels = {node: f"{node}{'(start)' if node == fsa.initial_state else ''}" for node in G.nodes()}
78-
nx.draw_networkx_labels(G, pos, labels=node_labels, font_size=11, font_weight='bold')
79-
80-
plt.title(f"FSA:\n(Accept states marked with thick borders)")
81-
plt.axis('off')
82-
83-
# 7. Save and Clean Up
84-
if save:
85-
plt.savefig(filename, bbox_inches='tight')
86-
87-
if show:
88-
plt.show()
89-
90-
plt.close()
91-
print(f"Process complete. Memory flushed.")
924

935
def make_fsa(states, alphabet, transitions, initial, accept):
946
return FSA(
@@ -101,24 +13,24 @@ def make_fsa(states, alphabet, transitions, initial, accept):
10113
accept_states=accept,
10214
)
10315

104-
if __name__ == "__main__":
105-
fsa = make_fsa(
106-
states=["q0", "q1"],
107-
alphabet=["a"],
108-
transitions=[
109-
{"from_state": "q0", "to_state": "q0", "symbol": "a"},
110-
{"from_state": "q0", "to_state": "q1", "symbol": "a"}, # Non-deterministic
111-
],
112-
initial="q0",
113-
accept=["q1"],
114-
)
115-
draw_fsa(fsa, False, True, "./valid.png")
116-
fsa = make_fsa(
117-
states=["q0"],
118-
alphabet=["a"],
119-
transitions=[],
120-
initial="q0",
121-
accept=["q1"], # q1 is not in states
122-
)
123-
draw_fsa(fsa, False, True, "./invalid.png")
16+
# if __name__ == "__main__":
17+
# fsa = make_fsa(
18+
# states=["q0", "q1"],
19+
# alphabet=["a"],
20+
# transitions=[
21+
# {"from_state": "q0", "to_state": "q0", "symbol": "a"},
22+
# {"from_state": "q0", "to_state": "q1", "symbol": "a"}, # Non-deterministic
23+
# ],
24+
# initial="q0",
25+
# accept=["q1"],
26+
# )
27+
# draw_fsa(fsa, False, True, "./valid.png")
28+
# fsa = make_fsa(
29+
# states=["q0"],
30+
# alphabet=["a"],
31+
# transitions=[],
32+
# initial="q0",
33+
# accept=["q1"], # q1 is not in states
34+
# )
35+
# draw_fsa(fsa, False, True, "./invalid.png")
12436

0 commit comments

Comments
 (0)