From e8560f712a2c0e82d2a5193a07af4e7d06032686 Mon Sep 17 00:00:00 2001 From: jasper256 Date: Sun, 1 Sep 2019 12:53:12 -0400 Subject: [PATCH 1/7] Added OOP aproach to matrices --- matrix/matrix_class.py | 159 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 matrix/matrix_class.py diff --git a/matrix/matrix_class.py b/matrix/matrix_class.py new file mode 100644 index 000000000000..bd95f761c1d7 --- /dev/null +++ b/matrix/matrix_class.py @@ -0,0 +1,159 @@ +# An OOP aproach to representing and manipulating matrices + +class Matrix: + ''' + Matrix object generated from a 2D array where each element is an array representing a row. + Rows can contain type int or float. + Common operations and information available. + >>> rows = [ + ... [1, 2, 3], + ... [4, 5, 6], + ... [7, 8, 9] + ... ] + >>> matrix = Matrix(rows) + >>> print(matrix) + [1, 2, 3] + [4, 5, 6] + [7, 8, 9] + >>> print(matrix.rows) + [[1, 2, 3], [4, 5, 6], [7, 8, 9]] + >>> print(matrix.columns) + [[1, 4, 7], [2, 5, 8], [3, 6, 9]] + + Order is returned as a tuple + >>> matrix.order + (3, 3) + + Scalar multiplication, addition, subtraction, and multiplication are available + >>> matrix2 = matrix * 3 + >>> print(matrix2) + [3, 6, 9] + [12, 15, 18] + [21, 24, 27] + >>> print(matrix + matrix2) + [4, 8, 12] + [16, 20, 24] + [28, 32, 36] + >>> print(matrix - matrix2) + [-2, -4, -6] + [-8, -10, -12] + [-14, -16, -18] + >>> print(matrix * matrix2) + [90, 108, 126] + [198, 243, 288] + [306, 378, 450] + + Matrices can also be modified + >>> matrix.add_row([10, 11, 12]) + >>> print(matrix) + [1, 2, 3] + [4, 5, 6] + [7, 8, 9] + [10, 11, 12] + >>> matrix2.add_column([8, 16, 32]) + >>> print(matrix2) + [3, 6, 9, 8] + [12, 15, 18, 16] + [21, 24, 27, 32] + >>> print(matrix * matrix2) + [90, 108, 126, 136] + [198, 243, 288, 304] + [306, 378, 450, 472] + [414, 513, 612, 640] + + ''' + def __init__(self, rows): + error = TypeError('Matrices must be formed from a list of zero or more lists containing at least one and the same number of values, \ + each of which must be of type int or float') + if len(rows) != 0: + cols = len(rows[0]) + if cols == 0: + raise error + for row in rows: + if not len(row) == cols: + raise error + for value in row: + if not isinstance(value, (int, float)): + raise error + self.rows = rows + else: + self.rows = [] + + # MATRIX INFORMATION + @property + def columns(self): + return [[row[i] for row in self.rows] for i in range(len(self.rows[0]))] + @property + def num_rows(self): + return len(self.rows) + @property + def num_columns(self): + return len(self.columns) + @property + def order(self): + return (self.num_rows, self.num_columns) + def __repr__(self): + return str(self.rows) + def __str__(self): + if self.num_rows == 0: + return str(self.rows) + if self.num_rows == 1: + return str(self.rows[0]) + return '\n'.join([str(row) for row in self.rows]) + + # MATRIX MANIPULATION + def add_row(self, row, position = None): + type_error = TypeError('Row must be a list containing all ints and/or floats') + if not isinstance(row, list): + raise type_error + for value in row: + if not isinstance(value, (int, float)): + raise type_error + if len(row) != self.num_columns: + raise ValueError('Row must be equal in length to the other rows in the matrix') + if position is None: + self.rows.append(row) + else: + self.rows = self.rows[0:position] + [row] + self.rows[position:] + def add_column(self, column, position = None): + type_error = TypeError('Column must be a list containing all ints and/or floats') + if not isinstance(column, list): + raise type_error + for value in column: + if not isinstance(value, (int, float)): + raise type_error + if len(column) != self.num_rows: + raise ValueError('Column must be equal in length to the other columns in the matrix') + if position is None: + self.rows = [self.rows[i] + [column[i]] for i in range(self.num_rows)] + else: + self.rows = [self.rows[i][0:position] + [column[i]] + self.rows[i][position:] for i in range(self.num_rows)] + + # MATRIX OPERATIONS + def __add__(self, other): + if self.order != other.order: + raise ValueError('Addition requires matrices of the same order') + return Matrix([[self.rows[i][j] + other.rows[i][j] for j in range(self.num_columns)] for i in range(self.num_rows)]) + def __sub__(self, other): + if self.order != other.order: + raise ValueError('Subtraction requires matrices of the same order') + return Matrix([[self.rows[i][j] - other.rows[i][j] for j in range(self.num_columns)] for i in range(self.num_rows)]) + def __mul__(self, other): + if not isinstance(other, (int, float, Matrix)): + raise TypeError('A matrix can only be multiplied by an int, float, or another matrix') + if type(other) in (int, float): + return Matrix([[element * other for element in row] for row in self.rows]) + if type(other) is Matrix: + if self.num_columns != other.num_rows: + raise ValueError('The number of columns in the first matrix must be equal to the number of rows in the second') + return Matrix([[Matrix.dot_product(row, column) for column in other.columns] for row in self.rows]) + + @classmethod + def dot_product(cls, row, column): + return sum([row[i] * column[i] for i in range(len(row))]) + + +if __name__ == '__main__': + import doctest + test = doctest.testmod() + print(test) \ No newline at end of file From 1abd3006bc81ef458beea31910e2bd5d3198d10e Mon Sep 17 00:00:00 2001 From: jasper256 Date: Mon, 2 Sep 2019 18:17:45 -0400 Subject: [PATCH 2/7] created methods for minors, cofactors, and determinants and added corresponding doctests --- matrix/matrix_class.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/matrix/matrix_class.py b/matrix/matrix_class.py index bd95f761c1d7..b43ff7a3f710 100644 --- a/matrix/matrix_class.py +++ b/matrix/matrix_class.py @@ -92,6 +92,37 @@ def num_columns(self): @property def order(self): return (self.num_rows, self.num_columns) + @property + def is_square(self): + if self.order[0] == self.order[1]: + return True + return False + @property + def determinant(self): + if not self.is_square: + return None + if self.order == (0, 0): + return 1 + if self.order == (1, 1): + return self.rows[0][0] + if self.order == (2, 2): + return (self.rows[0][0] * self.rows[1][1]) - (self.rows[0][1] * self.rows[1][0]) + else: + return sum([self.rows[0][column] * self.cofactors[0][column] for column in range(self.num_columns)]) + def get_minor(self, row, column): + values = [[self.rows[other_row][other_column] for other_column in range(self.num_columns) if other_column != column] + for other_row in range(self.num_rows) if other_row != row] + return Matrix(values).determinant + def get_cofactor(self, row, column): + if (row + column) % 2 == 0: + return self.get_minor(row, column) + return -1 * self.get_minor(row, column) + @property + def minors(self): + return Matrix([[self.get_minor(row, column) for column in range(self.num_columns)] for row in range(self.num_rows)]) + @property + def cofactors(self): + return [[self.minors.rows[row][column] if (row + column) % 2 == 0 else self.minors.rows[row][column] * -1 for column in range(self.minors.num_columns)] for row in range(self.minors.num_rows)] def __repr__(self): return str(self.rows) def __str__(self): From 29856307b09cce572ca4ba5ade9d90c612afc890 Mon Sep 17 00:00:00 2001 From: jasper256 Date: Mon, 2 Sep 2019 19:11:55 -0400 Subject: [PATCH 3/7] Added methods for adjugate, inverse, and identity (along with corresponding doctests) to matrix_class.py A small bug persists that causes the doctest to fail. After a couple Matrix objects are printed, the next one is printed in a different format. --- matrix/matrix_class.py | 55 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/matrix/matrix_class.py b/matrix/matrix_class.py index b43ff7a3f710..86b86db397f9 100644 --- a/matrix/matrix_class.py +++ b/matrix/matrix_class.py @@ -15,6 +15,8 @@ class Matrix: [1, 2, 3] [4, 5, 6] [7, 8, 9] + + Matrix rows and columns are available as 2D arrays >>> print(matrix.rows) [[1, 2, 3], [4, 5, 6], [7, 8, 9]] >>> print(matrix.columns) @@ -23,7 +25,37 @@ class Matrix: Order is returned as a tuple >>> matrix.order (3, 3) - + + Squareness and invertability are represented as bool + >>> matrix.is_square + True + >>> matrix.is_invertable + False + + Identity, Minors, Cofactors and Adjugate are returned as Matrices. Inverse can be a Matrix or Nonetype + >>> print(matrix.identity) + [1, 0, 0] + [0, 1, 0] + [0, 0, 1] + >>> print(matrix.minors) + [-3, -6, -3] + [-6, -12, -6] + [-3, -6, -3] + >>> print(matrix.cofactors) + [-3, 6, -3] + [6, -12, 6] + [-3, 6, -3] + >>> print(matrix.adjugate) # won't be apparent due to the nature of the cofactor matrix + [-3, 6, -3] + [6, -12, 6] + [-3, 6, -3] + >>> print(matrix.inverse) + None + + Determinant is an int, float, or Nonetype + >>> matrix.determinant + 0 + Scalar multiplication, addition, subtraction, and multiplication are available >>> matrix2 = matrix * 3 >>> print(matrix2) @@ -98,6 +130,10 @@ def is_square(self): return True return False @property + def identity(self): + values = [[0 if column_num != row_num else 1 for column_num in range(self.num_rows)] for row_num in range(self.num_rows)] + return Matrix(values) + @property def determinant(self): if not self.is_square: return None @@ -109,6 +145,11 @@ def determinant(self): return (self.rows[0][0] * self.rows[1][1]) - (self.rows[0][1] * self.rows[1][0]) else: return sum([self.rows[0][column] * self.cofactors[0][column] for column in range(self.num_columns)]) + @property + def is_invertable(self): + if self.determinant: + return True + return False def get_minor(self, row, column): values = [[self.rows[other_row][other_column] for other_column in range(self.num_columns) if other_column != column] for other_row in range(self.num_rows) if other_row != row] @@ -122,7 +163,17 @@ def minors(self): return Matrix([[self.get_minor(row, column) for column in range(self.num_columns)] for row in range(self.num_rows)]) @property def cofactors(self): - return [[self.minors.rows[row][column] if (row + column) % 2 == 0 else self.minors.rows[row][column] * -1 for column in range(self.minors.num_columns)] for row in range(self.minors.num_rows)] + return [[self.minors.rows[row][column] if (row + column) % 2 == 0 else self.minors.rows[row][column] * -1 for column in range(self.minors.num_columns)] + for row in range(self.minors.num_rows)] + @property + def adjugate(self): + values = [[self.cofactors[column][row] for column in range(self.num_columns)] for row in range(self.num_rows)] + return Matrix(values) + @property + def inverse(self): + if not self.is_invertable: + return None + return self.adjugate * (1 / self.determinant) def __repr__(self): return str(self.rows) def __str__(self): From e62dbe23fba1bc3c31de8f0ed92f4a33e1caa7e6 Mon Sep 17 00:00:00 2001 From: jasper256 Date: Mon, 2 Sep 2019 19:20:15 -0400 Subject: [PATCH 4/7] formatted matrix_class.py with python/black --- matrix/matrix_class.py | 147 ++++++++++++++++++++++++++++++++--------- 1 file changed, 115 insertions(+), 32 deletions(-) diff --git a/matrix/matrix_class.py b/matrix/matrix_class.py index 86b86db397f9..7b8070ee29e0 100644 --- a/matrix/matrix_class.py +++ b/matrix/matrix_class.py @@ -1,7 +1,8 @@ # An OOP aproach to representing and manipulating matrices + class Matrix: - ''' + """ Matrix object generated from a 2D array where each element is an array representing a row. Rows can contain type int or float. Common operations and information available. @@ -93,10 +94,13 @@ class Matrix: [306, 378, 450, 472] [414, 513, 612, 640] - ''' + """ + def __init__(self, rows): - error = TypeError('Matrices must be formed from a list of zero or more lists containing at least one and the same number of values, \ - each of which must be of type int or float') + error = TypeError( + "Matrices must be formed from a list of zero or more lists containing at least one and the same number of values, \ + each of which must be of type int or float" + ) if len(rows) != 0: cols = len(rows[0]) if cols == 0: @@ -110,29 +114,38 @@ def __init__(self, rows): self.rows = rows else: self.rows = [] - + # MATRIX INFORMATION @property def columns(self): return [[row[i] for row in self.rows] for i in range(len(self.rows[0]))] + @property def num_rows(self): return len(self.rows) + @property def num_columns(self): return len(self.columns) + @property def order(self): return (self.num_rows, self.num_columns) + @property def is_square(self): if self.order[0] == self.order[1]: return True return False + @property def identity(self): - values = [[0 if column_num != row_num else 1 for column_num in range(self.num_rows)] for row_num in range(self.num_rows)] + values = [ + [0 if column_num != row_num else 1 for column_num in range(self.num_rows)] + for row_num in range(self.num_rows) + ] return Matrix(values) + @property def determinant(self): if not self.is_square: @@ -142,100 +155,170 @@ def determinant(self): if self.order == (1, 1): return self.rows[0][0] if self.order == (2, 2): - return (self.rows[0][0] * self.rows[1][1]) - (self.rows[0][1] * self.rows[1][0]) - else: - return sum([self.rows[0][column] * self.cofactors[0][column] for column in range(self.num_columns)]) + return (self.rows[0][0] * self.rows[1][1]) - ( + self.rows[0][1] * self.rows[1][0] + ) + else: + return sum( + [ + self.rows[0][column] * self.cofactors[0][column] + for column in range(self.num_columns) + ] + ) + @property def is_invertable(self): if self.determinant: return True return False + def get_minor(self, row, column): - values = [[self.rows[other_row][other_column] for other_column in range(self.num_columns) if other_column != column] - for other_row in range(self.num_rows) if other_row != row] + values = [ + [ + self.rows[other_row][other_column] + for other_column in range(self.num_columns) + if other_column != column + ] + for other_row in range(self.num_rows) + if other_row != row + ] return Matrix(values).determinant + def get_cofactor(self, row, column): if (row + column) % 2 == 0: return self.get_minor(row, column) return -1 * self.get_minor(row, column) + @property def minors(self): - return Matrix([[self.get_minor(row, column) for column in range(self.num_columns)] for row in range(self.num_rows)]) + return Matrix( + [ + [self.get_minor(row, column) for column in range(self.num_columns)] + for row in range(self.num_rows) + ] + ) + @property def cofactors(self): - return [[self.minors.rows[row][column] if (row + column) % 2 == 0 else self.minors.rows[row][column] * -1 for column in range(self.minors.num_columns)] - for row in range(self.minors.num_rows)] + return [ + [ + self.minors.rows[row][column] + if (row + column) % 2 == 0 + else self.minors.rows[row][column] * -1 + for column in range(self.minors.num_columns) + ] + for row in range(self.minors.num_rows) + ] + @property def adjugate(self): - values = [[self.cofactors[column][row] for column in range(self.num_columns)] for row in range(self.num_rows)] + values = [ + [self.cofactors[column][row] for column in range(self.num_columns)] + for row in range(self.num_rows) + ] return Matrix(values) + @property def inverse(self): if not self.is_invertable: return None return self.adjugate * (1 / self.determinant) + def __repr__(self): return str(self.rows) + def __str__(self): if self.num_rows == 0: return str(self.rows) if self.num_rows == 1: return str(self.rows[0]) - return '\n'.join([str(row) for row in self.rows]) + return "\n".join([str(row) for row in self.rows]) # MATRIX MANIPULATION - def add_row(self, row, position = None): - type_error = TypeError('Row must be a list containing all ints and/or floats') + def add_row(self, row, position=None): + type_error = TypeError("Row must be a list containing all ints and/or floats") if not isinstance(row, list): raise type_error for value in row: if not isinstance(value, (int, float)): raise type_error if len(row) != self.num_columns: - raise ValueError('Row must be equal in length to the other rows in the matrix') + raise ValueError( + "Row must be equal in length to the other rows in the matrix" + ) if position is None: self.rows.append(row) else: self.rows = self.rows[0:position] + [row] + self.rows[position:] - def add_column(self, column, position = None): - type_error = TypeError('Column must be a list containing all ints and/or floats') + + def add_column(self, column, position=None): + type_error = TypeError( + "Column must be a list containing all ints and/or floats" + ) if not isinstance(column, list): raise type_error for value in column: if not isinstance(value, (int, float)): raise type_error if len(column) != self.num_rows: - raise ValueError('Column must be equal in length to the other columns in the matrix') + raise ValueError( + "Column must be equal in length to the other columns in the matrix" + ) if position is None: self.rows = [self.rows[i] + [column[i]] for i in range(self.num_rows)] else: - self.rows = [self.rows[i][0:position] + [column[i]] + self.rows[i][position:] for i in range(self.num_rows)] + self.rows = [ + self.rows[i][0:position] + [column[i]] + self.rows[i][position:] + for i in range(self.num_rows) + ] # MATRIX OPERATIONS def __add__(self, other): if self.order != other.order: - raise ValueError('Addition requires matrices of the same order') - return Matrix([[self.rows[i][j] + other.rows[i][j] for j in range(self.num_columns)] for i in range(self.num_rows)]) + raise ValueError("Addition requires matrices of the same order") + return Matrix( + [ + [self.rows[i][j] + other.rows[i][j] for j in range(self.num_columns)] + for i in range(self.num_rows) + ] + ) + def __sub__(self, other): if self.order != other.order: - raise ValueError('Subtraction requires matrices of the same order') - return Matrix([[self.rows[i][j] - other.rows[i][j] for j in range(self.num_columns)] for i in range(self.num_rows)]) + raise ValueError("Subtraction requires matrices of the same order") + return Matrix( + [ + [self.rows[i][j] - other.rows[i][j] for j in range(self.num_columns)] + for i in range(self.num_rows) + ] + ) + def __mul__(self, other): if not isinstance(other, (int, float, Matrix)): - raise TypeError('A matrix can only be multiplied by an int, float, or another matrix') + raise TypeError( + "A matrix can only be multiplied by an int, float, or another matrix" + ) if type(other) in (int, float): return Matrix([[element * other for element in row] for row in self.rows]) if type(other) is Matrix: if self.num_columns != other.num_rows: - raise ValueError('The number of columns in the first matrix must be equal to the number of rows in the second') - return Matrix([[Matrix.dot_product(row, column) for column in other.columns] for row in self.rows]) + raise ValueError( + "The number of columns in the first matrix must be equal to the number of rows in the second" + ) + return Matrix( + [ + [Matrix.dot_product(row, column) for column in other.columns] + for row in self.rows + ] + ) @classmethod def dot_product(cls, row, column): return sum([row[i] * column[i] for i in range(len(row))]) -if __name__ == '__main__': +if __name__ == "__main__": import doctest + test = doctest.testmod() - print(test) \ No newline at end of file + print(test) From cbf7ef64801e1384922223ff2133e76107b0106c Mon Sep 17 00:00:00 2001 From: jasper256 Date: Mon, 2 Sep 2019 20:21:21 -0400 Subject: [PATCH 5/7] implemented negation and exponentiation as well as corresponding doctests in matrix_class.py. Also implemented eq and ne comparison operations --- matrix/matrix_class.py | 49 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/matrix/matrix_class.py b/matrix/matrix_class.py index 7b8070ee29e0..567683c8b89b 100644 --- a/matrix/matrix_class.py +++ b/matrix/matrix_class.py @@ -57,7 +57,11 @@ class Matrix: >>> matrix.determinant 0 - Scalar multiplication, addition, subtraction, and multiplication are available + Negation, scalar multiplication, addition, subtraction, multiplication and exponentiation are available and all return a Matrix + >>> print(-matrix) + [-1, -2, -3] + [-4, -5, -6] + [-7, -8, -9] >>> matrix2 = matrix * 3 >>> print(matrix2) [3, 6, 9] @@ -71,10 +75,10 @@ class Matrix: [-2, -4, -6] [-8, -10, -12] [-14, -16, -18] - >>> print(matrix * matrix2) - [90, 108, 126] - [198, 243, 288] - [306, 378, 450] + >>> print(matrix ** 3) + [468, 576, 684] + [1062, 1305, 1548] + [1656, 2034, 2412] Matrices can also be modified >>> matrix.add_row([10, 11, 12]) @@ -273,6 +277,21 @@ def add_column(self, column, position=None): ] # MATRIX OPERATIONS + def __eq__(self, other): + if not isinstance(other, Matrix): + raise TypeError("A Matrix can only be compared with another Matrix") + if self.rows == other.rows: + return True + return False + + def __ne__(self, other): + if self == other: + return False + return True + + def __neg__(self): + return self * -1 + def __add__(self, other): if self.order != other.order: raise ValueError("Addition requires matrices of the same order") @@ -296,7 +315,7 @@ def __sub__(self, other): def __mul__(self, other): if not isinstance(other, (int, float, Matrix)): raise TypeError( - "A matrix can only be multiplied by an int, float, or another matrix" + "A Matrix can only be multiplied by an int, float, or another matrix" ) if type(other) in (int, float): return Matrix([[element * other for element in row] for row in self.rows]) @@ -312,6 +331,24 @@ def __mul__(self, other): ] ) + def __pow__(self, other): + if not isinstance(other, int): + raise TypeError("A Matrix can only be raised to the power of an int") + if not self.is_square: + raise ValueError("Only square matrices can be raised to a power") + if other == 0: + return self.identity + if other < 0: + if self.is_invertable: + return self.inverse ** (-other) + raise ValueError( + "Only invertable matrices can be raised to a negative power" + ) + result = self + for i in range(other - 1): + result *= self + return result + @classmethod def dot_product(cls, row, column): return sum([row[i] * column[i] for i in range(len(row))]) From 9028fc327a3b21bd8260a5fd37bf61614232dc7f Mon Sep 17 00:00:00 2001 From: jasper256 Date: Tue, 3 Sep 2019 16:50:09 -0400 Subject: [PATCH 6/7] changed __str__ method in matrix_class.py to align with numpy standard and fixed bug in cofactors method --- matrix/matrix_class.py | 117 ++++++++++++++++++++++------------------- 1 file changed, 64 insertions(+), 53 deletions(-) diff --git a/matrix/matrix_class.py b/matrix/matrix_class.py index 567683c8b89b..839c93ff1468 100644 --- a/matrix/matrix_class.py +++ b/matrix/matrix_class.py @@ -13,9 +13,9 @@ class Matrix: ... ] >>> matrix = Matrix(rows) >>> print(matrix) - [1, 2, 3] - [4, 5, 6] - [7, 8, 9] + [[1. 2. 3.] + [4. 5. 6.] + [7. 8. 9.]] Matrix rows and columns are available as 2D arrays >>> print(matrix.rows) @@ -35,21 +35,21 @@ class Matrix: Identity, Minors, Cofactors and Adjugate are returned as Matrices. Inverse can be a Matrix or Nonetype >>> print(matrix.identity) - [1, 0, 0] - [0, 1, 0] - [0, 0, 1] + [[1. 0. 0.] + [0. 1. 0.] + [0. 0. 1.]] >>> print(matrix.minors) - [-3, -6, -3] - [-6, -12, -6] - [-3, -6, -3] + [[-3. -6. -3.] + [-6. -12. -6.] + [-3. -6. -3.]] >>> print(matrix.cofactors) - [-3, 6, -3] - [6, -12, 6] - [-3, 6, -3] + [[-3. 6. -3.] + [6. -12. 6.] + [-3. 6. -3.]] >>> print(matrix.adjugate) # won't be apparent due to the nature of the cofactor matrix - [-3, 6, -3] - [6, -12, 6] - [-3, 6, -3] + [[-3. 6. -3.] + [6. -12. 6.] + [-3. 6. -3.]] >>> print(matrix.inverse) None @@ -59,44 +59,44 @@ class Matrix: Negation, scalar multiplication, addition, subtraction, multiplication and exponentiation are available and all return a Matrix >>> print(-matrix) - [-1, -2, -3] - [-4, -5, -6] - [-7, -8, -9] + [[-1. -2. -3.] + [-4. -5. -6.] + [-7. -8. -9.]] >>> matrix2 = matrix * 3 >>> print(matrix2) - [3, 6, 9] - [12, 15, 18] - [21, 24, 27] + [[3. 6. 9.] + [12. 15. 18.] + [21. 24. 27.]] >>> print(matrix + matrix2) - [4, 8, 12] - [16, 20, 24] - [28, 32, 36] + [[4. 8. 12.] + [16. 20. 24.] + [28. 32. 36.]] >>> print(matrix - matrix2) - [-2, -4, -6] - [-8, -10, -12] - [-14, -16, -18] + [[-2. -4. -6.] + [-8. -10. -12.] + [-14. -16. -18.]] >>> print(matrix ** 3) - [468, 576, 684] - [1062, 1305, 1548] - [1656, 2034, 2412] + [[468. 576. 684.] + [1062. 1305. 1548.] + [1656. 2034. 2412.]] Matrices can also be modified >>> matrix.add_row([10, 11, 12]) >>> print(matrix) - [1, 2, 3] - [4, 5, 6] - [7, 8, 9] - [10, 11, 12] + [[1. 2. 3.] + [4. 5. 6.] + [7. 8. 9.] + [10. 11. 12.]] >>> matrix2.add_column([8, 16, 32]) >>> print(matrix2) - [3, 6, 9, 8] - [12, 15, 18, 16] - [21, 24, 27, 32] + [[3. 6. 9. 8.] + [12. 15. 18. 16.] + [21. 24. 27. 32.]] >>> print(matrix * matrix2) - [90, 108, 126, 136] - [198, 243, 288, 304] - [306, 378, 450, 472] - [414, 513, 612, 640] + [[90. 108. 126. 136.] + [198. 243. 288. 304.] + [306. 378. 450. 472.] + [414. 513. 612. 640.]] """ @@ -165,7 +165,7 @@ def determinant(self): else: return sum( [ - self.rows[0][column] * self.cofactors[0][column] + self.rows[0][column] * self.cofactors.rows[0][column] for column in range(self.num_columns) ] ) @@ -204,20 +204,22 @@ def minors(self): @property def cofactors(self): - return [ + return Matrix( [ - self.minors.rows[row][column] - if (row + column) % 2 == 0 - else self.minors.rows[row][column] * -1 - for column in range(self.minors.num_columns) + [ + self.minors.rows[row][column] + if (row + column) % 2 == 0 + else self.minors.rows[row][column] * -1 + for column in range(self.minors.num_columns) + ] + for row in range(self.minors.num_rows) ] - for row in range(self.minors.num_rows) - ] + ) @property def adjugate(self): values = [ - [self.cofactors[column][row] for column in range(self.num_columns)] + [self.cofactors.rows[column][row] for column in range(self.num_columns)] for row in range(self.num_rows) ] return Matrix(values) @@ -233,10 +235,19 @@ def __repr__(self): def __str__(self): if self.num_rows == 0: - return str(self.rows) + return "[]" if self.num_rows == 1: - return str(self.rows[0]) - return "\n".join([str(row) for row in self.rows]) + return "[[" + ". ".join(self.rows[0]) + "]]" + return ( + "[" + + "\n ".join( + [ + "[" + ". ".join([str(value) for value in row]) + ".]" + for row in self.rows + ] + ) + + "]" + ) # MATRIX MANIPULATION def add_row(self, row, position=None): From e5eda4a681ed6a9a35f2333ba37681008d6ff022 Mon Sep 17 00:00:00 2001 From: jasper256 Date: Tue, 3 Sep 2019 17:23:24 -0400 Subject: [PATCH 7/7] removed property decorators from several methods in matrix_class.py --- matrix/matrix_class.py | 52 ++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/matrix/matrix_class.py b/matrix/matrix_class.py index 839c93ff1468..2cd43fc9ca8e 100644 --- a/matrix/matrix_class.py +++ b/matrix/matrix_class.py @@ -20,7 +20,7 @@ class Matrix: Matrix rows and columns are available as 2D arrays >>> print(matrix.rows) [[1, 2, 3], [4, 5, 6], [7, 8, 9]] - >>> print(matrix.columns) + >>> print(matrix.columns()) [[1, 4, 7], [2, 5, 8], [3, 6, 9]] Order is returned as a tuple @@ -30,31 +30,31 @@ class Matrix: Squareness and invertability are represented as bool >>> matrix.is_square True - >>> matrix.is_invertable + >>> matrix.is_invertable() False Identity, Minors, Cofactors and Adjugate are returned as Matrices. Inverse can be a Matrix or Nonetype - >>> print(matrix.identity) + >>> print(matrix.identity()) [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] - >>> print(matrix.minors) + >>> print(matrix.minors()) [[-3. -6. -3.] [-6. -12. -6.] [-3. -6. -3.]] - >>> print(matrix.cofactors) + >>> print(matrix.cofactors()) [[-3. 6. -3.] [6. -12. 6.] [-3. 6. -3.]] - >>> print(matrix.adjugate) # won't be apparent due to the nature of the cofactor matrix + >>> print(matrix.adjugate()) # won't be apparent due to the nature of the cofactor matrix [[-3. 6. -3.] [6. -12. 6.] [-3. 6. -3.]] - >>> print(matrix.inverse) + >>> print(matrix.inverse()) None Determinant is an int, float, or Nonetype - >>> matrix.determinant + >>> matrix.determinant() 0 Negation, scalar multiplication, addition, subtraction, multiplication and exponentiation are available and all return a Matrix @@ -120,7 +120,6 @@ def __init__(self, rows): self.rows = [] # MATRIX INFORMATION - @property def columns(self): return [[row[i] for row in self.rows] for i in range(len(self.rows[0]))] @@ -130,7 +129,7 @@ def num_rows(self): @property def num_columns(self): - return len(self.columns) + return len(self.rows[0]) @property def order(self): @@ -142,7 +141,6 @@ def is_square(self): return True return False - @property def identity(self): values = [ [0 if column_num != row_num else 1 for column_num in range(self.num_rows)] @@ -150,7 +148,6 @@ def identity(self): ] return Matrix(values) - @property def determinant(self): if not self.is_square: return None @@ -165,14 +162,13 @@ def determinant(self): else: return sum( [ - self.rows[0][column] * self.cofactors.rows[0][column] + self.rows[0][column] * self.cofactors().rows[0][column] for column in range(self.num_columns) ] ) - @property def is_invertable(self): - if self.determinant: + if self.determinant(): return True return False @@ -186,14 +182,13 @@ def get_minor(self, row, column): for other_row in range(self.num_rows) if other_row != row ] - return Matrix(values).determinant + return Matrix(values).determinant() def get_cofactor(self, row, column): if (row + column) % 2 == 0: return self.get_minor(row, column) return -1 * self.get_minor(row, column) - @property def minors(self): return Matrix( [ @@ -202,33 +197,30 @@ def minors(self): ] ) - @property def cofactors(self): return Matrix( [ [ - self.minors.rows[row][column] + self.minors().rows[row][column] if (row + column) % 2 == 0 - else self.minors.rows[row][column] * -1 - for column in range(self.minors.num_columns) + else self.minors().rows[row][column] * -1 + for column in range(self.minors().num_columns) ] - for row in range(self.minors.num_rows) + for row in range(self.minors().num_rows) ] ) - @property def adjugate(self): values = [ - [self.cofactors.rows[column][row] for column in range(self.num_columns)] + [self.cofactors().rows[column][row] for column in range(self.num_columns)] for row in range(self.num_rows) ] return Matrix(values) - @property def inverse(self): - if not self.is_invertable: + if not self.is_invertable(): return None - return self.adjugate * (1 / self.determinant) + return self.adjugate() * (1 / self.determinant()) def __repr__(self): return str(self.rows) @@ -337,7 +329,7 @@ def __mul__(self, other): ) return Matrix( [ - [Matrix.dot_product(row, column) for column in other.columns] + [Matrix.dot_product(row, column) for column in other.columns()] for row in self.rows ] ) @@ -348,10 +340,10 @@ def __pow__(self, other): if not self.is_square: raise ValueError("Only square matrices can be raised to a power") if other == 0: - return self.identity + return self.identity() if other < 0: if self.is_invertable: - return self.inverse ** (-other) + return self.inverse() ** (-other) raise ValueError( "Only invertable matrices can be raised to a negative power" )