168 lines
5.0 KiB
Python
168 lines
5.0 KiB
Python
"""Compute operations for matrices.
|
||
|
||
This module provides public functions for matrix multiplication, transposition,
|
||
matrix rotation, and generating 2D rotation matrices. The implementation is
|
||
lightweight, well typed and contains detailed Google‑style documentation.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import math
|
||
from typing import List
|
||
|
||
|
||
def matmul(
|
||
matrix_a: List[List[float]], matrix_b: List[List[float]]
|
||
) -> List[List[float]]:
|
||
"""Multiply two 2D matrices.
|
||
|
||
Both matrices are expected to be non‑empty and to have consistent row
|
||
lengths. The number of columns in ``matrix_a`` must equal the number of
|
||
rows in ``matrix_b``.
|
||
|
||
Args:
|
||
matrix_a: A matrix of shape (m, n) represented as a list of m rows,
|
||
each a list of n floats.
|
||
matrix_b: A matrix of shape (n, p) represented as a list of n rows,
|
||
each a list of p floats.
|
||
|
||
Returns:
|
||
A new matrix of shape (m, p) containing the product
|
||
``matrix_a @ matrix_b``.
|
||
|
||
Raises:
|
||
ValueError: If either matrix is empty or if the column count of
|
||
``matrix_a`` does not match the row count of ``matrix_b``.
|
||
"""
|
||
|
||
# SCHRITT 1: Validierung – Prüfen, ob die Matrizen überhaupt Daten enthalten
|
||
if not matrix_a or not matrix_b:
|
||
raise ValueError("Input matrices must not be empty.")
|
||
|
||
n_cols_a = len(matrix_a[0])
|
||
|
||
# SCHRITT 2: Konsistenzprüfung für Matrix A
|
||
if any(len(row) != n_cols_a for row in matrix_a):
|
||
raise ValueError("All rows of matrix_a must have the same number of columns.")
|
||
|
||
n_rows_b = len(matrix_b)
|
||
|
||
# SCHRITT 3: Mathematische Regel prüfen: Spalten von A müssen gleich Zeilen von B sein
|
||
if n_cols_a != n_rows_b:
|
||
raise ValueError(
|
||
f"Matrix dimension mismatch: matrix_a columns {n_cols_a} != matrix_b rows {n_rows_b}"
|
||
)
|
||
|
||
n_cols_b = len(matrix_b[0])
|
||
|
||
# SCHRITT 4: Konsistenzprüfung für Matrix B
|
||
if any(len(row) != n_cols_b for row in matrix_b):
|
||
raise ValueError("All rows of matrix_b must have the same number of columns.")
|
||
|
||
# SCHRITT 5: Eigentliche Matrixmultiplikation (Skalarprodukt der Zeilen und Spalten)
|
||
result: List[List[float]] = []
|
||
for i in range(len(matrix_a)): # Gehe jede Zeile von Matrix A durch
|
||
row: List[float] = []
|
||
for j in range(n_cols_b): # Gehe jede Spalte von Matrix B durch
|
||
elem = sum(matrix_a[i][k] * matrix_b[k][j] for k in range(n_cols_a))
|
||
row.append(elem)
|
||
result.append(row)
|
||
|
||
return result
|
||
|
||
|
||
def transpose(matrix: List[List[float]]) -> List[List[float]]:
|
||
"""Return the transpose of a 2‑D matrix.
|
||
|
||
Args:
|
||
matrix: A non‑empty matrix represented as a list of rows.
|
||
|
||
Returns:
|
||
A new matrix where rows become columns.
|
||
|
||
Raises:
|
||
ValueError: If the input matrix is empty or ragged.
|
||
"""
|
||
|
||
if not matrix:
|
||
raise ValueError("Cannot transpose an empty matrix.")
|
||
|
||
n_rows = len(matrix)
|
||
n_cols = len(matrix[0])
|
||
|
||
if any(len(row) != n_cols for row in matrix):
|
||
raise ValueError("All rows of the matrix must have the same number of columns.")
|
||
|
||
return [[matrix[i][j] for i in range(n_rows)] for j in range(n_cols)]
|
||
|
||
|
||
def rotate_clockwise(matrix: List[List[float]]) -> List[List[float]]:
|
||
"""Rotate a square matrix 90 degrees clockwise.
|
||
|
||
The function works for any rectangular matrix but will produce a matrix
|
||
of dimensions (cols, rows).
|
||
|
||
Args:
|
||
matrix: A non‑empty matrix.
|
||
|
||
Returns:
|
||
The rotated matrix.
|
||
|
||
Raises:
|
||
ValueError: If the matrix is empty.
|
||
"""
|
||
|
||
if not matrix:
|
||
raise ValueError("Cannot rotate an empty matrix.")
|
||
|
||
n_rows = len(matrix)
|
||
n_cols = len(matrix[0])
|
||
|
||
if any(len(row) != n_cols for row in matrix):
|
||
raise ValueError("All rows of the matrix must have the same number of columns.")
|
||
|
||
transposed = transpose(matrix)
|
||
return [row[::-1] for row in transposed]
|
||
|
||
|
||
def rot_2D(theta: float) -> List[List[float]]:
|
||
"""Return the 2D rotation matrix for a given angle.
|
||
|
||
Calculates the 2x2 rotation matrix used in computer graphics to rotate a point
|
||
counterclockwise around the origin by angle theta (in radians).
|
||
|
||
Args:
|
||
theta: The rotation angle in radians.
|
||
|
||
Returns:
|
||
A 2x2 rotation matrix represented as a list of lists of floats:
|
||
[[cos(theta), -sin(theta)],
|
||
[sin(theta), cos(theta)]]
|
||
"""
|
||
c = math.cos(theta)
|
||
s = math.sin(theta)
|
||
return [[c, -s], [s, c]]
|
||
|
||
|
||
if __name__ == "__main__":
|
||
# Testdaten für die Demonstration
|
||
a = [[1, 2, 3], [4, 5, 6]]
|
||
b = [[7, 8], [9, 10], [11, 12]]
|
||
|
||
print("matmul(a, b):")
|
||
print(matmul(a, b))
|
||
|
||
print("transpose(a):")
|
||
print(transpose(a))
|
||
|
||
c = [[1, 2], [3, 4], [5, 6]]
|
||
print("rotate_clockwise(c):")
|
||
print(rotate_clockwise(c))
|
||
|
||
# Demonstration von rot_2D (z. B. 90 Grad = pi / 2 Radiant)
|
||
angle = math.pi / 2
|
||
print(f"rot_2D (pi / 2 radians / 90 degrees):")
|
||
rotation_matrix = rot_2D(angle)
|
||
for row in rotation_matrix:
|
||
print([round(val, 4) for val in row])
|