From 4aa9d08e27b5c99efe73f4b6a3c791995435eb38 Mon Sep 17 00:00:00 2001 From: Andrijan Ostrun Date: Sun, 11 Feb 2018 14:23:35 +0100 Subject: [PATCH] Initial commit --- .gitignore | 1 + lu1.txt | 3 + lu2.txt | 3 + lu_solve.txt | 3 + matrix/__pycache__/models.cpython-35.pyc | Bin 0 -> 13218 bytes matrix/__pycache__/tests.cpython-35.pyc | Bin 0 -> 2946 bytes matrix/models.py | 433 +++++++++++++++++++++++ matrix/tests.py | 64 ++++ testScript.py | 65 ++++ test_files/b1.txt | 3 + test_files/b2.txt | 3 + test_files/test.txt | 3 + test_files/test2.txt | 3 + test_files/zad1_a.txt | 3 + test_files/zad1_b.txt | 3 + test_files/zad2_a.txt | 3 + test_files/zad2_b.txt | 3 + test_files/zad3_a.txt | 3 + test_files/zad3_b.txt | 3 + test_files/zad4_a.txt | 3 + test_files/zad4_b.txt | 3 + test_files/zad5_a.txt | 3 + test_files/zad5_b.txt | 3 + 23 files changed, 614 insertions(+) create mode 100644 .gitignore create mode 100644 lu1.txt create mode 100644 lu2.txt create mode 100644 lu_solve.txt create mode 100644 matrix/__pycache__/models.cpython-35.pyc create mode 100644 matrix/__pycache__/tests.cpython-35.pyc create mode 100644 matrix/models.py create mode 100644 matrix/tests.py create mode 100644 testScript.py create mode 100644 test_files/b1.txt create mode 100644 test_files/b2.txt create mode 100644 test_files/test.txt create mode 100644 test_files/test2.txt create mode 100644 test_files/zad1_a.txt create mode 100644 test_files/zad1_b.txt create mode 100644 test_files/zad2_a.txt create mode 100644 test_files/zad2_b.txt create mode 100644 test_files/zad3_a.txt create mode 100644 test_files/zad3_b.txt create mode 100644 test_files/zad4_a.txt create mode 100644 test_files/zad4_b.txt create mode 100644 test_files/zad5_a.txt create mode 100644 test_files/zad5_b.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723ef36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/lu1.txt b/lu1.txt new file mode 100644 index 0000000..f01be1a --- /dev/null +++ b/lu1.txt @@ -0,0 +1,3 @@ +2 2 3 +2 3 0 +0 4 2 \ No newline at end of file diff --git a/lu2.txt b/lu2.txt new file mode 100644 index 0000000..6a7c798 --- /dev/null +++ b/lu2.txt @@ -0,0 +1,3 @@ +3 2 1 +1 2 2 +4 3 4 \ No newline at end of file diff --git a/lu_solve.txt b/lu_solve.txt new file mode 100644 index 0000000..6aaa5e6 --- /dev/null +++ b/lu_solve.txt @@ -0,0 +1,3 @@ +4000000000.0 1000000000.0 3000000000.0 +0 1.0 4.0 +0 0 0 diff --git a/matrix/__pycache__/models.cpython-35.pyc b/matrix/__pycache__/models.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..609e0a044879e1de43691de482961fa9098f8bea GIT binary patch literal 13218 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 self.ROUND_DIG else self.ROUND_DIG) for x in lens) + table = [fmt.format(*row) for row in s] + return '\n'.join(table) + + def __len__(self): + return self.rows + + def __eq__(self, o: object) -> bool: + + if isinstance(o, Matrix): + if len(self.data) != len(o.data) or len(self.data[0]) != len(o.data[0]): + # Dimensions are not the same + return False + else: + return self.data == o.data + return False + + def __getitem__(self, index): + return self.data[index] + + def __setitem__(self, index, value): + self.data[index] = value + + def __round__(self, n=14): + for i in range(0, self.rows): + for j in range(0, self.columns): + if abs(self.data[i][j]) < self.EPS: + self.data[i][j] = 0 + else: + self.data[i][j] = round(self.data[i][j], n) + + def __add__(self, other): + + if not isinstance(other, Matrix): + raise AttributeError("unsupported operand type(s) for +: '{}' and '{}'".format(self.__class__, type(other))) + + new_data = [] + + if len(self.data) != len(other.data) or len(self.data[0]) != len(other.data[0]): + # If dimensions of other matrix doesn't match this matrix return None + return None + + for i in range(0, self.rows): + tmp = [] + for j in range(0, self.columns): + tmp.append(self.data[i][j] + other.data[i][j]) + new_data.append(tmp) + + new_matrix = Matrix(data=new_data) + return new_matrix + + def __sub__(self, other): + + if not isinstance(other, Matrix): + raise AttributeError("unsupported operand type(s) for +: '{}' and '{}'".format(self.__class__, type(other))) + + new_data = [] + + if len(self.data) != len(other.data) or len(self.data[0]) != len(other.data[0]): + # If dimensions of other matrix doesn't match this matrix return None + return None + + for i in range(0, self.rows): + tmp = [] + for j in range(0, self.columns): + tmp.append(self.data[i][j] - other.data[i][j]) + new_data.append(tmp) + + new_matrix = Matrix(data=new_data) + return new_matrix + + def __mul__(self, other): + + if not isinstance(other, Matrix) and not isinstance(other, Number) and not isinstance(other, list): + raise AttributeError("unsupported operand type(s) for +: '{}' and '{}'".format(self.__class__, type(other))) + + new_matrix = [] + + if isinstance(other, Number): + for row in self.data: + tmp = [] + for column in row: + tmp.append(column*other) + new_matrix.append(tmp) + return Matrix(data=new_matrix) + + if type(other) == Matrix or type(other) == list: + + if len(other) != self.columns: + raise AttributeError("unsupported matrix dimensions for operation: '{}'x'{}'".format(len(other), len(other[0]))) + else: + new_matrix = [] + for i in range(0, self.rows): + tmp = [] + for j in range(0, len(other[0])): + tmp_sum = 0 + for k in range(0, self.columns): + tmp_sum += self.data[i][k] * other[k][j] + tmp.append(tmp_sum) + new_matrix.append(tmp) + return Matrix(data=new_matrix) + + def __rmul__(self, other): + + if not isinstance(other, Matrix) and not isinstance(other, Number) and not isinstance(other, list): + raise AttributeError("unsupported operand type(s) for +: '{}' and '{}'".format(self.__class__, type(other))) + + new_matrix = [] + + if isinstance(other, Number): + for row in self.data: + tmp = [] + for column in row: + tmp.append(column*other) + new_matrix.append(tmp) + return Matrix(data=new_matrix) + + if type(other) == Matrix or type(other) == list: + + if len(other[0]) != self.rows: + raise AttributeError("unsupported matrix dimensions for operation: '{}'x'{}'".format(len(other), len(other[0]))) + else: + new_matrix = [] + for i in range(0, len(other)): + tmp = [] + for j in range(0, self.columns): + tmp_sum = 0 + for k in range(0, len(other[0])): + tmp_sum += other[i][k] * self[k][j] + tmp.append(tmp_sum) + new_matrix.append(tmp) + return Matrix(data=new_matrix) + + def __invert__(self): + #LUP decompozition + self.lup() + inversed_data = [] + + for i_tmp in range(self.rows): + inversed_data.append([]) + + for i in range(self.rows): + b = [] + for j in range(self.rows): + if i == j: + b.append([1]) + else: + b.append([0]) + b_mat = Matrix(b) + self.forward_substitution(b_mat) + self.back_substitution(b_mat) + + for k in range(b_mat.rows): + for v in range(b_mat.columns): + inversed_data[k].append(b_mat[k][v]) + + inversed_mat = Matrix(inversed_data) + return inversed_mat + + def init_p(self): + # Init p + + for i in range(0, self.rows): + temp = [] + for j in range(0, self.columns): + if i == j: + temp.append(1) + else: + temp.append(0) + if i < len(self.p): + self.p[i] = temp + else: + self.p.append(temp) + + def transpose(self): + + if self.rows >= self.columns: + for i in range(0, self.rows): + for j in range(0, self.columns): + if i == j: + break + tmp = self.data[i][j] + if i >= self.columns: + self.data[j].append(tmp) + else: + self.data[i][j] = self.data[j][i] + self.data[j][i] = tmp + for i in range(self.columns, self.rows): + self.data.remove(self.data[i]) + else: + + for i in range(self.rows, self.columns): + self.data.append([]) + + for i in range(0, self.rows): + new_row = [] + for j in range(i+1, self.columns): + if j < self.rows: + tmp = self.data[i][j] + self.data[i][j] = self.data[j][i] + self[j][i] = tmp + else: + self.data[j].append(self.data[i][j]) + self.data[i].remove(self.data[i][j]) + + tmp = self.rows + self.rows = self.columns + self.columns = tmp + + def lu(self): + if self.rows != self.columns: + raise AttributeError("only square matrices are supported for lu decomposition") + + for i in range(0, self.rows-1): + if abs(self.data[i][i]) <= self.EPS: + #print(self.data[i][i]) + raise ArithmeticError("Pivot element equals zero.") + for j in range(i+1, self.rows): + self.data[j][i] /= self.data[i][i] + for k in range(i+1, self.rows): + self.data[j][k] -= self.data[j][i]*self.data[i][k] + #round(self) + + def lup(self): + if self.rows != self.columns: + raise AttributeError("only square matrices are supported for lup decomposition") + + self.init_p() + for i in range(0, self.rows-1): + + max_idx = i + for k in range(i+1, self.columns): + # Find max pivot in column + if abs(self.data[k][i]) > abs(self.data[max_idx][i]): + max_idx = k + + if abs(self.data[max_idx][i]) <= self.EPS: + raise ArithmeticError("Pivot element equals zero.") + + if max_idx != i: + self.switch_row(i, max_idx) + + for j in range(i+1, self.rows): + self.data[j][i] /= self.data[i][i] + for k in range(i+1, self.rows): + self.data[j][k] -= self.data[j][i] * self.data[i][k] + #round(self) + + def get_u(self): + u_mat = copy.deepcopy(self) + for i in range(self.rows): + for j in range(self.columns): + if j < i: + u_mat[i][j] = 0 + return u_mat + + def get_l(self): + l_mat = copy.deepcopy(self) + for i in range(self.rows): + for j in range(self.columns): + if j > i: + l_mat[i][j] = 0 + elif j == i: + l_mat[i][j] = 1 + return l_mat + + def forward_substitution(self, b): + if not isinstance(b, Matrix): + raise AttributeError("unsupported parameter type '{}' for parameter b, b has to be Matrix".format(type(b))) + + if self.rows != self.columns: + raise AttributeError("only square matrices are supported for lu decomposition") + + b.data = (self.p * b).data + + for i in range(0, self.rows-1): + for j in range(i+1, self.rows): + b.data[j][0] -= self.data[j][i] * b[i][0] + + #round(b) + + def back_substitution(self, b): + if not isinstance(b, Matrix): + raise AttributeError("unsupported parameter type '{}' for parameter b, b has to be Matrix".format(type(b))) + + if self.rows != self.columns: + raise AttributeError("only square matrices are supported for lu decomposition") + + for i in range(self.rows-1, -1, -1): + if abs(self.data[i][i]) < self.EPS: + raise AttributeError("Matrix is singular.") + b.data[i][0] /= self.data[i][i] + for j in range(0, i): + b.data[j][0] -= self.data[j][i] * b.data[i][0] + + #round(b) + + + + + + def getelem(self, x, y): + """ + Returns the element of the matrix located on the (x,y) + :param x: x coordinate of the element + :param y: y coordinate of the element + :return: value or None if the element is missing + """ + try: + return self.data[x][y] + except IndexError: + return None + + def switch_row(self, first, second): + """ + Switch two rows of the matrix + :param first: Index of the first row + :param second: Index of the second row + :return: None if index out of range + """ + if first >= self.rows or second >= self.rows: + # Security against index out of range + return None + + try: + tmp = copy.deepcopy(self.data[first]) + self.data[first] = self.data[second] + self.data[second] = tmp + + except IndexError: + return None + + try: + tmp = copy.deepcopy(self.p[first]) + self.p[first] = self.p[second] + self.p[second] = tmp + except IndexError: + pass + + def switch_column(self, first, second): + """ + Switch two columns of the matrix + :param first: Index of the first column + :param second: Index of the second column + :return: None if index out of range + """ + if first >= self.columns or second >= self.columns: + # Security against index out of range + return None + + for i in range(0, self.rows): + try: + tmp = self.data[i][first] + self.data[i][first] = self.data[i][second] + self.data[i][second] = tmp + except IndexError: + return None + + def outputfile(self, filename): + with open(filename, "w") as f: + print(str(self), file=f) + + @classmethod + def fromtextfile(cls, file): + data = [] + with open(file) as f: + i = 0 + for line in f: + tmp = [] + j = 0 + line_split = line.split() + for line_char in line_split: + try: + value_tmp = float(line_char) + #if abs(value_tmp) < cls.EPS: + # value_tmp = 0 + tmp.append(value_tmp) + except ValueError: + tmp.append(0) + + j+=1 + data.append(tmp) + i+=1 + + matrix = Matrix(data=data) + return matrix + + @classmethod + def get_identity_matrix(self, n): + data = [] + + for i in range(n): + tmp = [] + for j in range(n): + if j == i: + tmp.append(1) + else: + tmp.append(0) + data.append(tmp) + matrix = Matrix(data=data) + return matrix \ No newline at end of file diff --git a/matrix/tests.py b/matrix/tests.py new file mode 100644 index 0000000..113130c --- /dev/null +++ b/matrix/tests.py @@ -0,0 +1,64 @@ +from unittest import TestCase +from matrix.models import Matrix +from decimal import Decimal + + + +class MatrixTestCase(TestCase): + + + def setUp(self): + self.matrix = Matrix.fromtextfile("../test_files/test.txt") + + def test_init(self): + + + tmp = [[12.5, 3.0, 9.0, 2.0], [4.0, 5.0, 6.0, 7.0], [8.0, 9.0, 10.0, 11.0]] + matrix_test = Matrix(data=tmp) + + self.assertEqual(self.matrix, matrix_test) + + def test_add(self): + + matrix_expected = Matrix(data=[[13.5, 5.0, 12.0, 6.0], [8.0, 8.0, 8.0, 8.0], [9.1, 11.2, 13.3, 15.4]]) + + matrix_test = Matrix(data=[[1.0, 2.0, 3.0, 4.0], [4.0, 3.0, 2.0, 1.0], [1.1, 2.2, 3.3, 4.4]]) + + ans_matrix = self.matrix + matrix_test + + self.assertRaises(AttributeError, self.matrix.__add__, 5.0) + + self.assertEqual(ans_matrix, matrix_expected) + + def test_sub(self): + + matrix_expected = Matrix(data=[[11.5, 1.0, 6.0, -2.0], [0.0, 2.0, 4.0, 6.0], [6.9, 6.8, 6.7, 6.6]]) + + matrix_test = Matrix(data=[[1.0, 2.0, 3.0, 4.0], [4.0, 3.0, 2.0, 1.0], [1.1, 2.2, 3.3, 4.4]]) + + ans_matrix = self.matrix - matrix_test + + self.assertRaises(AttributeError, self.matrix.__sub__, 5.0) + + self.assertEqual(ans_matrix, matrix_expected) + + def test_mul(self): + + matrix_test = Matrix(data=[[1.0, 1.0], [2.0, 2.0], [3.0, 3.0], [4.0, 4.0]]) + matrix_expected = Matrix(data=[[53.5, 53.5], [60.0, 60.0], [100.0, 100.0]]) + + matrix_expected_scalar = Matrix(data=[[41.25, 9.9, 29.7, 6.6], [13.2, 16.5, 19.8, 23.1], [26.4, 29.7, 33, 36.3]]) + + ans_matrix = self.matrix * matrix_test + ans_matrix_scalar = self.matrix * 3.3 + + round(ans_matrix_scalar, 2) + + self.assertEqual(ans_matrix, matrix_expected) + self.assertEqual(ans_matrix_scalar, matrix_expected_scalar) + + def test_lu_pivot_zero(self): + matrix_test_A = Matrix(data=[[1, 1, 1], [1, 1, 3], [1, 3, 3]]) + matrix_test_b = Matrix(data=[[0.5], [2], [1]]) + + self.assertRaises(ArithmeticError, matrix_test_A.lu) \ No newline at end of file diff --git a/testScript.py b/testScript.py new file mode 100644 index 0000000..5252263 --- /dev/null +++ b/testScript.py @@ -0,0 +1,65 @@ +from matrix.models import Matrix + +''' +Ax = B +A = a_matrix +B = b_matrix +''' + +a_matrix = ["zad1_a.txt", "zad2_a.txt", "zad3_a.txt", "zad4_a.txt", "zad5_a.txt"] +b_matrix = ["zad1_b.txt", "zad2_b.txt", "zad3_b.txt", "zad4_b.txt", "zad5_b.txt"] + +for i in range(0, len(a_matrix)): + """ + For all matrices calculate LU, LUP, forward and backward substitution + """ + + try: + matrix = Matrix.fromtextfile("./test_files/" + a_matrix[i]) + b = Matrix.fromtextfile("./test_files/" + b_matrix[i]) + except FileNotFoundError: + print("Missing file/s {}, {}...\n".format(a_matrix[i], b_matrix[i])) + continue + + print("\n\n**********{} + {}**********\n".format(a_matrix[i], b_matrix[i])) + + try: + print("############LU:############") + matrix.lu() + print("A:") + print(str(matrix)) + matrix.forward_substitution(b) + print("\n############LU: Forward substitution:####") + print("Y:") + print(str(b)) + matrix.back_substitution(b) + print("\n############LU: Backward substitution:####") + print("X:") + print(str(b)) + print("############END############\n\n") + + except Exception as err: + print(err) + + try: + # Reload matrices + matrix = Matrix.fromtextfile("./test_files/" + a_matrix[i]) + b = Matrix.fromtextfile("./test_files/" + b_matrix[i]) + + print("############LUP:############") + matrix.lup() + print("A:") + print(str(matrix)) + matrix.forward_substitution(b) + print("\n############LUP: Forward substitution:############") + print("Y:") + print(str(b)) + matrix.back_substitution(b) + print("\n############LUP: Backward substitution:############") + print("X:") + print(str(b)) + print("############END############\n\n") + + except Exception as err: + print(err) + diff --git a/test_files/b1.txt b/test_files/b1.txt new file mode 100644 index 0000000..40c71af --- /dev/null +++ b/test_files/b1.txt @@ -0,0 +1,3 @@ +25 +10 +10 \ No newline at end of file diff --git a/test_files/b2.txt b/test_files/b2.txt new file mode 100644 index 0000000..3ac9e24 --- /dev/null +++ b/test_files/b2.txt @@ -0,0 +1,3 @@ +4 +3 +8 \ No newline at end of file diff --git a/test_files/test.txt b/test_files/test.txt new file mode 100644 index 0000000..1407780 --- /dev/null +++ b/test_files/test.txt @@ -0,0 +1,3 @@ +12.5 3.0 9 2 +4 5 6 7 +8 9 10 11 \ No newline at end of file diff --git a/test_files/test2.txt b/test_files/test2.txt new file mode 100644 index 0000000..924007d --- /dev/null +++ b/test_files/test2.txt @@ -0,0 +1,3 @@ +12.5 3.0 9 1 +4 5 6 1 +8 9 10 1 \ No newline at end of file diff --git a/test_files/zad1_a.txt b/test_files/zad1_a.txt new file mode 100644 index 0000000..fbee3ed --- /dev/null +++ b/test_files/zad1_a.txt @@ -0,0 +1,3 @@ +3 9 6 +4 12 12 +1 -1 1 \ No newline at end of file diff --git a/test_files/zad1_b.txt b/test_files/zad1_b.txt new file mode 100644 index 0000000..a204ba7 --- /dev/null +++ b/test_files/zad1_b.txt @@ -0,0 +1,3 @@ +12 +12 +1 \ No newline at end of file diff --git a/test_files/zad2_a.txt b/test_files/zad2_a.txt new file mode 100644 index 0000000..86bb2a3 --- /dev/null +++ b/test_files/zad2_a.txt @@ -0,0 +1,3 @@ +1 2 3 +4 5 6 +7 8 9 \ No newline at end of file diff --git a/test_files/zad2_b.txt b/test_files/zad2_b.txt new file mode 100644 index 0000000..5f5fbe7 --- /dev/null +++ b/test_files/zad2_b.txt @@ -0,0 +1,3 @@ +1 +2 +3 \ No newline at end of file diff --git a/test_files/zad3_a.txt b/test_files/zad3_a.txt new file mode 100644 index 0000000..588ab8e --- /dev/null +++ b/test_files/zad3_a.txt @@ -0,0 +1,3 @@ +0.000001 3000000 2000000 +1000000 2000000 3000000 +2000000 1000000 2000000 diff --git a/test_files/zad3_b.txt b/test_files/zad3_b.txt new file mode 100644 index 0000000..44ec1ef --- /dev/null +++ b/test_files/zad3_b.txt @@ -0,0 +1,3 @@ +12000000.000001 +14000000 +10000000 \ No newline at end of file diff --git a/test_files/zad4_a.txt b/test_files/zad4_a.txt new file mode 100644 index 0000000..04e463e --- /dev/null +++ b/test_files/zad4_a.txt @@ -0,0 +1,3 @@ +0 1 2 +2 0 3 +3 5 1 \ No newline at end of file diff --git a/test_files/zad4_b.txt b/test_files/zad4_b.txt new file mode 100644 index 0000000..210e91a --- /dev/null +++ b/test_files/zad4_b.txt @@ -0,0 +1,3 @@ +6 +9 +3 \ No newline at end of file diff --git a/test_files/zad5_a.txt b/test_files/zad5_a.txt new file mode 100644 index 0000000..b4b3524 --- /dev/null +++ b/test_files/zad5_a.txt @@ -0,0 +1,3 @@ +4000000000 1000000000 3000000000 +4 2 7 +0.0000000003 0.0000000005 0.0000000002 \ No newline at end of file diff --git a/test_files/zad5_b.txt b/test_files/zad5_b.txt new file mode 100644 index 0000000..3443e2d --- /dev/null +++ b/test_files/zad5_b.txt @@ -0,0 +1,3 @@ +9000000000 +15 +0.0000000015 \ No newline at end of file