Files
2026-10-22 15:02:03 +02:00

155 lines
4.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
from collections.abc import Sequence
import math
Matrix = Sequence[Sequence[float]]
def matmul(left: Matrix, right: Matrix) -> list[list[float]]:
"""Multiply two matrices and return a new matrix.
Args:
left: Matrix of shape (rows_left, cols_left).
right: Matrix of shape (rows_right, cols_right).
Returns:
A new matrix of shape (rows_left, cols_right).
Raises:
ValueError: If either input is empty, ragged, or if
cols_left != rows_right.
"""
# Validate first matrix (rows and columns), raise if invalid.
_validate(left, "left")
# Validate second matrix for similar conditions.
_validate(right, "right")
# Number of columns in left matrix.
cols_left = len(left[0])
# Number of rows in right matrix.
rows_right = len(right)
# Ensure the inner dimensions are compatible.
if cols_left != rows_right:
raise ValueError(
f"dimension mismatch: left has {cols_left} columns, "
f"right has {rows_right} rows"
)
# Number of columns in right matrix.
cols_right = len(right[0])
# Prepare result container: rows of left × columns of right.
result: list[list[float]] = []
# Iterate over rows of left matrix.
for i in range(len(left)):
row: list[float] = []
# Iterate over columns of right matrix.
for j in range(cols_right):
total = 0.0
# Accumulate dot product of row i of left and column j of right.
for k in range(cols_left):
total += left[i][k] * right[k][j]
row.append(total)
result.append(row)
return result
def rot_2D(theta: float) -> list[list[float]]:
"""Return the 2‑D rotation matrix for a given angle.
Parameters
theta
Rotation angle in radians.
Returns
list[list[float]]
2×2 rotation matrix ``[[cosθ, -sinθ], [sinθ, cosθ]]``.
"""
c = math.cos(theta)
s = math.sin(theta)
return [[c, -s], [s, c]]
def _validate(matrix: Matrix, name: str) -> None:
"""Check that *matrix* is a non‑empty, rectangular 2D sequence.
Validation rules:
* The outer sequence must contain at least one row.
* Every row must have the same number of columns.
* No empty rows are allowed.
Raises:
ValueError: If the matrix is empty, has zero columns, or is ragged.
"""
if len(matrix) == 0:
raise ValueError(f"{name} matrix is empty")
width = len(matrix[0])
if width == 0:
raise ValueError(f"{name} matrix has no columns")
for index, row in enumerate(matrix):
if len(row) != width:
raise ValueError(
f"{name} matrix is ragged: row 0 has {width} columns, "
f"row {index} has {len(row)}"
)
def transpose(matrix: Matrix) -> list[list[float]]:
"""Transponieren: Alle Zeilen werden zu Spalten und alle Spalten zu Zeilen.
Parameters
matrix
Matrix in Form ``Sequence[Sequence[float]]``.
Returns
list[list[float]]
Die transponierte Matrix.
Raises
ValueError
Wenn ``matrix`` leer ist oder nicht gleichmäßig viele Spalten
in den Zeilen hat.
"""
_validate(matrix, "matrix")
rows = len(matrix)
cols = len(matrix[0])
transposed: list[list[float]] = []
for c in range(cols):
new_row: list[float] = []
for r in range(rows):
new_row.append(matrix[r][c])
transposed.append(new_row)
if __name__ == "__main__":
# Example 1: standard 2x3 * 3x2 multiplication.
a = [
[1, 2, 3],
[4, 5, 6],
]
b = [
[7, 8],
[9, 10],
[11, 12],
]
expected = [
[58, 64],
[139, 154],
]
assert matmul(a, b) == expected, f"example 1 failed: {matmul(a, b)}"
print("example 1 passed:", matmul(a, b))
# Example 2: multiplication with the identity matrix changes nothing.
identity = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
]
c = [
[2, -1, 0],
[0, 3, 4],
[5, 6, -2],
]
assert matmul(c, identity) == c, "example 2 failed"
print("example 2 passed:", matmul(c, identity))
# Example 3: mismatched dimensions must raise ValueError.
try:
matmul([[1, 2, 3]], [[1, 2]])
except ValueError as error:
print("example 3 passed:", error)