Initial MatrixMania layout
This commit is contained in:
commit
970b4beeb2
4 files changed
+182
No files matched your search
@@ -0,0 +1,3 @@
|
||||
.venv/
|
||||
__pycache__/
|
||||
.env
|
||||
@@ -0,0 +1,4 @@
|
||||
# Regeln für dieses Projekt
|
||||
- Code nach PEP 8, Zeilenlänge maximal 88 Zeichen.
|
||||
- Jede Funktion und Methode hat einen Docstring und Type Hints.
|
||||
- Vor jedem Commit: `ruff check` und `ruff format`.
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
|
||||
|
||||
"""Utility functions for matrix operations.
|
||||
|
||||
This module contains:
|
||||
* ``matmul`` – standard matrix multiplication.
|
||||
* ``transpose`` – row↔column swapping.
|
||||
* ``rotate`` – 90‑degree clockwise rotations.
|
||||
|
||||
All functions use type hints and raise informative errors where
|
||||
dimensions are incompatible or matrices are non‑rectangular.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from tabulate import tabulate
|
||||
|
||||
Matrix = list[list[int | float]]
|
||||
|
||||
|
||||
def matmul(a: Matrix, b: Matrix) -> Matrix:
|
||||
"""Return the matrix product a @ b.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
a : Matrix
|
||||
Left‑hand matrix.
|
||||
b : Matrix
|
||||
Right‑hand matrix.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Matrix
|
||||
Product matrix.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If matrices are not rectangular or dimensions are incompatible.
|
||||
"""
|
||||
def _rect(mat: Matrix) -> bool:
|
||||
return all(len(row) == len(mat[0]) for row in mat)
|
||||
|
||||
if not a or not b:
|
||||
raise ValueError("matrices may not be empty")
|
||||
if not _rect(a) or not _rect(b):
|
||||
raise ValueError("input matrices must be rectangular")
|
||||
|
||||
r1, c1 = len(a), len(a[0])
|
||||
r2, c2 = len(b), len(b[0])
|
||||
if c1 != r2:
|
||||
raise ValueError("first matrix columns must equal second matrix rows")
|
||||
|
||||
result: Matrix = [[0 for _ in range(c2)] for _ in range(r1)]
|
||||
for i in range(r1):
|
||||
for j in range(c2):
|
||||
s = 0
|
||||
for k in range(c1):
|
||||
s += a[i][k] * b[k][j]
|
||||
result[i][j] = s
|
||||
return result
|
||||
|
||||
|
||||
def transpose(matrix: Matrix) -> Matrix:
|
||||
"""Return the transpose of a 2‑D matrix.
|
||||
|
||||
The transpose operation swaps rows and columns: each row in the
|
||||
result is a column from the input matrix.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
matrix : Matrix
|
||||
Two‑dimensional list of numbers. All rows must contain the
|
||||
same number of columns.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Matrix
|
||||
Transposed matrix.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If *matrix* is empty or if the rows have differing lengths.
|
||||
"""
|
||||
if not matrix:
|
||||
raise ValueError("transpose requires a non‑empty matrix")
|
||||
if any(len(row) != len(matrix[0]) for row in matrix):
|
||||
raise ValueError("matrix must be rectangular to transpose")
|
||||
rows, cols = len(matrix), len(matrix[0])
|
||||
return [[matrix[i][j] for i in range(rows)] for j in range(cols)]
|
||||
|
||||
|
||||
def rot_2D(theta: float) -> list[list[float]]:
|
||||
"""Return the 2‑D rotation matrix for angle *theta* in degrees.
|
||||
|
||||
The matrix is
|
||||
``[[cos(θ), -sin(θ)],
|
||||
[sin(θ), cos(θ)]]``
|
||||
where θ is interpreted as a clockwise rotation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
theta : float
|
||||
Rotation angle in degrees (clockwise).
|
||||
|
||||
Returns
|
||||
-------
|
||||
list[list[float]]
|
||||
2x2 rotation matrix.
|
||||
"""
|
||||
import math
|
||||
rad = math.radians(theta)
|
||||
cos_t = math.cos(rad)
|
||||
sin_t = math.sin(rad)
|
||||
return [[cos_t, -sin_t], [sin_t, cos_t]]
|
||||
|
||||
|
||||
def rotate(matrix: Matrix, degrees: float) -> Matrix:
|
||||
"""Rotate the matrix clockwise by 0, 90, 180, or 270 degrees.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
matrix : Matrix
|
||||
degrees : float
|
||||
Must be one of 0, 90, 180, 270.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Matrix
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If *degrees* is unsupported.
|
||||
"""
|
||||
if degrees not in (0, 90, 180, 270):
|
||||
raise ValueError("degrees must be one of 0, 90, 180, 270")
|
||||
if not matrix:
|
||||
return []
|
||||
n_rows, n_cols = len(matrix), len(matrix[0])
|
||||
if degrees == 0:
|
||||
return [row[:] for row in matrix]
|
||||
if degrees == 90:
|
||||
return [[matrix[n_rows - 1 - j][i] for j in range(n_rows)] for i in range(n_cols)]
|
||||
if degrees == 180:
|
||||
return [[matrix[n_rows - 1 - i][n_cols - 1 - j] for j in range(n_cols)] for i in range(n_rows)]
|
||||
# 270
|
||||
return [[matrix[j][n_cols - 1 - i] for j in range(n_rows)] for i in range(n_cols)]
|
||||
|
||||
if __name__ == "__main__":
|
||||
# matmul tests
|
||||
a1 = [[1, 2], [3, 4]]
|
||||
b1 = [[5, 6], [7, 8]]
|
||||
assert matmul(a1, b1) == [[19, 22], [43, 50]], "matmul example 1 failed"
|
||||
a2 = [[1], [2], [3]]
|
||||
b2 = [[4, 5, 6]]
|
||||
assert matmul(a2, b2) == [[4, 5, 6], [8, 10, 12], [12, 15, 18]], "matmul example 2 failed"
|
||||
|
||||
# transpose test
|
||||
mat = [[1, 2, 3], [4, 5, 6]]
|
||||
assert transpose(mat) == [[1, 4], [2, 5], [3, 6]], "transpose failed"
|
||||
|
||||
# rotation tests
|
||||
mat3 = [[1, 2], [3, 4], [5, 6]]
|
||||
assert rotate(mat3, 90) == [[5, 3, 1], [6, 4, 2]], "rotate 90 failed"
|
||||
assert rotate(mat3, 180) == [[6, 5], [4, 3], [2, 1]], "rotate 180 failed"
|
||||
assert rotate(mat3, 270) == [[2, 4, 6], [1, 3, 5]], "rotate 270 failed"
|
||||
assert rotate(mat3, 0) == mat3, "rotate 0 failed"
|
||||
|
||||
print(tabulate(rot_2D(1.0)))
|
||||
@@ -0,0 +1,4 @@
|
||||
[project]
|
||||
name = "matrixmania"
|
||||
version = "0.1.0"
|
||||
dependencies = ["tabulate", "ruff"]
|
||||
Reference in new issue
Block a user