From 85e04b24b5396839c58a640f268b55bbfee61e3c Mon Sep 17 00:00:00 2001 From: naimabannert Date: Sat, 17 Oct 2026 13:53:10 +0200 Subject: [PATCH] Erster Stand: compute.py und Projektkonfiguration --- .gitignore | 3 + AGENTS.md | 6 ++ compute.py | 232 +++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 4 + 4 files changed, 245 insertions(+) create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 compute.py create mode 100644 pyproject.toml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d1eb6e8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.venv/ +__pycache__/ +.env \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..97552bb --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,6 @@ + +# Regeln für dieses Projekt + +- Code nach PEP 8, Zeilenlänge maximal 88 Zeichen. +- Jede Funktion und Methode hat einen Docstring und Typannotationen. +- Vor jedem Commit: `ruff check` und `ruff format`. diff --git a/compute.py b/compute.py new file mode 100644 index 0000000..9667c62 --- /dev/null +++ b/compute.py @@ -0,0 +1,232 @@ + +"""Functions for matrix calculations.""" + + +def _validate_matrix(matrix: list[list[int | float]], name: str) -> tuple[int, int]: + """Validate a non-empty, rectangular matrix with numeric entries. + + Args: + matrix: Matrix represented as a nested list. + name: Name of the matrix for error messages. + + Returns: + A tuple containing the number of rows and columns. + + Raises: + TypeError: If the matrix or its entries have invalid types. + ValueError: If the matrix is empty, has empty rows, + or contains rows with different lengths. + """ + if not isinstance(matrix, list): + raise TypeError(f"{name} must be a list of lists") + + if not matrix: + raise ValueError(f"{name} must not be empty") + + for row_index, row in enumerate(matrix): + if not isinstance(row, list): + raise TypeError(f"Row {row_index} of {name} must be a list") + + column_count = len(matrix[0]) + + if column_count == 0: + raise ValueError(f"{name} must not contain empty rows") + + for row_index, row in enumerate(matrix): + if len(row) != column_count: + raise ValueError(f"Rows of {name} must have equal lengths") + + for value in row: + if isinstance(value, bool) or not isinstance(value, (int, float)): + raise TypeError(f"All entries of {name} must be numbers") + + return len(matrix), column_count + + +def matmul(a: list[list[int | float]], + b: list[list[int | float]]) -> list[list[int | float]]: + """Multiply two matrices and return their product. + + Args: + a: Left matrix with m rows and n columns. + b: Right matrix with n rows and p columns. + + Returns: + The product matrix with m rows and p columns. + + Raises: + TypeError: If a matrix or an entry has an invalid type. + ValueError: If a matrix is empty, is not rectangular, + or the dimensions do not match. + """ + rows_a, columns_a = _validate_matrix(a, "a") + rows_b, columns_b = _validate_matrix(b, "b") + + if columns_a != rows_b: + raise ValueError( + f"Cannot multiply matrices with shapes " + f"{rows_a}x{columns_a} and {rows_b}x{columns_b}" + ) + + result = [] + + for i in range(rows_a): + result_row = [] + + for j in range(columns_b): + total = 0 + + for k in range(columns_a): + total += a[i][k] * b[k][j] + + result_row.append(total) + + result.append(result_row) + + return result + + +def transpose(matrix: list[list[int | float]]) -> list[list[int | float]]: + """Transpose a matrix by exchanging its rows and columns. + + Args: + matrix: A non-empty rectangular matrix. + + Returns: + The transposed matrix. + + Raises: + TypeError: If the matrix or its entries have invalid types. + ValueError: If the matrix is empty or not rectangular. + """ + rows, columns = _validate_matrix(matrix, "matrix") + + result = [] + + for j in range(columns): + new_row = [] + + for i in range(rows): + new_row.append(matrix[i][j]) + + result.append(new_row) + + return result + + +def rotate(matrix: list[list[int | float]]) -> list[list[int | float]]: + """Rotate a matrix 90 degrees clockwise. + + Args: + matrix: A non-empty rectangular matrix. + + Returns: + The matrix rotated 90 degrees clockwise. + + Raises: + TypeError: If the matrix or its entries have invalid types. + ValueError: If the matrix is empty or not rectangular. + """ + transposed = transpose(matrix) + return [row[::-1] for row in transposed] + + +import math + + +def rot_2D(theta: float) -> list[list[float]]: + """Return the 2D rotation matrix for an angle in radians. + + Args: + theta: Rotation angle in radians. + + Returns: + A 2x2 rotation matrix. + """ + cos_theta = math.cos(theta) + sin_theta = math.sin(theta) + + return [ + [cos_theta, -sin_theta], + [sin_theta, cos_theta] + ] + + +if __name__ == "__main__": + # Test 1: Multiplication of two 2x2 matrices + result1 = matmul([[1, 2], [3, 4]], [[5, 6], [7, 8]]) + expected1 = [[19, 22], [43, 50]] + assert result1 == expected1 + print("Test 1 passed:", result1) + + # Test 2: Multiplication of a 2x3 matrix and a 3x2 matrix + result2 = matmul([[1, 2, 3], [4, 5, 6]], + [[7, 8], [9, 10], [11, 12]]) + expected2 = [[58, 64], [139, 154]] + assert result2 == expected2 + print("Test 2 passed:", result2) + + # Test 3: Incompatible dimensions + try: + matmul([[1, 2], [3, 4]], [[1, 2]]) + except ValueError: + print("Test 3 passed: Invalid dimensions detected") + else: + raise AssertionError("Test 3 failed: ValueError was expected") + + + # Test 4: Transpose a 2x3 matrix + original = [[1, 2, 3], [4, 5, 6]] + transposed = transpose(original) + expected = [[1, 4], [2, 5], [3, 6]] + + assert transposed == expected + print("Test 4 passed:", transposed) + + # Test 5: Transpose a square matrix + original_square = [[1, 2], [3, 4]] + expected_square = [[1, 3], [2, 4]] + + assert transpose(original_square) == expected_square + print("Test 5 passed:", expected_square) + + + # Test 6: Rotate a 2x3 matrix clockwise + original = [[1, 2, 3], [4, 5, 6]] + expected = [[4, 1], [5, 2], [6, 3]] + + assert rotate(original) == expected + print("Test 6 passed:", rotate(original)) + + # Test 7: Rotate a square matrix clockwise + original_square = [[1, 2], [3, 4]] + expected_square = [[3, 1], [4, 2]] + + assert rotate(original_square) == expected_square + print("Test 7 passed:", rotate(original_square)) + + # Test 8: Rotation by 0 degrees (identity matrix) + rotation_0 = rot_2D(0) + expected_0 = [[1.0, 0.0], [0.0, 1.0]] + + for i in range(2): + for j in range(2): + assert math.isclose( + rotation_0[i][j], expected_0[i][j], abs_tol=1e-9 + ) + + print("Test 8 passed:", rotation_0) + + # Test 9: Rotation by 90 degrees counterclockwise + rotation_90 = rot_2D(math.pi / 2) + expected_90 = [[0.0, -1.0], [1.0, 0.0]] + + for i in range(2): + for j in range(2): + assert math.isclose( + rotation_90[i][j], expected_90[i][j], abs_tol=1e-9 + ) + + print("Test 9 passed:", rotation_90) + + diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..54dbd8c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,4 @@ + +[project] +name = "matrixmania" +version = "0.1.0"