Skip to content

Commit

Permalink
Begin work on DTM.validate_self
Browse files Browse the repository at this point in the history
  • Loading branch information
caleb531 committed May 10, 2016
1 parent 780980c commit a72743a
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 15 deletions.
29 changes: 17 additions & 12 deletions tests/test_dtm.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,23 @@ class TestDTM(test_tm.TestTM):
def test_init_dtm(self):
"""Should copy DTM if passed into DTM constructor."""
new_dtm = DTM(self.dtm1)
nose.assert_is_not(new_dtm.states, self.dtm1.states)
nose.assert_equal(new_dtm.states, self.dtm1.states)
nose.assert_is_not(new_dtm.input_symbols, self.dtm1.input_symbols)
nose.assert_equal(new_dtm.input_symbols, self.dtm1.input_symbols)
nose.assert_is_not(new_dtm.tape_symbols, self.dtm1.tape_symbols)
nose.assert_equal(new_dtm.tape_symbols, self.dtm1.tape_symbols)
nose.assert_is_not(new_dtm.transitions, self.dtm1.transitions)
nose.assert_equal(new_dtm.transitions, self.dtm1.transitions)
nose.assert_equal(new_dtm.initial_state, self.dtm1.initial_state)
nose.assert_equal(new_dtm.blank_symbol, self.dtm1.blank_symbol)
nose.assert_is_not(new_dtm.final_states, self.dtm1.final_states)
nose.assert_equal(new_dtm.final_states, self.dtm1.final_states)
self.assert_is_copy(new_dtm, self.dtm1)

def test_copy_dtm(self):
"""Should create exact copy of DTM if copy() method is called."""
new_dtm = self.dtm1.copy()
self.assert_is_copy(new_dtm, self.dtm1)

def test_dtm_equal(self):
"""Should correctly determine if two DTMs are equal."""
new_dtm = self.dtm1.copy()
nose.assert_true(self.dtm1 == new_dtm, 'DTMs are not equal')

def test_dtm_not_equal(self):
"""Should correctly determine if two DTMs are not equal."""
new_dtm = self.dtm1.copy()
new_dtm.final_states.add('q2')
nose.assert_true(self.dtm1 != new_dtm, 'DTMs are equal')

def test_validate_input_valid(self):
"""Should return correct stop state if valid TM input is given."""
Expand Down
17 changes: 17 additions & 0 deletions tests/test_tm.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/usr/bin/env python3
"""Classes and functions for testing the behavior of DTMs."""

import nose.tools as nose

from turingmachines.dtm import DTM


Expand Down Expand Up @@ -64,3 +66,18 @@ def setup(self):
blank_symbol='.',
final_states={'q4'}
)

def assert_is_copy(self, first, second):
"""Assert that the first FA is an exact copy of the second."""
nose.assert_is_not(first.states, second.states)
nose.assert_equal(first.states, second.states)
nose.assert_is_not(first.input_symbols, second.input_symbols)
nose.assert_equal(first.input_symbols, second.input_symbols)
nose.assert_is_not(first.tape_symbols, second.tape_symbols)
nose.assert_equal(first.tape_symbols, second.tape_symbols)
nose.assert_is_not(first.transitions, second.transitions)
nose.assert_equal(first.transitions, second.transitions)
nose.assert_equal(first.initial_state, second.initial_state)
nose.assert_equal(first.blank_symbol, second.blank_symbol)
nose.assert_is_not(first.final_states, second.final_states)
nose.assert_equal(first.final_states, second.final_states)
8 changes: 7 additions & 1 deletion turingmachines/dtm.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from turingmachines.tape import TMTape


class DTM(object):
class DTM(tm.TM):
"""A deterministic Turing machine."""

def __init__(self, obj=None, *, states=None, input_symbols=None,
Expand All @@ -24,6 +24,7 @@ def __init__(self, obj=None, *, states=None, input_symbols=None,
self.initial_state = initial_state
self.blank_symbol = blank_symbol
self.final_states = final_states.copy()
self.validate_self()

def _init_from_dtm(self, tm):
"""Initialize this DTM as an exact copy of the given DTM."""
Expand All @@ -33,6 +34,11 @@ def _init_from_dtm(self, tm):
initial_state=tm.initial_state, blank_symbol=tm.blank_symbol,
final_states=tm.final_states)

def validate_self(self):
"""Return True if this DTM is internally consistent."""
self._validate_initial_state()
self._validate_final_states()

def _get_transition(self, state, tape_symbol):
"""Get the transiton tuple for the given state and tape symbol."""
if (state in self.transitions and
Expand Down
32 changes: 30 additions & 2 deletions turingmachines/tm.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,30 @@
import abc


class TM(object):
class TM(metaclass=abc.ABCMeta):
"""An abstract base class for Turing machines."""

@abc.abstractmethod
def __init__(self, **kwargs):
"""Initialize a complete Turing machine."""
pass

def _validate_initial_state(self):
"""Raise an error if an initial state is invalid."""
if self.initial_state not in self.states:
raise InvalidStateError(
'{} is not a valid initial state'.format(self.initial_state))

def _validate_final_states(self):
"""Raise an error if any final states are invalid."""
invalid_states = self.final_states - self.states
if invalid_states:
raise InvalidStateError(
'final states are not valid ({})'.format(
', '.join(invalid_states)))

@abc.abstractmethod
def validate_machine(self):
def validate_self(self):
"""Return True if this machine is internally consistent."""
pass

Expand All @@ -22,13 +36,27 @@ def validate_input(self, input_str):
"""Check if the given string is accepted by this machine."""
pass

def copy(self):
"""Create an exact copy of the TM."""
return self.__class__(self)

def __eq__(self, other):
"""Check if two machines are equal."""
return self.__dict__ == other.__dict__


class TMError(Exception):
"""The base class for all machine-related errors."""

pass


class InvalidStateError(TMError):
"""A state is not a valid state for this machine."""

pass


class RejectionError(TMError):
"""The machine halted on a non-final state."""

Expand Down

0 comments on commit a72743a

Please sign in to comment.