Pure Python implementations of four core matrix operations — no NumPy used in the actual calculations. Each one is validated against NumPy/SciPy using floating-point tolerance comparisons, not exact equality, since determinant and LU involve division and exact equality against a floating-point reference isn't reliable.
Matrix Multiplication (matmul)
Multiplies two compatible matrices via the standard row-by-column dot product:
C_ij = Σ_k A_ik · B_kj
Checks dimensions first and raises ValueError on empty, ragged, or incompatible inputs.
Transpose (transpose_matrix)
Flips rows into columns:
(A^T)_ij = A_ji
Determinant (deter)
Recursive cofactor expansion along the first row. The determinant is a scalar that tells you things like whether a matrix is invertible — zero means singular.
This runs in O(n!) time. It's the standard from-scratch approach for learning purposes, not what NumPy/SciPy actually do internally (they use LU factorization, O(n^3)). Fine for small matrices, gets slow fast past 8x8 or so.
LU Decomposition (lu_decomposition)
Factors a square matrix into A = LU, where L is lower triangular with a unit diagonal and U is upper triangular. Uses Doolittle's method, no partial pivoting.
That last part matters: without pivoting, this fails outright (raises ValueError) on any matrix that produces a zero pivot mid-elimination, and can get numerically shaky on matrices that are solvable but would benefit from row swaps. SciPy's lu() pivots by default, so its L/U won't always match this implementation's directly, even when both are correct. The real check is whether L @ U reconstructs the original matrix, which is what this project verifies, alongside reporting whether SciPy chose to pivot on the same input so the difference is visible instead of hidden.
Validation
Every function gets run against its NumPy/SciPy counterpart and compared with numpy.allclose / math.isclose rather than ==, because floating-point results can differ in the last couple of decimal places even when both sides are right.
- Python 3.8+
- NumPy and SciPy — only needed for the validation block, the operations themselves have zero dependencies
pip install numpy scipy
from matrix_ops import matmul, transpose_matrix, deter, lu_decomposition
A = [[1, 2], [3, 4]]
B = [[5, 6], [7, 8]]
print(matmul(A, B))
print(transpose_matrix(A))
print(deter([[2, 1, 1], [4, -6, 0], [-2, 7, 2]]))
L, U = lu_decomposition([[2, 1, 1], [4, -6, 0], [-2, 7, 2]])Or just run the file to see all four validated against NumPy/SciPy directly:
python matrix_ops.py
There's also a read_matrix() helper for building a matrix from keyboard input, kept separate from the actual math so the module stays importable and testable without blocking on stdin every time it's loaded.
- Determinant: O(n!), impractical past small matrices
- LU decomposition: no partial pivoting, fails on zero pivots, can be unstable on ill-conditioned inputs
- Determinant and LU only support square matrices, by definition
Yashraj Sunil Patil