Loading objects.pydeleted 100644 → 0 +0 −587 Original line number Diff line number Diff line from __future__ import annotations from typing import Set, FrozenSet, List, Dict, Union, Tuple, Deque, Optional, TypeVar from enum import Enum import enum from copy import deepcopy from collections import deque class Composition(Enum): Union = enum.auto() Intersection = enum.auto() Subtraction = enum.auto() class Terminal: def __init__(self, name: str): self.name = name def __eq__(self, obj): if isinstance(obj, Terminal): return obj.name == self.name return False def __hash__(self): return hash(self.name) class Character: def __init__(self, name: str): self.name = name def __eq__(self, obj): if isinstance(obj, Character): return obj.name == self.name return False def __hash__(self): return hash(self.name) class Eps: pass class Nonterminal: def __init__(self, name: str): self.name = name def __eq__(self, obj): if isinstance(obj, Nonterminal): return obj.name == self.name return False def __hash__(self): return hash(self.name) class State: def __init__(self, name: str): self.name = name def __eq__(self, obj): if isinstance(obj, State): return obj.name == self.name return False def __hash__(self): return hash(self.name) class REG: Rules = Dict[Nonterminal, Set[Union[Terminal, Tuple[Terminal, Nonterminal]]]] type_var = TypeVar('type_var') # TODO should accept init -> epsilon def __init__(self, nonterminals: Set[Nonterminal], terminals: Set[Terminal], rules: Rules, init: Nonterminal): self.nonterminals = nonterminals self.terminals = terminals self.rules = rules self.init = init assert self.check # TODO ask about concept def check(self) -> bool: # scheme of grammar - empty sets and dicts are implemention only variant if len(self.nonterminals) == 0: assert len(self.terminals) == 0 and len(self.rules) == 0 and self.init == None, "nonempty scheme of grammar" return True nonterminal_names = set(map(lambda x: x.name, self.nonterminals)) terminal_names = set(map(lambda x: x.name, self.terminals)) assert len(nonterminal_names.intersection(terminal_names)) == 0, "name conflict" for nonterminal in self.rules: assert nonterminal in self.nonterminals, "unknown nonterminal " + nonterminal.name for rule in self.rules[nonterminal]: if isinstance(rule, Terminal): assert rule in self.terminals, "unknown terminal " + rule.name else: assert rule[0] in self.terminals, "unknown terminal " + rule[0].name assert rule[1] in self.nonterminals, "unknown nonterminal " + rule[1].name assert self.init in self.nonterminals, "init not in nonterminals" return True def reg_to_nfa(self) -> NFA: init = State(self.init.name) states: Set[State] = set() for nonterminal in self.nonterminals: state = State(nonterminal.name) states.add(state) characters: Set[Character] = set() for terminal in self.terminals: character = Character(terminal.name) characters.add(character) final = State("new_final") # TODO assert that name is unique, as for hell in make_total states.add(final) transition: Dict[Tuple[State, Character], Set[State]] = dict() for nonterminal in self.rules: state = State(nonterminal.name) for rule in self.rules[nonterminal]: # rule A -> a becomes d(A, a) = final if isinstance(rule, Terminal): # TODO and a is not \eps character = Character(rule.name) if (state, character) in transition: transition[state, character].add(final) else: transition[state, character] = {final} # rule A -> aB becomes d(A, a) = B elif (state, rule[0]) in transition: character = Character(rule[0].name) move = State(rule[1].name) transition[state, character].add(move) else: character = Character(rule[0].name) move = State(rule[1].name) transition[state, character] = {move} # TODO if init -> \eps: nfa.final.add(nfa.init) # I need to know how to treat \eps nfa = NFA(states, characters, transition, init, {final}) return nfa class DFA: Transition = Dict[Tuple[State, Character], State] type_var = TypeVar('type_var') def set_or_none(self, out: Optional[Set[type_var]]) -> Set[type_var]: return set() if out is None else out def dict_or_none(self, out: Optional[Transition]) -> Transition: return dict() if out is None else out def __init__(self, states: Set[State], characters: Set[Character], transition: Transition, init: State, final: Set[State]): self.states = self.set_or_none(states) self.characters = self.set_or_none(characters) self.transition = self.dict_or_none(transition) self.init = init self.final = self.set_or_none(final) assert self.check def check(self) -> bool: # # scheme of automaton - empty sets and dicts are implemention only variant # if len(self.states) == 0: # assert len(self.characters) == 0 and len(self.transition) == 0 and self.init == None \ # and len(self.final) == 0, "nonempty scheme of automaton" # return True assert len(self.states) > 0, "empty automaton" for (state, character) in self.transition: assert state in self.states, "unknown state " + state.name assert character in self.characters, "unknown character " + character.name assert self.transition[state, character] in self.states, "unknown state " + self.transition[ state, character].name assert self.init in self.states, "init not in states" for state in self.final: assert state in self.states, "unknown state " + state.name return True def total(self) -> DFA: if self.is_total(): return self dfa = deepcopy(self) total: Dict[Tuple[State, Character], State] = {} # check: consider import itertools hell_id = 0 hell = State(str(hell_id)) while hell in dfa.states: hell_id += 1 hell.name = str(hell_id) dfa.states.add(hell) for state in dfa.states: for character in dfa.characters: if (state, character) in dfa.transition: total[state, character] = dfa.transition[state, character] else: total[state, character] = hell dfa.transition = total return dfa def is_total(self) -> bool: for state in self.states: for character in self.characters: if (state, character) not in self.transition: return False return True def accepts(self, word: str) -> bool: state = self.init for letter in word: character = Character(letter) if (state, character) not in self.transition: return False state = self.transition[state, character] return state in self.final def complement(self) -> DFA: dfa = deepcopy(self) if not dfa.is_total(): dfa = dfa.total() dfa.final = dfa.states.difference(dfa.final) return dfa @staticmethod def composition(dfa_1: DFA, dfa_2: DFA, operation: Composition) -> DFA: # 2019 IB102 slides want both total if not dfa_1.is_total(): dfa_1 = dfa_1.total() if not dfa_2.is_total(): dfa_2 = dfa_2.total() # new characters characters = dfa_1.characters.union(dfa_2.characters) # ATTENTION: terminals may differ -> make total at the end? # new init init = State(f"{dfa_1.init.name}_{dfa_2.init.name}") # new states and transition states = {init} transition: Dict[Tuple[State, Character], State] = dict() for state_1 in dfa_1.states: for state_2 in dfa_2.states: state = State(f"{state_1.name}_{state_2.name}") # TODO avoid conflicts! states.add(state) # new transition for character in characters: if (state_1, character) in dfa_1.transition and \ (state_2, character) in dfa_2.transition: move_1 = dfa_1.transition[state_1, character] move_2 = dfa_2.transition[state_2, character] move = State(move_1.name + "_" + move_2.name) transition[state, character] = move # new final final: Set[State] = set() for state_1 in dfa_1.final: for state_2 in dfa_2.final: state = State(f"{state_1.name}_{state_2.name}") if operation == Composition.Union: final.add(state) elif operation == Composition.Intersection and \ state_1 in dfa_1.final and \ state_2 in dfa_2.final: final.add(state) elif operation == Composition.Subtraction and \ state_2 not in dfa_2.final: final.add(state) dfa = DFA(states, characters, transition, init, final) return dfa @staticmethod def union(dfa_1: DFA, dfa_2: DFA) -> DFA: return DFA.composition(dfa_1, dfa_2, Composition.Union) @staticmethod def intersection(dfa_1: DFA, dfa_2: DFA) -> DFA: return DFA.composition(dfa_1, dfa_2, Composition.Intersection) @staticmethod def subtraction(dfa_1: DFA, dfa_2: DFA) -> DFA: return DFA.composition(dfa_1, dfa_2, Composition.Subtraction) def eliminate_unreachable(self) -> DFA: dfa = deepcopy(self) previous: Set[State] = set() actual = {self.init} while previous != actual: previous = deepcopy(actual) for state in previous: for character in dfa.characters: if (state, character) in self.transition: actual.add(self.transition[state, character]) dfa.states = actual dfa.final = dfa.final.intersection(actual) for (state, character) in self.transition: if state not in actual: del dfa.transition[state, character] return dfa def sort_characters(self) -> List[Character]: return sorted(self.characters, key=lambda x: x.name) def minimize(self) -> DFA: dfa = deepcopy(self) dfa = dfa.eliminate_unreachable() dfa = dfa.total() characters = self.sort_characters() # bc I want to iterate always in same order previous: Optional[Dict[State, int]] = None actual = dict() for state in dfa.states: if state in dfa.final: actual[state] = 1 else: actual[state] = 0 while previous != actual: previous = deepcopy(actual) patterns: Dict[Tuple[int, ...], Set[State]] = dict() actual = dict() for state in dfa.states: pattern = [previous[state]] for character in characters: result = dfa.transition[state, character] pattern.append(previous[result]) if tuple(pattern) in patterns: patterns[tuple(pattern)].add(state) else: patterns[tuple(pattern)] = {state} i = 0 for states in patterns.values(): for state in states: actual[state] = i i += 1 new_transition: Dict[Tuple[State, Character], State] = {} dfa.states = set() dfa.final = set() for state in actual: new_state = State("new_" + str(actual[state])) dfa.states.add(new_state) for character in characters: if not (new_state, character) in new_transition: originally = dfa.transition[state, character] now = State("new_" + str(actual[originally])) new_transition[new_state, character] = now if state == self.init: dfa.init = new_state if state in self.final: dfa.final.add(new_state) dfa.transition = new_transition return dfa def rename_state(self, rename: State, name: str): new_state = State(name) self.states.add(new_state) self.states.remove(rename) transition = deepcopy(self.transition) if rename == self.init: self.init = new_state if rename in self.final: self.final.add(new_state) self.final.remove(rename) for character in self.characters: if (rename, character) in self.transition: transition[new_state, character] = self.transition[rename, character] del transition[rename, character] for state, character in transition: if transition[state, character] == rename: del transition[state, character] transition[state, character] = new_state self.transition = transition def canonize(self) -> DFA: # TODO total? dfa = deepcopy(self) remain = deepcopy(self.states) characters = dfa.sort_characters() i = 0 queue = [dfa.init] # TODO deque while len(queue) > 0: actual = queue.pop(0) for character in characters: state = dfa.transition[actual, character] if state in remain: queue.append(state) remain.remove(state) dfa.rename_state(actual, str(i)) i += 1 return dfa def dfa_to_reg(self) -> REG: nonterminals: Set[Nonterminal] = set() terminals: Set[Terminal] = set() rules: Dict[Nonterminal, Set[Union[Terminal, Tuple[Terminal, Nonterminal]]]] = dict() for state in self.states: nonterminal = Nonterminal(state.name) nonterminals.add(nonterminal) for character in self.characters: terminal = Terminal(character.name) terminals.add(terminal) init = Nonterminal("new_init") # TODO assert that name is unique, as for hell in make_total init = init nonterminals.add(init) for state, character in self.transition: move = self.transition[state, character] if state == self.init: nonterminal2 = Nonterminal(move.name) terminal = Terminal(character.name) if init in rules: rules[init].add((terminal, nonterminal2)) else: rules[init] = {(terminal, nonterminal2)} if move in self.final: rules[init].add(terminal) else: nonterminal1 = Nonterminal(state.name) nonterminal2 = Nonterminal(move.name) terminal = Terminal(character.name) if nonterminal1 in rules: rules[nonterminal1].add((terminal, nonterminal2)) else: rules[nonterminal1] = {(terminal, nonterminal2)} if move in self.final: rules[nonterminal1].add((terminal)) reg = REG(nonterminals, terminals, rules, init) if self.init in self.final: pass # TODO eg.rules[init].add((\eps)) return reg def is_empty(self) -> bool: reached: Deque[State] = deque() reached.append(self.init) # TODO ask: I need to do it like this bc State is not iterable - shall it be? reachable: Set[State] = set() reachable.add(self.init) while len(reached) > 0: actual = reached.popleft() for character in self.characters: if self.transition[actual, character] is not None and \ self.transition[actual, character] not in reachable: reached.append(self.transition[actual, character]) reachable.add(self.transition[actual, character]) return len(reachable.intersection(self.final)) > 0 # Ha! def is_universal(self): return self.complement().is_empty() def is_equivalent(self, dfa: DFA) -> bool: self.complement() dfa.complement() self.complement().is_empty() dfa.complement().is_empty() return self.intersection(self, dfa.complement()).is_empty() and \ self.intersection(self.complement(), dfa).is_empty() class NFA: Transition = Dict[Tuple[State, Character], Set[State]] type_var = TypeVar('type_var') def set_or_none(self, out: Optional[Set[type_var]]) -> Set[type_var]: return set() if out is None else out def dict_or_none(self, out: Optional[Transition]) -> Transition: return dict() if out is None else out def __init__(self, states: Set[State], characters: Set[Character], transition: Transition, init: State, final: Set[State]): self.states = self.set_or_none(states) self.characters = self.set_or_none(characters) self.transition = self.dict_or_none(transition) self.init = init self.final = self.set_or_none(final) def determinize(self) -> DFA: states: Set[FrozenSet[State]] = set() states.add(frozenset({self.init})) transition = {} final = set() done: Set[FrozenSet[State]] = set() while len(states.difference(done)) > 0: subset = (states.difference(done)).pop() # arbitrary element from set if len(subset.intersection(self.final)) > 0: final.add(subset) for character in self.characters: new_subset: Set[State] = set() for state in subset: if (state, character) in self.transition: new_subset = new_subset.union(self.transition[state, character]) states.add(frozenset(new_subset)) transition[subset, character] = frozenset(new_subset) done.add(subset) new_states: Set[State] = set() new_transition: Dict[Tuple[State, Character], State] = dict() new_final: Set[State] = set() for state_set in states: new_states.add(self.unset(state_set)) for state_set in final: new_final.add(self.unset(state_set)) for state_set, character in transition: new_transition[self.unset(state_set), character] = self.unset(transition[state_set, character]) dfa = DFA(new_states, self.characters, new_transition, self.init, new_final) return dfa def unset(self, states: FrozenSet[State]) -> State: return State('_'.join(set(map(lambda x: x.name, sorted(states, key=lambda x: x.name))))) # would be function for in epsilon NFA def eliminate_epsilon(self) -> NFA: nfa = NFA(self.states, self.characters, {}, self.init, set()) # rozsirena prechodova funkce: D_eps(p) je podmnozina stavů X tz p tam je # a pokud q je v X a d(q, eps) = r, pak r je v X # a jde to pro mnoziny stavu (sjednoceni vsech) return nfa def epsilon_surroundings(self, state: State) -> Set[State]: pass Loading
objects.pydeleted 100644 → 0 +0 −587 Original line number Diff line number Diff line from __future__ import annotations from typing import Set, FrozenSet, List, Dict, Union, Tuple, Deque, Optional, TypeVar from enum import Enum import enum from copy import deepcopy from collections import deque class Composition(Enum): Union = enum.auto() Intersection = enum.auto() Subtraction = enum.auto() class Terminal: def __init__(self, name: str): self.name = name def __eq__(self, obj): if isinstance(obj, Terminal): return obj.name == self.name return False def __hash__(self): return hash(self.name) class Character: def __init__(self, name: str): self.name = name def __eq__(self, obj): if isinstance(obj, Character): return obj.name == self.name return False def __hash__(self): return hash(self.name) class Eps: pass class Nonterminal: def __init__(self, name: str): self.name = name def __eq__(self, obj): if isinstance(obj, Nonterminal): return obj.name == self.name return False def __hash__(self): return hash(self.name) class State: def __init__(self, name: str): self.name = name def __eq__(self, obj): if isinstance(obj, State): return obj.name == self.name return False def __hash__(self): return hash(self.name) class REG: Rules = Dict[Nonterminal, Set[Union[Terminal, Tuple[Terminal, Nonterminal]]]] type_var = TypeVar('type_var') # TODO should accept init -> epsilon def __init__(self, nonterminals: Set[Nonterminal], terminals: Set[Terminal], rules: Rules, init: Nonterminal): self.nonterminals = nonterminals self.terminals = terminals self.rules = rules self.init = init assert self.check # TODO ask about concept def check(self) -> bool: # scheme of grammar - empty sets and dicts are implemention only variant if len(self.nonterminals) == 0: assert len(self.terminals) == 0 and len(self.rules) == 0 and self.init == None, "nonempty scheme of grammar" return True nonterminal_names = set(map(lambda x: x.name, self.nonterminals)) terminal_names = set(map(lambda x: x.name, self.terminals)) assert len(nonterminal_names.intersection(terminal_names)) == 0, "name conflict" for nonterminal in self.rules: assert nonterminal in self.nonterminals, "unknown nonterminal " + nonterminal.name for rule in self.rules[nonterminal]: if isinstance(rule, Terminal): assert rule in self.terminals, "unknown terminal " + rule.name else: assert rule[0] in self.terminals, "unknown terminal " + rule[0].name assert rule[1] in self.nonterminals, "unknown nonterminal " + rule[1].name assert self.init in self.nonterminals, "init not in nonterminals" return True def reg_to_nfa(self) -> NFA: init = State(self.init.name) states: Set[State] = set() for nonterminal in self.nonterminals: state = State(nonterminal.name) states.add(state) characters: Set[Character] = set() for terminal in self.terminals: character = Character(terminal.name) characters.add(character) final = State("new_final") # TODO assert that name is unique, as for hell in make_total states.add(final) transition: Dict[Tuple[State, Character], Set[State]] = dict() for nonterminal in self.rules: state = State(nonterminal.name) for rule in self.rules[nonterminal]: # rule A -> a becomes d(A, a) = final if isinstance(rule, Terminal): # TODO and a is not \eps character = Character(rule.name) if (state, character) in transition: transition[state, character].add(final) else: transition[state, character] = {final} # rule A -> aB becomes d(A, a) = B elif (state, rule[0]) in transition: character = Character(rule[0].name) move = State(rule[1].name) transition[state, character].add(move) else: character = Character(rule[0].name) move = State(rule[1].name) transition[state, character] = {move} # TODO if init -> \eps: nfa.final.add(nfa.init) # I need to know how to treat \eps nfa = NFA(states, characters, transition, init, {final}) return nfa class DFA: Transition = Dict[Tuple[State, Character], State] type_var = TypeVar('type_var') def set_or_none(self, out: Optional[Set[type_var]]) -> Set[type_var]: return set() if out is None else out def dict_or_none(self, out: Optional[Transition]) -> Transition: return dict() if out is None else out def __init__(self, states: Set[State], characters: Set[Character], transition: Transition, init: State, final: Set[State]): self.states = self.set_or_none(states) self.characters = self.set_or_none(characters) self.transition = self.dict_or_none(transition) self.init = init self.final = self.set_or_none(final) assert self.check def check(self) -> bool: # # scheme of automaton - empty sets and dicts are implemention only variant # if len(self.states) == 0: # assert len(self.characters) == 0 and len(self.transition) == 0 and self.init == None \ # and len(self.final) == 0, "nonempty scheme of automaton" # return True assert len(self.states) > 0, "empty automaton" for (state, character) in self.transition: assert state in self.states, "unknown state " + state.name assert character in self.characters, "unknown character " + character.name assert self.transition[state, character] in self.states, "unknown state " + self.transition[ state, character].name assert self.init in self.states, "init not in states" for state in self.final: assert state in self.states, "unknown state " + state.name return True def total(self) -> DFA: if self.is_total(): return self dfa = deepcopy(self) total: Dict[Tuple[State, Character], State] = {} # check: consider import itertools hell_id = 0 hell = State(str(hell_id)) while hell in dfa.states: hell_id += 1 hell.name = str(hell_id) dfa.states.add(hell) for state in dfa.states: for character in dfa.characters: if (state, character) in dfa.transition: total[state, character] = dfa.transition[state, character] else: total[state, character] = hell dfa.transition = total return dfa def is_total(self) -> bool: for state in self.states: for character in self.characters: if (state, character) not in self.transition: return False return True def accepts(self, word: str) -> bool: state = self.init for letter in word: character = Character(letter) if (state, character) not in self.transition: return False state = self.transition[state, character] return state in self.final def complement(self) -> DFA: dfa = deepcopy(self) if not dfa.is_total(): dfa = dfa.total() dfa.final = dfa.states.difference(dfa.final) return dfa @staticmethod def composition(dfa_1: DFA, dfa_2: DFA, operation: Composition) -> DFA: # 2019 IB102 slides want both total if not dfa_1.is_total(): dfa_1 = dfa_1.total() if not dfa_2.is_total(): dfa_2 = dfa_2.total() # new characters characters = dfa_1.characters.union(dfa_2.characters) # ATTENTION: terminals may differ -> make total at the end? # new init init = State(f"{dfa_1.init.name}_{dfa_2.init.name}") # new states and transition states = {init} transition: Dict[Tuple[State, Character], State] = dict() for state_1 in dfa_1.states: for state_2 in dfa_2.states: state = State(f"{state_1.name}_{state_2.name}") # TODO avoid conflicts! states.add(state) # new transition for character in characters: if (state_1, character) in dfa_1.transition and \ (state_2, character) in dfa_2.transition: move_1 = dfa_1.transition[state_1, character] move_2 = dfa_2.transition[state_2, character] move = State(move_1.name + "_" + move_2.name) transition[state, character] = move # new final final: Set[State] = set() for state_1 in dfa_1.final: for state_2 in dfa_2.final: state = State(f"{state_1.name}_{state_2.name}") if operation == Composition.Union: final.add(state) elif operation == Composition.Intersection and \ state_1 in dfa_1.final and \ state_2 in dfa_2.final: final.add(state) elif operation == Composition.Subtraction and \ state_2 not in dfa_2.final: final.add(state) dfa = DFA(states, characters, transition, init, final) return dfa @staticmethod def union(dfa_1: DFA, dfa_2: DFA) -> DFA: return DFA.composition(dfa_1, dfa_2, Composition.Union) @staticmethod def intersection(dfa_1: DFA, dfa_2: DFA) -> DFA: return DFA.composition(dfa_1, dfa_2, Composition.Intersection) @staticmethod def subtraction(dfa_1: DFA, dfa_2: DFA) -> DFA: return DFA.composition(dfa_1, dfa_2, Composition.Subtraction) def eliminate_unreachable(self) -> DFA: dfa = deepcopy(self) previous: Set[State] = set() actual = {self.init} while previous != actual: previous = deepcopy(actual) for state in previous: for character in dfa.characters: if (state, character) in self.transition: actual.add(self.transition[state, character]) dfa.states = actual dfa.final = dfa.final.intersection(actual) for (state, character) in self.transition: if state not in actual: del dfa.transition[state, character] return dfa def sort_characters(self) -> List[Character]: return sorted(self.characters, key=lambda x: x.name) def minimize(self) -> DFA: dfa = deepcopy(self) dfa = dfa.eliminate_unreachable() dfa = dfa.total() characters = self.sort_characters() # bc I want to iterate always in same order previous: Optional[Dict[State, int]] = None actual = dict() for state in dfa.states: if state in dfa.final: actual[state] = 1 else: actual[state] = 0 while previous != actual: previous = deepcopy(actual) patterns: Dict[Tuple[int, ...], Set[State]] = dict() actual = dict() for state in dfa.states: pattern = [previous[state]] for character in characters: result = dfa.transition[state, character] pattern.append(previous[result]) if tuple(pattern) in patterns: patterns[tuple(pattern)].add(state) else: patterns[tuple(pattern)] = {state} i = 0 for states in patterns.values(): for state in states: actual[state] = i i += 1 new_transition: Dict[Tuple[State, Character], State] = {} dfa.states = set() dfa.final = set() for state in actual: new_state = State("new_" + str(actual[state])) dfa.states.add(new_state) for character in characters: if not (new_state, character) in new_transition: originally = dfa.transition[state, character] now = State("new_" + str(actual[originally])) new_transition[new_state, character] = now if state == self.init: dfa.init = new_state if state in self.final: dfa.final.add(new_state) dfa.transition = new_transition return dfa def rename_state(self, rename: State, name: str): new_state = State(name) self.states.add(new_state) self.states.remove(rename) transition = deepcopy(self.transition) if rename == self.init: self.init = new_state if rename in self.final: self.final.add(new_state) self.final.remove(rename) for character in self.characters: if (rename, character) in self.transition: transition[new_state, character] = self.transition[rename, character] del transition[rename, character] for state, character in transition: if transition[state, character] == rename: del transition[state, character] transition[state, character] = new_state self.transition = transition def canonize(self) -> DFA: # TODO total? dfa = deepcopy(self) remain = deepcopy(self.states) characters = dfa.sort_characters() i = 0 queue = [dfa.init] # TODO deque while len(queue) > 0: actual = queue.pop(0) for character in characters: state = dfa.transition[actual, character] if state in remain: queue.append(state) remain.remove(state) dfa.rename_state(actual, str(i)) i += 1 return dfa def dfa_to_reg(self) -> REG: nonterminals: Set[Nonterminal] = set() terminals: Set[Terminal] = set() rules: Dict[Nonterminal, Set[Union[Terminal, Tuple[Terminal, Nonterminal]]]] = dict() for state in self.states: nonterminal = Nonterminal(state.name) nonterminals.add(nonterminal) for character in self.characters: terminal = Terminal(character.name) terminals.add(terminal) init = Nonterminal("new_init") # TODO assert that name is unique, as for hell in make_total init = init nonterminals.add(init) for state, character in self.transition: move = self.transition[state, character] if state == self.init: nonterminal2 = Nonterminal(move.name) terminal = Terminal(character.name) if init in rules: rules[init].add((terminal, nonterminal2)) else: rules[init] = {(terminal, nonterminal2)} if move in self.final: rules[init].add(terminal) else: nonterminal1 = Nonterminal(state.name) nonterminal2 = Nonterminal(move.name) terminal = Terminal(character.name) if nonterminal1 in rules: rules[nonterminal1].add((terminal, nonterminal2)) else: rules[nonterminal1] = {(terminal, nonterminal2)} if move in self.final: rules[nonterminal1].add((terminal)) reg = REG(nonterminals, terminals, rules, init) if self.init in self.final: pass # TODO eg.rules[init].add((\eps)) return reg def is_empty(self) -> bool: reached: Deque[State] = deque() reached.append(self.init) # TODO ask: I need to do it like this bc State is not iterable - shall it be? reachable: Set[State] = set() reachable.add(self.init) while len(reached) > 0: actual = reached.popleft() for character in self.characters: if self.transition[actual, character] is not None and \ self.transition[actual, character] not in reachable: reached.append(self.transition[actual, character]) reachable.add(self.transition[actual, character]) return len(reachable.intersection(self.final)) > 0 # Ha! def is_universal(self): return self.complement().is_empty() def is_equivalent(self, dfa: DFA) -> bool: self.complement() dfa.complement() self.complement().is_empty() dfa.complement().is_empty() return self.intersection(self, dfa.complement()).is_empty() and \ self.intersection(self.complement(), dfa).is_empty() class NFA: Transition = Dict[Tuple[State, Character], Set[State]] type_var = TypeVar('type_var') def set_or_none(self, out: Optional[Set[type_var]]) -> Set[type_var]: return set() if out is None else out def dict_or_none(self, out: Optional[Transition]) -> Transition: return dict() if out is None else out def __init__(self, states: Set[State], characters: Set[Character], transition: Transition, init: State, final: Set[State]): self.states = self.set_or_none(states) self.characters = self.set_or_none(characters) self.transition = self.dict_or_none(transition) self.init = init self.final = self.set_or_none(final) def determinize(self) -> DFA: states: Set[FrozenSet[State]] = set() states.add(frozenset({self.init})) transition = {} final = set() done: Set[FrozenSet[State]] = set() while len(states.difference(done)) > 0: subset = (states.difference(done)).pop() # arbitrary element from set if len(subset.intersection(self.final)) > 0: final.add(subset) for character in self.characters: new_subset: Set[State] = set() for state in subset: if (state, character) in self.transition: new_subset = new_subset.union(self.transition[state, character]) states.add(frozenset(new_subset)) transition[subset, character] = frozenset(new_subset) done.add(subset) new_states: Set[State] = set() new_transition: Dict[Tuple[State, Character], State] = dict() new_final: Set[State] = set() for state_set in states: new_states.add(self.unset(state_set)) for state_set in final: new_final.add(self.unset(state_set)) for state_set, character in transition: new_transition[self.unset(state_set), character] = self.unset(transition[state_set, character]) dfa = DFA(new_states, self.characters, new_transition, self.init, new_final) return dfa def unset(self, states: FrozenSet[State]) -> State: return State('_'.join(set(map(lambda x: x.name, sorted(states, key=lambda x: x.name))))) # would be function for in epsilon NFA def eliminate_epsilon(self) -> NFA: nfa = NFA(self.states, self.characters, {}, self.init, set()) # rozsirena prechodova funkce: D_eps(p) je podmnozina stavů X tz p tam je # a pokud q je v X a d(q, eps) = r, pak r je v X # a jde to pro mnoziny stavu (sjednoceni vsech) return nfa def epsilon_surroundings(self, state: State) -> Set[State]: pass