From 995b06c4e9617c44429cc4d0ae3d0518384d6471 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Mon, 16 Jan 2017 04:51:23 +0100 Subject: NFA: initial solution Python code is not cleaned up, it is working now... Generated LaTeX code was not positioned correctly, so just use GraphViz then. --- nfa/generate-nfa.py | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100755 nfa/generate-nfa.py (limited to 'nfa/generate-nfa.py') diff --git a/nfa/generate-nfa.py b/nfa/generate-nfa.py new file mode 100755 index 0000000..6255bca --- /dev/null +++ b/nfa/generate-nfa.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python +# Usage: +# $0 [number-of-states] | z3 -smt -in +# Examples: +# $0 25 + +import itertools +import sys + +# Number of steps +m = 25 if len(sys.argv) <= 1 else int(sys.argv[1]) + +# all possible strings +lang = [ + "aa", + "bb", + "aba", + "baa", + "abab", + "babb", + "bbba", +] + +# sledgehammer approach: specify all bad words +fulllang = [] +for n in range(1, 4 + 1): + fulllang += [ + "".join(x) + for x in itertools.product(*["ab"] * n) + ] +# Remove good words. +badlang = [word for word in fulllang if word not in lang] + +literals = [] +preds = [] +extrafuns = [] + +# Populate Q = { q0, q1, ..., q_(m-1) } with unique values. +literals += ["q%d" % i for i in range(m)] +preds += ["(= q%d %d)" % (i, i) for i in range(m)] + +def mks(word_index, letter_index): + """Return variable for a state of a word after some transitions.""" + return "s%d_%d" % (word_index, letter_index) + +# Assuming a single initial state (which may or may not be the final state), +# add states for every letter j in each word i. +for i, word in enumerate(lang): + for j, letter in enumerate(word): + v = mks(i, j) + literals += [v] + # Each of the states must be assigned to any of the states in Q + #preds += ["(>= %s 0)" % v, "(< %s %d)" % (v, m)] + +# Transition Δ: Q × Σ -> P(Q). For Σ = {a,b}, we assume this range: +# transitions (0..; 1..2; 0..) +extrafuns += ["(trans Int Int Int)"] +# final states F \subset Q +extrafuns += ["(final Int Bool)"] +# Require a transition between each letter +for i, word in enumerate(lang): + state_prev = "q0" + for j, letter in enumerate(word): + state_next = mks(i, j) + label = "ab".index(letter) + preds += ["(= (trans %s %d) %s)" % (state_prev, label, state_next)] + state_prev = state_next + preds += ["(final %s)" % state_prev] + +# "a" and "b" are not allowed to accept (length 1) +#preds += ["(not (final (trans q0 %d)))" % label for label in range(2)] +for i, word in enumerate(badlang): + state_prev = "q0" + move = "q0" + preconds = [] + for j, letter in enumerate(word): + label = "ab".index(letter) + move = "(trans %s %d)" % (move, label) + # Ensure result is within range. + preconds += ["(>= %s 0)" % move, "(< %s %d)" % (move, m)] + preds += ["(=> (and %s) (not (final %s)))" % (" ".join(preconds), move)] + #preds += ["(not (final %s))" % move] + #valid = "(>= {move} 0) (< {move} {m})".format(**vars()) + #preds += ["(=> (and %s) (not (final %s)))" % (valid, move)] + +# Try to fit any of the letter states with any state from Q +for i, word in enumerate(lang): + for j, letter in enumerate(word): + alts = [] + for qi in range(m): + state = "q%d" % qi + alts += ["(= %s %s)" % (state, mks(i, j))] + preds += ["(or %s)" % " ".join(alts)] + +# Begin generator +s = """(benchmark test.smt +:logic QF_UFLIA +:extrafuns ( +""" +s += "\n".join("(%s Int)" % x for x in literals) +s += "\n" + "\n".join(extrafuns) +s += """ +) +:formula (and +""" +s += "\n".join(preds) +s += """ +)) +""" + +try: + import sys + print(s) + # Hack to avoid printing a ugly error in case stdin of next pipe is closed. + sys.stdout.flush() + sys.stdout.close() +except BrokenPipeError: + pass -- cgit v1.2.1