From 259cb3f54b90d3d27af083603ef906569a2e4e97 Mon Sep 17 00:00:00 2001 From: Niklas Birk Date: Sun, 28 Apr 2024 00:52:25 +0200 Subject: [PATCH] Major refactoring to lambdas and list comprehension, because those can be faster --- src/matrix.py | 96 +++++++++++++---------------------------------- src/matrix_mpi.py | 2 +- src/vector.py | 27 +++++-------- 3 files changed, 36 insertions(+), 89 deletions(-) diff --git a/src/matrix.py b/src/matrix.py index a5e776c..522a998 100644 --- a/src/matrix.py +++ b/src/matrix.py @@ -85,11 +85,8 @@ class Matrix: @staticmethod def flatten_internal(matrices): - flattened_data = [] - rows = 0 - for matrix in matrices: - flattened_data.extend(matrix.get_data()) - rows += matrix.__shape__[0] + flattened_data = [element for matrix in matrices for row in matrix.get_data() for element in row] + rows = sum(matrix.__shape__[0] for matrix in matrices) cols = matrices[0].__shape__[1] return flattened_data, (rows, cols) @@ -114,13 +111,8 @@ class Matrix: return self.__shape__ def __transpose_internal__(self): - rows = self.__shape__[0] - cols = self.__shape__[1] - transposed_data = [([0] * 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) + rows, cols = self.__shape__ + return [[self.__data__[i][j] for i in range(rows)] for j in range(cols)], (cols, rows) def transpose(self): """ @@ -144,6 +136,9 @@ class Matrix: :param other: The object to compare to; must be either a ``Matrix``, a ``list`` or a ``numpy.ndarray`` :return: True if data in the matrix are equal to the given data in other for each component, otherwise False """ + if not isinstance(other, (Matrix, list, numpy.ndarray)): + raise ValueError("Matrix type is not comparable to type of given ``other``") + data_to_compare = other if isinstance(other, Matrix): if self.__shape__ != other.__shape__: return False @@ -154,45 +149,33 @@ class Matrix: return False elif isinstance(other, numpy.ndarray): data_to_compare = other.tolist() - else: - raise ValueError("Matrix type is not comparable to type of given ``other``") - - for i in range(len(self.__data__)): - for j in range(len(self.__data__[i])): - if self.__data__[i][j] != data_to_compare[i][j]: - return False - return True + return all(value == other_value + for row, other_row in zip(self.__data__, data_to_compare) + for value, other_value in zip(row, other_row)) def __str__(self): return str(numpy.array(self.__data__)) def __neg_internal__(self): - rows = range(self.__shape__[0]) - cols = range(self.__shape__[1]) - return [[-(self.__data__[i][j]) for j in cols] for i in rows] + return list(map(lambda row: [-value for value in row], self.__data__)) def __neg__(self): return Matrix(self.__neg_internal__(), self.__shape__) def __add_matrix_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)] + return [list(map(sum, zip(*rows))) for rows in zip(self.__data__, other.__data__)] def __add_scalar_internal__(self, other): - rows = self.__shape__[0] - cols = self.__shape__[1] - return [[(self.__data__[i][j] + other) for j in range(cols)] for i in range(rows)] + return [[value + other for value in row] for row in self.__data__] def __add__(self, other): + if not isinstance(other, (Matrix, int, float)): + raise ValueError("Only a number or another ``Matrix`` can be added to a ``Matrix``") if isinstance(other, Matrix): if self.__shape__ != other.__shape__: raise ValueError("The shape of the operands must be the same") return Matrix(self.__add_matrix_internal__(other), self.__shape__) - elif isinstance(other, int) or isinstance(other, float): - return Matrix(self.__add_scalar_internal__(other), self.__shape__) - else: - raise ValueError("Only a number or another ``Matrix`` can be added to a ``Matrix``") + return Matrix(self.__add_scalar_internal__(other), self.__shape__) def __radd__(self, other): return self + other @@ -204,28 +187,21 @@ class Matrix: return -self + other def __truediv_scalar_internal__(self, other): - rows = self.__shape__[0] - cols = self.__shape__[1] - return [[(self.__data__[i][j] / other) for j in range(cols)] for i in range(rows)] + return [list(map(lambda value: value / other, row)) for row in self.__data__] def __truediv__(self, other): - if isinstance(other, int) or isinstance(other, float): - return Matrix(self.__truediv_scalar_internal__(other), self.__shape__) - else: + if not isinstance(other, (int, float)): raise ValueError("A ``Matrix`` can only be divided ba a number") + return Matrix(self.__truediv_scalar_internal__(other), self.__shape__) def __mul_rowmatrix_matrix__internal__(self, other): - cols = other.__shape__[1] - new_data = [0] * cols - for i in range(cols): - new_data[i] = sum([self.__data__[0][j] * other.__data__[j][i] for j in range(self.__shape__[1])]) - return new_data + rows, cols = self.__shape__[1], other.__shape__[1] + return [sum(self.__data__[0][j] * other.__data__[j][i] for j in range(rows)) for i in range(cols)] def __mul_matrix_internal__(self, other): if self.__shape__[0] == 1: return self.__mul_rowmatrix_matrix__internal__(other) - rows = self.__shape__[0] - cols = other.__shape__[1] + rows, cols = self.__shape__[0], other.__shape__[1] new_data = [([0] * cols) for _ in range(rows)] for i in range(rows): for k in range(cols): @@ -233,9 +209,7 @@ class Matrix: return new_data def __mul_scalar_internal__(self, other): - 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 [list(map(lambda value: value * other, row)) for row in self.__data__] def __mul__(self, other): if isinstance(other, Matrix): @@ -255,31 +229,13 @@ class Matrix: return self.__abs_sum_of_squares__() def __abs_sum_of_squares__(self): - rows = self.__shape__[0] - cols = self.__shape__[1] - abs_sum = 0 - for i in range(rows): - for j in range(cols): - abs_sum += abs(self.__data__[i][j]) ** 2 - return abs_sum + return sum(abs(element) ** 2 for row in self.__data__ for element in row) def __col_sums__(self): - rows = self.__shape__[0] - cols = self.__shape__[1] - col_sums = [0] * cols - for j in range(cols): - for i in range(rows): - col_sums[j] += abs(self.__data__[i][j]) - return col_sums + return [sum(abs(row[j]) for row in self.__data__) for j in range(self.__shape__[1])] def __row_sums__(self): - rows = self.__shape__[0] - cols = self.__shape__[1] - row_sums = [0] * rows - for i in range(rows): - for j in range(cols): - row_sums[i] += abs(self.__data__[i][j]) - return row_sums + return [sum(abs(value) for value in row) for row in self.__data__] def norm(self, f: str = "frobenius"): """ diff --git a/src/matrix_mpi.py b/src/matrix_mpi.py index c23f53f..eb247d4 100644 --- a/src/matrix_mpi.py +++ b/src/matrix_mpi.py @@ -108,7 +108,7 @@ class MatrixMPI: :return: True if data in the matrix are equal to the given data in other for each component, otherwise False """ if isinstance(other, MatrixMPI): - return self.__data__ == other.__data__ + return all(self.__mpi_comm__.allgather(self.__rank_subdata__ == other.__rank_subdata__)) else: return self.__data__ == other diff --git a/src/vector.py b/src/vector.py index ad7455a..23016d9 100644 --- a/src/vector.py +++ b/src/vector.py @@ -80,8 +80,7 @@ class Vector(Matrix): 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] + rows, cols = self.__shape__ if rows >= cols: new_data = [(self.__data__[i][0] * other.__data__[i][0]) for i in range(rows)] else: @@ -89,8 +88,7 @@ class Vector(Matrix): return new_data def __mul_tensor_internal__(self, other): - rows = self.__shape__[0] - cols = other.__shape__[1] + rows, cols = self.__shape__[0], 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): @@ -112,31 +110,24 @@ class Vector(Matrix): "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 + rows, vector_rows = other.__shape__[0], self.__shape__[0] + return [sum([other.__data__[i][j] * self.__data__[j][0] for j in range(vector_rows)]) for i in range(rows)] def __rmul__(self, other): - if isinstance(other, Matrix): - return Vector(self.__mul_matrix_vector_internal__(other)) - return self * other + return Vector(self.__mul_matrix_vector_internal__(other)) if isinstance(other, Matrix) else self * other def __truediv_vector_internal__(self, other): - rows = self.__shape__[0] - cols = self.__shape__[1] + rows, cols = self.__shape__ return [[(self.__data__[i][j] / other.__data__[i][j]) for j in range(cols)] for i in range(rows)] def __truediv__(self, other): + if not isinstance(other, (Vector, int, float)): + raise ValueError("A ``Vector`` can only be divided ba a number or another same-shaped ``Vector``") 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``") + return Vector(super().__truediv_scalar_internal__(other)) def norm(self, **kwargs): """