diff --git a/matrix_calculus/matrix_processor.py b/matrix_calculus/matrix_processor.py new file mode 100644 index 0000000..b3f9652 --- /dev/null +++ b/matrix_calculus/matrix_processor.py @@ -0,0 +1,70 @@ +from __future__ import annotations + + +class MatrixProcessor: + @staticmethod + def gaussian_elimination(matrix: list[list[float]]) -> list[list[float]]: + M = [row[:] for row in matrix] + n = len(M) + eps = 1e-12 + + def swap_rows(row1, row2): + for col in range(n): + M[row1][col], M[row2][col] = M[row2][col], M[row1][col] + + for diagonal_element in range(n): + + diagonal_element_value = M[diagonal_element][diagonal_element] + + if abs(diagonal_element_value) < eps: + for row in range(diagonal_element + 1, n): + if abs(M[row][diagonal_element]) > eps: + diagonal_element_value = M[row][diagonal_element] + + swap_rows(diagonal_element, row) + + if abs(diagonal_element_value) < eps: + raise ValueError("Matrix is singular") + + for col in range(diagonal_element, n): + M[diagonal_element][col] /= diagonal_element_value + + for row in range(diagonal_element + 1, n): + factor = M[row][diagonal_element] + for col in range(diagonal_element, n): + M[row][col] -= factor * M[diagonal_element][col] + + return M + + @staticmethod + def gaussian_elimination_pivoting(matrix: list[list[float]]) -> list[list[float]]: + M = [row[:] for row in matrix] + n = len(M) + eps = 1e-12 + + def swap_rows(row1, row2): + for col in range(n): + M[row1][col], M[row2][col] = M[row2][col], M[row1][col] + + for diagonal_element in range(n): + + max_pivot_value = abs(M[diagonal_element][diagonal_element]) + highest_pivot_row = diagonal_element + for row in range(diagonal_element + 1, n): + if abs(M[row][diagonal_element]) > max_pivot_value: + max_pivot_value = abs(M[row][diagonal_element]) + highest_pivot_row = row + + if highest_pivot_row != diagonal_element: + swap_rows(diagonal_element, highest_pivot_row) + + pivot_value = M[diagonal_element][diagonal_element] + if abs(pivot_value) < eps: + raise ValueError("Matrix is singular") + + for row in range(diagonal_element + 1, n): + factor = M[row][diagonal_element] / pivot_value + for col in range(diagonal_element, n): + M[row][col] -= factor * M[diagonal_element][col] + + return M diff --git a/tests/gaussian_elimination_test.py b/tests/gaussian_elimination_test.py new file mode 100644 index 0000000..c78e090 --- /dev/null +++ b/tests/gaussian_elimination_test.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +import pytest # noqa: F401 + +from matrix_calculus.matrix_processor import MatrixProcessor + + +def test_gaussian_elimination(): + # Test 1x1 matrix + matrix = [[2]] + res = MatrixProcessor.gaussian_elimination(matrix) + assert res == [[1.0]] + + # Test 2x2 matrix + matrix = [[2, 4], [1, 3]] + res = MatrixProcessor.gaussian_elimination(matrix) + assert res == [[1.0, 2.0], [0.0, 1.0]] + + # Test 3x3 singular matrix + with pytest.raises(ValueError): + matrix = [[2, 4, 6], [1, 3, 5], [0, 2, 4]] + MatrixProcessor.gaussian_elimination(matrix) + + # Test 3x3 matrix + matrix = [[2, 4, 6], [1, 2, 5], [0, 2, 4]] + res = MatrixProcessor.gaussian_elimination(matrix) + assert res == [[1.0, 2.0, 3.0], [0.0, 1.0, 2.0], [0.0, 0.0, 1.0]] + + +def test_gaussian_elimination_pivoting(): + # Test 1x1 matrix + matrix = [[2]] + res = MatrixProcessor.gaussian_elimination_pivoting(matrix) + assert res == [[2.0]] + + # Test 2x2 matrix + matrix = [[2, 4], [1, 3]] + res = MatrixProcessor.gaussian_elimination_pivoting(matrix) + assert res == [[2.0, 4.0], [0.0, 1.0]] + + # Test 3x3 singular matrix + with pytest.raises(ValueError): + matrix = [[2, 4, 6], [1, 3, 5], [0, 2, 4]] + MatrixProcessor.gaussian_elimination_pivoting(matrix) + + # Test 3x3 matrix + matrix = [[2, 4, 6], [1, 2, 5], [0, 2, 4]] + res = MatrixProcessor.gaussian_elimination_pivoting(matrix) + assert res == [[2, 4, 6], [0, 2, 4], [0, 0, 2]]