import numpy from matrix import Matrix class Vector(Matrix): 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): self.__init__(data.tolist()) elif isinstance(data, list): 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``, a ``numpy.ndarray`` or an integer for dimension") 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: 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.__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] 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_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 __mul_matrix_vector_internal__(self, other): rows = other.__shape__[0] new_data = [0] * rows for i in range(rows): new_data[i] = sum([other.__data__[i][j] * self.__data__[j][0] for j in range(self.__shape__[0])]) return new_data def __rmul__(self, other): if isinstance(other, Matrix): return Vector(self.__mul_matrix_vector_internal__(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. :param kwargs: ignored :return: the 2-norm of the vector """ return super().norm() def normalize(self): """ A normalized vector has the length (norm) 1. To achieve that the vector is divided by the norm of itself. :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__