Skip to content

Commit d43017f

Browse files
Package restructuring, package cleanup, and skeleton implementation of constructEquivelantDFAFromNFA()
1 parent 883595e commit d43017f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+177
-531
lines changed

.travis.yml

-17
This file was deleted.

FiniteAutomaton.py

-110
This file was deleted.

MANIFEST

-6
This file was deleted.

setup.cfg

-2
This file was deleted.

setup.py

-45
This file was deleted.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

sudkampPython/finiteAutomaton.py

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
from sudkampPython.transitions import Machine
2+
3+
"""
4+
Algorithm 5.7.2
5+
Determination of Equivalent States of DFA
6+
7+
input: DFA M = (Q, Alphabet, TransitionFunctoin, q<sub>0</sub>, F)
8+
9+
1. (Initialization)
10+
for every pair of states q<sub>i</sub> and q<sub>j</sub>, i < j, do
11+
1.1 D[i,j] := 0
12+
1.2 S[i,j] := null set
13+
end for
14+
2. for every pair i, j, i < j, if one of q<sub>i</sub> or q<sub>j</sub> is
15+
and accepting state and the other is not an accepting state, then set D[i,j] := 1
16+
3. for every pair i, j, i < j, with D[i,j] = 0, do
17+
3.1 if there exists an a which is element of Alphabet such that delta(q<sub>i</sub>, a) = q<sub>m</sub>,
18+
delta(q<sub>j</sub>, a) = q<sub>n</sub> and D[m,n] = 1 or D[n,m] = 1, then DIST(i,j)
19+
20+
3.2 else for each a that is element of Alphabet, do: Let delta(q<sub>i</sub>, a) = q<sub>m</sub>,
21+
delta(q<sub>j</sub>m, a) = q<sub>n</sub>
22+
if m < n and [i,j] != [m,n], then add [i,j] to S[m,n]
23+
else if m > n and [i,j] != [n,m], then add [i,j] to S[n,m]
24+
end for
25+
26+
DIST(i,j);
27+
begin
28+
D[i,j] := 1
29+
for all [m,n] that are elements of S[i,j], DIST(m,n)
30+
end
31+
"""
32+
def determineEquivelantStatesOfDFA( dfa ): # TODO: Improve algorithmic efficiency
33+
states = list(dfa.states.items())
34+
equivStates = set()
35+
D, S = [], []
36+
# initialization
37+
for i in range(len(states)):
38+
D.append([])
39+
S.append([])
40+
for j in range(len(states)):
41+
D[-1].append(False) if i < j else D[-1].append(None)
42+
S[-1].append(set()) if i < j else S[-1].append(None)
43+
# Step 2
44+
for i in range(len(states)):
45+
for j in range(len(states)):
46+
if (i < j and any(s.is_final() for s in states[i:j+1])
47+
and not all(s.is_final() for s in states[i:j+1])):
48+
# one is final and other isn't
49+
D[i][j] = 1
50+
# Step 3
51+
for i in range(len(states)):
52+
for j in range(len(states)):
53+
if i < j and D[i][j] == 0: # Do
54+
# if there is an "a" for which both qi and qj have a transition and the
55+
# resulting state m,n are not equal (m!=n) and D[m][n] or D[n][m] == 1 then
56+
# DIST(i,j)
57+
# 3.1
58+
for symbol in dfa.alphabet.keys():
59+
qi_transition = getTransitionWithSymbol( symbol, states[i].name )
60+
qj_transition = getTransitionWithSymbol( symbol, states[j].name )
61+
m_state_name, n_state_name = qi_transition.dest, qj_transition.dest
62+
# get indexes
63+
m = [s.name for s in states].index(m_state_name)
64+
n = [s.name for s in states].index(n_state_name)
65+
if D[m][n] == 1 or D[n][m] == 1: # if n = m, D[n,m] will never equal 1
66+
DIST(i, j, D, S)
67+
# 3.2
68+
for symbol in dfa.alphabet.key():
69+
qi_transition = getTransitionWithSymbol( symbol, states[i].name )
70+
qj_transition = getTransitionWithSymbol( symbol, states[j].name )
71+
m_state_name, n_state_name = qi_transition.dest, qj_transition.dest
72+
# get indexes
73+
m = [s.name for s in states].index(m_state_name)
74+
n = [s.name for s in states].index(n_state_name)
75+
if m < n and (i,j) != (m,n):
76+
S[m][n].add((i,j))
77+
elif m > n and (i,j) != (n,m):
78+
S[n][m].add((i,j))
79+
80+
# Now determine the set of equiv states. If D[i][j] = 0 then i and j are equiv
81+
for i in range(len(states)):
82+
for j in range(len(states)):
83+
if D[i][j] == 0:
84+
equivStates.add(states[i])
85+
equivStates.add(states[j])
86+
return equivStates
87+
88+
def DIST(i, j, D, S):
89+
""" Helper function for our main algorithm """
90+
D[i][j] = 1
91+
for m,n in S[i][j]:
92+
DIST(m, n, D, S)
93+
94+
def getTransitionWithSymbol( dfa, symbol, source ):
95+
"""
96+
Find a transition, if one exists, that involves the named
97+
symbol and source
98+
"""
99+
for event in dfa.events.keys():
100+
for transition in event.transitions.values():
101+
if event == symbol and transition.source == source:
102+
return transition
103+
104+
"""
105+
input: an NFA-null M = (Q, Alphabet, TransitionFunction, q<sub>0</sub>,F)
106+
input transition function t of M
107+
108+
1. initialize Q' to null-closure(q<sub>0</sub>)
109+
2. repeat
110+
2.1 if there is a node X, X is element of Q' and a symbol a that's element of Alphabet
111+
with no arc leaving X labeled "a", then
112+
2.1.1 let Y = UNION over q<sub>i</sub>, where q<sub>i</sub> is element of X of
113+
t(q<sub>i</sub>,a)
114+
2.1.2 if Y /= Q', then set Q' := Q UNION {Y}
115+
2.1.3 add an arc from X to Y labeled a
116+
else done:= true
117+
until done
118+
3. the set of accepting states of DM is F' = {X, where X is element of Q' | X contains
119+
an element q<sub>i</sub> that is element of F}
120+
"""
121+
def constructEquivelantDFAFromNFA( nfa ):
122+
dfa = Machine( model=None, states=nfa.initial.name, initial=nfa.initial.name, transitions=None)
123+
Q_ = nullClosure(nfa, nfa.initial) # state q0
124+
while True:
125+
for X in Q_:
126+
validSymbols = [sym for sym in nfa.alphabet if not nfa.events[sym].transitions[X.name]]
127+
if validSymbols:
128+
a = validSymbols[0]
129+
destinations = [t.dest for qi in X for t in nfa.events[a].transitions[qi.name]]
130+
Y = set([nfa.states[x] for x in destinations])
131+
if Y != Q_:
132+
Q_ |= Y
133+
dfa.add_transition(a, str(X), str(Y))
134+
else: # done = true
135+
break
136+
# the set of final states in F'
137+
F_ = [X for X in Q_ if any(q in X for q in nfa.final_states)]
138+
139+
140+
def nullClosure( machine, state ):
141+
""" Calculate the set of states that can be reached without processing a symbol """
142+
closureSet = set()
143+
closureSet.add(state)
144+
# TODO: If "*" trigger doesn't exist then we can just return the set with just the named state?
145+
while True: # do
146+
prev = set(closureSet) # update previous states set
147+
# add all states that can be reached by null
148+
for state in closureSet:
149+
reachableWithNull = [t.dest for t in machine.events['*'].transitions[state.name]]
150+
closureSet |= set(reachableWithNull)
151+
if prev == closureSet: # has closureSet changed?
152+
break
153+
return closureSet
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from __future__ import absolute_import
2+
from dfaFactory import buildDfaWithEquivelantStates

