Python NumPy: How to Calculate Matrix Determinant in NumPy
The determinant is a scalar value that reveals important matrix properties: whether a matrix is invertible, how it scales volumes, and its relationship to eigenvalues.
Basic Calculation
Use np.linalg.det() to compute the determinant:
import numpy as np
matrix = np.array([
[1, 2],
[3, 4]
])
det = np.linalg.det(matrix)
print(f"Determinant: {det}")
Output:
Determinant: -2.0000000000000004
Manual Verification (2×2)
For a 2×2 matrix [[a, b], [c, d]], the determinant is ad - bc:
import numpy as np
matrix = np.array([[1, 2], [3, 4]])
# Manual: (1×4) - (2×3) = 4 - 6 = -2
manual_det = matrix[0, 0] * matrix[1, 1] - matrix[0, 1] * matrix[1, 0]
# NumPy calculation
numpy_det = np.linalg.det(matrix)
print(f"Manual: {manual_det}")
print(f"NumPy: {numpy_det}")
Output:
Manual: -2
NumPy: -2.0000000000000004
Checking for Singular Matrices
A determinant of zero means the matrix is singular (non-invertible):
import numpy as np
# Singular matrix: rows are proportional
singular = np.array([
[1, 2],
[2, 4] # 2× first row
])
det = np.linalg.det(singular)
print(f"Determinant: {det}") # 0.0 (or very close to 0)
# Attempting to invert will fail or give unreliable results
try:
inv = np.linalg.inv(singular)
except np.linalg.LinAlgError:
print("Cannot invert singular matrix")
Output:
Determinant: 0.0
Cannot invert singular matrix
Floating-Point Precision
Due to floating-point arithmetic, check for near-zero rather than exact zero:
import numpy as np
# Nearly singular matrix
nearly_singular = np.array([
[1, 2],
[2, 4.0000000001]
])
det = np.linalg.det(nearly_singular)
print(f"Determinant: {det}") # Very small, not exactly 0
# Use isclose for reliable checking
if np.isclose(det, 0, atol=1e-10):
print("Matrix is effectively singular")
else:
print("Matrix is invertible")
Output:
Determinant: 1.0000000827403705e-10
Matrix is invertible
Always use np.isclose(det, 0) instead of det == 0 when checking for singularity to account for floating-point imprecision.
Determinant Properties
import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[2, 0], [1, 3]])
# Property 1: det(AB) = det(A) × det(B)
det_AB = np.linalg.det(A @ B)
det_A_times_det_B = np.linalg.det(A) * np.linalg.det(B)
print(f"det(AB) = {det_AB:.4f}")
print(f"det(A)×det(B) = {det_A_times_det_B:.4f}")
# Property 2: det(A^T) = det(A)
print(f"det(A) = {np.linalg.det(A):.4f}")
print(f"det(A.T) = {np.linalg.det(A.T):.4f}")
# Property 3: det(cA) = c^n × det(A) for n×n matrix
c = 2
n = A.shape[0]
print(f"det(2A) = {np.linalg.det(c * A):.4f}")
print(f"2^2 × det(A) = {c**n * np.linalg.det(A):.4f}")
# Property 4: det(A^-1) = 1/det(A)
if not np.isclose(np.linalg.det(A), 0):
A_inv = np.linalg.inv(A)
print(f"det(A^-1) = {np.linalg.det(A_inv):.4f}")
print(f"1/det(A) = {1/np.linalg.det(A):.4f}")
Output:
det(AB) = -12.0000
det(A)×det(B) = -12.0000
det(A) = -2.0000
det(A.T) = -2.0000
det(2A) = -8.0000
2^2 × det(A) = -8.0000
det(A^-1) = -0.5000
1/det(A) = -0.5000
Large Matrices: Using slogdet()
For large matrices, determinants can overflow or underflow. Use slogdet() to get the sign and log of the absolute determinant:
import numpy as np
# Large matrix where regular determinant might overflow
large_matrix = np.random.randn(100, 100)
# Regular determinant (might be very large or very small)
det = np.linalg.det(large_matrix)
print(f"Regular det: {det}") # May show inf or 0
# Log-determinant (numerically stable)
sign, logdet = np.linalg.slogdet(large_matrix)
print(f"Sign: {sign}")
print(f"Log|det|: {logdet}")
# Reconstruct: det = sign × exp(logdet)
reconstructed = sign * np.exp(logdet)
print(f"Reconstructed: {reconstructed}")
Output:
Regular det: -1.0398456917186009e+78
Sign: -1.0
Log|det|: 179.64070958233293
Reconstructed: -1.0398456917186009e+78
When to Use slogdet()
import numpy as np
def stable_determinant(matrix):
"""Compute determinant with overflow protection."""
sign, logdet = np.linalg.slogdet(matrix)
if sign == 0:
return 0.0 # Singular matrix
# Check if result would overflow
if logdet > 700: # exp(700) ≈ 10^304
print(f"Warning: |det| ≈ exp({logdet:.1f}) - extremely large")
return sign * np.inf
elif logdet < -700:
print(f"Warning: |det| ≈ exp({logdet:.1f}) - extremely small")
return 0.0
return sign * np.exp(logdet)
# Test with matrices of different sizes
for n in [10, 50, 100, 200]:
matrix = np.random.randn(n, n)
det = stable_determinant(matrix)
print(f"{n}×{n} matrix: det ≈ {det:.2e}")
Output:
10×10 matrix: det ≈ 1.13e+04
50×50 matrix: det ≈ -3.88e+31
100×100 matrix: det ≈ 5.68e+77
200×200 matrix: det ≈ -1.36e+185
slogdet() returns (sign, logdet) where the actual determinant is sign * exp(logdet). This representation avoids overflow for large matrices.
Determinant and Volume
The absolute value of the determinant represents the scaling factor for volumes under the linear transformation:
import numpy as np
import matplotlib.pyplot as plt
# Original unit square vertices
unit_square = np.array([
[0, 0],
[1, 0],
[1, 1],
[0, 1],
[0, 0] # Close the square
]).T
# Transformation matrix
A = np.array([
[2, 1],
[0, 3]
])
# Transform the square
transformed = A @ unit_square
det = np.linalg.det(A)
print(f"Determinant: {det}")
print(f"Original area: 1")
print(f"Transformed area: {abs(det)}")
# The determinant (6) tells us the area is scaled by 6×
Output:
Determinant: 6.0
Original area: 1
Transformed area: 6.0
Determinant and Eigenvalues
The determinant equals the product of eigenvalues:
import numpy as np
matrix = np.array([
[4, 2],
[1, 3]
])
# Calculate determinant
det = np.linalg.det(matrix)
# Calculate eigenvalues
eigenvalues = np.linalg.eigvals(matrix)
product_of_eigenvalues = np.prod(eigenvalues)
print(f"Determinant: {det:.4f}")
print(f"Eigenvalues: {eigenvalues}")
print(f"Product of eigenvalues: {product_of_eigenvalues.real:.4f}")
Output:
Determinant: 10.0000
Eigenvalues: [5. 2.]
Product of eigenvalues: 10.0000
Practical Example: Checking System Solvability
import numpy as np
def analyze_system(A, b):
"""Analyze a linear system Ax = b."""
det = np.linalg.det(A)
print(f"Coefficient matrix:\n{A}")
print(f"Determinant: {det:.6f}")
if np.isclose(det, 0, atol=1e-10):
print("System is singular - no unique solution")
print("Possible scenarios:")
print(" - No solution (inconsistent)")
print(" - Infinite solutions (dependent)")
return None
x = np.linalg.solve(A, b)
print(f"Unique solution: {x}")
# Verify
residual = np.linalg.norm(A @ x - b)
print(f"Residual: {residual:.2e}")
return x
# Solvable system
A1 = np.array([[2, 1], [1, 3]])
b1 = np.array([5, 5])
analyze_system(A1, b1)
print("\n" + "="*40 + "\n")
# Singular system
A2 = np.array([[1, 2], [2, 4]])
b2 = np.array([3, 6])
analyze_system(A2, b2)
Output:
Coefficient matrix:
[[2 1]
[1 3]]
Determinant: 5.000000
Unique solution: [2. 1.]
Residual: 0.00e+00
========================================
Coefficient matrix:
[[1 2]
[2 4]]
Determinant: 0.000000
System is singular - no unique solution
Possible scenarios:
- No solution (inconsistent)
- Infinite solutions (dependent)
Summary
| Function | Use Case | Returns |
|---|---|---|
np.linalg.det(A) | Standard determinant | float |
np.linalg.slogdet(A) | Large matrices | (sign, log|det|) |
| Determinant Value | Matrix Property |
|---|---|
| ≠ 0 | Invertible (non-singular) |
| = 0 | Singular (no inverse) |
| > 0 | Preserves orientation |
| < 0 | Reverses orientation |
| |det| | Volume scaling factor |
Best Practice: Use np.linalg.det() for standard calculations and np.isclose(det, 0) for singularity checks. Switch to slogdet() for matrices larger than ~50×50 to avoid numerical overflow.