Skip to content

Commit f49ce75

Browse files
committed
rewrite the generator without networkx
1 parent d72eff8 commit f49ce75

File tree

1 file changed

+34
-22
lines changed

1 file changed

+34
-22
lines changed

pyformlang/finite_automaton/finite_automaton.py

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
""" A general finite automaton representation """
22

3-
from typing import List, Iterable, Any
3+
from typing import List, Iterable, Set, Any
44

55
import networkx as nx
66
from networkx.drawing.nx_pydot import write_dot
@@ -42,7 +42,6 @@ def __init__(self):
4242
self._transition_function = None
4343
self._start_state = set()
4444
self._final_states = set()
45-
self.__transitive_closure = None
4645

4746
def add_transition(self, s_from: State, symb_by: Symbol,
4847
s_to: State) -> int:
@@ -602,42 +601,55 @@ def get_accepted_words(self, max_length: int = -1) \
602601
"""
603602
states_to_visit = [(start_state, [])
604603
for start_state in self.start_states]
605-
self.__set_transitive_closure()
604+
states_leading_to_final = self._get_states_leading_to_final()
606605
while states_to_visit:
607606
current_state, current_word = states_to_visit.pop(0)
608607
if len(current_word) > max_length and max_length != -1:
609608
continue
610609
transitions = self._transition_function.get_transitions_from(
611610
current_state)
612611
for symbol, next_state in transitions:
613-
if self.__exists_any_final_path_from(next_state):
612+
if symbol == Epsilon() and next_state == current_state:
613+
continue
614+
if next_state in states_leading_to_final:
614615
temp_word = current_word.copy()
615616
if symbol != Epsilon():
616617
temp_word.append(symbol)
617618
states_to_visit.append((next_state, temp_word))
618619
if self.is_final_state(current_state):
619620
yield current_word
620621

621-
def __set_transitive_closure(self):
622+
def _get_states_leading_to_final(self) -> Set[State]:
622623
"""
623-
Bulds MultiDiGraph transitive closure \
624-
of FA and sets it to the private field.
624+
Gets a set of states from which one
625+
of the final states can be reached.
625626
"""
626-
self.__transitive_closure = nx.transitive_closure(
627-
self.to_networkx())
628-
629-
def __exists_any_final_path_from(self, source: State) -> bool:
630-
"""
631-
Checks if there are any paths from \
632-
given state to one of the final states.
633-
"""
634-
return any(self.__exists_path(source, final)
635-
for final in self.final_states)
636-
637-
def __exists_path(self, source: State, target: State) -> bool:
638-
""" Checks if the target state can be reached from the source state """
639-
return target == source or \
640-
target in self.__transitive_closure[source].keys()
627+
leading_to_final = self.final_states.copy()
628+
visited = set()
629+
states_to_process = [(None, start_state)
630+
for start_state in self.start_states]
631+
while states_to_process:
632+
previous_state, current_state = states_to_process.pop()
633+
if previous_state and current_state in leading_to_final:
634+
leading_to_final.add(previous_state)
635+
continue
636+
if current_state in visited:
637+
continue
638+
visited.add(current_state)
639+
next_states = self._get_next_states_from(current_state)
640+
if next_states:
641+
states_to_process.append((previous_state, current_state))
642+
for next_state in next_states:
643+
states_to_process.append((current_state, next_state))
644+
return leading_to_final
645+
646+
def _get_next_states_from(self, state_from: State) -> Set[State]:
647+
""" Gets a set of states that are next to the given one """
648+
next_states = set()
649+
for _, next_state in \
650+
self._transition_function.get_transitions_from(state_from):
651+
next_states.add(next_state)
652+
return next_states
641653

642654
def to_deterministic(self):
643655
""" Turns the automaton into a deterministic one"""

0 commit comments

Comments
 (0)