Files
matrixmania/compute.py
T
2026-10-15 15:16:06 +02:00

102 lines
2.7 KiB
Python

"""Matrix multiplication with explicit dimension checks."""
from __future__ import annotations
from collections.abc import Sequence
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(left, "left")
_validate(right, "right")
cols_left = len(left[0])
rows_right = len(right)
if cols_left != rows_right:
raise ValueError(
f"dimension mismatch: left has {cols_left} columns, "
f"right has {rows_right} rows"
)
cols_right = len(right[0])
result: list[list[float]] = []
for i in range(len(left)):
row: list[float] = []
for j in range(cols_right):
total = 0.0
for k in range(cols_left):
total += left[i][k] * right[k][j]
row.append(total)
result.append(row)
return result
def _validate(matrix: Matrix, name: str) -> None:
"""Check that *matrix* is a non-empty, rectangular 2D sequence."""
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)}"
)
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)
else:
raise AssertionError("example 3 failed: no ValueError raised")