def _get_next_configuration(self, old_config): """Advance to the next configuration.""" transitions = set() if old_config.remaining_input: transitions.add( self._get_transition(old_config.state, old_config.remaining_input[0], old_config.stack.top())) transitions.add( self._get_transition(old_config.state, '', old_config.stack.top())) if None in transitions: transitions.remove(None) if len(transitions) == 0: raise exceptions.RejectionException( 'The automaton entered a configuration for which no ' 'transition is defined ({}, {}, {})'.format( old_config.state, old_config.remaining_input[0], old_config.stack.top())) if len(transitions) > 1: raise pda_exceptions.NondeterminismError( 'The automaton entered a configuration for which more' 'than one transition is defined ({}, {}'.format( old_config.state, old_config.stack.top())) input_symbol, new_state, new_stack_top = transitions.pop() remaining_input = old_config.remaining_input if input_symbol: remaining_input = remaining_input[1:] new_config = PDAConfiguration( new_state, remaining_input, self._replace_stack_top(old_config.stack, new_stack_top)) return new_config
def _check_for_input_rejection(self, current_state, stack): """Raise an error if the given config indicates rejected input.""" # If current state is not a final state and stack is not empty if current_state not in self.final_states and stack: raise exceptions.RejectionException( 'the DPDA stopped in a non-accepting configuration ' '({}, {})'.format(current_state, stack))
def read_input_stepwise(self, input_str): """ Check if the given string is accepted by this Turing machine. Yield the current configurations of the machine at each step. """ current_configurations = { TMConfiguration(self.initial_state, TMTape(input_str, blank_symbol=self.blank_symbol)) } yield current_configurations # The initial state cannot be a final state for a NTM, so the first # iteration is always guaranteed to run (as it should) while current_configurations: new_configurations = set() for config in current_configurations: if self._has_accepted(config): # One accepting configuration is enough. return new_configurations.update( self._get_next_configurations(config)) current_configurations = new_configurations yield current_configurations raise exceptions.RejectionException( 'the NTM did not reach an accepting configuration')
def read_input_stepwise(self, input_str): """ Check if the given string is accepted by this NPDA. Yield the NPDA's current configurations at each step. """ current_configurations = set() current_configurations.add( PDAConfiguration(self.initial_state, input_str, PDAStack([self.initial_stack_symbol]))) yield current_configurations while current_configurations: new_configurations = set() for config in current_configurations: if self._has_accepted(config): # One accepting configuration is enough. return if config.remaining_input: new_configurations.update( self._get_next_configurations(config)) elif self._has_lambda_transition(config.state, config.stack.top()): new_configurations.update( self._get_next_configurations(config)) current_configurations = new_configurations yield current_configurations raise exceptions.RejectionException( 'the NPDA did not reach an accepting configuration')
def read_input_stepwise(self, input_str): """Checks if the given string is accepted by this Turing machine, using a BFS of every possible configuration from each configuration. Yields the current configuration of the machine at each step. """ self._restart_configuration(input_str) queue = deque([self]) while len(queue) > 0: current_tm = queue.popleft() yield {MTMConfiguration(self.current_state, tuple(self.tapes))} possible_transitions = current_tm._get_transition() if possible_transitions is None: if current_tm._has_accepted(): return { MTMConfiguration(self.current_state, tuple(self.tapes)) } else: for transition in possible_transitions[1:]: queue.append( current_tm.copy()._get_next_configuration(transition)) queue.append( current_tm._get_next_configuration( possible_transitions[0])) raise exceptions.RejectionException( 'the multitape MNTM did not reach an accepting configuration')
def _get_transition(self, state, tape_symbol): """Get the transiton tuple for the given state and tape symbol.""" if (state in self.transitions and tape_symbol in self.transitions[state]): return self.transitions[state][tape_symbol] else: raise exceptions.RejectionException( 'The machine entered a non-final configuration for which no ' 'transition is defined ({}, {})'.format(state, tape_symbol))
def _get_next_current_state(self, current_state, input_symbol): """ Follow the transition for the given input symbol on the current state. Raise an error if the transition does not exist. """ if input_symbol in self.transitions[current_state]: return self.transitions[current_state][input_symbol] else: raise exceptions.RejectionException( '{} is not a valid input symbol'.format(input_symbol))
def _get_transition(self, state, input_symbol, stack_symbol): """Get the transiton tuple for the given state and symbols.""" if (state in self.transitions and input_symbol in self.transitions[state] and stack_symbol in self.transitions[state][input_symbol]): return self.transitions[state][input_symbol][stack_symbol] else: raise exceptions.RejectionException( 'The automaton entered a configuration for which no ' 'transition is defined ({}, {}, {})'.format( state, input_symbol, stack_symbol))
def _get_next_configuration(self, old_config): """Advance to the next configuration.""" transitions = { self._get_transition(old_config.state, old_config.tape.read_symbol()) } if None in transitions: transitions.remove(None) if len(transitions) == 0: raise exceptions.RejectionException( 'The machine entered a non-final configuration for which no ' 'transition is defined ({}, {})'.format( old_config.state, old_config.tape.read_symbol())) tape = old_config.tape (new_state, new_tape_symbol, direction) = transitions.pop() tape = tape.write_symbol(new_tape_symbol) tape = tape.move(direction) return TMConfiguration(new_state, tape)
def _check_for_input_rejection(self, current_state): """Raise an error if the given config indicates rejected input.""" if current_state not in self.final_states: raise exceptions.RejectionException( 'the DFA stopped on a non-final state ({})'.format( current_state))
def _check_for_input_rejection(self, current_states): """Raise an error if the given config indicates rejected input.""" if not (current_states & self.final_states): raise exceptions.RejectionException( 'the NFA stopped on all non-final states ({})'.format( ', '.join(current_states)))
def _check_for_input_rejection(self, current_configuration): """Raise an error if the given config indicates rejected input.""" if not self._has_accepted(current_configuration): raise exceptions.RejectionException( 'the DPDA stopped in a non-accepting configuration ' '({state}, {stack})'.format(**current_configuration._asdict()))
def read_input_as_ntm(self, input_str): """Simulates the machine as a single-tape Turing machine. Yields the configuration at each step.""" self._restart_configuration(input_str) head_symbol = '^' tape_separator_symbol = '_' extended_tape = '' tapes_copy = self.tapes.copy() for tape_copy in tapes_copy: tape_str = tape_copy.get_symbols_as_str() extended_tape += tape_str[0] + head_symbol + \ tape_str[1:] + tape_separator_symbol current_state = self.initial_state yield { TMConfiguration( current_state, TMTape(extended_tape, blank_symbol=self.blank_symbol, current_position=0)) } # If the machine has not reached an accepting state. while current_state not in self.final_states: i = 0 # current position virtual_heads = self._read_extended_tape(extended_tape, head_symbol, tape_separator_symbol) try: next_config = self.transitions[current_state][virtual_heads] except KeyError: raise exceptions.RejectionException( 'the multitape NTM did not reach an accepting ' 'configuration') next_state, moves = next_config[0] for move in moves: new_head, direction = move executing_changes = True while executing_changes: if extended_tape[i] == head_symbol: # Head has been found (previous symbol is the head). # This replaces the previous symbol with the new_head. extended_tape = (extended_tape[:i - 1] + new_head + extended_tape[i:]) extended_tape = extended_tape[:i] + \ '' + extended_tape[i + 1:] # After replacing, the machine must change the # position of the virtual head of the current virtual # tape. if direction == 'R': i += 1 elif direction == 'L': i -= 1 else: # direction == 'N' i += 0 if extended_tape[i - 1] == tape_separator_symbol: i -= 1 extended_tape = extended_tape[:i] + \ self.blank_symbol + \ head_symbol + extended_tape[i:] i += 1 else: extended_tape = (extended_tape[:i] + head_symbol + extended_tape[i:]) elif extended_tape[i] == tape_separator_symbol: executing_changes = False i += 1 current_state = next_state yield { TMConfiguration( current_state, TMTape(extended_tape, blank_symbol=self.blank_symbol, current_position=i - 1)) }