def nfa_to_dfa(self): try: self.alphabet.remove('ε') except: pass self.new_ss = state({s.id for s in self.start_states}) temp_op = {key: set() for key in self.alphabet + ['ε']} for ss in self.start_states: for k, v in ss.outpaths.items(): set_v = set(v) temp_op[k] = temp_op[k].union(set_v) self.new_ss.outpaths = {k: list(v) for k, v in temp_op.items()} self.new_ss.made_of = set(self.start_states) self.states.append(self.new_ss) self.e_transitions() if 'ε' in self.new_ss.outpaths: del self.new_ss.outpaths['ε'] existing_states = [self.new_ss.made_of] new_states = [self.new_ss] dfa_states = [self.new_ss] while len(new_states) > 0: next_round = [] for es in new_states: new_dict = {key: set() for key in self.alphabet} for sub_state in es.made_of: for char, states in sub_state.ep_outpaths.items(): new_dict[char] = new_dict[char].union(states) for char, states in new_dict.items(): if states not in existing_states: id = set([s.id for s in states]) if id == set(): id = '∅' new_state = state(id) new_state.made_of = states es.outpaths[char] = new_state dfa_states.append(new_state) existing_states.append(states) next_round.append(new_state) else: for x in dfa_states: if x.made_of == states: es.outpaths[char] = x break new_states = next_round self.my_dfa = DFA(dfa_states) self.my_dfa.alphabet = self.alphabet self.my_dfa.start_state = self.new_ss acc_ids = {st.id for st in self.accept_states} for st in self.my_dfa.states: for s in st.made_of: if s.id in acc_ids: self.my_dfa.accept_states.append(st) break
def test1(): s0 = state(0) s1 = state(1) s0.outpaths = {'b': [s0, s1]} s1.outpaths = {'a': [s1], 'b': [s0]} n = NFA([s0, s1]) n.start_states = [s0] n.accept_states = [s1] n.alphabet = ['a', 'b'] n.nfa_to_dfa() n.my_dfa.print_dfa() """
def test1(): #strings in the language {a^n b^n | n >= 1} s0 = state(0) s1 = state(1) s2 = state(2) s0.outpaths = { ('a', 'A'): {(s0, 'AA')}, ('a', '$'): {(s0, 'A$')}, ('b', 'A'): {(s1, 'ε')} } s1.outpaths = {('b', 'A'): {(s1, 'ε')}, ('ε', '$'): {(s2, 'ε')}} p = PDA({s0, s1, s2}) p.input_alphabet = {'a', 'b'} p.stack_symbols = {'$', 'A', 'B'} p.start_state = s0 p.accept_states = {s2} p.to_cfg() p.my_cfg.print_converted_cfg()
def test2(): #Example 13.1 from Automata & Computability print('Test 2') s0 = state(0) s1 = state(1) s2 = state(2) s3 = state(3) s0.outpaths = {'a': s1, 'b': s3} s1.outpaths = {'a': s2, 'b': s2} s2.outpaths = {'a': s2, 'b': s2} s3.outpaths = {'a': s2, 'b': s2} d = DFA([s0,s1,s2,s3]) d.start_state = s0 d.accept_states = [s1, s3] d.alphabet = ['a', 'b'] d.minimize() d.print_dfa(True) """
def make_nfa_part(self, part, num): if '*' not in part: states = [state(i) for i in range(num, num + len(part) + 1)] for i in range(len(states) - 1): states[i].outpaths[part[i]] = [states[i + 1]] return states, num + len(part) + 1 else: starred_letters_index = set() for i in range(len(part)): if part[i] == '*': starred_letters_index.add(i - 1) states = [state(num)] num += 1 i = 0 while i < len(part): if i not in starred_letters_index: new_state = state(num) if part[i] in states[-1].outpaths: states[-1].outpaths.append(new_state) else: states[-1].outpaths[part[i]] = [new_state] states.append(new_state) num += 1 i += 1 else: if part[i] in states[-1].outpaths: states[-1].outpaths.append(states[-1]) else: states[-1].outpaths[part[i]] = [states[-1]] if i < len(part) - 2 and i + 2 in starred_letters_index: new_state = state(num) if 'ε' in states[ -1].outpaths: #check not necessary unless 'ε' in alphabet? states[-1].outpaths['ε'].append(new_state) else: states[-1].outpaths['ε'] = [new_state] states.append(new_state) num += 1 i += 2 return states, num
def test1(): #from https://www.gatevidyalay.com/minimization-of-dfa-minimize-dfa-example/ print('Test 1') s0 = state(0) s1 = state(1) s2 = state(2) s3 = state(3) s4 = state(4) s0.outpaths = {'a': s1, 'b': s2} s1.outpaths = {'a': s1, 'b': s3} s2.outpaths = {'a': s1, 'b': s2} s3.outpaths = {'a': s1, 'b': s4} s4.outpaths = {'a': s1, 'b': s2} d = DFA([s0,s1,s2,s3,s4]) d.start_state = s1 d.accept_states = [s4] d.alphabet = ['a', 'b'] d.minimize() d.print_dfa(True) """
def test2(): #strings in the language {a^m b^m c^n d^n | m,n >= 1} #https://scanftree.com/automata/dpda-for-a-to-power-n-b-to-power-n-c-to-power-m-d-to-power-m s0 = state(0) s1 = state(1) s2 = state(2) s3 = state(3) s4 = state(4) s0.outpaths = { ('a', 'A'): {(s0, 'AA')}, ('a', '$'): {(s0, 'A$')}, ('b', 'A'): {(s1, 'ε')} } s1.outpaths = {('b', 'A'): {(s1, 'ε')}, ('c', '$'): {(s2, 'C$')}} s2.outpaths = {('c', 'C'): {(s2, 'CC')}, ('d', 'C'): {(s3, 'ε')}} s3.outpaths = {('d', 'C'): {(s3, 'ε')}, ('ε', '$'): {(s4, 'ε')}} p = PDA({s0, s1, s2, s3, s4}) p.input_alphabet = {'a', 'b'} p.stack_symbols = {'$', 'A', 'B'} p.start_state = s0 p.accept_states = {s4} p.to_cfg() p.my_cfg.print_converted_cfg()
def plus(self, nfa_parts, finalize=False): if len(nfa_parts) == 1: if finalize: self.my_nfa.start_states = [nfa_parts[0][0]] self.my_nfa.accept_states = [nfa_parts[0][-1]] return nfa_parts[0] else: new_state = state(self.num) self.num += 1 new_state.outpaths = {'ε': [states[0] for states in nfa_parts]} new_state2 = state(self.num) self.num += 1 new_states = [new_state] for states in nfa_parts: if 'ε' not in states[-1].outpaths: states[-1].outpaths['ε'] = [new_state2] else: states[-1].outpaths['ε'].append(new_state2) new_states += states new_states += [new_state2] if finalize: self.my_nfa.start_states = [new_states[0]] self.my_nfa.accept_states = [new_states[-1]] return new_states
def convert_to_pda(self): from automata.pda import PDA self.convert_to_gnf() s = state(0) p = PDA(set([s]), self.start) self.find_terms_nonterms() p.alphabet = self.terminals.difference(set(['ε'])) p.stack_symbols = self.nonterminals.union(p.alphabet) terms = self.terminals.difference(set(['ε'])) for t in terms: s.outpaths[(t, t)] = set([(s, 'ε')]) for nt in self.nonterminals: prods = self.rules[nt] for str in prods: if ('ε', nt) in s.outpaths: s.outpaths[('ε', nt)].add((s, str)) else: s.outpaths[('ε', nt)] = set([(s, str)]) self.my_pda = p self.my_pda.start_state = s
def test3(): #from https://www.geeksforgeeks.org/minimization-of-dfa/ print('Test 3') s0 = state(0) s1 = state(1) s2 = state(2) s3 = state(3) s4 = state(4) s5 = state(5) s0.outpaths = {'0': s3, '1': s1} s1.outpaths = {'0': s2, '1': s5} s2.outpaths = {'0': s2, '1': s5} s3.outpaths = {'0': s0, '1': s4} s4.outpaths = {'0': s2, '1': s5} s5.outpaths = {'0': s5, '1': s5} d = DFA([s0,s1,s2,s3,s4,s5]) d.start_state = s0 d.accept_states = [s1, s2, s4] d.alphabet = ['0', '1'] d.minimize() d.print_dfa(True) """
def test4(): #Example 13.2 from Automata & Computability print('Test 4') s0 = state(0) s1 = state(1) s2 = state(2) s3 = state(3) s4 = state(4) s5 = state(5) s0.outpaths = {'a': s1, 'b': s2} s1.outpaths = {'a': s3, 'b': s4} s2.outpaths = {'a': s4, 'b': s3} s3.outpaths = {'a': s5, 'b': s5} s4.outpaths = {'a': s5, 'b': s5} s5.outpaths = {'a': s5, 'b': s5} d = DFA([s0,s1,s2,s3,s4,s5]) d.start_state = s0 d.accept_states = [s1, s2, s5] d.alphabet = ['a', 'b'] d.minimize() d.print_dfa(True) """
def minimize(self): i = 0 for st in self.states: st.index = i i += 1 num_states = len(self.states) self.matrix = [[None for _ in range(num_states)] for _ in range(num_states)] for i in range(num_states): is_acc = True if self.states[i] in self.accept_states else False for j in range(i + 1, num_states): if self.states[j] in self.accept_states: if not is_acc: self.matrix[i][j] = 0 else: if is_acc: self.matrix[i][j] = 0 round_num = 1 cont = True while cont: cont = False for i in range(num_states): for j in range(i + 1, num_states): if self.matrix[i][j] is None: for sym in self.alphabet: s1 = self.states[i].outpaths[ sym] if sym in self.states[i].outpaths else None s2 = self.states[j].outpaths[ sym] if sym in self.states[j].outpaths else None if s1 is not None and s2 is not None: max_index = max(s1.index, s2.index) min_index = min(s1.index, s2.index) if self.matrix[min_index][max_index] is not None and \ self.matrix[min_index][max_index] != round_num: #2nd condition not required but makes #for better distinction between rounds #maybe delete self.matrix[i][j] = round_num cont = True break elif (s1 is None and s2 is not None) or (s1 is not None and s2 is None): self.matrix[i][j] = round_num cont = True break round_num += 1 unique = 0 count = 0 for i in range(num_states): for j in range(i + 1, num_states): if self.matrix[i][j] is not None: unique += 1 count += 1 if unique == count: print('Your DFA is already minimized.') return self.new_states = {} count = num_states for i in range(num_states): for j in range(i + 1, num_states): if self.matrix[i][j] is None: found = False for key, val in self.new_states.items(): if self.states[i] in val and self.states[j] not in val: self.new_states[key].add(self.states[j]) found = True break elif self.states[j] in val and self.states[ i] not in val: self.new_states[key].add(self.states[j]) found = True break elif self.states[j] in val and self.states[i] in val: found = True break if not found: self.new_states[state(count)] = { self.states[i], self.states[j] } count += 1 existing_states = set() for key, val in self.new_states.items(): existing_states = existing_states.union(val) missing_states = set(self.states).difference(existing_states) for st in missing_states: if st.id != '∅': self.new_states[state(count)] = {st} count += 1 else: self.new_states[st] = {st} self.new_start_state = None self.new_acc_states = [] for new, old in self.new_states.items(): for s in old: if s.id == self.start_state.id: self.new_start_state = new if s in self.accept_states and new not in self.new_acc_states: self.new_acc_states.append(new) for key, val in s.outpaths.items(): for new2, old2 in self.new_states.items(): if val in old2: new.outpaths[key] = new2 self.states = [key for key in self.new_states] self.start_state = self.new_start_state self.accept_states = self.new_acc_states self.remove_unreachable()