Skip to content

Commit b83c71a

Browse files
committed
Merge branch 'master' of github.com:FormalLanguageConstrainedPathQuerying/pyformlang into rework_finite_automaton
2 parents 72cd938 + 0c9930d commit b83c71a

File tree

4 files changed

+131
-53
lines changed

4 files changed

+131
-53
lines changed

pyformlang/finite_automaton/finite_automaton.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,19 +603,24 @@ def _get_states_leading_to_final(self) -> Set[State]:
603603
visited = set()
604604
states_to_process: deque[Any] = \
605605
deque((None, start_state) for start_state in self.start_states)
606+
delayed_states = deque()
606607
while states_to_process:
607608
previous_state, current_state = states_to_process.pop()
608609
if previous_state and current_state in leading_to_final:
609610
leading_to_final.add(previous_state)
610611
continue
611612
if current_state in visited:
613+
delayed_states.append((previous_state, current_state))
612614
continue
613615
visited.add(current_state)
614616
next_states = self.get_next_states_from(current_state)
615617
if next_states:
616618
states_to_process.append((previous_state, current_state))
617619
for next_state in next_states:
618620
states_to_process.append((current_state, next_state))
621+
for previous_state, current_state in delayed_states:
622+
if previous_state and current_state in leading_to_final:
623+
leading_to_final.add(previous_state)
619624
return leading_to_final
620625

621626
def _get_reachable_states(self) -> Set[State]:

pyformlang/finite_automaton/tests/test_deterministic_finite_automaton.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,15 @@ def test_word_generation(self):
257257
assert [Symbol("b"), Symbol("d")] in accepted_words
258258
assert len(accepted_words) == 3
259259

260+
def test_cyclic_word_generation(self):
261+
dfa = get_cyclic_dfa_example()
262+
accepted_words = list(dfa.get_accepted_words(5))
263+
assert ["a", "f"] in accepted_words
264+
assert ["a", "b", "e", "f"] in accepted_words
265+
assert ["a", "b", "c", "e", "f"] in accepted_words
266+
assert ["a", "b", "d", "a", "f"] in accepted_words
267+
assert len(accepted_words) == 4
268+
260269
def test_dfa_generating_no_words(self):
261270
dfa = get_dfa_example_without_accepted_words()
262271
accepted_words = list(dfa.get_accepted_words())
@@ -331,6 +340,21 @@ def get_dfa_example_for_word_generation():
331340
return dfa
332341

333342

343+
def get_cyclic_dfa_example():
344+
""" Gets DFA example with several cycles on path to final """
345+
dfa = DeterministicFiniteAutomaton(start_state=0,
346+
final_states={3})
347+
dfa.add_transitions([
348+
(0, "a", 1),
349+
(1, "b", 2),
350+
(2, "c", 2),
351+
(2, "d", 0),
352+
(2, "e", 1),
353+
(1, "f", 3),
354+
])
355+
return dfa
356+
357+
334358
def get_dfa_example_without_accepted_words():
335359
""" DFA example accepting no words """
336360
dfa = DeterministicFiniteAutomaton()

pyformlang/finite_automaton/tests/test_epsilon_nfa.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ def test_epsilon_cycle_word_generation(self):
533533
accepted_words = list(enfa.get_accepted_words(max_length))
534534
assert [] in accepted_words
535535
assert [Symbol("a"), Symbol("c")] in accepted_words
536-
assert [Symbol("a"), Symbol("b"),Symbol("c")] in accepted_words
536+
assert [Symbol("a"), Symbol("b"), Symbol("c")] in accepted_words
537537
assert [Symbol("a"), Symbol("b"),
538538
Symbol("b"), Symbol("c")] in accepted_words
539539
assert len(accepted_words) == 4

pyformlang/finite_automaton/tests/test_nondeterministic_finite_automaton.py

Lines changed: 101 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,17 @@ def test_epsilon_refused(self):
116116
with pytest.raises(InvalidEpsilonTransition):
117117
dfa.add_transition(state0, Epsilon(), state1)
118118

119+
def test_copy(self):
120+
nfa = get_nfa_example_with_duplicates().copy()
121+
assert len(nfa.states) == 9
122+
assert len(nfa.symbols) == 3
123+
assert len(nfa.start_states) == 4
124+
assert len(nfa.final_states) == 3
125+
assert nfa.get_number_transitions() == 7
126+
assert nfa.accepts([Symbol("a"), Symbol("c")])
127+
assert nfa.accepts([Symbol("b"), Symbol("c")])
128+
assert not nfa.is_deterministic()
129+
119130
def test_word_generation(self):
120131
nfa = get_nfa_example_for_word_generation()
121132
accepted_words = list(nfa.get_accepted_words())
@@ -133,72 +144,110 @@ def test_for_duplicate_generation(self):
133144
assert [Symbol("b"), Symbol("c")] in accepted_words
134145
assert len(accepted_words) == 2
135146

136-
def test_copy(self):
137-
nfa = get_nfa_example_with_duplicates().copy()
138-
assert len(nfa.states) == 9
139-
assert len(nfa.symbols) == 3
140-
assert len(nfa.start_states) == 4
141-
assert len(nfa.final_states) == 3
142-
assert nfa.get_number_transitions() == 7
143-
assert nfa.accepts([Symbol("a"), Symbol("c")])
144-
assert nfa.accepts([Symbol("b"), Symbol("c")])
145-
assert not nfa.is_deterministic()
147+
def test_cyclic_word_generation(self):
148+
nfa = get_cyclic_nfa_example()
149+
accepted_words = list(nfa.get_accepted_words(5))
150+
assert ["a", "d", "g"] in accepted_words
151+
assert ["a", "b", "c", "d", "g"] in accepted_words
152+
assert ["a", "d", "e", "f", "g"] in accepted_words
153+
assert ["b", "f", "g"] in accepted_words
154+
assert ["b", "f", "e", "f", "g"] in accepted_words
155+
assert len(accepted_words) == 5
156+
157+
def test_final_state_at_start_generation(self):
158+
nfa = get_nfa_example_with_final_state_at_start()
159+
accepted_words = list(nfa.get_accepted_words())
160+
assert accepted_words == [[]]
161+
162+
def test_start_state_at_the_end_generation(self):
163+
nfa = get_nfa_example_with_start_state_at_the_end()
164+
accepted_words = list(nfa.get_accepted_words(5))
165+
assert [] in accepted_words
166+
assert ["a", "b", "c"] in accepted_words
167+
assert ["a", "b", "e", "b", "c"] in accepted_words
168+
assert ["d", "b", "c"] in accepted_words
169+
assert ["d", "b", "e", "b", "c"] in accepted_words
170+
assert len(accepted_words) == 5
146171

147172

148173
def get_nfa_example_for_word_generation():
149174
"""
150175
Gets Nondeterministic Finite Automaton \
151176
example for the word generation test.
152177
"""
153-
nfa = NondeterministicFiniteAutomaton()
154-
states = [State(x) for x in range(9)]
155-
symbol_a = Symbol("a")
156-
symbol_b = Symbol("b")
157-
symbol_c = Symbol("c")
158-
symbol_d = Symbol("d")
159-
symbol_e = Symbol("e")
160-
symbol_f = Symbol("f")
178+
nfa = NondeterministicFiniteAutomaton(start_states={0, 4},
179+
final_states={3, 4, 6, 8})
161180
nfa.add_transitions([
162-
(states[0], symbol_a, states[1]),
163-
(states[0], symbol_a, states[2]),
164-
(states[1], symbol_a, states[1]),
165-
(states[2], symbol_b, states[3]),
166-
(states[2], symbol_c, states[3]),
167-
(states[4], symbol_d, states[5]),
168-
(states[5], symbol_e, states[6]),
169-
(states[5], symbol_e, states[7]),
170-
(states[7], symbol_f, states[8]),
181+
(0, "a", 1),
182+
(0, "a", 2),
183+
(1, "a", 1),
184+
(2, "b", 3),
185+
(2, "c", 3),
186+
(4, "d", 5),
187+
(5, "e", 6),
188+
(5, "e", 7),
189+
(7, "f", 8),
171190
])
172-
nfa.add_start_state(states[0])
173-
nfa.add_start_state(states[4])
174-
nfa.add_final_state(states[3])
175-
nfa.add_final_state(states[4])
176-
nfa.add_final_state(states[6])
177-
nfa.add_final_state(states[8])
178191
return nfa
179192

180193

181194
def get_nfa_example_with_duplicates():
182195
""" Gets NFA example with duplicate word chains """
183-
nfa = NondeterministicFiniteAutomaton()
184-
states = [State(x) for x in range(9)]
185-
symbol_a = Symbol("a")
186-
symbol_b = Symbol("b")
187-
symbol_c = Symbol("c")
196+
nfa = NondeterministicFiniteAutomaton(start_states={0, 1, 5, 6},
197+
final_states={3, 4, 8})
198+
nfa.add_transitions([
199+
(0, "a", 2),
200+
(1, "a", 2),
201+
(2, "c", 3),
202+
(2, "c", 4),
203+
(5, "a", 7),
204+
(6, "b", 7),
205+
(7, "c", 8),
206+
])
207+
return nfa
208+
209+
210+
def get_cyclic_nfa_example():
211+
""" Gets NFA example with several cycles on path to final """
212+
nfa = NondeterministicFiniteAutomaton(start_states={0, 5},
213+
final_states={4})
214+
nfa.add_transitions([
215+
(0, "a", 1),
216+
(1, "b", 2),
217+
(2, "c", 1),
218+
(1, "d", 3),
219+
(3, "e", 6),
220+
(6, "f", 3),
221+
(3, "g", 4),
222+
(5, "b", 6),
223+
])
224+
return nfa
225+
226+
227+
def get_nfa_example_with_final_state_at_start():
228+
""" Gets NFA example with final state at start """
229+
nfa = NondeterministicFiniteAutomaton(start_states={0, 5},
230+
final_states={0})
231+
nfa.add_transitions([
232+
(0, "a", 1),
233+
(1, "b", 2),
234+
(2, "c", 3),
235+
(2, "d", 4),
236+
(5, "e", 1),
237+
(5, "e", 2),
238+
])
239+
return nfa
240+
241+
242+
def get_nfa_example_with_start_state_at_the_end():
243+
""" Gets NFA example with start state at the end """
244+
nfa = NondeterministicFiniteAutomaton(start_states={0, 3, 4},
245+
final_states={3})
188246
nfa.add_transitions([
189-
(states[0], symbol_a, states[2]),
190-
(states[1], symbol_a, states[2]),
191-
(states[2], symbol_c, states[3]),
192-
(states[2], symbol_c, states[4]),
193-
(states[5], symbol_a, states[7]),
194-
(states[6], symbol_b, states[7]),
195-
(states[7], symbol_c, states[8]),
247+
(0, "a", 1),
248+
(1, "b", 2),
249+
(2, "e", 1),
250+
(2, "c", 3),
251+
(4, "d", 1),
196252
])
197-
nfa.add_start_state(states[0])
198-
nfa.add_start_state(states[1])
199-
nfa.add_start_state(states[5])
200-
nfa.add_start_state(states[6])
201-
nfa.add_final_state(states[3])
202-
nfa.add_final_state(states[4])
203-
nfa.add_final_state(states[8])
204253
return nfa

0 commit comments

Comments
 (0)