Skip to content

Commit 0c9930d

Browse files
authored
Merge pull request #13 from FormalLanguageConstrainedPathQuerying/fa_string_generator
Fix bug in FA string generator
2 parents bffade7 + ac97aec commit 0c9930d

File tree

4 files changed

+121
-43
lines changed

4 files changed

+121
-43
lines changed

pyformlang/finite_automaton/finite_automaton.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,19 +635,24 @@ def _get_states_leading_to_final(self) -> Set[State]:
635635
visited = set()
636636
states_to_process = deque((None, start_state)
637637
for start_state in self.start_states)
638+
delayed_states = deque()
638639
while states_to_process:
639640
previous_state, current_state = states_to_process.pop()
640641
if previous_state and current_state in leading_to_final:
641642
leading_to_final.add(previous_state)
642643
continue
643644
if current_state in visited:
645+
delayed_states.append((previous_state, current_state))
644646
continue
645647
visited.add(current_state)
646648
next_states = self._get_next_states_from(current_state)
647649
if next_states:
648650
states_to_process.append((previous_state, current_state))
649651
for next_state in next_states:
650652
states_to_process.append((current_state, next_state))
653+
for previous_state, current_state in delayed_states:
654+
if previous_state and current_state in leading_to_final:
655+
leading_to_final.add(previous_state)
651656
return leading_to_final
652657

653658
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
@@ -288,6 +288,15 @@ def test_word_generation(self):
288288
assert [Symbol("b"), Symbol("d")] in accepted_words
289289
assert len(accepted_words) == 3
290290

291+
def test_cyclic_word_generation(self):
292+
dfa = get_cyclic_dfa_example()
293+
accepted_words = list(dfa.get_accepted_words(5))
294+
assert ["a", "f"] in accepted_words
295+
assert ["a", "b", "e", "f"] in accepted_words
296+
assert ["a", "b", "c", "e", "f"] in accepted_words
297+
assert ["a", "b", "d", "a", "f"] in accepted_words
298+
assert len(accepted_words) == 4
299+
291300
def test_dfa_generating_no_words(self):
292301
dfa = get_dfa_example_without_accepted_words()
293302
accepted_words = list(dfa.get_accepted_words())
@@ -362,6 +371,21 @@ def get_dfa_example_for_word_generation():
362371
return dfa
363372

364373

374+
def get_cyclic_dfa_example():
375+
""" Gets DFA example with several cycles on path to final """
376+
dfa = DeterministicFiniteAutomaton(start_state=0,
377+
final_states={3})
378+
dfa.add_transitions([
379+
(0, "a", 1),
380+
(1, "b", 2),
381+
(2, "c", 2),
382+
(2, "d", 0),
383+
(2, "e", 1),
384+
(1, "f", 3),
385+
])
386+
return dfa
387+
388+
365389
def get_dfa_example_without_accepted_words():
366390
""" DFA example accepting no words """
367391
dfa = DeterministicFiniteAutomaton()

pyformlang/finite_automaton/tests/test_epsilon_nfa.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -648,7 +648,7 @@ def test_epsilon_cycle_word_generation(self):
648648
accepted_words = list(enfa.get_accepted_words(max_length))
649649
assert [] in accepted_words
650650
assert [Symbol("a"), Symbol("c")] in accepted_words
651-
assert [Symbol("a"), Symbol("b"),Symbol("c")] in accepted_words
651+
assert [Symbol("a"), Symbol("b"), Symbol("c")] in accepted_words
652652
assert [Symbol("a"), Symbol("b"),
653653
Symbol("b"), Symbol("c")] in accepted_words
654654
assert len(accepted_words) == 4

pyformlang/finite_automaton/tests/test_nondeterministic_finite_automaton.py

Lines changed: 91 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -133,61 +133,110 @@ def test_for_duplicate_generation(self):
133133
assert [Symbol("b"), Symbol("c")] in accepted_words
134134
assert len(accepted_words) == 2
135135

136+
def test_cyclic_word_generation(self):
137+
nfa = get_cyclic_nfa_example()
138+
accepted_words = list(nfa.get_accepted_words(5))
139+
assert ["a", "d", "g"] in accepted_words
140+
assert ["a", "b", "c", "d", "g"] in accepted_words
141+
assert ["a", "d", "e", "f", "g"] in accepted_words
142+
assert ["b", "f", "g"] in accepted_words
143+
assert ["b", "f", "e", "f", "g"] in accepted_words
144+
assert len(accepted_words) == 5
145+
146+
def test_final_state_at_start_generation(self):
147+
nfa = get_nfa_example_with_final_state_at_start()
148+
accepted_words = list(nfa.get_accepted_words())
149+
assert accepted_words == [[]]
150+
151+
def test_start_state_at_the_end_generation(self):
152+
nfa = get_nfa_example_with_start_state_at_the_end()
153+
accepted_words = list(nfa.get_accepted_words(5))
154+
assert [] in accepted_words
155+
assert ["a", "b", "c"] in accepted_words
156+
assert ["a", "b", "e", "b", "c"] in accepted_words
157+
assert ["d", "b", "c"] in accepted_words
158+
assert ["d", "b", "e", "b", "c"] in accepted_words
159+
assert len(accepted_words) == 5
160+
136161

