Repository: squaresLab/Houston

Issue:
Implement State.equivalent or alter semantics of equality
Implement `State.equiv` and `State.exact`.

Discussion:
coveralls: 
[![Coverage Status](https://coveralls.io/builds/18988873/badge)](https://coveralls.io/builds/18988873)

Coverage increased (+0.04%) to 44.354% when pulling **7fda6f4500aa23fad0d89a64ddbdb36510c23bee on state-equiv** into **50127bd1c4e3bbdc577c7e2aaf300980e8241b0c on master**.


Patch:
diff --git a/houston/state.py b/houston/state.py
index 5fdefb2..18b1fce 100644
--- a/houston/state.py
+++ b/houston/state.py
@@ -211,12 +211,20 @@ class State(object, metaclass=StateMeta):
     def time_offset(self) -> float:
         return self.__time_offset
 
-    def __eq__(self, other: 'State') -> bool:
+    def equiv(self, other: 'State') -> bool:
         if type(self) != type(other):
             msg = "illegal comparison of states: [{}] vs. [{}]"
             msg = msg.format(self.__class__.__name__, state.__class__.__name__)
             raise Exception(msg)  # FIXME use HoustonException
-        return self.__dict__ == other.__dict__
+        for v in self.__class__.variables:
+            if self.__dict__[v._field] != other.__dict__[v._field]:
+                return False
+        return True
+
+    def exact(self, other: 'State') -> bool:
+        return self.equiv(other) and self.time_offset == other.time_offset
+
+    __eq__ = exact
 
     def __getitem__(self, name: str) -> Any:
         # FIXME use frozendict


Test patch:
diff --git a/test/test_state.py b/test/test_state.py
index 0711712..007f014 100644
--- a/test/test_state.py
+++ b/test/test_state.py
@@ -66,6 +66,16 @@ def test_eq():
         assert S(foo=1, bar=2, time_offset=0.0) == Y(foo=1, bar=2, time_offset=0.0)
 
 
+def test_equiv():
+    class S(State):
+        foo = var(int, lambda c: 0)
+        bar = var(int, lambda c: 0)
+
+    assert S(foo=1, bar=1, time_offset=0.0).equiv(S(foo=1, bar=1, time_offset=0.0))
+    assert S(foo=1, bar=1, time_offset=0.0).equiv(S(foo=1, bar=1, time_offset=1.0))
+    assert not S(foo=0, bar=1, time_offset=0.0).equiv(S(foo=1, bar=1, time_offset=0.0))
+
+
 def test_to_and_from_json():
     class S(State):
         foo = var(int, lambda c: 0)