add python labs

This commit is contained in:
JasterV 2026-03-08 19:00:05 +01:00
parent 6791f4ff5b
commit 15f54af40a
6 changed files with 501 additions and 0 deletions

View file

@ -0,0 +1,122 @@
# Created by https://www.gitignore.io/api/python
# Edit at https://www.gitignore.io/?templates=python
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# End of https://www.gitignore.io/api/python
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode
### VisualStudioCode ###
.vscode/*
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
# End of https://www.toptal.com/developers/gitignore/api

View file

@ -0,0 +1,22 @@
# FINITE AUTOMATONS COMPUTATION
A program that runs finite automatons and operate with them (reading, determinizing and minimizing).
I'm working on automaton algebra operations such as concatenation, union and intersection. ^^
To work with the program you need to create a json file with the automaton settings.
The json must contains the following attributes:
+ states -> A map whose keys are the label of the state
and whose values are a list with each type
corresponding to the state.
The types are: 'I' for initial states,
'F' for final states,
'N' for non-initial and non-final states.
+ alphabet -> A list with each symbol accepted by the automaton.
+ transitions -> A map whose keys are the label of the state
and whose values are another map that represents
each transition for each symbol for the corresponding state.
The transition map must have as key a valid symbol on the alphabet
and as value a list of the states the corresponding state goes with that symbol.

View file

@ -0,0 +1,25 @@
{
"states": {
"0": ["I", "F"],
"1": ["N"],
"2": ["F"]
},
"alphabet": [
"a",
"b"
],
"transitions": {
"0": {
"b" : ["0"],
"a" : ["1"]
},
"1": {
"a" : ["2"],
"b" : ["1"]
},
"2": {
"a" : ["2"],
"b" : ["2"]
}
}
}

View file

@ -0,0 +1,25 @@
{
"states": {
"0": ["I"],
"1": ["I"],
"2": ["F"]
},
"alphabet": [
"a",
"b"
],
"transitions": {
"0": {
"b" : [],
"a" : ["1"]
},
"1": {
"a" : ["2"],
"b" : []
},
"2": {
"a" : ["2"],
"b" : ["2", "0"]
}
}
}

View file

@ -0,0 +1,247 @@
from collections.abc import Iterable
from queue import Queue
import random
empty_set = u"\u00F8"
lmbda = u"\u03BB"
class FSM():
def __init__(self, json: dict):
try:
states = json["states"]
alphabet = json["alphabet"]
transitions = json["transitions"]
except Exception:
raise ValueError(
"The json file must have the keys 'states', 'alphabet' and 'transitions'")
self.__assert_states(states)
self.__assert_alphabet(alphabet)
self.__assert_transitions(transitions, alphabet, states)
# Create all the Automaton Attributes
self.__states = set(states.keys())
self.__initial_states = set(
filter(lambda state: 'I' in states[state], states))
self.__final_states = set(
filter(lambda state: 'F' in states[state], states))
self.__alphabet = set(alphabet)
self.__transitions = transitions
# -----------------------------------------------------
# ----------------- PUBLIC METHODS --------------------
# -----------------------------------------------------
def determinize(self):
""" This method determinize the automata
using BFS algorithm with a Queue
"""
queue = Queue()
transitions = dict()
initial_state = tuple(self.__initial_states)
queue.put(initial_state)
print("Determinizing automaton... ")
while not queue.empty():
curr_state = queue.get()
if curr_state not in transitions:
transitions[curr_state] = dict()
for symbol in self.__alphabet:
next_state = self.__get_next_state(curr_state, symbol)
if next_state == empty_set:
if empty_set not in transitions:
self.__add_empty_set(transitions)
transitions[curr_state][symbol] = [next_state]
queue.put(next_state)
self.__states = set(transitions.keys())
self.__initial_states = set([initial_state])
self.__final_states = set(
filter(lambda state: self.__is_final_state(state), transitions.keys()))
self.__transitions = transitions
print("Automaton determinized! Enter 'show' to see the changes\n")
def read(self, word):
""" Represents word reading
"""
if not self.is_deterministic():
print("\nThis automaton is non-deterministic")
self.determinize()
init_state = list(self.__initial_states)[0]
print("Reading word...")
self.__read(word, init_state)
def minimize(self):
if not self.is_deterministic():
print("\nThis automaton is non-deterministic")
self.determinize()
eq_classes = [self.__states - self.__final_states, self.__final_states]
self.__minimize(eq_classes, [])
def show(self):
print(f" States => {self.__states}")
print(f" Initial states => {self.__initial_states}")
print(f" Final states => {self.__final_states}")
print("\n Transitions map => ")
for state, transition in self.__transitions.items():
print(f" {state} =>")
for symbol, dest in transition.items():
print(f" with {symbol} => {dest}")
def is_deterministic(self):
if len(self.__initial_states) > 1:
return False
for transition in self.__transitions.values():
for states in transition.values():
if len(states) != 1:
return False
return True
# -----------------------------------------------------
# ----------------- PRIVATE METHODS -------------------
# -----------------------------------------------------
# -------------------- READ METHODS -------------------
def __read(self, word, state):
if len(word) < 1 or word == lmbda:
if state in self.__final_states:
print("WORD ACCEPTED")
else:
print("WORD NOT ACCEPTED")
return
symbol = word[0]
if symbol not in self.__alphabet:
print(f"SYMBOL {symbol} NOT RECOGNIZED => WORD NOT ACCEPTED")
return
next_state = self.__transitions[state][symbol][0]
next_word = lmbda if len(word) <= 1 else word[1:]
print(
f"\t({state}, {word}) => ({next_state}, {next_word})")
self.__read(next_word, next_state)
# ---------------- MINIMIZE METHODS ------------------
def __minimize(self, current, previous):
if current == previous:
print(f"Final equivalence classes: {current}")
self.__update_states(current)
print("Minimization completed!")
return
equivalence = []
for s in current:
eq_class = s.copy()
while eq_class:
new_set = set()
r_state = eq_class.pop()
new_set.add(r_state)
for state in eq_class:
if self.__are_equivalent(r_state, state, current):
new_set.add(state)
eq_class -= new_set
equivalence.append(new_set)
self.__minimize(equivalence, current)
def __update_states(self, eq_classes):
for c in eq_classes:
if len(c) > 1:
new_state = c.pop()
print(f"States {c} minimized to => {new_state}")
self.__states -= c
self.__final_states -= c
for state in c:
del self.__transitions[state]
for transition in self.__transitions.values():
for dest in transition.values():
if dest[0] in c:
dest[0] = new_state
def __are_equivalent(self, state1, state2, groups):
""" Checks equivalence between two states
"""
for symbol in self.__alphabet:
s1 = self.__transitions[state1][symbol][0]
s2 = self.__transitions[state2][symbol][0]
for eq_class in groups:
if s1 in eq_class and s2 not in eq_class or s2 in eq_class and s1 not in eq_class:
return False
return True
# --------------- DETERMINIZE METHODS ----------------
def __get_next_state(self, curr_state, symbol):
new_state = set()
for state in curr_state:
new_state.update(set(self.__transitions[state][symbol]))
if len(new_state) == 0:
return empty_set
return tuple(new_state)
def __add_empty_set(self, transitions):
transitions[empty_set] = dict()
for letter in self.__alphabet:
transitions[empty_set][letter] = [empty_set]
def __is_final_state(self, t):
for elem in t:
if elem in self.__final_states:
return True
return False
# ------------ ASSERTS --------------
def __assert_states(self, states):
if not isinstance(states, dict):
raise TypeError("Bad states type, has to be a map")
has_initial = False
has_final = False
for value in states.values():
if not isinstance(value, list):
raise TypeError("Bad states value, has to be a list")
if 'I' in value:
has_initial = True
if 'F' in value:
has_final = True
if not has_initial:
raise ValueError("States map doesn't has an initial state")
if not has_final:
raise ValueError("States map does not has a final state")
def __assert_alphabet(self, alphabet):
if not isinstance(alphabet, list):
raise TypeError("Bad alphabet type, has to be a list")
def __assert_transitions(self, transitions, alphabet, states):
if not isinstance(transitions, dict):
raise TypeError("Bad transitions format, has to be a map")
# Check if each state is defined in transitions map
for s in states:
if s not in transitions:
raise ValueError(f"State {s} not defined on transitions table")
# Check the transitions map format
for state in transitions:
# Check if each state is in the states map
if state not in states:
raise ValueError(
f"State {state} in transitions map not defined in states map")
# Check the transition object type
transition = transitions[state]
if not isinstance(transition, dict):
raise TypeError(
f"Transaction value {transition} has to be a symbol-states map")
# Check if each state has defined a behaviour for each symbol in the alphabet
for symbol in alphabet:
if symbol not in transition.keys():
raise ValueError(
f"Symbol {symbol} behaviour not defined in state {state}, each symbol has to be defined even if its value is an empty list.")
# Check the transition map content
for symbol, dest in transition.items():
# Check if each symbol is in the alphabet
if symbol not in alphabet:
raise ValueError(
f"Symbol {symbol} in state {state} transition is not in the alphabet")
# Check the destination states object type
if not isinstance(dest, Iterable):
raise TypeError(
f"Value {dest} in transitions table has to be an Iterable")
# Check if each state in destination list is in the states map
for s in dest:
if s not in states:
raise ValueError(
f"State {s} in {dest} not in states map")

View file

@ -0,0 +1,60 @@
from automaton import FSM
from os import system, name
import json
def clear():
# for windows
if name == 'nt':
system('cls')
# for mac and linux(here, os.name is 'posix')
else:
system('clear')
def show_options():
print("D => Determinize automat")
print("M => Minimize automat")
print("read => Read a word")
print("show => Show automat")
print("clear => Clear screen")
print("help => Show commands")
print("quit => Close program")
if __name__ == '__main__':
try:
filename = input("Enter the automaton json filepath: ")
fp = open(filename, 'r')
automaton_json = json.load(fp)
automat = FSM(automaton_json)
show_options()
while True:
option = input(">> ").lower()
if option == 'd':
if automat.is_deterministic():
print("This automaton is already determinized")
continue
automat.determinize()
elif option == 'm':
automat.minimize()
elif option == 'read':
word = input("Enter the word you want to read: ")
automat.read(word)
elif option == 'show':
automat.show()
elif option == 'clear':
clear()
elif option == 'help':
show_options()
elif option == 'quit':
print("Bye!")
break
else:
print("Command not recognized")
except OSError as err:
print("OSError:", err)
except ValueError as err:
print("ValueError:", err)
except TypeError as err:
print("TypeError:", err)