Erster Stand: compute.py und Projektkonfiguration
This commit is contained in:
commit
85e04b24b5
4 files changed
+245
No files matched your search
@@ -0,0 +1,3 @@
|
||||
.venv/
|
||||
__pycache__/
|
||||
.env
|
||||
@@ -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`.
|
||||
+232
@@ -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)
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
[project]
|
||||
name = "matrixmania"
|
||||
version = "0.1.0"
|
||||
Reference in new issue
Block a user