From de87b52416ff2766adb3b0bf80a7f211ddd8e7b1 Mon Sep 17 00:00:00 2001 From: John Waldrep Date: Wed, 13 May 2015 13:33:13 -0400 Subject: [PATCH 01/13] Add Matrix and Vector classes; 2/2 tests pass --- matrix_math.py | 8 ++++++++ test_matrix_math.py | 6 ++++++ 2 files changed, 14 insertions(+) create mode 100644 matrix_math.py create mode 100644 test_matrix_math.py diff --git a/matrix_math.py b/matrix_math.py new file mode 100644 index 0000000..a2b56b4 --- /dev/null +++ b/matrix_math.py @@ -0,0 +1,8 @@ +class Matrix(): + def __init__(self, my_list): + pass + + +class Vector(): + def __init__(self, my_vector): + pass diff --git a/test_matrix_math.py b/test_matrix_math.py new file mode 100644 index 0000000..8494fb5 --- /dev/null +++ b/test_matrix_math.py @@ -0,0 +1,6 @@ +from matrix_math import * +def test_matrix_class(): + m = Matrix([[0, 1, 0], [1, 0, 0], [0, 0, 1]]) + +def test_vector_class(): + v = Vector([1, 5]) From 833eaf66d3d0ca791e9432f9d2f2170e80113125 Mon Sep 17 00:00:00 2001 From: John Waldrep Date: Wed, 13 May 2015 14:03:20 -0400 Subject: [PATCH 02/13] Add error/shape handling; 7/7 pass --- matrix_math.py | 23 ++++++++++++++++++++--- test_matrix_math.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/matrix_math.py b/matrix_math.py index a2b56b4..45005d5 100644 --- a/matrix_math.py +++ b/matrix_math.py @@ -1,8 +1,25 @@ class Matrix(): def __init__(self, my_list): - pass + self.my_list = my_list + self.my_shape = self.shape() + def shape(self): + try: + return (len(self.my_list), len(self.my_list[0])) + except (TypeError,IndexError): + raise ValueError('Not a 2d matrix') + class Vector(): - def __init__(self, my_vector): - pass + def __init__(self, my_list): + self.my_list = my_list + self.my_shape = self.shape() + + def shape(self): + try: + if len(self.my_list[0]) > 1: + raise ValueError('Not a 1d vector') + except TypeError: + return (len(self.my_list),) + except: + raise ValueError('Not a 1d vector') diff --git a/test_matrix_math.py b/test_matrix_math.py index 8494fb5..244a14c 100644 --- a/test_matrix_math.py +++ b/test_matrix_math.py @@ -1,6 +1,34 @@ from matrix_math import * +from nose.tools import raises + def test_matrix_class(): m = Matrix([[0, 1, 0], [1, 0, 0], [0, 0, 1]]) def test_vector_class(): v = Vector([1, 5]) + +@raises(ValueError) +def test_2d_vector_exception(): + v = Vector([[0, 1, 0], [1, 0, 0], [0, 0, 1]]) + +@raises(ValueError) +def test_empty_vector_exception(): + v = Vector([]) + +@raises(ValueError) +def test_1d_matrix_exception(): + m = Matrix([1,5]) + +@raises(ValueError) +def test_empty_matrix_exception(): + v = Matrix([]) + +def test_matrix_class(): + v = Vector([1, 5]) + assert v.shape() == (2,) + +def test_matrix_shape(): + m = Matrix([[0, 1, 0], [1, 0, 0], [0, 0, 1]]) + assert m.shape() == (3,3) + m = Matrix([[1,2],[3,4],[5,6]]) + assert m.shape() == (3,2) From b713340aba916845c9c7e3785d4843e7aca0582b Mon Sep 17 00:00:00 2001 From: John Waldrep Date: Wed, 13 May 2015 15:11:11 -0400 Subject: [PATCH 03/13] Vector add/sub/scalar multiplication added with lots of exceptions. 13/13 pass --- matrix_math.py | 42 ++++++++++++++++++++------- test_matrix_math.py | 71 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 99 insertions(+), 14 deletions(-) diff --git a/matrix_math.py b/matrix_math.py index 45005d5..94101b9 100644 --- a/matrix_math.py +++ b/matrix_math.py @@ -1,19 +1,25 @@ -class Matrix(): - def __init__(self, my_list): - self.my_list = my_list - self.my_shape = self.shape() - - - def shape(self): - try: - return (len(self.my_list), len(self.my_list[0])) - except (TypeError,IndexError): - raise ValueError('Not a 2d matrix') +class ShapeException(Exception): + pass class Vector(): def __init__(self, my_list): self.my_list = my_list self.my_shape = self.shape() + def __add__(self, other): + if self.shape() != other.shape(): + raise ShapeException + return Vector([self.my_list[i] + other.my_list[i] for i in range(self.shape()[0])]) + + def __sub__(self, other): + other = other * -1 + return self + other + def __mul__(self, other): + if type(other) is int or type(other) is float: + return Vector([i * other for i in self.my_list]) + else: + return None + def __eq__(self, other): + return self.my_list == other.my_list def shape(self): try: @@ -23,3 +29,17 @@ def shape(self): return (len(self.my_list),) except: raise ValueError('Not a 1d vector') + def dot(self, other): + pass + +class Matrix(): + def __init__(self, my_list): + self.my_list = my_list + self.my_shape = self.shape() + + + def shape(self): + try: + return (len(self.my_list), len(self.my_list[0])) + except (TypeError,IndexError): + raise ValueError('Not a 2d matrix') diff --git a/test_matrix_math.py b/test_matrix_math.py index 244a14c..cbb2598 100644 --- a/test_matrix_math.py +++ b/test_matrix_math.py @@ -23,12 +23,77 @@ def test_1d_matrix_exception(): def test_empty_matrix_exception(): v = Matrix([]) -def test_matrix_class(): - v = Vector([1, 5]) - assert v.shape() == (2,) + +m = Vector([3, 4]) +n = Vector([5, 0]) + +v = Vector([1, 3, 0]) +w = Vector([0, 2, 4]) +u = Vector([1, 1, 1]) +y = Vector([10, 20, 30]) +z = Vector([0, 0, 0]) + +A = Matrix([[1, 0, 0], + [0, 1, 0], + [0, 0, 1]]) +B = Matrix([[1, 2, 3], + [4, 5, 6], + [7, 8, 9]]) +C = Matrix([[1, 2], + [2, 1], + [1, 2]]) +D = Matrix([[1, 2, 3], + [3, 2, 1]]) + +def test_matrix_class_and_shape(): + assert m.shape() == (2,) + assert v.shape() == (3,) def test_matrix_shape(): m = Matrix([[0, 1, 0], [1, 0, 0], [0, 0, 1]]) assert m.shape() == (3,3) m = Matrix([[1,2],[3,4],[5,6]]) assert m.shape() == (3,2) + assert A.shape() == (3, 3) + assert C.shape() == (3, 2) + assert D.shape() == (2, 3) +def test_vector_eq(): + p = Vector([3,4]) + assert m == p + +def test_vector_add(): + """ + [a b] + [c d] = [a+c b+d] + + Matrix + Matrix = Matrix + """ + assert v + w == Vector([1, 5, 4]) + assert u + y == Vector([11, 21, 31]) + assert u + z == u + +def test_vector_add_is_communicative(): + assert w + y == y + w + +def test_vector_sub(): + """ + [a b] - [c d] = [a-c b-d] + + Matrix + Matrix = Matrix + """ + assert v - w == Vector([1, 1, -4]) + assert w - v == Vector([-1, -1, 4]) + assert y - z == y + assert w - u == (z - (u - w)) + + +@raises(ShapeException) +def test_vector_sub_checks_shapes(): + """Shape rule: the vectors must be the same size.""" + m - v + +""" +def test_matrix_add(): + assert False +def test_matrix_sub(): + assert False +""" From fa5d23b3a883c14fe054f6db1aee3a414d12472c Mon Sep 17 00:00:00 2001 From: John Waldrep Date: Wed, 13 May 2015 15:25:09 -0400 Subject: [PATCH 04/13] Add dot product; 16/16 pass --- matrix_math.py | 8 ++++++-- test_matrix_math.py | 31 ++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/matrix_math.py b/matrix_math.py index 94101b9..bc09c49 100644 --- a/matrix_math.py +++ b/matrix_math.py @@ -20,6 +20,8 @@ def __mul__(self, other): return None def __eq__(self, other): return self.my_list == other.my_list + def __repr__(self): + return 'Vector({})'.format(self.my_list) def shape(self): try: @@ -30,8 +32,10 @@ def shape(self): except: raise ValueError('Not a 1d vector') def dot(self, other): - pass - + if self.shape() != other.shape(): + raise ShapeException + return sum([self.my_list[i] * other.my_list[i] for i in range(len(self.my_list))]) + class Matrix(): def __init__(self, my_list): self.my_list = my_list diff --git a/test_matrix_math.py b/test_matrix_math.py index cbb2598..5d3b63b 100644 --- a/test_matrix_math.py +++ b/test_matrix_math.py @@ -60,7 +60,7 @@ def test_matrix_shape(): def test_vector_eq(): p = Vector([3,4]) assert m == p - + def test_vector_add(): """ [a b] + [c d] = [a+c b+d] @@ -91,6 +91,35 @@ def test_vector_sub_checks_shapes(): """Shape rule: the vectors must be the same size.""" m - v +def test_dot(): + """ + dot([a b], [c d]) = a * c + b * d + + dot(Vector, Vector) = Scalar + """ + assert w.dot(y) == 160 + assert m.dot(n) == 15 + assert u.dot(z) == 0 + + +@raises(ShapeException) +def test_dot_checks_shapes(): + """Shape rule: the vectors must be the same size.""" + v.dot(m) + + +def test_vector_multiply(): + """ + [a b] * Z = [a*Z b*Z] + + Vector * Scalar = Vector + """ + assert v * 0.5 == Vector([0.5, 1.5, 0]) + assert m * 2 == Vector([6, 8]) + + + + """ def test_matrix_add(): assert False From 2247a48e2c1a40e9acd42c83a8fccd20010b5940 Mon Sep 17 00:00:00 2001 From: John Waldrep Date: Wed, 13 May 2015 16:16:24 -0400 Subject: [PATCH 05/13] Matrix math partially implemented; 23/25 passing --- matrix_math.py | 35 ++++++++++++++++ test_matrix_math.py | 98 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 127 insertions(+), 6 deletions(-) diff --git a/matrix_math.py b/matrix_math.py index bc09c49..0961be3 100644 --- a/matrix_math.py +++ b/matrix_math.py @@ -35,11 +35,46 @@ def dot(self, other): if self.shape() != other.shape(): raise ShapeException return sum([self.my_list[i] * other.my_list[i] for i in range(len(self.my_list))]) + def magnitude(self): + return self.dot(self)**0.5 class Matrix(): def __init__(self, my_list): self.my_list = my_list self.my_shape = self.shape() + def __add__(self, other): + if self.shape() != other.shape(): + raise ShapeException + return Vector([self.my_list[i] + other.my_list[i] for i in range(self.shape()[0])]) + + def __sub__(self, other): + other = other * -1 + return self + other + def __mul__(self, other): + if type(other) is int or type(other) is float: + return Matrix([[i * other for i in row] for row in self.my_list]) + elif type(other) is Vector: + if self.shape()[1] != other.shape()[0]: + raise ShapeException + + step1 = [[val * other.my_list[idx] for idx, val in enumerate(row)] + for row in self.my_list] + return Vector([sum(x) for x in step1]) + elif type(other) is Matrix: + if self.shape()[1] != other.shape()[0]: + raise ShapeException + + y_transposed = [[row[i] for row in other.my_list] for i in range(len(other.my_list[0]))] + + return [[dot(row, col) for col in y_transposed] for row in self.my_list] + + else: + return None + def __eq__(self, other): + return self.my_list == other.my_list + def __repr__(self): + return 'Vector({})'.format(self.my_list) + def shape(self): diff --git a/test_matrix_math.py b/test_matrix_math.py index 5d3b63b..9bca418 100644 --- a/test_matrix_math.py +++ b/test_matrix_math.py @@ -1,4 +1,5 @@ from matrix_math import * +import math from nose.tools import raises def test_matrix_class(): @@ -85,6 +86,13 @@ def test_vector_sub(): assert y - z == y assert w - u == (z - (u - w)) +def test_hard_mode_vector(): + Vector([1, 2]) + Vector([0, 4]) + Vector([1, 2]) - Vector([0, 4]) + Vector([1, 2]) * 3 + + assert Vector([1, 2]) == Vector([1, 2]) # results in True + @raises(ShapeException) def test_vector_sub_checks_shapes(): @@ -117,12 +125,90 @@ def test_vector_multiply(): assert v * 0.5 == Vector([0.5, 1.5, 0]) assert m * 2 == Vector([6, 8]) +def test_magnitude(): + """ + magnitude([a b]) = sqrt(a^2 + b^2) + + magnitude(Vector) = Scalar + """ + assert m.magnitude() == 5 + assert v.magnitude() == math.sqrt(10) + assert y.magnitude() == math.sqrt(1400) + assert z.magnitude() == 0 + + + + +def test_shape_matrices(): + """shape should take a vector or matrix and return a tuple with the + number of rows (for a vector) or the number of rows and columns + (for a matrix.)""" + assert A.shape() == (3, 3) + assert C.shape() == (3, 2) + assert D.shape() == (2, 3) +def test_matrix_scalar_multiply(): + """ + [[a b] * Z = [[a*Z b*Z] + [c d]] [c*Z d*Z]] + + Matrix * Scalar = Matrix + """ + assert C * 3 == Matrix([[3, 6], + [6, 3], + [3, 6]]) + + +def test_matrix_vector_multiply(): + """ + [[a b] * [x = [a*x+b*y + [c d] y] c*x+d*y + [e f] e*x+f*y] + + Matrix * Vector = Vector + """ + assert A * Vector([2, 5, 4]) == Vector([2, 5, 4]) + assert B * Vector([1, 2, 3]) == Vector([14, 32, 50]) + assert C * Vector([3, 4]) == Vector([11, 10, 11]) + assert D * Vector([0, 1, 2]) == Vector([8, 4]) + + +@raises(ShapeException) +def test_matrix_vector_multiply_checks_shapes(): + """Shape Rule: The number of rows of the vector must equal the number of + columns of the matrix.""" + C * Vector([1, 2, 3]) + + +def test_matrix_matrix_multiply(): + """ + [[a b] * [[w x] = [[a*w+b*y a*x+b*z] + [c d] [y z]] [c*w+d*y c*x+d*z] + [e f] [e*w+f*y e*x+f*z]] + + Matrix * Matrix = Matrix + """ + assert A * B == B + assert B * C == Matrix([[8, 10], + [20, 25], + [32, 40]]) + assert C * D == Matrix([[7, 6, 5], + [5, 6, 7], + [7, 6, 5]]) + assert D * C == Matrix([[8, 10], [8, 10]]) + + +@raises(ShapeException) +def test_matrix_matrix_multiply_checks_shapes(): + """Shape Rule: The number of columns of the first matrix must equal the + number of rows of the second matrix.""" + A * D +def test_hard_mode_matrix(): + Matrix([[0, 1], [1, 0]]) + Matrix([[1, 1], [0, 0]]) + Matrix([[0, 1], [1, 0]]) - Matrix([[1, 1], [0, 0]]) + Matrix([[0, 1], [1, 0]]) * 3 + Matrix([[0, 1], [1, 0]]) * Vector([1, 2]) + Matrix([[1, 1, 1], [0, 0, 0]]) * Matrix([[1, 1], [2, 2], [3, 3]]) -""" -def test_matrix_add(): - assert False -def test_matrix_sub(): - assert False -""" + assert not Matrix([[0, 1], [1, 0]]) == Matrix([[1, 1], [0, 0]]) # results in False From bbf61db95792114468411bbfc07f56ad573ce3ba Mon Sep 17 00:00:00 2001 From: John Waldrep Date: Wed, 13 May 2015 16:35:28 -0400 Subject: [PATCH 06/13] matrix_matrix_multiply implemented; 25/25 test passing --- matrix_math.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix_math.py b/matrix_math.py index 0961be3..849390c 100644 --- a/matrix_math.py +++ b/matrix_math.py @@ -45,7 +45,7 @@ def __init__(self, my_list): def __add__(self, other): if self.shape() != other.shape(): raise ShapeException - return Vector([self.my_list[i] + other.my_list[i] for i in range(self.shape()[0])]) + return Matrix([self.my_list[i] + other.my_list[i] for i in range(self.shape()[0])]) def __sub__(self, other): other = other * -1 @@ -66,7 +66,7 @@ def __mul__(self, other): y_transposed = [[row[i] for row in other.my_list] for i in range(len(other.my_list[0]))] - return [[dot(row, col) for col in y_transposed] for row in self.my_list] + return Matrix([[Vector(row).dot(Vector(col)) for col in y_transposed] for row in self.my_list]) else: return None From bea646e5fbf5fd8b52cf162d4b503987b19628ab Mon Sep 17 00:00:00 2001 From: John Waldrep Date: Wed, 13 May 2015 16:39:52 -0400 Subject: [PATCH 07/13] Clean up for PEP8 --- matrix_math.py | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/matrix_math.py b/matrix_math.py index 849390c..4fa4d23 100644 --- a/matrix_math.py +++ b/matrix_math.py @@ -1,25 +1,31 @@ class ShapeException(Exception): pass + class Vector(): def __init__(self, my_list): self.my_list = my_list self.my_shape = self.shape() + def __add__(self, other): if self.shape() != other.shape(): raise ShapeException - return Vector([self.my_list[i] + other.my_list[i] for i in range(self.shape()[0])]) + return Vector([self.my_list[i] + other.my_list[i] + for i in range(self.shape()[0])]) def __sub__(self, other): other = other * -1 return self + other + def __mul__(self, other): if type(other) is int or type(other) is float: return Vector([i * other for i in self.my_list]) else: return None + def __eq__(self, other): return self.my_list == other.my_list + def __repr__(self): return 'Vector({})'.format(self.my_list) @@ -31,28 +37,36 @@ def shape(self): return (len(self.my_list),) except: raise ValueError('Not a 1d vector') + def dot(self, other): if self.shape() != other.shape(): raise ShapeException - return sum([self.my_list[i] * other.my_list[i] for i in range(len(self.my_list))]) + return sum([self.my_list[i] * other.my_list[i] + for i in range(len(self.my_list))]) + def magnitude(self): return self.dot(self)**0.5 + class Matrix(): def __init__(self, my_list): self.my_list = my_list self.my_shape = self.shape() + def __add__(self, other): if self.shape() != other.shape(): raise ShapeException - return Matrix([self.my_list[i] + other.my_list[i] for i in range(self.shape()[0])]) + return Matrix([self.my_list[i] + other.my_list[i] + for i in range(self.shape()[0])]) def __sub__(self, other): other = other * -1 return self + other + def __mul__(self, other): if type(other) is int or type(other) is float: return Matrix([[i * other for i in row] for row in self.my_list]) + elif type(other) is Vector: if self.shape()[1] != other.shape()[0]: raise ShapeException @@ -60,25 +74,28 @@ def __mul__(self, other): step1 = [[val * other.my_list[idx] for idx, val in enumerate(row)] for row in self.my_list] return Vector([sum(x) for x in step1]) + elif type(other) is Matrix: if self.shape()[1] != other.shape()[0]: raise ShapeException - y_transposed = [[row[i] for row in other.my_list] for i in range(len(other.my_list[0]))] + y_transposed = [[row[i] for row in other.my_list] + for i in range(len(other.my_list[0]))] - return Matrix([[Vector(row).dot(Vector(col)) for col in y_transposed] for row in self.my_list]) + return Matrix([[Vector(row).dot(Vector(col)) + for col in y_transposed] for row in self.my_list]) else: return None + def __eq__(self, other): return self.my_list == other.my_list + def __repr__(self): return 'Vector({})'.format(self.my_list) - - def shape(self): try: return (len(self.my_list), len(self.my_list[0])) - except (TypeError,IndexError): + except (TypeError, IndexError): raise ValueError('Not a 2d matrix') From 1223bbe2fdf8cf2c78ddd6c1e291cee228311bb7 Mon Sep 17 00:00:00 2001 From: John Waldrep Date: Wed, 13 May 2015 17:18:33 -0400 Subject: [PATCH 08/13] Add ipython notebook with population growth solution --- Population Growth.ipynb | 116 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/Population Growth.ipynb b/Population Growth.ipynb index 6ae6009..f232dbd 100644 --- a/Population Growth.ipynb +++ b/Population Growth.ipynb @@ -225,6 +225,122 @@ "Using the above math plus your matrix library, calculate how many drakes of each age I will have in 20 2-year cycles." ] }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from matrix_math import *" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "leslie = Matrix([[0,0.25,0.6,0.8,0.15,0],\n", + "[0.7,0,0,0,0,0],\n", + "[0,0.95,0,0,0,0],\n", + "[0,0,0.9,0,0,0],\n", + "[0,0,0,0.9,0,0],\n", + "[0,0,0,0,0.5,0]])" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector([[0, 0.25, 0.6, 0.8, 0.15, 0], [0.7, 0, 0, 0, 0, 0], [0, 0.95, 0, 0, 0, 0], [0, 0, 0.9, 0, 0, 0], [0, 0, 0, 0.9, 0, 0], [0, 0, 0, 0, 0.5, 0]])" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "leslie" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector([10, 0, 0, 0, 0, 0])" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "drakes = Vector([10,0,0,0,0,0])\n", + "drakes" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[10, 0, 0, 0, 0, 0] Drakes in year 0 \n", + "[0.0, 7.0, 0.0, 0.0, 0.0, 0.0] Drakes in year 2 \n", + "[1.0, 0.0, 6.0, 0.0, 0.0, 0.0] Drakes in year 4 \n", + "[3.0, 1.0, 0.0, 5.0, 0.0, 0.0] Drakes in year 6 \n", + "[5.0, 2.0, 1.0, 0.0, 5.0, 0.0] Drakes in year 8 \n", + "[2.0, 3.0, 2.0, 1.0, 0.0, 2.0] Drakes in year 10 \n", + "[3.0, 1.0, 3.0, 2.0, 0.0, 0.0] Drakes in year 12 \n", + "[4.0, 2.0, 1.0, 3.0, 2.0, 0.0] Drakes in year 14 \n", + "[4.0, 3.0, 2.0, 1.0, 2.0, 1.0] Drakes in year 16 \n", + "[3.0, 2.0, 2.0, 1.0, 1.0, 1.0] Drakes in year 18 \n", + "[4.0, 2.0, 2.0, 2.0, 1.0, 0.0] Drakes in year 20 \n", + "[4.0, 3.0, 2.0, 2.0, 2.0, 0.0] Drakes in year 22 \n", + "[4.0, 3.0, 2.0, 2.0, 2.0, 1.0] Drakes in year 24 \n", + "[4.0, 3.0, 3.0, 2.0, 1.0, 1.0] Drakes in year 26 \n", + "[5.0, 3.0, 3.0, 2.0, 2.0, 0.0] Drakes in year 28 \n", + "[5.0, 3.0, 3.0, 2.0, 2.0, 1.0] Drakes in year 30 \n", + "[5.0, 3.0, 3.0, 2.0, 2.0, 1.0] Drakes in year 32 \n", + "[5.0, 3.0, 3.0, 3.0, 2.0, 1.0] Drakes in year 34 \n", + "[5.0, 3.0, 3.0, 3.0, 2.0, 1.0] Drakes in year 36 \n", + "[5.0, 4.0, 3.0, 3.0, 2.0, 1.0] Drakes in year 38 \n", + "[6.0, 4.0, 3.0, 3.0, 2.0, 1.0] Drakes in year 40 \n" + ] + } + ], + "source": [ + "drakes = Vector([10,0,0,0,0,0])\n", + "multiplier = leslie\n", + "for i in range(0,41,2):\n", + " print('{} Drakes in year {} '.format([drake//1 for drake in drakes.my_list], i))\n", + " drakes = leslie * drakes\n", + " \n" + ] + }, { "cell_type": "code", "execution_count": null, From 4ef671d4df3974bb7116b416e705272bc6304f6d Mon Sep 17 00:00:00 2001 From: John Waldrep Date: Wed, 13 May 2015 17:24:52 -0400 Subject: [PATCH 09/13] Update readme.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 97f003e..e0382d4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +This module may be used with "import matrix_math" within a python script. +test_matrix_math.py is nose and py.test compatible (run with 'nosetests' or 'py.test' respectively) + +=========================== # Vector and Matrix Objects ## Description From 485c24b2909f12451cd6ee812d28bca45edc1452 Mon Sep 17 00:00:00 2001 From: John Waldrep Date: Wed, 13 May 2015 18:24:13 -0400 Subject: [PATCH 10/13] Add class method create accepting size and function and test; 26/26 passing --- README.md | 1 + matrix_math.py | 4 ++++ test_matrix_math.py | 14 ++++++++++++++ 3 files changed, 19 insertions(+) diff --git a/README.md b/README.md index e0382d4..4fbe5e3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ This module may be used with "import matrix_math" within a python script. + test_matrix_math.py is nose and py.test compatible (run with 'nosetests' or 'py.test' respectively) =========================== diff --git a/matrix_math.py b/matrix_math.py index 4fa4d23..242926c 100644 --- a/matrix_math.py +++ b/matrix_math.py @@ -53,6 +53,10 @@ def __init__(self, my_list): self.my_list = my_list self.my_shape = self.shape() + @classmethod + def create(cls, size=(2,2), fn = lambda x,y: 1): + return Matrix([[fn(i,j) for j in range(size[0])] for i in range(size[1])]) + def __add__(self, other): if self.shape() != other.shape(): raise ShapeException diff --git a/test_matrix_math.py b/test_matrix_math.py index 9bca418..2c27cc0 100644 --- a/test_matrix_math.py +++ b/test_matrix_math.py @@ -1,6 +1,7 @@ from matrix_math import * import math from nose.tools import raises +import random def test_matrix_class(): m = Matrix([[0, 1, 0], [1, 0, 0], [0, 0, 1]]) @@ -212,3 +213,16 @@ def test_hard_mode_matrix(): Matrix([[1, 1, 1], [0, 0, 0]]) * Matrix([[1, 1], [2, 2], [3, 3]]) assert not Matrix([[0, 1], [1, 0]]) == Matrix([[1, 1], [0, 0]]) # results in False + +def test_create_class_method(): + m1 = Matrix.create() + assert m1 == Matrix([[1,1],[1,1]]) + m2 = Matrix.create((3,3), lambda x,y: x+y) + assert m2 == Matrix([[0,1,2],[1,2,3],[2,3,4]]) + random.seed(0) + def rnd(x,y): + return random.randint(0,9) + m3 = Matrix.create((3,2), rnd) + assert m3 != Matrix([[6,6],[0,4],[8,7]]) + m2 = Matrix.create((3,3), lambda x,y: x**2+y**2) + assert m2 == Matrix([[0,1,4],[1,2,5],[4,5,8]]) From 49c81ce00250492a29ac9045ee95861155c4bf44 Mon Sep 17 00:00:00 2001 From: John Waldrep Date: Wed, 13 May 2015 23:50:46 -0400 Subject: [PATCH 11/13] Add rotate Matrix rotate() method; fix issue with Matrix.create() built with tuples instead of lists; 27/27 tests pass --- matrix_math.py | 11 ++++-- test_matrix_math.py | 91 +++++++++++++++++++++++++++++---------------- 2 files changed, 67 insertions(+), 35 deletions(-) diff --git a/matrix_math.py b/matrix_math.py index 242926c..fb3a97c 100644 --- a/matrix_math.py +++ b/matrix_math.py @@ -54,8 +54,9 @@ def __init__(self, my_list): self.my_shape = self.shape() @classmethod - def create(cls, size=(2,2), fn = lambda x,y: 1): - return Matrix([[fn(i,j) for j in range(size[0])] for i in range(size[1])]) + def create(cls, size=(2, 2), fn = lambda x, y: 1): + return Matrix([[fn(i, j) for j in range(size[0])] + for i in range(size[1])]) def __add__(self, other): if self.shape() != other.shape(): @@ -96,10 +97,14 @@ def __eq__(self, other): return self.my_list == other.my_list def __repr__(self): - return 'Vector({})'.format(self.my_list) + return 'Matrix({})'.format(self.my_list) def shape(self): try: return (len(self.my_list), len(self.my_list[0])) except (TypeError, IndexError): raise ValueError('Not a 2d matrix') + + def rotate(self): + tuple_list = list(zip(*self.my_list[::-1])) + return Matrix([list(row) for row in tuple_list]) diff --git a/test_matrix_math.py b/test_matrix_math.py index 2c27cc0..ea7d364 100644 --- a/test_matrix_math.py +++ b/test_matrix_math.py @@ -1,25 +1,33 @@ -from matrix_math import * +from matrix_math import Matrix, Vector, ShapeException import math from nose.tools import raises import random -def test_matrix_class(): - m = Matrix([[0, 1, 0], [1, 0, 0], [0, 0, 1]]) def test_vector_class(): v = Vector([1, 5]) + assert isinstance(v, Vector) + + +def test_matrix_class(): + m = Matrix([[0, 1, 0], [1, 0, 0], [0, 0, 1]]) + assert isinstance(m, Matrix) + @raises(ValueError) def test_2d_vector_exception(): v = Vector([[0, 1, 0], [1, 0, 0], [0, 0, 1]]) + @raises(ValueError) def test_empty_vector_exception(): v = Vector([]) + @raises(ValueError) def test_1d_matrix_exception(): - m = Matrix([1,5]) + m = Matrix([1, 5]) + @raises(ValueError) def test_empty_matrix_exception(): @@ -36,33 +44,38 @@ def test_empty_matrix_exception(): z = Vector([0, 0, 0]) A = Matrix([[1, 0, 0], - [0, 1, 0], - [0, 0, 1]]) + [0, 1, 0], + [0, 0, 1]]) B = Matrix([[1, 2, 3], - [4, 5, 6], - [7, 8, 9]]) + [4, 5, 6], + [7, 8, 9]]) C = Matrix([[1, 2], - [2, 1], - [1, 2]]) + [2, 1], + [1, 2]]) D = Matrix([[1, 2, 3], - [3, 2, 1]]) + [3, 2, 1]]) + def test_matrix_class_and_shape(): assert m.shape() == (2,) assert v.shape() == (3,) + def test_matrix_shape(): m = Matrix([[0, 1, 0], [1, 0, 0], [0, 0, 1]]) - assert m.shape() == (3,3) - m = Matrix([[1,2],[3,4],[5,6]]) - assert m.shape() == (3,2) + assert m.shape() == (3, 3) + m = Matrix([[1, 2], [3, 4], [5, 6]]) + assert m.shape() == (3, 2) assert A.shape() == (3, 3) assert C.shape() == (3, 2) assert D.shape() == (2, 3) + + def test_vector_eq(): - p = Vector([3,4]) + p = Vector([3, 4]) assert m == p + def test_vector_add(): """ [a b] + [c d] = [a+c b+d] @@ -73,9 +86,11 @@ def test_vector_add(): assert u + y == Vector([11, 21, 31]) assert u + z == u + def test_vector_add_is_communicative(): assert w + y == y + w + def test_vector_sub(): """ [a b] - [c d] = [a-c b-d] @@ -87,12 +102,13 @@ def test_vector_sub(): assert y - z == y assert w - u == (z - (u - w)) + def test_hard_mode_vector(): Vector([1, 2]) + Vector([0, 4]) Vector([1, 2]) - Vector([0, 4]) Vector([1, 2]) * 3 - assert Vector([1, 2]) == Vector([1, 2]) # results in True + assert Vector([1, 2]) == Vector([1, 2]) # results in True @raises(ShapeException) @@ -100,6 +116,7 @@ def test_vector_sub_checks_shapes(): """Shape rule: the vectors must be the same size.""" m - v + def test_dot(): """ dot([a b], [c d]) = a * c + b * d @@ -126,6 +143,7 @@ def test_vector_multiply(): assert v * 0.5 == Vector([0.5, 1.5, 0]) assert m * 2 == Vector([6, 8]) + def test_magnitude(): """ magnitude([a b]) = sqrt(a^2 + b^2) @@ -138,8 +156,6 @@ def test_magnitude(): assert z.magnitude() == 0 - - def test_shape_matrices(): """shape should take a vector or matrix and return a tuple with the number of rows (for a vector) or the number of rows and columns @@ -147,6 +163,8 @@ def test_shape_matrices(): assert A.shape() == (3, 3) assert C.shape() == (3, 2) assert D.shape() == (2, 3) + + def test_matrix_scalar_multiply(): """ [[a b] * Z = [[a*Z b*Z] @@ -190,11 +208,11 @@ def test_matrix_matrix_multiply(): """ assert A * B == B assert B * C == Matrix([[8, 10], - [20, 25], - [32, 40]]) + [20, 25], + [32, 40]]) assert C * D == Matrix([[7, 6, 5], - [5, 6, 7], - [7, 6, 5]]) + [5, 6, 7], + [7, 6, 5]]) assert D * C == Matrix([[8, 10], [8, 10]]) @@ -212,17 +230,26 @@ def test_hard_mode_matrix(): Matrix([[0, 1], [1, 0]]) * Vector([1, 2]) Matrix([[1, 1, 1], [0, 0, 0]]) * Matrix([[1, 1], [2, 2], [3, 3]]) - assert not Matrix([[0, 1], [1, 0]]) == Matrix([[1, 1], [0, 0]]) # results in False + assert not Matrix([[0, 1], [1, 0]]) == Matrix([[1, 1], [0, 0]]) + # results in False + def test_create_class_method(): m1 = Matrix.create() - assert m1 == Matrix([[1,1],[1,1]]) - m2 = Matrix.create((3,3), lambda x,y: x+y) - assert m2 == Matrix([[0,1,2],[1,2,3],[2,3,4]]) + assert m1 == Matrix([[1, 1], [1, 1]]) + m2 = Matrix.create((3, 3), lambda x, y: x + y) + assert m2 == Matrix([[0, 1, 2], [1, 2, 3], [2, 3, 4]]) random.seed(0) - def rnd(x,y): - return random.randint(0,9) - m3 = Matrix.create((3,2), rnd) - assert m3 != Matrix([[6,6],[0,4],[8,7]]) - m2 = Matrix.create((3,3), lambda x,y: x**2+y**2) - assert m2 == Matrix([[0,1,4],[1,2,5],[4,5,8]]) + + def rnd(x, y): + return random.randint(0, 9) + m3 = Matrix.create((3, 2), rnd) + assert m3 != Matrix([[6, 6], [0, 4], [8, 7]]) + m2 = Matrix.create((3, 3), lambda x, y: x ** 2 + y ** 2) + assert m2 == Matrix([[0, 1, 4], [1, 2, 5], [4, 5, 8]]) + + +def test_matrix_rotate(): + assert A.rotate() == Matrix([[0, 0, 1], + [0, 1, 0], + [1, 0, 0]]) From 7da0b9a547986971b490f0aefc44cad920e69be5 Mon Sep 17 00:00:00 2001 From: John Waldrep Date: Wed, 13 May 2015 23:51:50 -0400 Subject: [PATCH 12/13] Add shell script to run coverage with html option -- shows 93% coverage --- nt-coverage.sh | 2 ++ 1 file changed, 2 insertions(+) create mode 100755 nt-coverage.sh diff --git a/nt-coverage.sh b/nt-coverage.sh new file mode 100755 index 0000000..c49e516 --- /dev/null +++ b/nt-coverage.sh @@ -0,0 +1,2 @@ +nosetests --with-coverage --cover-html --cover-package=matrix_math +open cover/index.html From 9fb38b4e7c62a6bc06dbeae0b1b901d34d3a10de Mon Sep 17 00:00:00 2001 From: John Waldrep Date: Thu, 14 May 2015 06:56:20 -0400 Subject: [PATCH 13/13] Hard Mode with 100% coverage; 32/32 tests pass --- matrix_math.py | 6 ++++-- test_matrix_math.py | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/matrix_math.py b/matrix_math.py index fb3a97c..efc79d5 100644 --- a/matrix_math.py +++ b/matrix_math.py @@ -21,7 +21,8 @@ def __mul__(self, other): if type(other) is int or type(other) is float: return Vector([i * other for i in self.my_list]) else: - return None + raise ValueError('Vector can only be multiplied by \ + scalar (int/float)') def __eq__(self, other): return self.my_list == other.my_list @@ -91,7 +92,8 @@ def __mul__(self, other): for col in y_transposed] for row in self.my_list]) else: - return None + raise ValueError('Matrix can only be multiplied by ' + 'scalar, Vector, or Matrix') def __eq__(self, other): return self.my_list == other.my_list diff --git a/test_matrix_math.py b/test_matrix_math.py index ea7d364..5b9cd39 100644 --- a/test_matrix_math.py +++ b/test_matrix_math.py @@ -253,3 +253,26 @@ def test_matrix_rotate(): assert A.rotate() == Matrix([[0, 0, 1], [0, 1, 0], [1, 0, 0]]) + + +# Add tests to get coverage > 93% +@raises(ValueError) +def test_vector_bad_multiplication_is_none(): + assert m * '1' is None + +@raises(ValueError) +def test_matrix_bad_multiplication_is_none(): + assert A * '1' is None + +def test_vector_matrix_repr(): + assert 'Vector(' in repr(m) + assert 'Matrix(' in repr(A) + +@raises(ShapeException) +def test_vector_shape_exceptions_add(): + m + v + + +@raises(ShapeException) +def test_matrix_shape_exceptions_add(): + A + C