137162
def get_nfa_example_for_word_generation():
138163
"""
139164
Gets Nondeterministic Finite Automaton \
140165
example for the word generation test.
141166
"""
142-
nfa = NondeterministicFiniteAutomaton()
143-
states = [State(x) for x in range(9)]
144-
symbol_a = Symbol("a")
145-
symbol_b = Symbol("b")
146-
symbol_c = Symbol("c")
147-
symbol_d = Symbol("d")
148-
symbol_e = Symbol("e")
149-
symbol_f = Symbol("f")
167+
nfa = NondeterministicFiniteAutomaton(start_state={0, 4},
168+
final_states={3, 4, 6, 8})
150169
nfa.add_transitions([
151-
(states[0], symbol_a, states[1]),
152-
(states[0], symbol_a, states[2]),
153-
(states[1], symbol_a, states[1]),
154-
(states[2], symbol_b, states[3]),
155-
(states[2], symbol_c, states[3]),
156-
(states[4], symbol_d, states[5]),
157-
(states[5], symbol_e, states[6]),
158-
(states[5], symbol_e, states[7]),
159-
(states[7], symbol_f, states[8]),
170+
(0, "a", 1),
171+
(0, "a", 2),
172+
(1, "a", 1),
173+
(2, "b", 3),
174+
(2, "c", 3),
175+
(4, "d", 5),
176+
(5, "e", 6),
177+
(5, "e", 7),
178+
(7, "f", 8),
160179
])
161-
nfa.add_start_state(states[0])
162-
nfa.add_start_state(states[4])
163-
nfa.add_final_state(states[3])
164-
nfa.add_final_state(states[4])
165-
nfa.add_final_state(states[6])
166-
nfa.add_final_state(states[8])
167180
return nfa
168181

169182

170183
def get_nfa_example_with_duplicates():
171184
""" Gets NFA example with duplicate word chains """
172-
nfa = NondeterministicFiniteAutomaton()
173-
states = [State(x) for x in range(9)]
174-
symbol_a = Symbol("a")
175-
symbol_b = Symbol("b")
176-
symbol_c = Symbol("c")
185+
nfa = NondeterministicFiniteAutomaton(start_state={0, 1, 5, 6},
186+
final_states={3, 4, 8})
187+
nfa.add_transitions([
188+
(0, "a", 2),
189+
(1, "a", 2),
190+
(2, "c", 3),
191+
(2, "c", 4),
192+
(5, "a", 7),
193+
(6, "b", 7),
194+
(7, "c", 8),
195+
])
196+
return nfa
197+
198+
199+
def get_cyclic_nfa_example():
200+
""" Gets NFA example with several cycles on path to final """
201+
nfa = NondeterministicFiniteAutomaton(start_state={0, 5},
202+
final_states={4})
203+
nfa.add_transitions([
204+
(0, "a", 1),
205+
(1, "b", 2),
206+
(2, "c", 1),
207+
(1, "d", 3),
208+
(3, "e", 6),
209+
(6, "f", 3),
210+
(3, "g", 4),
211+
(5, "b", 6),
212+
])
213+
return nfa
214+
215+
216+
def get_nfa_example_with_final_state_at_start():
217+
""" Gets NFA example with final state at start """
218+
nfa = NondeterministicFiniteAutomaton(start_state={0, 5},
219+
final_states={0})
220+
nfa.add_transitions([
221+
(0, "a", 1),
222+
(1, "b", 2),
223+
(2, "c", 3),
224+
(2, "d", 4),
225+
(5, "e", 1),
226+
(5, "e", 2),
227+
])
228+
return nfa
229+
230+
231+
def get_nfa_example_with_start_state_at_the_end():
232+
""" Gets NFA example with start state at the end """
233+
nfa = NondeterministicFiniteAutomaton(start_state={0, 3, 4},
234+
final_states={3})
177235
nfa.add_transitions([
178-
(states[0], symbol_a, states[2]),
179-
(states[1], symbol_a, states[2]),
180-
(states[2], symbol_c, states[3]),
181-
(states[2], symbol_c, states[4]),
182-
(states[5], symbol_a, states[7]),
183-
(states[6], symbol_b, states[7]),
184-
(states[7], symbol_c, states[8]),
236+
(0, "a", 1),
237+
(1, "b", 2),
238+
(2, "e", 1),
239+
(2, "c", 3),
240+
(4, "d", 1),
185241
])
186-
nfa.add_start_state(states[0])
187-
nfa.add_start_state(states[1])
188-
nfa.add_start_state(states[5])
189-
nfa.add_start_state(states[6])
190-
nfa.add_final_state(states[3])
191-
nfa.add_final_state(states[4])
192-
nfa.add_final_state(states[8])
193242
return nfa

0 commit comments

Comments
 (0)