Skip to content

Commit 883595e

Browse files
Reworked transitions and implemented skeleton code for equivStates algorithm
1 parent 224549f commit 883595e

File tree

15 files changed

+155
-98
lines changed

15 files changed

+155
-98
lines changed

.coveragerc

-3
This file was deleted.

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ dist/*
1010
.coverage
1111
.ipynb_checkpoints
1212
examples/state.png
13+
14+
# Ignore Virtualenv files
15+
*env/

Changelog.md

-8
This file was deleted.

FiniteAutomaton.py

+100-32
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,110 @@
1-
import random
1+
import transitions
22

3-
class ExceptionFSM(Exception):
4-
pass
3+
"""
4+
Algorithm 5.7.2
5+
Determination of Equivalent States of DFA
56
6-
class State(object):
7+
input: DFA M = (Q, Alphabet, TransitionFunctoin, q<sub>0</sub>, F)
78
8-
def __init__(self):
9-
pass
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)
1019
11-
def isFinalState(self):
12-
return fState
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
1325
14-
def isAcceptState(self):
15-
return aState
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 ):
33+
equivStates = set()
34+
states = list(dfa.states.items())
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+
D[i][j] = 1
49+
# Step 3
50+
for i in range(len(states)):
51+
for j in range(len(states)):
52+
if i < j and D[i][j] == 0: # Do
53+
# if there is an "a" for which both qi and qj have a transition and the
54+
# resulting state m,n are not equal (m!=n) and D[m][n] or D[n][m] == 1 then
55+
# DIST(i,j)
56+
# 3.1
57+
for symbol in dfa.alphabet.keys():
58+
qi_transition = getTransitionWithSymbol( symbol, states[i].name )
59+
qj_transition = getTransitionWithSymbol( symbol, states[j].name )
60+
m_state, n_state = qi_transition.dest, qj_transition.dest
61+
# get indexes
62+
m_index, n_index = 0,0
63+
if D[m_index][n_index] == 1 or D[n_index][m_index] == 1:
64+
DIST(i. j)
65+
# 3.2
66+
for symbol in dfa.alphabet.key():
67+
pass
68+
# Now determine the set of equiv states. If D[i][j] = 0 then i and j are equiv
69+
for i in range(len(states)):
70+
for j in range(len(states)):
71+
if D[i][j] == 0:
72+
equivStates.add(states[i])
73+
equivStates.add(states[j])
1674

17-
class Transition(object):
1875

19-
def __init__(self, input_state, symbol, result_state):
20-
self.state = input_state
21-
self.symbol = symbol
22-
self.result_state
76+
def DIST(i, j, D, S):
77+
""" Helper function for our main algorithm """
78+
D[i][j] = 1
79+
for m,n in S[i][j]:
80+
DIST(m, n)
2381

24-
class FiniteAutomaton(object):
82+
def getTransitionWithSymbol( dfa, symbol, source ):
83+
"""
84+
Find a transition, if one exists, that involves the named
85+
symbol and source
86+
"""
87+
for event in dfa.events.keys():
88+
for transition in event.transitions.values():
89+
if event == symbol and transition.source == source:
90+
return transition
2591

26-
def __init__(self, initial_state):
27-
self.init_state = initial_state
28-
self.Q = set()
29-
self.F = set()
30-
self.alphabet = set()
92+
"""
93+
input: an NFA-null M = (Q, Alphabet, TransitionFunction, q<sub>0</sub>,F)
94+
input transition function t of M
3195
32-
def add_transition(self, symbol, state, action, next_state):
33-
pass
34-
35-
def getOtherStates(self,state):
36-
return [s for s in Q if s != state]
37-
38-
def getNonFinalStates(self):
39-
return self.Q.difference(self.F)
40-
41-
def getANonFinalState(self):
42-
return random.choice(self.getNonFinalStates())
96+
1. initialize Q' to null-closure(q<sub>0</sub>)
97+
2. repeat
98+
2.1 if there is a node X, X is element of Q' and a symbol a that's element of Alphabet
99+
with no arc leaving X labeled "a", then
100+
2.1.1 let Y = UNION over q<sub>i</sub>, where q<sub>i</sub> is element of X of
101+
t(q<sub>i</sub>,a)
102+
2.1.2 if Y /= Q', then set Q' := Q UNION {Y}
103+
2.1.3 add an arc from X to Y labeled a
104+
else done:= true
105+
until done
106+
3. the set of accepting states of DM if F' = {X, where X is element of Q' | X contains
107+
an element q<sub>i</sub> that is element of F}
108+
"""
109+
def constructEquivelantDFAFromNFA( nfa ):
110+
raise NotImplementedError

LICENSE

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
License
33
*******
44

5-
The transitions package, including all examples, code snippets and attached
5+
This package, including all examples, code snippets and attached
66
documentation is covered by the MIT license.
77

88
::
99

1010
The MIT License
1111

12-
Copyright (c) 2014 Tal Yarkoni
12+
Copyright (c) 2016 Jonathon Belotti
1313

1414
Permission is hereby granted, free of charge, to any person obtaining a copy
1515
of this software and associated documentation files (the "Software"), to deal
@@ -27,4 +27,4 @@ documentation is covered by the MIT license.
2727
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2828
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2929
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30-
THE SOFTWARE.
30+
THE SOFTWARE.

finite_state_machines/dfaFactory.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from transitions import Machine
2+
3+
def buildDfaWithEquivelantState():
4+
"""
5+
This DFA contains equivelant states and can be used to test the
6+
'determination of equivelant states' algorithm.
7+
"""
8+
9+
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']]
15+
dfa = Machine(model=None, states=states, transitions=transitions, initial='q0')
16+
dfa.add_transition(trigger='a', source='q0', dest='q1')
17+
dfa.set_final('q4')
18+
dfa.set_final('q5')
File renamed without changes.

transitions_core/core.py renamed to transitions/core.py

+31
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,9 @@ 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
336337
self.states = OrderedDict()
338+
self.final_states = set()
337339
self.events = {}
338340
self.current_state = None
339341
self.send_event = send_event
@@ -379,6 +381,11 @@ def initial(self):
379381
""" Return the initial state. """
380382
return self._initial
381383

384+
@property
385+
def final(self):
386+
""" Return the set of final (or 'accepting') states """
387+
return self.final_states
388+
382389
@property
383390
def has_queue(self):
384391
""" Return boolean indicating if machine has queue or not """
@@ -388,6 +395,10 @@ def is_state(self, state):
388395
""" Check whether the current state matches the named state. """
389396
return self.current_state.name == state
390397

398+
def is_final(self, state):
399+
""" Check whether the named state is a final state. """
400+
return state in self.final_states
401+
391402
def get_state(self, state):
392403
""" Return the State instance with the passed name. """
393404
if state not in self.states:
@@ -401,6 +412,12 @@ def set_state(self, state):
401412
self.current_state = state
402413
self.model.state = self.current_state.name
403414

415+
def set_final(self, state):
416+
""" Set state as a final ('accepting') state. """
417+
if state not in self.states:
418+
raise MachineError("Can't set state as final. State not in machine.")
419+
self.final_states.add(state)
420+
404421
def add_state(self, *args, **kwargs):
405422
""" Alias for add_states. """
406423
self.add_states(*args, **kwargs)
@@ -546,6 +563,20 @@ def _callback(self, func, event_data):
546563
else:
547564
func(*event_data.args, **event_data.kwargs)
548565

566+
def read(self, symbol):
567+
"""
568+
This has been added to the stock "transitions" library to be a more appropiate
569+
transition handling method for algorithm implementation.
570+
571+
For a transition q0 -[a]-> q1 the stock library can do
572+
machine.a()
573+
machine._process(s.a)
574+
machine.events['a']_trigger()
575+
none of which are particularly good for our purposes. With read() you can simply do
576+
machine.read('a')
577+
"""
578+
return self.events[symbol]._trigger()
579+
549580
def _process(self, trigger):
550581

551582
# default processing

transitions_core/fsm_algorithms.py

-51
This file was deleted.

transitions_core/version.py

-1
This file was deleted.

0 commit comments

Comments
 (0)