Created
January 13, 2014 23:49
-
-
Save agrif/8410290 to your computer and use it in GitHub Desktop.
demo vector implementation with quickcheck tests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import math | |
| class Vector: | |
| """A test vector class, with dot products, cross products, | |
| addition, and scalar multiplication. | |
| """ | |
| def __init__(self, x, y, z): | |
| self.val = (x, y, z) | |
| def __repr__(self): | |
| return "<Vector: {}>".format(self.val) | |
| def __add__(self, other): | |
| if not isinstance(other, Vector): | |
| raise TypeError("cannot add vector to non-vector") | |
| return Vector(*(a + b for a, b in zip(self.val, other.val))) | |
| def __neg__(self): | |
| return Vector(*(-a for a in self.val)) | |
| def __mul__(self, other): | |
| if isinstance(other, Vector): | |
| return sum(a * b for a, b in zip(self.val, other.val)) | |
| return Vector(*(a * other for a in self.val)) | |
| def __rmul__(self, other): | |
| return self.__mul__(other) | |
| def __eq__(self, other, eps=0.0001): | |
| if not isinstance(other, Vector): | |
| raise TypeError("cannot compare vector to non-vector") | |
| try: | |
| return all(abs(math.log(abs(a/b))) <= eps for a, b in zip(self.val, other.val)) | |
| except ZeroDivisionError: | |
| return all(abs(a - b) <= eps for a, b in zip(self.val, other.val)) | |
| def magnitude(self): | |
| return sum(a**2 for a in self.val) | |
| def cross(self, other): | |
| if not isinstance(other, Vector): | |
| raise TypeError("cannot cross vector with non-vector") | |
| a = self.val | |
| b = other.val | |
| res = ( | |
| a[1] * b[2] - a[2] * b[1], | |
| a[2] * b[0] - a[0] * b[2], | |
| a[0] * b[1] - a[1] * b[0] | |
| ) | |
| return Vector(*res) | |
| # a zero vector | |
| Vector.zero = Vector(0, 0, 0) | |
| ## | |
| ## Tests | |
| ## | |
| import quickcheck as qc | |
| import unittest | |
| @qc.arbitrary.register(Vector) | |
| def arbitrary_vector(_): | |
| """returns a random vector""" | |
| return Vector(*(qc.arbitrary(float) for _ in range(3))) | |
| @qc.simplify.register(Vector) | |
| def simplify_vector(v): | |
| """simplifies a vector""" | |
| for s in qc.simplify(v.val): | |
| yield Vector(*s) | |
| class TestVector(unittest.TestCase): | |
| @qc.quickcheck() | |
| def test_add_neg(self, a: Vector): | |
| self.assertEqual(a + (-a), Vector.zero) | |
| @qc.quickcheck() | |
| def test_add_double(self, a: Vector): | |
| self.assertEqual(a + a, 2 * a) | |
| @qc.quickcheck() | |
| def test_add_n(self, a: Vector, n: qc.Integer.exponential(0, 10)): | |
| v = Vector.zero | |
| for _ in range(n): | |
| v += a | |
| self.assertEqual(v, a * n) | |
| @qc.quickcheck() | |
| def test_dot_positive_definite(self, a: Vector): | |
| c = a * a | |
| if a == Vector.zero: | |
| self.assertEqual(c, 0) | |
| else: | |
| self.assertGreater(c, 0) | |
| @qc.quickcheck() | |
| def test_dot_magnitude(self, a: Vector): | |
| self.assertEqual(a.magnitude(), a * a) | |
| @qc.quickcheck() | |
| def test_dot_commute(self, a: Vector, b: Vector): | |
| self.assertEqual(a * b, b * a) | |
| @qc.quickcheck() | |
| def test_cross_self(self, a: Vector): | |
| self.assertEqual(a.cross(a), Vector.zero) | |
| @qc.quickcheck() | |
| def test_cross_anticommute(self, a: Vector, b: Vector): | |
| self.assertEqual(a.cross(b), -b.cross(a)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment