144 lines
3.8 KiB
Python
144 lines
3.8 KiB
Python
|
|
import math
|
|
|
|
|
|
def _is_rectangular(matrix: list[list[float]]) -> bool:
|
|
"""Check whether a matrix is non-empty and rectangular."""
|
|
if not matrix:
|
|
return False
|
|
|
|
first_length = len(matrix[0])
|
|
return first_length > 0 and all(
|
|
len(row) == first_length for row in matrix
|
|
)
|
|
|
|
|
|
def matmul(
|
|
a: list[list[float]],
|
|
b: list[list[float]],
|
|
) -> list[list[float]]:
|
|
"""Multiply two matrices and return their product.
|
|
|
|
Raises:
|
|
ValueError: If a matrix is invalid or dimensions do not match.
|
|
"""
|
|
if not _is_rectangular(a) or not _is_rectangular(b):
|
|
raise ValueError("Both matrices must be non-empty and rectangular.")
|
|
|
|
if len(a[0]) != len(b):
|
|
raise ValueError("Number of columns in a must equal rows in b.")
|
|
|
|
result = []
|
|
for row in a:
|
|
result_row = []
|
|
for column_index in range(len(b[0])):
|
|
total = sum(
|
|
row[k] * b[k][column_index]
|
|
for k in range(len(b))
|
|
)
|
|
result_row.append(total)
|
|
result.append(result_row)
|
|
|
|
return result
|
|
|
|
|
|
def transpose(matrix: list[list[float]]) -> list[list[float]]:
|
|
"""Return the transpose of a matrix.
|
|
|
|
Raises:
|
|
ValueError: If the matrix is invalid.
|
|
"""
|
|
if not _is_rectangular(matrix):
|
|
raise ValueError("Matrix must be non-empty and rectangular.")
|
|
|
|
return [
|
|
[matrix[row][column] for row in range(len(matrix))]
|
|
for column in range(len(matrix[0]))
|
|
]
|
|
|
|
|
|
def rot_2D(theta: float) -> list[list[float]]:
|
|
"""Create a 2D rotation matrix for an angle in radians."""
|
|
cos_theta = math.cos(theta)
|
|
sin_theta = math.sin(theta)
|
|
|
|
return [
|
|
[cos_theta, -sin_theta],
|
|
[sin_theta, cos_theta],
|
|
]
|
|
|
|
|
|
def rotate(matrix: list[list[float]]) -> list[list[float]]:
|
|
"""Rotate a matrix 90 degrees clockwise.
|
|
|
|
Raises:
|
|
ValueError: If the matrix is invalid.
|
|
"""
|
|
if not _is_rectangular(matrix):
|
|
raise ValueError("Matrix must be non-empty and rectangular.")
|
|
|
|
return [
|
|
[
|
|
matrix[row][column]
|
|
for row in range(len(matrix) - 1, -1, -1)
|
|
]
|
|
for column in range(len(matrix[0]))
|
|
]
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Test 1: Matrix multiplication
|
|
a = [[1, 2, 3], [4, 5, 6]]
|
|
b = [[7, 8], [9, 10], [11, 12]]
|
|
assert matmul(a, b) == [[58, 64], [139, 154]]
|
|
print("Matrix multiplication: passed")
|
|
print(matmul(a, b))
|
|
|
|
# Test 2: Matrix multiplication with another example
|
|
assert matmul([[1, 2]], [[3], [4]]) == [[11]]
|
|
print("Second multiplication test: passed")
|
|
|
|
# Test 3: Transpose
|
|
matrix = [[1, 2, 3], [4, 5, 6]]
|
|
assert transpose(matrix) == [[1, 4], [2, 5], [3, 6]]
|
|
print("Transpose: passed")
|
|
|
|
# Test 4: Rotation clockwise
|
|
assert rotate(matrix) == [[4, 1], [5, 2], [6, 3]]
|
|
print("Rotation: passed")
|
|
|
|
# Test 5: 2D rotation matrix
|
|
rotation = rot_2D(0.01)
|
|
assert len(rotation) == 2 and len(rotation[0]) == 2
|
|
print("2D rotation matrix: passed")
|
|
print(rotation)
|
|
|
|
# Test 6: Invalid multiplication dimensions
|
|
try:
|
|
matmul([[1, 2], [3, 4]], [[5, 6, 7]])
|
|
except ValueError:
|
|
print("Invalid multiplication dimensions: handled")
|
|
else:
|
|
raise AssertionError("Expected ValueError for matmul")
|
|
|
|
# Test 7: Invalid matrices
|
|
for function in (transpose, rotate):
|
|
try:
|
|
function([])
|
|
except ValueError:
|
|
print(f"{function.__name__}: invalid matrix handled")
|
|
else:
|
|
raise AssertionError(
|
|
f"Expected ValueError for {function.__name__}"
|
|
)
|
|
|
|
# Test 8: Non-rectangular matrix
|
|
try:
|
|
transpose([[1, 2], [3]])
|
|
except ValueError:
|
|
print("Non-rectangular matrix: handled")
|
|
else:
|
|
raise AssertionError("Expected ValueError for transpose")
|
|
|
|
print("All tests passed!")
|