Files

178 lines
5.8 KiB
Python

"""Mathematische Hilfsfunktionen für Matrixoperationen."""
import math
from tabulate import tabulate
def matmul(matrix_a: list[list[float]], matrix_b: list[list[float]]) -> list[list[float]]:
"""Berechnet das Matrixprodukt zweier Matrizen (matrix_a @ matrix_b).
:param matrix_a: Die linke Matrix.
:param matrix_b: Die rechte Matrix.
:return: Die Ergebnismatrix der Multiplikation.
:raises ValueError: Wenn eine Matrix leer ist, Zeilen ungleich lang sind
oder die Spaltenanzahl von matrix_a nicht der Zeilenanzahl
von matrix_b entspricht.
"""
if not matrix_a or not matrix_b or not matrix_a[0] or not matrix_b[0]:
raise ValueError("Beide Matrizen müssen nicht-leer sein.")
rows_a, cols_a = len(matrix_a), len(matrix_a[0])
rows_b, cols_b = len(matrix_b), len(matrix_b[0])
if any(len(row) != cols_a for row in matrix_a) or any(len(row) != cols_b for row in matrix_b):
raise ValueError("Alle Zeilen einer Matrix müssen dieselbe Länge besitzen.")
if cols_a != rows_b:
raise ValueError("Matrixdimensionen stimmen für die Multiplikation nicht überein.")
result: list[list[float]] = []
for i in range(rows_a):
result_row: list[float] = []
for j in range(cols_b):
dot_product = sum(matrix_a[i][k] * matrix_b[k][j] for k in range(cols_a))
result_row.append(dot_product)
result.append(result_row)
return result
def transpose(matrix: list[list[float]]) -> list[list[float]]:
"""Transponiert eine Matrix, sodass alle Zeilen zu Spalten und Spalten zu Zeilen werden.
:param matrix: Die zu transponierende Matrix.
:return: Die transponierte Matrix.
:raises ValueError: Wenn die Matrix leer ist oder die Zeilen ungleichmäßig
viele Spalten besitzen.
"""
if not matrix or not matrix[0]:
raise ValueError("Die Matrix darf nicht leer sein.")
num_cols = len(matrix[0])
if any(len(row) != num_cols for row in matrix):
raise ValueError("Alle Zeilen müssen dieselbe Länge besitzen.")
num_rows = len(matrix)
return [
[matrix[row_idx][col_idx] for row_idx in range(num_rows)]
for col_idx in range(num_cols)
]
def rot_2d(angle: float) -> list[list[float]]:
"""Berechnet die 2D-Rotationsmatrix für einen gegebenen Drehwinkel.
Die Rotationsmatrix R(theta) lautet:
[[cos(theta), -sin(theta)],
[sin(theta), cos(theta)]]
:param angle: Der Rotationswinkel im Bogenmaß (Radiant).
:return: Die 2x2-Rotationsmatrix als verschachtelte Liste.
"""
cos_theta = math.cos(angle)
sin_theta = math.sin(angle)
return [
[cos_theta, -sin_theta],
[sin_theta, cos_theta],
]
def test_functions():
a1 = [
[3, 4, -1, 4],
[-2, 2, 5, 1],
]
b1 = [
[1, 3, -2],
[2, 5, 1],
[-1, 4, -4],
[2, 3, 6],
]
expected_c1 = [
[20, 37, 26],
[-1, 27, -8],
]
c1 = matmul(a1, b1)
assert c1 == expected_c1, f"Fehler in Beispiel 1: Erwartet {expected_c1}, erhalten {c1}"
print("Test 1 (Skript-Beispiel) erfolgreich bestanden!")
a2 = [
[1, 2],
[3, 4],
]
identity = [
[1, 0],
[0, 1],
]
c2 = matmul(a2, identity)
assert c2 == a2, f"Fehler in Beispiel 2: Erwartet {a2}, erhalten {c2}"
print("Test 2 (Einheitsmatrix) erfolgreich bestanden!")
incompatible_b = [
[1, 2],
[3, 4],
]
try:
matmul(a1, incompatible_b)
raise AssertionError("Fehlerfall wurde nicht abgefangen!")
except ValueError:
print("Test 3 (Fehlerfall inkompatible Dimensionen) erfolgreich abgefangen!")
# Tests für Transpose
example_matrix = [
[1, 2, 3],
[4, 5, 6],
]
expected_transposed = [
[1, 4],
[2, 5],
[3, 6],
]
assert transpose(example_matrix) == expected_transposed, (
f"Erwartet: {expected_transposed}, Erhalten: {transpose(example_matrix)}"
)
print("Test 4 (transpose) erfolgreich bestanden!")
# Test transpose: Leere Matrix
try:
transpose([])
raise AssertionError("Leere Matrix wurde nicht abgefangen!")
except ValueError:
print("Test 5 (transpose leere Matrix) erfolgreich abgefangen!")
# Test transpose: Ungleichmäßige Zeilenlänge
uneven_matrix = [
[1, 2, 3],
[4, 5],
]
try:
transpose(uneven_matrix)
raise AssertionError("Ungleichmäßige Matrix wurde nicht abgefangen!")
except ValueError:
print("Test 6 (transpose ungleichmäßige Matrix) erfolgreich abgefangen!")
# Test 7: Rotation um 0 Radianten (entspricht der Einheitsmatrix)
rot_0 = rot_2d(0.0)
assert math.isclose(rot_0[0][0], 1.0, abs_tol=1e-9)
assert math.isclose(rot_0[0][1], 0.0, abs_tol=1e-9)
assert math.isclose(rot_0[1][0], 0.0, abs_tol=1e-9)
assert math.isclose(rot_0[1][1], 1.0, abs_tol=1e-9)
print("Test rot_2D (0 Radiant) erfolgreich bestanden!")
# Test 8: Rotation um 90 Grad (pi / 2) -> [[0, -1], [1, 0]]
rot_90 = rot_2d(math.pi / 2)
assert math.isclose(rot_90[0][0], 0.0, abs_tol=1e-9)
assert math.isclose(rot_90[0][1], -1.0, abs_tol=1e-9)
assert math.isclose(rot_90[1][0], 1.0, abs_tol=1e-9)
assert math.isclose(rot_90[1][1], 0.0, abs_tol=1e-9)
print("Test rot_2D (pi / 2 Radiant) erfolgreich bestanden!")
# Test 9: Drehung des Vektors (1, 0) um 90 Grad ergibt (0, 1) via matmul
point = [[1.0], [0.0]]
rotated_point = matmul(rot_90, point)
assert math.isclose(rotated_point[0][0], 0.0, abs_tol=1e-9)
assert math.isclose(rotated_point[1][0], 1.0, abs_tol=1e-9)
print("Test rot_2D (Vektordrehung mit matmul) erfolgreich bestanden!")
if __name__ == "__main__":
print(tabulate(rot_2d(1.0)))