diff --git a/src/matrix.py b/src/matrix.py index 2a17ba4..356efda 100644 --- a/src/matrix.py +++ b/src/matrix.py @@ -2,10 +2,37 @@ import numpy class Matrix: - __data__ = [] - __shape__ = () + """ + This Matrix class represents a real 2D-matrix. + """ + __data__: numpy.ndarray + __shape__: (int, int) - def __init__(self, data=None, shape=None, structure=None, model=None, size=None): + def __init__(self, data=None, shape=None, structure=None, model=None, n=None): + """ + Creates a new matrix. + Thy type of the matrix depends on the signature and arguments. + + - ``Matrix(numpy.ndarray)``: will create a new matrix with the given data in ndarray and its shape. + - ``Matrix(list, (int,int))``: will create a new nxm matrix with the given rows and columns and data in list. + - ``Matrix(list, str, int)``: will create a new square matrix of given size and structure of either \"unity\", \"diagonal\" or \"tridiagonal\" + - ``Matrix(str, int)``: will create a new square matrix of given size and TODO + + + :param data: Either a list or an numpy ndarray + :param shape: A tuple containing the amount of rows and columns + :param structure: Either \"unity\", \"diagonal\" or \"tridiagonal\" + :param model: TODO + :param n: Amount of rows of a square matrix or offset in case of diagonal structure + + :type data: list | numpy.ndarray + :type shape: (int, int) + :type structure: str + :type model: str + :type n: int + + :rtype: Matrix + """ if isinstance(data, numpy.ndarray): try: data.shape[1] @@ -13,23 +40,61 @@ class Matrix: self.__shape__ = (data.shape[0], 1) else: self.__shape__ = data.shape - self.__data__ = data.tolist() + self.__data__ = data elif isinstance(data, list) and isinstance(shape, tuple): self.__shape__ = shape - self.__data__ = numpy.array(data).reshape(shape).tolist() - elif isinstance(data, list) and isinstance(structure, str) and isinstance(size, int): - ... - elif isinstance(model, str) and isinstance(size, int): - ... + self.__data__ = numpy.array(data).reshape(shape) + elif isinstance(data, list) and isinstance(structure, str) and isinstance(n, int): + if structure == "unity": + ... # TODO: what does it mean? + elif structure == "diagonal": + diag = numpy.diag(data, n) + self.__data__ = diag + self.__shape__ = diag.shape + elif structure == "tridiagonal": + if len(data) != 3: + raise ValueError("If structure is tridiagonal, then the given data must be of length 3") + tridiag = numpy.diag([data[0]] * (n-1), -1) + numpy.diag([data[1]] * n, 0) + numpy.diag([data[2]] * (n-1), 1) + self.__data__ = tridiag + self.__shape__ = tridiag.shape + elif isinstance(model, str) and isinstance(n, int): + ... # TODO: what shall one do here? else: - raise ValueError("Only following signatures are allowed: " - "(numpy.ndarray), (list, tuple), (list, str, int), (str, int)") + raise ValueError("Only following signatures are allowed: (numpy.ndarray), (list, tuple), (list, str, int), (str, int)") def get_data(self): + """ + :return: the data of the matrix as a ``numpy.ndarray`` + """ return self.__data__ def shape(self): + """ + :return: the shape of the matrix, which is ``(rows, columns)`` + """ return self.__shape__ + def transpose(self): + """ + :return: the transpose of the matrix + """ + return Matrix(self.__data__.transpose()) + def __eq__(self, other): - return self.__data__ == other.__data__ + """ + Return ``self==value`` + + :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 is totally equal to the given data in other, otherwise False + """ + if isinstance(other, Matrix): + return (self.__data__ == other.__data__).all() + elif isinstance(other, list): + return (self.__data__ == numpy.array(other)).all() + elif isinstance(other, numpy.ndarray): + return (self.__data__ == other).all() + else: + raise ValueError("Matrix type is not comparable to type of given ``other``") + + def __str__(self): + return str(self.__data__) diff --git a/src/vector.py b/src/vector.py index f8b5b07..15254b4 100644 --- a/src/vector.py +++ b/src/vector.py @@ -1,3 +1,5 @@ +import numpy + from matrix import Matrix @@ -5,6 +7,11 @@ class Vector(Matrix): __data__ = [] def __init__(self, data): + """ + + :type data: list | int + """ + super().__init__(numpy.array([0])) # TODO: remove in future if isinstance(data, list): self.__data__ = data elif isinstance(data, int): diff --git a/test/test_matrix.py b/test/test_matrix.py index 48f1912..303e378 100644 --- a/test/test_matrix.py +++ b/test/test_matrix.py @@ -8,48 +8,88 @@ from matrix import Matrix class TestMatrix(TestCase): def test_should_create_matrix_from_numpy_array_with_shape_3_2(self): data = numpy.array([[0, 1], [2, 3], [4, 5]]) - m = Matrix(data) + actual = Matrix(data) - actual_shape = m.shape() + actual_shape = actual.shape() expected_shape = (3, 2) self.assertEqual(expected_shape, actual_shape) - actual_data = m.get_data() - expected_data = [[0, 1], [2, 3], [4, 5]] - self.assertEqual(expected_data, actual_data) + expected = [[0, 1], [2, 3], [4, 5]] + self.assertEqual(expected, actual) def test_should_create_matrix_from_numpy_array_with_shape_1_3(self): data = numpy.array([[0, 1, 2]]) - m = Matrix(data) + actual = Matrix(data) - actual_shape = m.shape() + actual_shape = actual.shape() expected_shape = (1, 3) self.assertEqual(expected_shape, actual_shape) - actual_data = m.get_data() - expected_data = [[0, 1, 2]] - self.assertEqual(expected_data, actual_data) + expected = [[0, 1, 2]] + self.assertEqual(expected, actual) def test_should_create_vectorlike_matrix_from_numpy_array_with_shape_3_1(self): data = numpy.array([0, 1, 2]) - m = Matrix(data) + actual = Matrix(data) - actual_shape = m.shape() + actual_shape = actual.shape() expected_shape = (3, 1) self.assertEqual(expected_shape, actual_shape) - actual_data = m.get_data() - expected_data = [0, 1, 2] - self.assertEqual(expected_data, actual_data) + expected = [0, 1, 2] + self.assertEqual(expected, actual) def test_should_create_matrix_from_list_with_shape_2_2(self): data = [0, 1, 2, 3] - m = Matrix(data, shape=(2, 2)) + actual = Matrix(data, shape=(2, 2)) - actual_shape = m.shape() + actual_shape = actual.shape() expected_shape = (2, 2) self.assertEqual(expected_shape, actual_shape) - actual_data = m.get_data() - expected_data = [[0, 1], [2, 3]] - self.assertEqual(expected_data, actual_data) + expected = [[0, 1], [2, 3]] + self.assertEqual(expected, actual) + + def test_should_create_diagonal_matrix_from_list(self): + data = [1, 1, 1] + actual = Matrix(data, structure="diagonal", n=0) + + actual_shape = actual.shape() + expected_shape = (3, 3) + self.assertEqual(expected_shape, actual_shape) + + expected = [[1, 0, 0], [0, 1, 0], [0, 0, 1]] + self.assertEqual(expected, actual) + + def test_should_create_diagonal_matrix_from_list_with_offset_1(self): + data = [1, 1, 1] + actual = Matrix(data, structure="diagonal", n=1) + + actual_shape = actual.shape() + expected_shape = (4, 4) + self.assertEqual(expected_shape, actual_shape) + + expected = [[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [0, 0, 0, 0]] + self.assertEqual(expected, actual) + + def test_should_raise_value_error_while_creating_tridiagonal_matrix(self): + self.assertRaises(ValueError, lambda: Matrix([-1, 1, -1, 0], structure="tridiagonal", n=1)) + + def test_should_create_tridiagonal_matrix_from_list_with_size_4(self): + data = [-1, 1, -1] + actual = Matrix(data, structure="tridiagonal", n=4) + + actual_shape = actual.shape() + expected_shape = (4, 4) + self.assertEqual(expected_shape, actual_shape) + + expected = [[1, -1, 0, 0], [-1, 1, -1, 0], [0, -1, 1, -1], [0, 0, -1, 1]] + self.assertEqual(expected, actual) + + def test_should_transpose_matrix(self): + data = [1, 2, 3, 4, 5, 6, 7, 8, 9] + actual = Matrix(data, (3, 3)) + + actual = actual.transpose() + expected = [[1, 4, 7], [2, 5, 8], [3, 6, 9]] + self.assertEqual(expected, actual)