Add distributed computing for negation and addition (no memory distributing)
This commit is contained in:
		| @@ -84,6 +84,25 @@ class Matrix: | |||||||
|         """ |         """ | ||||||
|         return self.__data__ |         return self.__data__ | ||||||
|  |  | ||||||
|  |     @staticmethod | ||||||
|  |     def flatten(matrices: list): | ||||||
|  |         """ | ||||||
|  |         Flattens a list of matrices into one bigger matrix. | ||||||
|  |         The columns must match the first ``Matrix`` in the list and the rows can be arbitrarily. | ||||||
|  |  | ||||||
|  |         :param matrices: A list of matrices. | ||||||
|  |         :type matrices: list | ||||||
|  |         :return: A ``Matrix`` extended by all matrices in the list. | ||||||
|  |         :rtype: ``Matrix`` | ||||||
|  |         """ | ||||||
|  |         flattened_data = [] | ||||||
|  |         rows = 0 | ||||||
|  |         for matrix in matrices: | ||||||
|  |             flattened_data.extend(matrix.get_data()) | ||||||
|  |             rows += matrix.__shape__[0] | ||||||
|  |         cols = matrices[0].__shape__[1] | ||||||
|  |         return Matrix(flattened_data, (rows, cols)) | ||||||
|  |  | ||||||
|     def shape(self): |     def shape(self): | ||||||
|         """ |         """ | ||||||
|         :return: the shape of the matrix, which is ``(rows, columns)`` |         :return: the shape of the matrix, which is ``(rows, columns)`` | ||||||
| @@ -219,7 +238,7 @@ class Matrix: | |||||||
|     def __rmul__(self, other): |     def __rmul__(self, other): | ||||||
|         return self * other |         return self * other | ||||||
|  |  | ||||||
|     def __abs_sum__(self): |     def __abs_sum_of_squares__(self): | ||||||
|         rows = self.__shape__[0] |         rows = self.__shape__[0] | ||||||
|         cols = self.__shape__[1] |         cols = self.__shape__[1] | ||||||
|         abs_sum = 0 |         abs_sum = 0 | ||||||
| @@ -258,7 +277,7 @@ class Matrix: | |||||||
|         :return: the norm as a number |         :return: the norm as a number | ||||||
|         """ |         """ | ||||||
|         if f == "frobenius": |         if f == "frobenius": | ||||||
|             norm = math.sqrt(self.__abs_sum__()) |             norm = math.sqrt(self.__abs_sum_of_squares__()) | ||||||
|         elif f == "col sum": |         elif f == "col sum": | ||||||
|             norm = max(self.__col_sums__()) |             norm = max(self.__col_sums__()) | ||||||
|         elif f == "row sum": |         elif f == "row sum": | ||||||
|   | |||||||
| @@ -5,9 +5,9 @@ from matrix import Matrix | |||||||
|  |  | ||||||
|  |  | ||||||
| class MatrixMPI: | class MatrixMPI: | ||||||
|     __comm__ = MPI.COMM_WORLD |     __mpi_comm__ = MPI.COMM_WORLD | ||||||
|     __size__ = __comm__.Get_size() |     __mpi_size__ = __mpi_comm__.Get_size() | ||||||
|     __rank__ = __comm__.Get_rank() |     __mpi_rank__ = __mpi_comm__.Get_rank() | ||||||
|  |  | ||||||
|     __matrix__: Matrix = None |     __matrix__: Matrix = None | ||||||
|     __chunk__: list = None |     __chunk__: list = None | ||||||
| @@ -16,23 +16,103 @@ class MatrixMPI: | |||||||
|         self.__matrix__ = Matrix(data=data, shape=shape, structure=structure, model=model, offset=offset, n=n) |         self.__matrix__ = Matrix(data=data, shape=shape, structure=structure, model=model, offset=offset, n=n) | ||||||
|  |  | ||||||
|         total_amount_of_rows = self.__matrix__.shape()[0] |         total_amount_of_rows = self.__matrix__.shape()[0] | ||||||
|         chunks = numpy.array_split(list(range(total_amount_of_rows)), self.__size__) |         chunks = numpy.array_split(list(range(total_amount_of_rows)), self.__mpi_size__) | ||||||
|         self.__chunk__ = chunks[self.__rank__].tolist() |         self.__chunk__ = chunks[self.__mpi_rank__].tolist() | ||||||
|  |  | ||||||
|  |     @staticmethod | ||||||
|  |     def of(matrix: Matrix): | ||||||
|  |         return MatrixMPI(matrix.get_data(), matrix.shape()) | ||||||
|  |  | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         return str(self.__matrix__) |         return str(self.__matrix__) | ||||||
|  |  | ||||||
|     def get_rank_data(self): |     def get_rank_submatrix(self): | ||||||
|         rows = len(self.__chunk__) |         rows = len(self.__chunk__) | ||||||
|         cols = self.__matrix__.shape()[1] |         cols = self.__matrix__.shape()[1] | ||||||
|         return Matrix(self.__matrix__[self.__chunk__], (rows, cols)) |         return Matrix(self.__matrix__[self.__chunk__], (rows, cols)) | ||||||
|  |  | ||||||
|     def transpose(self): |     def transpose(self): | ||||||
|         return self.__matrix__.transpose() |         """ | ||||||
|  |         :return: the transpose of the matrix | ||||||
|  |         """ | ||||||
|  |         return MatrixMPI.of(self.__matrix__.transpose()) | ||||||
|  |  | ||||||
|     def T(self): |     def T(self): | ||||||
|  |         """ | ||||||
|  |         Same as ``matrix.transpose()`` | ||||||
|  |  | ||||||
|  |         :return: see ``matrix.transpose()`` | ||||||
|  |         """ | ||||||
|         return self.transpose() |         return self.transpose() | ||||||
|  |  | ||||||
|  |     def __eq__(self, other): | ||||||
|  |         """ | ||||||
|  |         Return ``self==value`` | ||||||
|  |  | ||||||
| mpi_matrix = MatrixMPI(list(range(1, 25)), (12, 2)) |         :param other: The object to compare to; must be either a ``MatrixMPI``, ``Matrix``, a ``list`` or a ``numpy.ndarray`` | ||||||
| print(mpi_matrix.transpose()) |         :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.__matrix__ == other.__matrix__ | ||||||
|  |         else: | ||||||
|  |             return self.__matrix__ == other | ||||||
|  |  | ||||||
|  |     def __neg__(self): | ||||||
|  |         gathered_data = self.__mpi_comm__.gather(-self.get_rank_submatrix()) | ||||||
|  |         data = self.__mpi_comm__.bcast(gathered_data) | ||||||
|  |         return MatrixMPI.of(Matrix.flatten(data)) | ||||||
|  |  | ||||||
|  |     def __add__(self, other): | ||||||
|  |         if isinstance(other, MatrixMPI): | ||||||
|  |             other = other.get_rank_submatrix() | ||||||
|  |         gathered_data = self.__mpi_comm__.gather(self.get_rank_submatrix() + other) | ||||||
|  |         data = self.__mpi_comm__.bcast(gathered_data) | ||||||
|  |         return MatrixMPI.of(Matrix.flatten(data)) | ||||||
|  |  | ||||||
|  |     def __radd__(self, other): | ||||||
|  |         return self + other | ||||||
|  |  | ||||||
|  |     def __sub__(self, other): | ||||||
|  |         return self + (-other) | ||||||
|  |  | ||||||
|  |     def __rsub__(self, other): | ||||||
|  |         return -self + other | ||||||
|  |  | ||||||
|  |     def __truediv__(self, other): | ||||||
|  |         gathered_data = self.__mpi_comm__.gather(self.get_rank_submatrix() / other) | ||||||
|  |         data = self.__mpi_comm__.bcast(gathered_data) | ||||||
|  |         return MatrixMPI.of(Matrix.flatten(data)) | ||||||
|  |  | ||||||
|  |     def __mul__(self, other): | ||||||
|  |         if isinstance(other, MatrixMPI): | ||||||
|  |             return self.__matrix__ * other.__matrix__ | ||||||
|  |         else: | ||||||
|  |             return self.__matrix__ * other | ||||||
|  |  | ||||||
|  |     def __rmul__(self, other): | ||||||
|  |         return self * other | ||||||
|  |  | ||||||
|  |     def norm(self, f: str = "frobenius"): | ||||||
|  |         """ | ||||||
|  |         Calculates the norm of the matrix. | ||||||
|  |  | ||||||
|  |         A norm is a positive definit, absolute homogeneous and subadditive function. | ||||||
|  |         For Matrices a norm is also sub-multiplicative. | ||||||
|  |  | ||||||
|  |         :param f: The norm to be used, could be either "frobenius", "row sum" or "col sum" | ||||||
|  |  | ||||||
|  |         :return: the norm as a number | ||||||
|  |         """ | ||||||
|  |         return self.__matrix__.norm(f) | ||||||
|  |  | ||||||
|  |     def __getitem__(self, key): | ||||||
|  |         return self.__matrix__[key] | ||||||
|  |  | ||||||
|  |     def __setitem__(self, key, value): | ||||||
|  |         self.__matrix__[key] = value | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # m1 = MatrixMPI(numpy.random.uniform(0, 1, 1_000_000), (1000, 1000)) | ||||||
|  | m1 = MatrixMPI([1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6], (5, 3)) | ||||||
|  |  | ||||||
|  | print(m1 - m1) | ||||||
|   | |||||||
| @@ -378,3 +378,12 @@ class TestMatrix(TestCase): | |||||||
|         expected = Matrix([1, 2, 3, 4, 5, 60, 70, 8, 9, 100, 110, 12, 13, 14, 15, 16], (4, 4)) |         expected = Matrix([1, 2, 3, 4, 5, 60, 70, 8, 9, 100, 110, 12, 13, 14, 15, 16], (4, 4)) | ||||||
|  |  | ||||||
|         self.assertEqual(expected, actual) |         self.assertEqual(expected, actual) | ||||||
|  |  | ||||||
|  |     def test_should_flatten_list_of_matrices(self): | ||||||
|  |         m1 = Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9], (3, 3)) | ||||||
|  |         m2 = Matrix([1, 2, 3, 4, 5, 6], (2, 3)) | ||||||
|  |  | ||||||
|  |         actual = m1.flatten([m1, m2]) | ||||||
|  |         expected = Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6], (5, 3)) | ||||||
|  |  | ||||||
|  |         self.assertEqual(expected, actual) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user