Advertisement
Guest User

NFA -> DFA

a guest
Jan 26th, 2015
505
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 13.34 KB | None | 0 0
  1.  
  2. from collections import deque
  3.  
  4. from math import factorial as f
  5.  
  6. def nCr(n, r):
  7.     if n < r:
  8.         return 0
  9.     return f(n) / (f(r) * f(n-r))
  10.  
  11. def generate_all_permutations(elements):
  12.     n = len(elements)
  13.  
  14.     perms = elements[: ]
  15.    
  16.     low = 0
  17.     size = nCr(n, 1)
  18.     ctr = 0
  19.  
  20.     while len(perms) < 2 ** n - 1:
  21.    
  22.         for element1 in perms[low: low + size]:
  23.             for element2 in xrange(n):
  24.                 if ctr == 0: #there's no tuple first up
  25.                     x = element1
  26.                 else:
  27.                     x = element1[-1] #the last element of the tuple would be compared
  28.  
  29.                 if element2 > x:
  30.                     if ctr == 0:
  31.                         element_n = [element1, element2]
  32.                     else:
  33.                         element_n = list(element1)
  34.                         element_n.append(element2)
  35.                    
  36.                     perms.append(tuple(element_n))  
  37.        
  38.         ctr += 1
  39.         low = low + size
  40.         size = nCr(n, ctr)
  41.        
  42.     return perms
  43.  
  44. def format(lst, n):
  45.    
  46.     new_str = ""
  47.    
  48.     labels = list()
  49.  
  50.     for i in xrange(n):
  51.         labels.append(str(i))
  52.  
  53.     for number in lst[n: ]:
  54.         labels.append("".join(map(str, number)))
  55.        
  56.  
  57.     return labels
  58.  
  59. def get_all_labels(lst):
  60.     '''
  61.    makes the powerset out of the list
  62.    '''
  63.     return format(generate_all_permutations(lst), len(lst))
  64.  
  65.  
  66. transition_table_DFA = '''3 2
  67. 0 1 2
  68. 0 1
  69. 0
  70. 2
  71. 0
  72. 1
  73. 2
  74. 1
  75. 0
  76. 1'''
  77.  
  78.  
  79. #the first line would contain the no of states and the letters in the alphabet
  80. #the second line would contain the labels of the states
  81. #the third line would contain the letters in the alphabet accepted by the automaton
  82. #the fourth line would contain the starting state
  83. #the fifth line would contain the accepting states separated by spaces
  84. #and onwards would describe the table
  85.  
  86. #this one is for strings ending in 10
  87. transition_table_NFA = '''4 2
  88. 0 1 2 d
  89. 0 1
  90. 0
  91. 2
  92. 0
  93. 0 1
  94. 2
  95. d
  96. d
  97. d
  98. d
  99. d'''
  100.  
  101. #this one is for strings having 11 as a substring
  102. transition_table_NFA2 = '''4 2
  103. 0 1 2 d
  104. 0 1
  105. 0
  106. 2
  107. 0
  108. 0 1
  109. d
  110. 2
  111. 2
  112. 2
  113. d
  114. d'''
  115.  
  116. #this is for strings that end with 'abc' over the alphabet a, b, c
  117. transition_table_NFA3 = '''5 3
  118. 0 1 2 3 d
  119. a b c
  120. 0
  121. 3
  122. 0 1
  123. 0
  124. 0
  125. d
  126. 2
  127. d
  128. d
  129. d
  130. 3
  131. d
  132. d
  133. d
  134. d
  135. d
  136. d'''
  137.  
  138. #this one is for strings having 101 as a substring
  139. transition_table_NFA4 = '''5 2
  140. 0 1 2 3 d
  141. 0 1
  142. 0
  143. 3
  144. 0
  145. 0 1
  146. 2
  147. d
  148. d
  149. 3
  150. 3
  151. 3
  152. d
  153. d'''
  154.  
  155.  
  156.  
  157.  
  158. class State(object):
  159.     '''
  160.    ADT for handling each state in the automaton
  161.    '''
  162.  
  163.     def __init__(self, label):
  164.         '''
  165.        initializer for a state object
  166.        '''
  167.         self._label = label #the label of the state
  168.  
  169.         #dictionaries that'll have decision values as the key and the other states as the values
  170.         self._transitions = dict()
  171.         self._is_accepting = False
  172.         self._is_starting = False
  173.  
  174.     def __repr__(self):
  175.         '''
  176.        representation of the state object
  177.        '''
  178.         return str(self._label)
  179.  
  180.     def __str__(self):
  181.         '''
  182.        string representation of the state object
  183.        '''
  184.         st = "\nState " + str(self._label) + "\n"
  185.         st += "Transitions: " + str(self._transitions) + "\n"
  186.  
  187.         return st
  188.  
  189.     def add_neighbor(self, symbol, other_state):
  190.         '''
  191.        adds the neighbor to the state. state (symbol) other_state
  192.        '''
  193.         if symbol in self._transitions:
  194.             self._transitions[symbol].add(other_state)
  195.  
  196.         else:
  197.             self._transitions[symbol] = set([other_state])
  198.  
  199.         #print self._transitions
  200.  
  201.     def get_label(self):
  202.         '''
  203.        returns the label of a state
  204.        '''
  205.         return self._label
  206.  
  207.     def get_neighbor(self, symbol):
  208.         '''
  209.        returns the neighbor of the state with symbol
  210.        '''
  211.         neighbor_list = list()
  212.         # print "symbol", symbol, "transitions ", self._transitions
  213.  
  214.         for state in self._transitions[symbol]:
  215.             neighbor_list.append(state)
  216.  
  217.         return neighbor_list
  218.  
  219.     def get_transitions(self):
  220.         return self._transitions
  221.  
  222.     def set_accepting(self):
  223.         self._is_accepting = True
  224.  
  225.     def set_starting(self):
  226.         self._is_starting = True
  227.  
  228.        
  229. class Automaton(object):
  230.     '''
  231.    Automaton ADT. Feed a transition table.
  232.     '''
  233.  
  234.     def __init__(self, transition_table, state_list = list()):
  235.         '''
  236.        initializer for the Automaton object
  237.        '''
  238.         self._states = state_list
  239.  
  240.         self._accepting_states = set()
  241.         self._starting_state = None
  242.         self._transition_table = transition_table
  243.         self._label_set = set() #this will have all the labels of the states in the automaton
  244.        
  245.         for state in self._states:
  246.             self._label_set.add(state)
  247.        
  248.  
  249.         if self._transition_table == '':
  250.             return
  251.  
  252.         #pair of lines constitute the transitions for each state
  253.         #first line gives the state labels for symbol 0
  254.         #second line gives the state labels for symbol 1
  255.  
  256.         lines = transition_table.split("\n")
  257.  
  258.         #0th line has the no of states and no of symbols
  259.         self._no_of_states, self._no_of_symbols = map(int, lines[0].split())
  260.  
  261.         #1st line has the state labels
  262.         #state_labels = lines[1].split()
  263.         #print state_labels
  264.  
  265.         #allocating the states
  266.         num_states = len(state_list)
  267.         if num_states == 0:
  268.             for label in lines[1].split():
  269.                 self._states.append((State(label)))
  270.                 self._label_set.add(label)
  271.  
  272.  
  273.         #2nd line has the symbols
  274.         self._symbols = lines[2].split()
  275.         #print symbols
  276.            
  277.         #print self._accepting_states
  278.         state_ctr = 0
  279.  
  280.         if num_states == 0:
  281.             for i, line in enumerate(lines[5: ]):
  282.  
  283.                 neighbor_labels = line.split()
  284.  
  285.                 symbol = self._symbols[i % self._no_of_symbols]
  286.  
  287.                 #for the first of the pair of lines, it gives  
  288.                 for label in neighbor_labels:
  289.                     dest = self.get_state(label)
  290.                     self._states[i / self._no_of_symbols].add_neighbor(symbol, dest)
  291.                     #print dest
  292.            
  293.         # #setting the neighbors of the dead state
  294.         # for symbol in self._symbols:
  295.         #   #a dead state should stay dead regardless of the symbol it receives
  296.         #   self._states[-1].add_neighbor(symbol, self._states[-1])
  297.  
  298.         # self._states.pop() #temporary fix
  299.  
  300.         #3rd line has the starting state
  301.         self._starting_state = self.get_state(lines[3])
  302.  
  303.         #print self._starting_state
  304.  
  305.         #4th line has the accepting states
  306.         self._accepting_states = set()
  307.         for label in lines[4].split():
  308.             self._accepting_states.add(self.get_state(label))
  309.  
  310.         #print self._accepting_states
  311.  
  312.     def __str__(self):
  313.         '''
  314.        string representation of the object
  315.        '''
  316.         st = "Member states: "
  317.  
  318.         for state in self._states:
  319.             st += str(state)
  320.  
  321.         return st
  322.  
  323.     def update_transitions(self, table):
  324.        
  325.         lines = table.split("\n")
  326.         #print lines
  327.        
  328.         self._no_of_states, self._no_of_symbols = map(int, lines[0].split())
  329.         self._symbols = lines[2].split()
  330.  
  331.         self._starting_state = self.get_state(lines[3])
  332.  
  333.         for label in lines[4].split():
  334.             self._accepting_states.add(self.get_state(label))
  335.        
  336.         #this populates the unary states
  337.  
  338.         for i, line in enumerate(lines[5: len(lines) - self._no_of_symbols]):
  339.  
  340.             source_label = self._states[i / self._no_of_symbols].get_label()
  341.  
  342.             symbol = self._symbols[i % self._no_of_symbols]
  343.  
  344.             dest_label = ''
  345.  
  346.             for dest in line.split():
  347.                 dest_label += dest
  348.                 #print "d: ", dest_label
  349.  
  350.             dest_state = self.get_state(dest_label)
  351.  
  352.             self._states[i / self._no_of_symbols].add_neighbor(symbol, dest_state)
  353.             #print i / self._no_of_symbols
  354.  
  355.         for symbol in self._symbols:
  356.             self._states[-1].add_neighbor(symbol, self._states[-1])
  357.  
  358.         #now for the hybrid states
  359.         dest_label_list = list()
  360.  
  361.         for state in self._states[3: -1]:
  362.  
  363.             for symbol in self._symbols:
  364.                 complex_state_label = state.get_label()
  365.                 dest_label = ""
  366.  
  367.                 for st_label in complex_state_label:
  368.  
  369.                     dest = self.get_state(st_label)
  370.                     lab = dest.get_neighbor(symbol)[0].get_label()
  371.  
  372.                     if lab not in dest_label and lab != 'd':
  373.                         dest_label += lab
  374.  
  375.                 if len(dest_label) == 0:
  376.                     dest_label = 'd'
  377.  
  378.                 state.add_neighbor(symbol, self.get_state(dest_label))
  379.  
  380.     def delta(self, inp_str):
  381.         '''
  382.        given an input string, it would return the end state which would be reached once the string is processed
  383.        '''
  384.         route = ""
  385.         visited_labels = list()
  386.  
  387.         end_state = self.get_state(self._starting_state.get_label())
  388.  
  389.         for symbol in inp_str:
  390.             visited_labels.append(end_state.get_label())
  391.             #print end_state.get_transitions()[symbol]
  392.             end_state, = end_state.get_transitions()[symbol]
  393.             #print end_state.get_label()
  394.             end_state = self.get_state(end_state.get_label())
  395.        
  396.         route = ' -> '.join(visited_labels)        
  397.        
  398.         new_state = set()
  399.  
  400.         #updating the accepting states
  401.         for ac_st in self._accepting_states:
  402.             for state in self._states:
  403.                 if ac_st.get_label() in state.get_label():
  404.                     new_state.add(state)
  405.  
  406.         self._accepting_states = new_state
  407.  
  408.         #print new_state
  409.  
  410.         if end_state in self._accepting_states:
  411.             print "%s is accepted" % (inp_str)
  412.            
  413.         else: #stopping at die status
  414.             print "%s is rejected" % (inp_str)
  415.            
  416.         return route
  417.  
  418.     def minimize(self):
  419.         '''
  420.        removes the 'redundant' states from the transition table
  421.        '''
  422.         #starting_state = self.get_state(self._starting_state.get_label())
  423.         state = self._starting_state
  424.  
  425.         queue = deque()
  426.         queue.append(state)
  427.         visited = set()
  428.         i = 0
  429.        
  430.         #while len(queue) != 0:
  431.         for _ in xrange(5):
  432.             if len(queue) == 0:
  433.                 break
  434.             state = queue.popleft()
  435.             visited.add(state.get_label())
  436.  
  437.             neighbors = self.get_all_neighbors(state.get_label())
  438.  
  439.             #print "n", neighbors
  440.  
  441.             for neighbor in neighbors:
  442.                 if neighbor.get_label() not in visited:
  443.                     queue.append(neighbor)
  444.                     visited.add(neighbor.get_label())
  445.  
  446.         updated_states = list()
  447.  
  448.         for label in visited:
  449.             updated_states.append(self.get_state(label))
  450.  
  451.         self._states = updated_states
  452.         self._states.sort() #this is optional
  453.  
  454.     #getters
  455.     def get_accepting_states(self):
  456.         '''
  457.        returns the set of accepting_states of the automaton
  458.        '''
  459.         return self._accepting_states
  460.  
  461.     def get_states(self):
  462.         '''
  463.        returns the list of states of the automaton
  464.        '''
  465.         return self._states
  466.  
  467.     def get_starting_state(self):
  468.         '''
  469.        returns the starting state of the automaton
  470.        '''
  471.         return self._starting_state
  472.  
  473.     def get_state(self, label):
  474.         '''
  475.        returns the state in the automaton with the label 'label'
  476.        '''
  477.  
  478.         for state in self._states:
  479.             if state._label == label:
  480.                 return state
  481.  
  482.     def get_no_of_states(self):
  483.         '''
  484.        returns th no of states in the automaton
  485.        '''
  486.         return (self._no_of_states)
  487.  
  488.     def get_all_neighbors(self, label):
  489.         '''
  490.        returns the list of neighbors of the state with label 'label'
  491.        '''
  492.         state = self.get_state(label)
  493.        
  494.         neighbors = list()
  495.  
  496.         for symbol in self._symbols:
  497.             neighbors.append(State(state.get_neighbor(symbol)[0].get_label()))
  498.  
  499.         #print neighbors
  500.         return neighbors
  501.  
  502.     #setters
  503.     def set_accepting_states(self, accepting_states):
  504.         '''
  505.        sets the accepting states of the automaton
  506.        '''
  507.         self._accepting_states = accepting_states
  508.  
  509.     def set_starting_state(self, starting_state):
  510.         '''
  511.        sets the starting state of the automaton
  512.        '''
  513.         self._starting_state = starting_state
  514.  
  515.  
  516. def gen_states(n):
  517.     '''
  518.    returns a list of states whose labels are the power_set of n
  519.    works!
  520.    '''
  521.     labels = get_all_labels(range(n))
  522.     labels.append('d')
  523.  
  524.     #print labels
  525.  
  526.     state_list = [State(label) for label in labels]
  527.  
  528.     return state_list
  529.  
  530. def test():
  531.  
  532.     table = transition_table_NFA4
  533.    
  534.     NFA = Automaton(table)
  535.  
  536.     DFA = Automaton('', gen_states(NFA.get_no_of_states() - 1))
  537.  
  538.     DFA.update_transitions(table)
  539.  
  540.     #print DFA
  541.     DFA.minimize()
  542.  
  543.     print(DFA)
  544.     print DFA.delta('101')
  545.  
  546. def main():
  547.     test()
  548.  
  549. if __name__ == '__main__':
  550.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement