Skip to content

Commit fedc928

Browse files
author
Simone Gasperini
committed
Initial commit
0 parents  commit fedc928

7 files changed

Lines changed: 347 additions & 0 deletions

File tree

.gitignore

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
build/
12+
develop-eggs/
13+
dist/
14+
downloads/
15+
eggs/
16+
.eggs/
17+
lib/
18+
lib64/
19+
parts/
20+
sdist/
21+
var/
22+
wheels/
23+
pip-wheel-metadata/
24+
share/python-wheels/
25+
*.egg-info/
26+
.installed.cfg
27+
*.egg
28+
MANIFEST
29+
30+
# PyInstaller
31+
# Usually these files are written by a python script from a template
32+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
33+
*.manifest
34+
*.spec
35+
36+
# Installer logs
37+
pip-log.txt
38+
pip-delete-this-directory.txt
39+
40+
# Unit test / coverage reports
41+
htmlcov/
42+
.tox/
43+
.nox/
44+
.coverage
45+
.coverage.*
46+
.cache
47+
nosetests.xml
48+
coverage.xml
49+
*.cover
50+
*.py,cover
51+
.hypothesis/
52+
.pytest_cache/
53+
54+
# Translations
55+
*.mo
56+
*.pot
57+
58+
# Django stuff:
59+
*.log
60+
local_settings.py
61+
db.sqlite3
62+
db.sqlite3-journal
63+
64+
# Flask stuff:
65+
instance/
66+
.webassets-cache
67+
68+
# Scrapy stuff:
69+
.scrapy
70+
71+
# Sphinx documentation
72+
docs/_build/
73+
74+
# PyBuilder
75+
target/
76+
77+
# Jupyter Notebook
78+
.ipynb_checkpoints
79+
80+
# IPython
81+
profile_default/
82+
ipython_config.py
83+
84+
# pyenv
85+
.python-version
86+
87+
# pipenv
88+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
90+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
91+
# install all needed dependencies.
92+
#Pipfile.lock
93+
94+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
95+
__pypackages__/
96+
97+
# Celery stuff
98+
celerybeat-schedule
99+
celerybeat.pid
100+
101+
# SageMath parsed files
102+
*.sage.py
103+
104+
# Environments
105+
.env
106+
.venv
107+
env/
108+
venv/
109+
ENV/
110+
env.bak/
111+
venv.bak/
112+
113+
# Spyder project settings
114+
.spyderproject
115+
.spyproject
116+
117+
# Rope project settings
118+
.ropeproject
119+
120+
# mkdocs documentation
121+
/site
122+
123+
# mypy
124+
.mypy_cache/
125+
.dmypy.json
126+
dmypy.json
127+
128+
# Pyre type checker
129+
.pyre/

TMsim/__init__.py

Whitespace-only changes.

TMsim/machine.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
from TMsim.parsing import parse_text, get_delta_dict
2+
from TMsim.tape import Tape
3+
4+
5+
class TuringMachine:
6+
7+
init_state = 'Qinit'
8+
halt_states = {'Qhalt', 'Qaccept', 'Qreject'}
9+
10+
def __init__(self, num_tapes, alphabet, states, transition_func):
11+
self.num_tapes = num_tapes
12+
self.alphabet = alphabet
13+
self.states = states
14+
self.transition_func = transition_func
15+
self._initialize()
16+
17+
def __repr__(self):
18+
class_name = self.__class__.__qualname__
19+
params = list(self.__init__.__code__.co_varnames)
20+
params.remove('self')
21+
params.remove('transition_func')
22+
args = '\n'.join([f'\t{key} = {getattr(self, key)}' for key in params])
23+
return f'{class_name}(\n{args}\n)'
24+
25+
@classmethod
26+
def from_txt(cls, filepath):
27+
with open(filepath) as file:
28+
lines = file.readlines()
29+
num_tapes, alphabet, states, transitions = parse_text(lines)
30+
transition_func = get_delta_dict(transitions, alphabet)
31+
return cls(num_tapes=num_tapes, alphabet=alphabet, states=states,
32+
transition_func=transition_func)
33+
34+
def _initialize(self):
35+
self._check_symbols()
36+
self._check_init_state()
37+
self.state = self.init_state
38+
self.steps = 0
39+
self.tapes = self._init_tapes()
40+
41+
def _check_symbols(self):
42+
for symbol in {'>', '_'}:
43+
if symbol not in self.alphabet:
44+
raise ValueError(
45+
f'The required symbol "{symbol}" is not in the alphabet!')
46+
47+
def _check_init_state(self):
48+
if self.init_state not in self.states:
49+
raise ValueError(
50+
f'The required state "{self.init_state}" is not in the set of states!')
51+
52+
def _init_tapes(self):
53+
empty_tapes = [Tape() for i in range(self.num_tapes)]
54+
return empty_tapes
55+
56+
def load_input(self, string):
57+
for s in string:
58+
self.tapes[0].move(key='R')
59+
self.tapes[0].write(s)
60+
self.tapes[0].head = self.tapes[0].min_head+1
61+
62+
def get_config(self):
63+
config = (self.state,)
64+
for tape in self.tapes:
65+
config += (tape.read(),)
66+
return config
67+
68+
def execute(self, instructions):
69+
self.state = instructions[0]
70+
k = 1+self.num_tapes
71+
symbols = instructions[1:k]
72+
moves = instructions[k:]
73+
for i, s, m in zip(range(self.num_tapes), symbols, moves):
74+
self.tapes[i].write(symbol=s)
75+
self.tapes[i].move(key=m)
76+
77+
def make_step(self):
78+
config = self.get_config()
79+
instructions = self.transition_func[config]
80+
self.execute(instructions)
81+
82+
def run(self):
83+
while self.state not in self.halt_states:
84+
self.make_step()
85+
self.steps += 1
86+
87+
def show_state(self, prefix=''):
88+
print(f'{prefix}state = {self.state}')
89+
90+
def show_input(self):
91+
self.show_state(prefix='init_')
92+
print(f'input = {self.tapes[0]}')
93+
94+
def show_output(self):
95+
self.show_state(prefix='final_')
96+
print(f'output = {self.tapes[-1]}')
97+
print(f'num_steps = {self.steps}')
98+
99+
100+
if __name__ == '__main__':
101+
102+
filepath = './../codes/1tape_inverse.txt'
103+
TM = TuringMachine.from_txt(filepath)
104+
105+
input_string = '00011101'
106+
TM.load_input(input_string)
107+
108+
TM.show_input()
109+
TM.run()
110+
TM.show_output()

