Determinant and Inverse¶
Compute matrix determinants and inverses using np.linalg.
np.linalg.det¶
1. Basic Usage¶
import numpy as np
def main():
A = np.array([[1, 2],
[3, 4]])
det = np.linalg.det(A)
print("A =")
print(A)
print(f"det(A) = {det}")
if __name__ == "__main__":
main()
Output:
A =
[[1 2]
[3 4]]
det(A) = -2.0
2. Mathematical Form¶
For 2×2 matrix:
\[\det\begin{pmatrix} a & b \\ c & d \end{pmatrix} = ad - bc\]
import numpy as np
def main():
a, b, c, d = 1, 2, 3, 4
A = np.array([[a, b],
[c, d]])
det_numpy = np.linalg.det(A)
det_manual = a * d - b * c
print(f"NumPy det: {det_numpy}")
print(f"Manual det: {det_manual}")
if __name__ == "__main__":
main()
3. Larger Matrices¶
import numpy as np
def main():
A = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 10]])
det = np.linalg.det(A)
print("A =")
print(A)
print(f"det(A) = {det:.4f}")
if __name__ == "__main__":
main()
Singular Matrices¶
1. Zero Determinant¶
import numpy as np
def main():
# Singular matrix (rows are linearly dependent)
A = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
det = np.linalg.det(A)
print("A =")
print(A)
print(f"det(A) = {det:.2e}") # Essentially zero
if __name__ == "__main__":
main()
2. Numerical Precision¶
import numpy as np
def main():
A = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
det = np.linalg.det(A)
# Check if "essentially zero"
is_singular = np.abs(det) < 1e-10
print(f"det(A) = {det:.2e}")
print(f"Is singular: {is_singular}")
if __name__ == "__main__":
main()
3. Condition Number¶
Better way to check near-singularity.
import numpy as np
def main():
A = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9.0001]]) # Nearly singular
cond = np.linalg.cond(A)
print(f"Condition number: {cond:.2e}")
print("High condition number indicates near-singularity")
if __name__ == "__main__":
main()
np.linalg.inv¶
1. Basic Usage¶
import numpy as np
def main():
A = np.array([[1, 2],
[3, 4]])
A_inv = np.linalg.inv(A)
print("A =")
print(A)
print()
print("A^(-1) =")
print(A_inv)
if __name__ == "__main__":
main()
2. Verify Inverse¶
import numpy as np
def main():
A = np.array([[1, 2],
[3, 4]])
A_inv = np.linalg.inv(A)
# A @ A^(-1) should be identity
product = A @ A_inv
print("A @ A^(-1) =")
print(product)
print()
print("Close to identity:", np.allclose(product, np.eye(2)))
if __name__ == "__main__":
main()
3. Singular Matrix Error¶
import numpy as np
def main():
A = np.array([[1, 2],
[2, 4]]) # Singular (row 2 = 2 * row 1)
try:
A_inv = np.linalg.inv(A)
except np.linalg.LinAlgError as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()
np.linalg.pinv¶
1. Pseudo-Inverse¶
Moore-Penrose pseudo-inverse for non-square or singular matrices.
import numpy as np
def main():
# Non-square matrix
A = np.array([[1, 2],
[3, 4],
[5, 6]])
A_pinv = np.linalg.pinv(A)
print(f"A shape: {A.shape}")
print(f"A+ shape: {A_pinv.shape}")
print()
print("A+ =")
print(A_pinv)
if __name__ == "__main__":
main()
2. Least Squares¶
Pseudo-inverse gives least squares solution.
import numpy as np
def main():
# Overdetermined system
A = np.array([[1, 1],
[1, 2],
[1, 3]])
b = np.array([1, 2, 2])
# Least squares via pinv
x = np.linalg.pinv(A) @ b
print(f"x = {x}")
print(f"Residual: {np.linalg.norm(A @ x - b):.4f}")
if __name__ == "__main__":
main()
3. Singular Matrix¶
import numpy as np
def main():
# Singular matrix
A = np.array([[1, 2],
[2, 4]])
# inv fails, pinv works
A_pinv = np.linalg.pinv(A)
print("A (singular) =")
print(A)
print()
print("A+ =")
print(A_pinv)
if __name__ == "__main__":
main()
Applications¶
1. Linear Transformation¶
import numpy as np
def main():
# Transform points
A = np.array([[2, 0],
[0, 3]])
points = np.array([[1, 0],
[0, 1],
[1, 1]])
# Forward transform
transformed = (A @ points.T).T
# Inverse transform
A_inv = np.linalg.inv(A)
recovered = (A_inv @ transformed.T).T
print("Original points:")
print(points)
print()
print("Transformed:")
print(transformed)
print()
print("Recovered:")
print(recovered)
if __name__ == "__main__":
main()
2. Covariance Inverse¶
import numpy as np
def main():
# Sample covariance matrix
np.random.seed(42)
X = np.random.randn(100, 3)
cov = np.cov(X.T)
# Precision matrix (inverse covariance)
precision = np.linalg.inv(cov)
print("Covariance:")
print(cov.round(3))
print()
print("Precision:")
print(precision.round(3))
if __name__ == "__main__":
main()
3. Matrix Equation¶
Solve \(AXB = C\) for \(X\).
import numpy as np
def main():
A = np.array([[1, 2],
[3, 4]])
B = np.array([[5, 6],
[7, 8]])
C = np.array([[10, 20],
[30, 40]])
# X = A^(-1) @ C @ B^(-1)
X = np.linalg.inv(A) @ C @ np.linalg.inv(B)
# Verify
print("X =")
print(X)
print()
print("A @ X @ B =")
print(A @ X @ B)
if __name__ == "__main__":
main()
Best Practices¶
1. Avoid Inverse¶
Prefer np.linalg.solve over computing inverse explicitly.
import numpy as np
def main():
A = np.random.randn(100, 100)
b = np.random.randn(100)
# Bad: compute inverse
x_bad = np.linalg.inv(A) @ b
# Good: solve directly
x_good = np.linalg.solve(A, b)
print(f"Results close: {np.allclose(x_bad, x_good)}")
if __name__ == "__main__":
main()
2. Check Condition¶
Always check condition number for numerical stability.
3. Use pinv for Robustness¶
When matrix may be singular or non-square, use pinv.