1
0

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:
2024-02-22 02:21:05 +01:00
parent 91f4191a65
commit 3ec62d99f7
4 changed files with 249 additions and 39 deletions

View File

@ -92,20 +92,21 @@ class Matrix:
"""
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):
"""
:return: the transpose of the matrix
"""
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 Matrix(transposed_data, (cols, rows))
transposed_data, shape = self.__transpose_internal__()
return Matrix(transposed_data, shape)
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
"""
if isinstance(other, Matrix):
data_to_compare = other.__data__
if self.__shape__ != other.__shape__:
return False
data_to_compare = other.__data__
elif isinstance(other, list):
data_to_compare = other
if self.__shape__[0] != len(other) or self.__shape__[1] != len(other[0]):
@ -144,10 +145,13 @@ class Matrix:
def __str__(self):
return str(numpy.array(self.__data__))
def __neg__(self):
def __neg_internal__(self):
rows = range(self.__shape__[0])
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):
rows = self.__shape__[0]
@ -201,8 +205,8 @@ class Matrix:
return new_data
def __mul_scalar_internal__(self, other):
cols = range(self.__shape__[1])
rows = range(self.__shape__[0])
cols = range(self.__shape__[1])
return [[(self.__data__[i][j] * other) for j in cols] for i in rows]
def __mul__(self, other):

View File

@ -4,39 +4,118 @@ from matrix import Matrix
class Vector(Matrix):
def __init__(self, data):
def __init__(self, data=None, shape=None):
"""
: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):
super().__init__(data)
self.__init__(data.tolist())
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):
self.__init__([0] * data)
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):
return super().shape()[0]
def __eq__(self, other):
"""
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):
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):
if isinstance(other, Vector):
if self.shape() == other.shape():
return Vector(self.__data__ * other.__data__)
return super().__mul__(other)[0][0]
if self.__shape__ == other.__shape__:
return Vector(self.__mul_vector_same_shape_internal__(other))
elif self.__shape__ == tuple(reversed(other.__shape__)):
if self.__shape__[0] == 1: # Case (_ ... _) * (_\n...\n_) = scalar
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:
raise ValueError("The shapes of the operands must be the compatible")
elif isinstance(other, int) or isinstance(other, float):
return Vector(super().__mul__(other).__data__)
return Vector(super().__mul_scalar_internal__(other))
else:
raise ValueError("A vector can only be multiplied with an vector (dot product) or a scalar")
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):
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):
"""
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 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__