TMsim/parsing.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
def parse_string(s, par='{}', sep=',', ret=set):
2+
s = s.strip(par[0]).strip(par[1])
3+
return ret(s.split(sep))
4+
5+
6+
def parse_text(lines):
7+
lines = [l.strip('\n').replace(' ', '') for l in lines if l != '\n']
8+
for line in lines:
9+
if line.startswith('NUM_TAPES='):
10+
num_tapes = int(line.strip('NUM_TAPES='))
11+
elif line.startswith('ALPHABET='):
12+
alphabet = parse_string(line.strip('ALPHABET='))
13+
elif line.startswith('STATES='):
14+
states = parse_string(line.strip('STATES='))
15+
elif line.startswith('TRANSITION_FUNC='):
16+
transitions = lines[lines.index(line)+1:]
17+
break
18+
return num_tapes, alphabet, states, transitions
19+
20+
21+
def get_delta_dict(transitions, alphabet):
22+
transitions_list = [t.replace('*', s) for s in alphabet
23+
for t in transitions if '*' in t]
24+
transitions_list.extend([t for t in transitions if '*' not in t])
25+
delta_dict = {parse_string(string.split('-->')[0], par='()', ret=tuple):
26+
parse_string(string.split('-->')[1], par='()', ret=tuple)
27+
for string in transitions_list}
28+
return delta_dict

TMsim/tape.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
class Tape:
2+
3+
moves = {'S': lambda head: head,
4+
'R': lambda head: head+1,
5+
'L': lambda head: head-1}
6+
7+
def __init__(self):
8+
self.head = 0
9+
self.cells = {self.head-1: '_',
10+
self.head: '>',
11+
self.head+1: '_'}
12+
13+
def __repr__(self):
14+
symbols = [self.cells[i] if i != self.head else '|' + self.cells[i]
15+
for i in range(self.min_head, self.max_head+1)]
16+
return '..|' + '|'.join(symbols) + '|..'
17+
18+
@property
19+
def min_head(self):
20+
return min(list(self.cells.keys()))
21+
22+
@property
23+
def max_head(self):
24+
return max(list(self.cells.keys()))
25+
26+
def _blank_first_cell(self):
27+
first = self.min_head
28+
if self.cells[first] != '_':
29+
self.cells[first-1] = '_'
30+
31+
def _blank_last_cell(self):
32+
last = self.max_head
33+
if self.cells[last] != '_':
34+
self.cells[last+1] = '_'
35+
36+
def move(self, key):
37+
self.head = self.moves[key](self.head)
38+
39+
def read(self):
40+
return self.cells[self.head]
41+
42+
def write(self, symbol):
43+
self.cells[self.head] = symbol
44+
self._blank_first_cell()
45+
self._blank_last_cell()

codes/1tape_inverse.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
NUM_TAPES = 1
2+
3+
ALPHABET = {0, 1, >, _}
4+
5+
STATES = {Qinit, Q1, Q2, Qhalt}
6+
7+
TRANSITION_FUNC =
8+
(Qinit, >) --> (Q1, >, S)
9+
(Q1, >) --> (Q1, >, R)
10+
(Q1, 0) --> (Q1, 0, R)
11+
(Q1, 1) --> (Q1, 1, R)
12+
(Q1, _) --> (Q2, _, S)
13+
(Q2, _) --> (Q2, _, L)
14+
(Q2, 0) --> (Q2, 1, L)
15+
(Q2, 1) --> (Q2, 0, L)
16+
(Q2, >) --> (Qhalt, >, S)

setup.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from setuptools import setup
2+
from setuptools import find_packages
3+
4+
5+
setup(
6+
7+
name='TMsimulator',
8+
version='0.0.1',
9+
10+
author='SimoneGasperini',
11+
author_email='simone.gasperini2@studio.unibo.it',
12+
13+
description='Turing Machine simulator written in Python',
14+
url='https://github.com/SimoneGasperini/TMsimulator.git',
15+
16+
packages=find_packages(),
17+
python_requires='>=3.8',
18+
19+
)

0 commit comments

Comments
 (0)