diff --git a/HISTORY.rst b/HISTORY.rst
index 3b700af..481a1dd 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -10,6 +10,11 @@ History
0.1.1 (2018-03-15)
------------------
-* basic DFA and NFA support;
+* Basic DFA and NFA support;
* Algorithms for DFA minimization and trimming;
* Algorithm for NFA determinization.
+
+0.1.2 (2018-03-16)
+------------------
+
+* Minor bug fixes
diff --git a/README.rst b/README.rst
index 9dc68fa..adb6dda 100644
--- a/README.rst
+++ b/README.rst
@@ -9,6 +9,9 @@ Pythomata
.. image:: https://img.shields.io/pypi/pyversions/pythomata.svg
:target: https://pypi.python.org/pypi/pythomata
+.. image:: https://img.shields.io/badge/status-development-orange.svg
+ :target: https://img.shields.io/badge/status-development-orange.svg
+
.. image:: https://img.shields.io/travis/MarcoFavorito/pythomata.svg
:target: https://travis-ci.org/MarcoFavorito/pythomata
@@ -40,7 +43,9 @@ Install
Features
--------
-* TODO
+* Basic DFA and NFA support;
+* Algorithms for DFA minimization and trimming;
+* Algorithm for NFA determinization.
Credits
-------
diff --git a/pythomata/__init__.py b/pythomata/__init__.py
index 9e4bf1b..8fd6adf 100644
--- a/pythomata/__init__.py
+++ b/pythomata/__init__.py
@@ -4,4 +4,4 @@
__author__ = """Marco Favorito"""
__email__ = 'marco.favorito@gmail.com'
-__version__ = '0.1.dev2'
+__version__ = '0.1b2'
diff --git a/pythomata/base/Alphabet.py b/pythomata/base/Alphabet.py
index 54ec2a6..f563ba8 100644
--- a/pythomata/base/Alphabet.py
+++ b/pythomata/base/Alphabet.py
@@ -10,3 +10,9 @@ def __init__(self, symbols: Set[Symbol]):
@staticmethod
def fromStrings(symbol_strings:Set[str]):
return Alphabet(set(Symbol(s) for s in symbol_strings))
+
+ def __eq__(self, other):
+ if type(self)==type(other):
+ return self.symbols == other.symbols
+ else:
+ return False
diff --git a/pythomata/base/DFA.py b/pythomata/base/DFA.py
index d49329e..ac4f114 100644
--- a/pythomata/base/DFA.py
+++ b/pythomata/base/DFA.py
@@ -33,20 +33,18 @@ def _check_input(self, alphabet, states, initial_state, accepting_states, transi
if s not in states or any(sym not in alphabet.symbols and next_state not in states for sym, next_state in sym2state.items()):
raise ValueError
- @classmethod
- def complete(cls, dfa):
+ def complete(self):
sink = Sink()
- transitions = deepcopy(dfa.transition_function)
- for state, sym2state in dfa.transition_function.items():
- for action in dfa.alphabet.symbols:
+ transitions = deepcopy(self.transition_function)
+ for state, sym2state in self.transition_function.items():
+ for action in self.alphabet.symbols:
if not action in sym2state:
transitions[state][action]= sink
- return DFA(dfa.alphabet, dfa.states.union({sink}), dfa.initial_state, dfa.accepting_states,
+ return DFA(self.alphabet, self.states.union({sink}), self.initial_state, self.accepting_states,
dict(transitions))
- @classmethod
- def minimize(cls, orig_dfa):
- dfa = DFA.complete(orig_dfa)
+ def minimize(self):
+ dfa = self.complete()
# Greatest−fixpoint
z_current = set()
@@ -96,47 +94,45 @@ def minimize(cls, orig_dfa):
return DFA(dfa.alphabet, frozenset(equiv_class2new_state.values()), equiv_class2new_state[state2equiv_class[dfa.initial_state]], new_final_states, new_transition_function)
- @classmethod
- def reachable(cls, dfa):
+ def reachable(self):
# least fixpoint
z_current, z_next = set(), set()
- z_next.add(dfa.initial_state)
+ z_next.add(self.initial_state)
while z_current != z_next:
z_current = z_next
z_next = deepcopy(z_current)
for s in z_current:
- for a in dfa.transition_function.get(s, []):
- next_state = dfa.transition_function[s][a]
+ for a in self.transition_function.get(s, []):
+ next_state = self.transition_function[s][a]
z_next.add(next_state)
new_states = z_current
new_transition_function = {}
for s in new_states:
- for a in dfa.transition_function.get(s, []):
- next_state = dfa.transition_function[s][a]
+ for a in self.transition_function.get(s, []):
+ next_state = self.transition_function[s][a]
if next_state in new_states:
new_transition_function.setdefault(s, {})
new_transition_function[s].setdefault(a, {})
new_transition_function[s][a] = next_state
- new_final_states = frozenset(new_states.intersection(dfa.accepting_states))
+ new_final_states = frozenset(new_states.intersection(self.accepting_states))
- return DFA(dfa.alphabet, new_states, dfa.initial_state, new_final_states, new_transition_function)
+ return DFA(self.alphabet, new_states, self.initial_state, new_final_states, new_transition_function)
- @classmethod
- def coreachable(cls, dfa):
+ def coreachable(self):
# least fixpoint
z_current, z_next = set(), set()
- z_next = set(dfa.accepting_states)
+ z_next = set(self.accepting_states)
while z_current != z_next:
z_current = z_next
z_next = deepcopy(z_current)
- for state in dfa.states:
- for a in dfa.transition_function.get(state, []):
- next_state = dfa.transition_function[state][a]
+ for state in self.states:
+ for a in self.transition_function.get(state, []):
+ next_state = self.transition_function[state][a]
if next_state in z_next:
z_next.add(state)
break
@@ -144,31 +140,35 @@ def coreachable(cls, dfa):
new_states = z_current
new_transition_function = {}
for s in new_states:
- for a in dfa.transition_function.get(s, []):
- next_state = dfa.transition_function[s][a]
+ for a in self.transition_function.get(s, []):
+ next_state = self.transition_function[s][a]
if next_state in new_states:
new_transition_function.setdefault(s, {})
new_transition_function[s].setdefault(a, {})
new_transition_function[s][a] = next_state
- if dfa.initial_state not in new_states:
+ if self.initial_state not in new_states:
initial_state = None
else:
- initial_state = dfa.initial_state
- return DFA(dfa.alphabet, new_states, initial_state, dfa.accepting_states, new_transition_function)
+ initial_state = self.initial_state
+ return DFA(self.alphabet, new_states, initial_state, self.accepting_states, new_transition_function)
- @classmethod
- def trim(cls, dfa):
- dfa = DFA.reachable(dfa)
- dfa = DFA.coreachable(dfa)
+ def trim(self):
+ dfa = self.reachable()
+ dfa = dfa.coreachable()
return dfa
def word_acceptance(self, word:List[Symbol]):
assert all(char in self.alphabet.symbols for char in word)
+
current_state = self.initial_state
+ # return false if current_state is None
+ if current_state is None:
+ return False
+
for char in word:
if char not in self.transition_function[current_state]:
return False
diff --git a/pythomata/base/NFA.py b/pythomata/base/NFA.py
index ee976c5..4934b89 100644
--- a/pythomata/base/NFA.py
+++ b/pythomata/base/NFA.py
@@ -61,18 +61,17 @@ def to_dot(self, path):
g.render(filename=path)
- @classmethod
- def determinize(cls, nfa):
- new_states = powerset(nfa.states)
- initial_state = nfa.initial_states
- final_states = frozenset([q for q in new_states if len(q.intersection(nfa.accepting_states))!=0])
+ def determinize(self):
+ new_states = powerset(self.states)
+ initial_state = frozenset(self.initial_states)
+ final_states = frozenset([q for q in new_states if len(q.intersection(self.accepting_states)) != 0])
transition_function = {}
for state_set in new_states:
- for action in nfa.alphabet.symbols:
+ for action in self.alphabet.symbols:
next_states = set()
for s in state_set:
- for s_prime in nfa.transition_function.get(s, {}).get(action, []):
+ for s_prime in self.transition_function.get(s, {}).get(action, []):
next_states.add(s_prime)
# next_states = set(s_prime for s in state_set for s_prime in nfa.transition_function.get(s, {}).get(action, []))
@@ -80,7 +79,7 @@ def determinize(cls, nfa):
next_states = frozenset(next_states)
transition_function.setdefault(state_set, {})[action] = next_states
- return DFA(nfa.alphabet, new_states, initial_state, final_states, transition_function)
+ return DFA(self.alphabet, new_states, initial_state, final_states, transition_function)
@classmethod
diff --git a/pythomata/base/Symbol.py b/pythomata/base/Symbol.py
index 47e37ef..7964f38 100644
--- a/pythomata/base/Symbol.py
+++ b/pythomata/base/Symbol.py
@@ -7,7 +7,7 @@ def __init__(self, name):
self.name = name
def __str__(self):
- return self.name
+ return str(self.name)
def _members(self):
return (self.name)
@@ -22,7 +22,7 @@ def __hash__(self):
return hash(self._members())
def __repr__(self):
- return ", ".join(map(str,self._members()))
+ return str(self._members())
def __lt__(self, other):
return self.name.__lt__(other.name)
diff --git a/setup.py b/setup.py
index cc1c744..37efab4 100644
--- a/setup.py
+++ b/setup.py
@@ -22,7 +22,7 @@
setup(
name='pythomata',
- version='0.1.dev2',
+ version='0.1b2',
description="Python implementation of automata.",
long_description=readme + '\n\n' + history,
author="Marco Favorito",
diff --git a/tests/automata/nfa_strings.dot b/tests/automata/nfa_strings.dot
index 697b41c..c70e996 100644
--- a/tests/automata/nfa_strings.dot
+++ b/tests/automata/nfa_strings.dot
@@ -1,22 +1,22 @@
digraph {
fake0 [style=invisible]
fake1 [style=invisible]
- s2 [shape=doublecircle]
- s3 [shape=doublecircle]
- s1 [root=true]
s4 [root=true]
+ s1 [root=true]
+ s3 [shape=doublecircle]
+ s2 [shape=doublecircle]
s5
- fake1 -> s1 [style=bold]
- fake0 -> s4 [style=bold]
- s2 -> s1 [label=b]
- s3 -> s1 [label=b]
- s1 -> s5 [label=c]
- s1 -> s2 [label=b]
- s1 -> s3 [label=b]
- s4 -> s1 [label=c]
+ fake1 -> s4 [style=bold]
+ fake0 -> s1 [style=bold]
s4 -> s1 [label=b]
s4 -> s5 [label=b]
- s5 -> s5 [label=c]
+ s4 -> s1 [label=c]
+ s1 -> s2 [label=b]
+ s1 -> s3 [label=b]
+ s1 -> s5 [label=c]
+ s2 -> s1 [label=b]
s5 -> s5 [label=b]
s5 -> s5 [label=a]
+ s5 -> s5 [label=c]
+ s3 -> s1 [label=b]
}
diff --git a/tests/automata/nfa_strings.dot.svg b/tests/automata/nfa_strings.dot.svg
index 0391bca..9eb20f0 100644
--- a/tests/automata/nfa_strings.dot.svg
+++ b/tests/automata/nfa_strings.dot.svg
@@ -4,119 +4,119 @@
-
diff --git a/tests/automata/strings_minimized.dot b/tests/automata/strings_minimized.dot
index 430fc00..bef466e 100644
--- a/tests/automata/strings_minimized.dot
+++ b/tests/automata/strings_minimized.dot
@@ -1,21 +1,21 @@
digraph {
fake [style=invisible]
0
- 1 [root=true]
- 2
+ 1
+ 2 [root=true]
3 [shape=doublecircle]
4
- fake -> 1 [style=bold]
+ fake -> 2 [style=bold]
+ 0 -> 1 [label=b]
+ 0 -> 1 [label=a]
0 -> 0 [label=c]
- 0 -> 4 [label=b]
- 0 -> 4 [label=a]
- 1 -> 0 [label=c]
- 1 -> 3 [label=b]
- 1 -> 3 [label=a]
- 2 -> 1 [label=c]
- 2 -> 4 [label=b]
- 2 -> 4 [label=a]
- 3 -> 4 [label=c]
- 3 -> 1 [label=b]
- 3 -> 4 [label=a]
+ 2 -> 3 [label=a]
+ 2 -> 3 [label=b]
+ 2 -> 0 [label=c]
+ 3 -> 2 [label=b]
+ 3 -> 1 [label=a]
+ 3 -> 1 [label=c]
+ 4 -> 1 [label=b]
+ 4 -> 1 [label=a]
+ 4 -> 2 [label=c]
}
diff --git a/tests/automata/strings_minimized.dot.svg b/tests/automata/strings_minimized.dot.svg
index 781c8cc..6579902 100644
--- a/tests/automata/strings_minimized.dot.svg
+++ b/tests/automata/strings_minimized.dot.svg
@@ -4,23 +4,23 @@
-
+
%3
-
+
fake
-
-1
-
-1
+
+2
+
+2
-
-fake->1
-
-
+
+fake->2
+
+
0
@@ -28,33 +28,33 @@
0
-0->0
+0->0
c
-
-4
+
+1
-4
+1
-
-0->4
+
+0->1
b
-
-0->4
+
+0->1
-a
+a
-
-1->0
-
-
-c
+
+2->0
+
+
+c
3
@@ -62,58 +62,58 @@
3
-
-1->3
-
-
-b
-
-
-1->3
-
-
-a
+
+2->3
+
+
+a
-
-2
-
-2
-
-
-2->1
-
-
-c
-
-
-2->4
-
-
-b
-
-
-2->4
-
-
-a
+
+2->3
+
+
+b
-3->1
-
-
-b
-
-
-3->4
+3->1
-c
+a
-
-3->4
+
+3->1
-a
+c
+
+
+3->2
+
+
+b
+
+
+4
+
+4
+
+
+4->1
+
+
+b
+
+
+4->1
+
+
+a
+
+
+4->2
+
+
+c
diff --git a/tests/automata/strings_minimized_and_trimmed.dot b/tests/automata/strings_minimized_and_trimmed.dot
index 19f0ba4..c022c83 100644
--- a/tests/automata/strings_minimized_and_trimmed.dot
+++ b/tests/automata/strings_minimized_and_trimmed.dot
@@ -1,9 +1,9 @@
digraph {
fake [style=invisible]
- 1 [root=true]
+ 2 [root=true]
3 [shape=doublecircle]
- fake -> 1 [style=bold]
- 1 -> 3 [label=b]
- 1 -> 3 [label=a]
- 3 -> 1 [label=b]
+ fake -> 2 [style=bold]
+ 2 -> 3 [label=a]
+ 2 -> 3 [label=b]
+ 3 -> 2 [label=b]
}
diff --git a/tests/automata/strings_minimized_and_trimmed.dot.svg b/tests/automata/strings_minimized_and_trimmed.dot.svg
index 0ddb9be..1c035bb 100644
--- a/tests/automata/strings_minimized_and_trimmed.dot.svg
+++ b/tests/automata/strings_minimized_and_trimmed.dot.svg
@@ -12,13 +12,13 @@
fake
-
-1
+
+2
-1
+2
-
-fake->1
+
+fake->2
@@ -28,20 +28,20 @@
3
-
-1->3
+
+2->3
-b
+a
-
-1->3
+
+2->3
-a
+b
-
-3->1
+
+3->2
b
diff --git a/tests/automata/strings_reachable.dot b/tests/automata/strings_reachable.dot
index 912eecf..ca949b1 100644
--- a/tests/automata/strings_reachable.dot
+++ b/tests/automata/strings_reachable.dot
@@ -1,14 +1,14 @@
digraph {
fake [style=invisible]
+ s1 [root=true]
s2 [shape=doublecircle]
s3 [shape=doublecircle]
- s1 [root=true]
s5
fake -> s1 [style=bold]
- s2 -> s1 [label=b]
- s3 -> s1 [label=b]
- s1 -> s5 [label=c]
- s1 -> s3 [label=b]
s1 -> s2 [label=a]
+ s1 -> s3 [label=b]
+ s1 -> s5 [label=c]
+ s2 -> s1 [label=b]
s5 -> s5 [label=c]
+ s3 -> s1 [label=b]
}
diff --git a/tests/automata/strings_reachable.dot.svg b/tests/automata/strings_reachable.dot.svg
index 6f63d62..943a567 100644
--- a/tests/automata/strings_reachable.dot.svg
+++ b/tests/automata/strings_reachable.dot.svg
@@ -13,7 +13,7 @@
fake
-s1
+s1
s1
@@ -23,37 +23,25 @@
-s2
+s2
s2
-
-s2->s1
-
-
-b
+
+s1->s2
+
+
+a
-s3
+s3
s3
-
-s3->s1
-
-
-b
-
-
-s1->s2
-
-
-a
-
-s1->s3
+s1->s3
b
@@ -69,8 +57,20 @@
c
+
+s2->s1
+
+
+b
+
+
+s3->s1
+
+
+b
+
-s5->s5
+s5->s5
c
diff --git a/tests/automata/strings_trimmed.dot b/tests/automata/strings_trimmed.dot
index 059018e..5600f1a 100644
--- a/tests/automata/strings_trimmed.dot
+++ b/tests/automata/strings_trimmed.dot
@@ -1,11 +1,11 @@
digraph {
fake [style=invisible]
+ s1 [root=true]
s2 [shape=doublecircle]
s3 [shape=doublecircle]
- s1 [root=true]
fake -> s1 [style=bold]
+ s1 -> s2 [label=a]
+ s1 -> s3 [label=b]
s2 -> s1 [label=b]
s3 -> s1 [label=b]
- s1 -> s3 [label=b]
- s1 -> s2 [label=a]
}
diff --git a/tests/automata/strings_trimmed.dot.svg b/tests/automata/strings_trimmed.dot.svg
index f5a98a7..e8404d1 100644
--- a/tests/automata/strings_trimmed.dot.svg
+++ b/tests/automata/strings_trimmed.dot.svg
@@ -13,7 +13,7 @@
fake
-s1
+s1
s1
@@ -23,40 +23,40 @@
-s2
+s2
s2
-
-s2->s1
-
-
-b
+
+s1->s2
+
+
+a
-s3
+s3
s3
-
-s3->s1
-
-
-b
-
-
-s1->s2
-
-
-a
-
-s1->s3
+s1->s3
b
+
+s2->s1
+
+
+b
+
+
+s3->s1
+
+
+b
+
diff --git a/tests/test_nfa.py b/tests/test_nfa.py
index 7642f6e..41316f1 100644
--- a/tests/test_nfa.py
+++ b/tests/test_nfa.py
@@ -68,3 +68,105 @@ def test_nfa_determinization(self):
dfa = DFA.minimize(dfa)
dfa = DFA.trim(dfa)
dfa.to_dot("tests/automata/nfa_strings_determinized_minimized.dot")
+
+class TestNFAOnSets(unittest.TestCase):
+
+ def test_sets(self):
+ inc = Symbol("inc")
+ doub = Symbol("doub")
+ inc_ = frozenset({inc})
+ doub_ = frozenset({doub})
+ not_ = frozenset()
+
+ initial_states = frozenset({
+ frozenset({1}),
+ frozenset({2})
+ })
+ final_states = frozenset({
+ frozenset({5}),
+ frozenset({6})
+ })
+ states = frozenset({
+ frozenset({1}),
+ frozenset({2}),
+ frozenset({3}),
+ frozenset({4}),
+ frozenset({5}),
+ frozenset({6}),
+ })
+
+ # as list of transitions
+ transitions = frozenset({
+ (frozenset({1}), inc_, frozenset({2})),
+ (frozenset({2}), doub_, frozenset({4})),
+ (frozenset({4}), doub_, frozenset({6})),
+ (frozenset({6}), doub_, frozenset({2})),
+ })
+
+ alphabet = Alphabet({inc_, doub_, not_})
+
+ nfa = NFA.fromTransitions(alphabet,states,initial_states,final_states,transitions)
+ nfa.to_dot("tests/automata/qui.nfa")
+
+ dfa = nfa.determinize().minimize().trim()
+ dfa.to_dot("tests/automata/qui.dfa")
+
+
+ def test_ldlf_formulas(self):
+ a = Symbol("a")
+ tt = Symbol("TT")
+ eventually_true_tt = Symbol("tt")
+
+ alphabet = {frozenset(), frozenset({a})}
+
+ delta = {
+ (frozenset(), frozenset(), frozenset()),
+ (frozenset(), frozenset({a}), frozenset()),
+ (frozenset({eventually_true_tt}), frozenset(), frozenset({tt})),
+ (frozenset({eventually_true_tt}), frozenset({a}), frozenset({tt})),
+ (frozenset({tt}), frozenset(), frozenset()),
+ (frozenset({tt}), frozenset({a}), frozenset()),
+
+ }
+ final_states = {frozenset(), frozenset([tt])}
+ initial_state = {frozenset([eventually_true_tt])}
+ states = {frozenset(), frozenset([eventually_true_tt]), frozenset([tt])}
+
+ x = {}
+ x["alphabet"] = alphabet
+ x["states"] = states
+ x["initial_states"] = initial_state
+ x["accepting_states"] = final_states
+ x["transitions"] = delta
+
+ nfa = NFA.fromTransitions(Alphabet(x["alphabet"]), x["states"], x["initial_states"],
+ x["accepting_states"], x["transitions"])
+ nfa.to_dot("tests/automata/formulas.nfa")
+
+ transition_function = {
+ frozenset(): {
+ frozenset(): {frozenset()},
+ frozenset({a}): {frozenset()}
+ },
+ frozenset({eventually_true_tt}): {
+ frozenset(): {frozenset({tt})},
+ frozenset({a}): {frozenset({tt})}
+ },
+ frozenset({tt}): {
+ frozenset(): {frozenset()},
+ frozenset({a}): {frozenset()}
+ },
+ }
+
+ self.assertEqual(nfa.alphabet, Alphabet(alphabet))
+ self.assertEqual(nfa.states, states)
+ self.assertEqual(nfa.initial_states, initial_state)
+ self.assertEqual(nfa.accepting_states, final_states)
+ self.assertEqual(nfa.accepting_states, final_states)
+ self.assertEqual(nfa.transition_function, transition_function)
+
+ dfa = nfa.determinize().minimize()
+ dfa.to_dot("tests/automata/formulas.dfa")
+
+
+