Finish vector.py;
Adjust matrix.py for use of vector.py as subclass; Adjust vector.py and matrix.py to match test_serial.py
This commit is contained in:
parent
91f4191a65
commit
3ec62d99f7
@ -92,20 +92,21 @@ class Matrix:
|
|||||||
"""
|
"""
|
||||||
return self.__shape__
|
return self.__shape__
|
||||||
|
|
||||||
|
def __transpose_internal__(self):
|
||||||
|
rows = self.__shape__[0]
|
||||||
|
cols = self.__shape__[1]
|
||||||
|
transposed_data = [[0 for _ in range(rows)] for _ in range(cols)]
|
||||||
|
for i in range(rows):
|
||||||
|
for j in range(cols):
|
||||||
|
transposed_data[j][i] = self.__data__[i][j]
|
||||||
|
return transposed_data, (cols, rows)
|
||||||
|
|
||||||
def transpose(self):
|
def transpose(self):
|
||||||
"""
|
"""
|
||||||
:return: the transpose of the matrix
|
:return: the transpose of the matrix
|
||||||
"""
|
"""
|
||||||
rows = self.__shape__[0]
|
transposed_data, shape = self.__transpose_internal__()
|
||||||
cols = self.__shape__[1]
|
return Matrix(transposed_data, shape)
|
||||||
|
|
||||||
transposed_data = [[0 for _ in range(rows)] for _ in range(cols)]
|
|
||||||
|
|
||||||
for i in range(rows):
|
|
||||||
for j in range(cols):
|
|
||||||
transposed_data[j][i] = self.__data__[i][j]
|
|
||||||
|
|
||||||
return Matrix(transposed_data, (cols, rows))
|
|
||||||
|
|
||||||
def T(self):
|
def T(self):
|
||||||
"""
|
"""
|
||||||
@ -123,9 +124,9 @@ class Matrix:
|
|||||||
:return: True if data in the matrix are equal to the given data in other for each component, otherwise False
|
:return: True if data in the matrix are equal to the given data in other for each component, otherwise False
|
||||||
"""
|
"""
|
||||||
if isinstance(other, Matrix):
|
if isinstance(other, Matrix):
|
||||||
data_to_compare = other.__data__
|
|
||||||
if self.__shape__ != other.__shape__:
|
if self.__shape__ != other.__shape__:
|
||||||
return False
|
return False
|
||||||
|
data_to_compare = other.__data__
|
||||||
elif isinstance(other, list):
|
elif isinstance(other, list):
|
||||||
data_to_compare = other
|
data_to_compare = other
|
||||||
if self.__shape__[0] != len(other) or self.__shape__[1] != len(other[0]):
|
if self.__shape__[0] != len(other) or self.__shape__[1] != len(other[0]):
|
||||||
@ -144,10 +145,13 @@ class Matrix:
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(numpy.array(self.__data__))
|
return str(numpy.array(self.__data__))
|
||||||
|
|
||||||
def __neg__(self):
|
def __neg_internal__(self):
|
||||||
rows = range(self.__shape__[0])
|
rows = range(self.__shape__[0])
|
||||||
cols = range(self.__shape__[1])
|
cols = range(self.__shape__[1])
|
||||||
return Matrix([[-(self.__data__[i][j]) for j in cols] for i in rows], self.__shape__)
|
return [[-(self.__data__[i][j]) for j in cols] for i in rows]
|
||||||
|
|
||||||
|
def __neg__(self):
|
||||||
|
return Matrix(self.__neg_internal__(), self.__shape__)
|
||||||
|
|
||||||
def __add_matrix_internal__(self, other):
|
def __add_matrix_internal__(self, other):
|
||||||
rows = self.__shape__[0]
|
rows = self.__shape__[0]
|
||||||
@ -201,8 +205,8 @@ class Matrix:
|
|||||||
return new_data
|
return new_data
|
||||||
|
|
||||||
def __mul_scalar_internal__(self, other):
|
def __mul_scalar_internal__(self, other):
|
||||||
cols = range(self.__shape__[1])
|
|
||||||
rows = range(self.__shape__[0])
|
rows = range(self.__shape__[0])
|
||||||
|
cols = range(self.__shape__[1])
|
||||||
return [[(self.__data__[i][j] * other) for j in cols] for i in rows]
|
return [[(self.__data__[i][j] * other) for j in cols] for i in rows]
|
||||||
|
|
||||||
def __mul__(self, other):
|
def __mul__(self, other):
|
||||||
|
118
src/vector.py
118
src/vector.py
@ -4,39 +4,118 @@ from matrix import Matrix
|
|||||||
|
|
||||||
|
|
||||||
class Vector(Matrix):
|
class Vector(Matrix):
|
||||||
def __init__(self, data):
|
def __init__(self, data=None, shape=None):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
:type data: numpy.ndarray | list | int
|
:type data: numpy.ndarray | list | int
|
||||||
|
:type shape: (int, int)
|
||||||
"""
|
"""
|
||||||
|
if shape is None and not isinstance(data, int):
|
||||||
|
shape = (len(data), 1)
|
||||||
|
|
||||||
if isinstance(data, numpy.ndarray):
|
if isinstance(data, numpy.ndarray):
|
||||||
super().__init__(data)
|
self.__init__(data.tolist())
|
||||||
elif isinstance(data, list):
|
elif isinstance(data, list):
|
||||||
super().__init__(data, (len(data), 1))
|
if len(data) == 1 and isinstance(data[0], list):
|
||||||
|
shape = (1, len(data[0]))
|
||||||
|
super().__init__(data, shape)
|
||||||
elif isinstance(data, int):
|
elif isinstance(data, int):
|
||||||
self.__init__([0] * data)
|
self.__init__([0] * data)
|
||||||
else:
|
else:
|
||||||
raise ValueError("data must be a list or an integer for dimension")
|
raise ValueError("data must be a ``list``, a ``numpy.ndarray`` or an integer for dimension")
|
||||||
|
|
||||||
def get_dimension(self):
|
def __eq__(self, other):
|
||||||
return super().shape()[0]
|
"""
|
||||||
|
Return ``self==value``
|
||||||
|
|
||||||
|
:param other: The object to compare to; must be either a ``Vector``, a ``list`` or a ``numpy.ndarray``
|
||||||
|
:return: True if data in the same-shaped vectors are equal to the given data in other for each component otherwise False
|
||||||
|
"""
|
||||||
|
if isinstance(other, Vector):
|
||||||
|
if self.__shape__ != other.__shape__:
|
||||||
|
return False
|
||||||
|
data_to_compare = numpy.array(other.__data__).flatten().tolist()
|
||||||
|
elif isinstance(other, list):
|
||||||
|
data_to_compare = numpy.array(other).flatten().tolist()
|
||||||
|
elif isinstance(other, numpy.ndarray):
|
||||||
|
data_to_compare = other.flatten().tolist()
|
||||||
|
else:
|
||||||
|
raise ValueError("Vector type is not comparable to type of given ``other``")
|
||||||
|
return numpy.array_equal(data_to_compare, numpy.array(self.__data__).flatten().tolist())
|
||||||
|
|
||||||
def transpose(self):
|
def transpose(self):
|
||||||
return Vector(self.__data__.reshape(self.__shape__[1], self.__shape__[0]))
|
"""
|
||||||
|
:return: the transpose of the vector
|
||||||
|
"""
|
||||||
|
transposed_data, shape = super().__transpose_internal__()
|
||||||
|
return Vector(transposed_data, shape)
|
||||||
|
|
||||||
|
def T(self):
|
||||||
|
return self.transpose()
|
||||||
|
|
||||||
|
def __neg__(self):
|
||||||
|
return Vector(super().__neg_internal__(), self.__shape__)
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
if isinstance(other, Vector):
|
||||||
|
if self.__shape__ != other.__shape__:
|
||||||
|
raise ValueError("The shape of the operands must be the same")
|
||||||
|
return Vector(super().__add_matrix_internal__(other), self.__shape__)
|
||||||
|
elif isinstance(other, int) or isinstance(other, float):
|
||||||
|
return Vector(super().__add_scalar_internal__(other), self.__shape__)
|
||||||
|
else:
|
||||||
|
raise ValueError("Only a number or another ``Vector`` can be added to a ``Vector``")
|
||||||
|
|
||||||
|
def __mul_vector_same_shape_internal__(self, other):
|
||||||
|
rows = self.__shape__[0]
|
||||||
|
cols = self.__shape__[1]
|
||||||
|
if rows >= cols:
|
||||||
|
new_data = [(self.__data__[i][0] * other.__data__[i][0]) for i in range(rows)]
|
||||||
|
else:
|
||||||
|
new_data = [(self.__data__[0][j] * other.__data__[0][j]) for j in range(cols)]
|
||||||
|
return new_data
|
||||||
|
|
||||||
|
def __mul_tensor_internal__(self, other):
|
||||||
|
rows = self.__shape__[0]
|
||||||
|
cols = other.__shape__[1]
|
||||||
|
return [[self.__data__[i][0] * other.__data__[0][j] for j in range(cols)] for i in range(rows)], (rows, cols)
|
||||||
|
|
||||||
def __mul__(self, other):
|
def __mul__(self, other):
|
||||||
if isinstance(other, Vector):
|
if isinstance(other, Vector):
|
||||||
if self.shape() == other.shape():
|
if self.__shape__ == other.__shape__:
|
||||||
return Vector(self.__data__ * other.__data__)
|
return Vector(self.__mul_vector_same_shape_internal__(other))
|
||||||
return super().__mul__(other)[0][0]
|
elif self.__shape__ == tuple(reversed(other.__shape__)):
|
||||||
elif isinstance(other, int) or isinstance(other, float):
|
if self.__shape__[0] == 1: # Case (_ ... _) * (_\n...\n_) = scalar
|
||||||
return Vector(super().__mul__(other).__data__)
|
return super().__mul_matrix_internal__(other)[0][0]
|
||||||
|
else: # Case (_\n...\n_) * (_ ... _) = Matrix
|
||||||
|
new_data, shape = self.__mul_tensor_internal__(other)
|
||||||
|
return Matrix(new_data, shape)
|
||||||
else:
|
else:
|
||||||
raise ValueError("A vector can only be multiplied with an vector (dot product) or a scalar")
|
raise ValueError("The shapes of the operands must be the compatible")
|
||||||
|
elif isinstance(other, int) or isinstance(other, float):
|
||||||
|
return Vector(super().__mul_scalar_internal__(other))
|
||||||
|
else:
|
||||||
|
raise ValueError("A ``Vector`` can only be multiplied with an ``Vector`` (dot product or tensor),"
|
||||||
|
"a compatible ``Matrix`` or a scalar")
|
||||||
|
|
||||||
def __rmul__(self, other):
|
def __rmul__(self, other):
|
||||||
return self * other
|
return self * other
|
||||||
|
|
||||||
|
def __truediv_vector_internal__(self, other):
|
||||||
|
rows = self.__shape__[0]
|
||||||
|
cols = self.__shape__[1]
|
||||||
|
return [[(self.__data__[i][j] / other.__data__[i][j]) for j in range(cols)] for i in range(rows)]
|
||||||
|
|
||||||
|
def __truediv__(self, other):
|
||||||
|
if isinstance(other, Vector):
|
||||||
|
if self.__shape__ != other.__shape__:
|
||||||
|
raise ValueError("The ``Vector``s to be divided must have the same shape")
|
||||||
|
return Vector(self.__truediv_vector_internal__(other))
|
||||||
|
elif isinstance(other, int) or isinstance(other, float):
|
||||||
|
return Vector(super().__truediv_scalar_internal__(other))
|
||||||
|
else:
|
||||||
|
raise ValueError("A ``Vector`` can only be divided ba a number or another same-shaped ``Vector``")
|
||||||
|
|
||||||
def norm(self, **kwargs):
|
def norm(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Computes the 2-norm of the vector which is the Frobenius-Norm of a nx1 matrix.
|
Computes the 2-norm of the vector which is the Frobenius-Norm of a nx1 matrix.
|
||||||
@ -54,3 +133,16 @@ class Vector(Matrix):
|
|||||||
:return: the normalized vector
|
:return: the normalized vector
|
||||||
"""
|
"""
|
||||||
return self / self.norm()
|
return self / self.norm()
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
if isinstance(key, tuple):
|
||||||
|
return numpy.array(self.__data__).flatten()[[key]].tolist()
|
||||||
|
return numpy.array(self.__data__).flatten()[key].tolist()
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
manipulated_data = numpy.array(self.__data__).flatten()
|
||||||
|
if isinstance(key, tuple):
|
||||||
|
manipulated_data[[key]] = value
|
||||||
|
else:
|
||||||
|
manipulated_data[key] = value
|
||||||
|
self.__data__ = Vector(manipulated_data.tolist(), self.__shape__).__data__
|
||||||
|
@ -93,7 +93,8 @@ print("End 1f\n")
|
|||||||
print("Start 1g norm")
|
print("Start 1g norm")
|
||||||
# intitialization
|
# intitialization
|
||||||
a = Vector([1, 3, 5, 7, 9])
|
a = Vector([1, 3, 5, 7, 9])
|
||||||
a_norm, a_normalized = a.normalize()
|
a_norm = a.norm()
|
||||||
|
a_normalized = a.normalize()
|
||||||
print(f"a_norm = {a_norm} | must be {np.linalg.norm([1, 3, 5, 7, 9])}")
|
print(f"a_norm = {a_norm} | must be {np.linalg.norm([1, 3, 5, 7, 9])}")
|
||||||
print(f"a_normalize = {str(a_normalized)} | must be {np.array([1, 3, 5, 7, 9]) / np.linalg.norm([1, 3, 5, 7, 9])}")
|
print(f"a_normalize = {str(a_normalized)} | must be {np.array([1, 3, 5, 7, 9]) / np.linalg.norm([1, 3, 5, 7, 9])}")
|
||||||
print("End 1g\n")
|
print("End 1g\n")
|
||||||
@ -111,7 +112,7 @@ print("Start 1i manipulation")
|
|||||||
# intitialization
|
# intitialization
|
||||||
a = Vector([1, 3, 5, 7, 9])
|
a = Vector([1, 3, 5, 7, 9])
|
||||||
print(
|
print(
|
||||||
f"a[{str([1, 2, 4])}] = {str(a[1, 2, 4].reshape(3, ))} | must be {np.array([1, 3, 5, 7, 9]).reshape(5, 1)[np.array([1, 2, 4])].reshape(3, )}")
|
f"a[{str([1, 2, 4])}] = {str(np.array(a[1, 2, 4]).reshape(3, ))} | must be {np.array([1, 3, 5, 7, 9]).reshape(5, 1)[np.array([1, 2, 4])].reshape(3, )}")
|
||||||
a[1, 2, 4] = [-1, -1, -1]
|
a[1, 2, 4] = [-1, -1, -1]
|
||||||
print(f"a = {str(a)} | must be {np.array([1, -1, -1, 5, 7, -1])}")
|
print(f"a = {str(a)} | must be {np.array([1, -1, -1, 5, 7, -1])}")
|
||||||
print("End 1i\n")
|
print("End 1i\n")
|
||||||
|
@ -4,12 +4,6 @@ from vector import Vector
|
|||||||
|
|
||||||
|
|
||||||
class TestVector(TestCase):
|
class TestVector(TestCase):
|
||||||
def test_should_create_vector_dim_5(self):
|
|
||||||
actual = Vector(5).get_dimension()
|
|
||||||
expected = 5
|
|
||||||
|
|
||||||
self.assertEqual(expected, actual)
|
|
||||||
|
|
||||||
def test_should_create_zero_vector(self):
|
def test_should_create_zero_vector(self):
|
||||||
actual = Vector(5)
|
actual = Vector(5)
|
||||||
expected = Vector([0, 0, 0, 0, 0])
|
expected = Vector([0, 0, 0, 0, 0])
|
||||||
@ -22,7 +16,7 @@ class TestVector(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(expected, actual)
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
def test_should_transpose_vector(self):
|
def test_should_transpose_col_vector(self):
|
||||||
data = [1, 2, 3, 4, 5, 6]
|
data = [1, 2, 3, 4, 5, 6]
|
||||||
actual = Vector(data)
|
actual = Vector(data)
|
||||||
|
|
||||||
@ -30,6 +24,14 @@ class TestVector(TestCase):
|
|||||||
expected = [[1, 2, 3, 4, 5, 6]]
|
expected = [[1, 2, 3, 4, 5, 6]]
|
||||||
self.assertEqual(expected, actual)
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_should_transpose_row_vector(self):
|
||||||
|
data = [[1, 2, 3, 4, 5, 6]]
|
||||||
|
actual = Vector(data)
|
||||||
|
|
||||||
|
actual = actual.transpose()
|
||||||
|
expected = [[1], [2], [3], [4], [5], [6]]
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
def test_should_neg_vector(self):
|
def test_should_neg_vector(self):
|
||||||
v = Vector([1, 2])
|
v = Vector([1, 2])
|
||||||
|
|
||||||
@ -104,6 +106,24 @@ class TestVector(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(expected, actual)
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_should_truediv_scalar(self):
|
||||||
|
v = Vector([1, 2])
|
||||||
|
s = 5
|
||||||
|
|
||||||
|
expected = Vector([1/5, 2/5])
|
||||||
|
actual = v / s
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_should_truediv_same_shape_vectors(self):
|
||||||
|
v1 = Vector([1, 2])
|
||||||
|
v2 = Vector([3, 4])
|
||||||
|
|
||||||
|
expected = Vector([1/3, 1/2])
|
||||||
|
actual = v1 / v2
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
def test_should_mul_same_shape_vectors(self):
|
def test_should_mul_same_shape_vectors(self):
|
||||||
v1 = Vector([1, 2])
|
v1 = Vector([1, 2])
|
||||||
v2 = Vector([3, 4])
|
v2 = Vector([3, 4])
|
||||||
@ -122,6 +142,15 @@ class TestVector(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(expected, actual)
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_should_mul_vectors_tensor(self):
|
||||||
|
v1 = Vector([1, 2])
|
||||||
|
v2 = Vector([3, 4])
|
||||||
|
|
||||||
|
expected = [[3, 4], [6, 8]]
|
||||||
|
actual = v1 * v2.T()
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
def test_should_mul_scalar_with_vector(self):
|
def test_should_mul_scalar_with_vector(self):
|
||||||
v = Vector([1, 2])
|
v = Vector([1, 2])
|
||||||
s = 2
|
s = 2
|
||||||
@ -160,5 +189,89 @@ class TestVector(TestCase):
|
|||||||
actual = v.normalize()
|
actual = v.normalize()
|
||||||
expected = [1 / 2.236, 2 / 2.236]
|
expected = [1 / 2.236, 2 / 2.236]
|
||||||
|
|
||||||
self.assertAlmostEqual(expected[0], actual[0][0], 3)
|
self.assertAlmostEqual(expected[0], actual[0], 3)
|
||||||
self.assertAlmostEqual(expected[1], actual[1][0], 3)
|
self.assertAlmostEqual(expected[1], actual[1], 3)
|
||||||
|
|
||||||
|
def test_should_return_first_element_of_column_vector(self):
|
||||||
|
m = Vector([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||||
|
|
||||||
|
actual = m[0]
|
||||||
|
expected = 1
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_should_return_first_element_of_row_vector(self):
|
||||||
|
m = Vector([[1, 2, 3, 4, 5, 6, 7, 8, 9]])
|
||||||
|
|
||||||
|
actual = m[0]
|
||||||
|
expected = 1
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_should_return_last_element_of_column_vector(self):
|
||||||
|
m = Vector([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||||
|
|
||||||
|
actual = m[8]
|
||||||
|
expected = 9
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_should_return_last_element_of_row_vector(self):
|
||||||
|
m = Vector([[1, 2, 3, 4, 5, 6, 7, 8, 9]])
|
||||||
|
|
||||||
|
actual = m[8]
|
||||||
|
expected = 9
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_should_return_all_except_last_element(self):
|
||||||
|
m = Vector([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||||
|
|
||||||
|
actual = m[0:8]
|
||||||
|
expected = Vector([1, 2, 3, 4, 5, 6, 7, 8])
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_should_return_all_except_first_and_last_element(self):
|
||||||
|
m = Vector([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||||
|
|
||||||
|
actual = m[1:8]
|
||||||
|
expected = Vector([2, 3, 4, 5, 6, 7, 8])
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_should_return_some_element(self):
|
||||||
|
m = Vector([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||||
|
|
||||||
|
actual = m[0, 2, 4, 6]
|
||||||
|
expected = Vector([1, 3, 5, 7])
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_should_set_first_element(self):
|
||||||
|
m = Vector([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||||
|
|
||||||
|
m[0] = 10
|
||||||
|
actual = m
|
||||||
|
expected = Vector([10, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_should_set_all_except_first_and_last_element(self):
|
||||||
|
m = Vector([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||||
|
|
||||||
|
m[1:8] = [4, 4, 4, 4, 4, 4, 4]
|
||||||
|
actual = m
|
||||||
|
expected = Vector([1, 4, 4, 4, 4, 4, 4, 4, 9])
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_should_set_some_elements(self):
|
||||||
|
m = Vector([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||||
|
|
||||||
|
m[0, 2, 4, 6] = [10, 30, 50, 70]
|
||||||
|
actual = m
|
||||||
|
expected = Vector([10, 2, 30, 4, 50, 6, 70, 8, 9])
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user