finite_state_machines/dfaFactory.py renamed to sudkampPython/finite_state_machines/dfaFactory.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
from transitions import Machine
22

3-
def buildDfaWithEquivelantState():
3+
def buildDfaWithEquivelantStates():
44
"""
55
This DFA contains equivelant states and can be used to test the
66
'determination of equivelant states' algorithm.
77
"""
88

99
states = ['q0','q1','q2','q3','q4','q5']
10-
transitions = [['b', 'q0', 'q2'],['b', 'q1', 'q4'],
11-
['a', 'q1', 'q3'],['a', 'q2', 'q3'],
12-
['b', 'q2', 'q5'],['a', 'q3', 'q3'],
13-
['a', 'q3', 'q3'],['a', 'q3', 'q3'],
14-
['a', 'q3', 'q3'],['a', 'q3', 'q3']]
10+
transitions = [['a', 'q0','q1'],['b', 'q0', 'q2'],['b', 'q1', 'q4'],
11+
['a', 'q1', 'q3'],['a', 'q2', 'q3'],['b', 'q2', 'q5'],
12+
['a', 'q3', 'q3'],['b', 'q3', 'q3'],['a', 'q4', 'q4'],
13+
['b', 'q4', 'q4'],['a', 'q5', 'q5'],['b', 'q5', 'q5']]
1514
dfa = Machine(model=None, states=states, transitions=transitions, initial='q0')
1615
dfa.add_transition(trigger='a', source='q0', dest='q1')
1716
dfa.set_final('q4')
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

sudkampPython/tests/test_finiteAutoAlgs.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import pytest
2+
from finite_state_machines import buildDfaWithEquivelantStates
3+
import finiteAutomaton
4+
5+
class TestFiniteAutomatonAlgorithms(TestCase):
6+
7+
def setUp(self):
8+
pass
9+
10+
def tearDown(self):
11+
pass
12+
13+
def testDetermineEquivStates(self):
14+
dfa = buildDfaWithEquivelantStates()
15+
equivStates = finiteAutomaton.determineEquivelantStatesOfDFA( dfa )
16+
self.assertTrue( all(x in equivStates for x in ['q4','q5']))
File renamed without changes.
File renamed without changes.

transitions/core.py renamed to sudkampPython/transitions/core.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ def __init__(self, model=None, states=None, initial=None, transitions=None,
333333
raise MachineError('Passing arguments {0} caused an inheritance error: {1}'.format(kwargs.keys(), e))
334334

335335
self.model = self if model is None else model
336-
self.alphabet = self.events # a simple renaming
336+
self.alphabet = self.events.keys() # a simple renaming
337337
self.states = OrderedDict()
338338
self.final_states = set()
339339
self.events = {}
File renamed without changes.
File renamed without changes.

tests/test_finiteAutomatons.py

-1
This file was deleted.

0 commit comments

Comments
 (0)