Python NumPy: How to Calculate Matrix Condition Number in NumPy
The condition number measures how sensitive a matrix's solution is to small changes in input data. High condition numbers indicate numerical instability, making matrix operations unreliable.
Basic Calculation
Use np.linalg.cond() to compute the condition number:
import numpy as np
matrix = np.array([
[4, 2],
[3, 1]
])
cond = np.linalg.cond(matrix)
print(f"Condition number: {cond:.2f}") # Condition number: 14.93
What It Means
The condition number indicates how much output error can result from input error. A condition number of 100 means a 1% error in input could produce up to 100% error in output.
import numpy as np
# Well-conditioned matrix (close to identity)
good_matrix = np.array([
[1, 0.1],
[0.1, 1]
])
print(f"Well-conditioned: {np.linalg.cond(good_matrix):.2f}") # ~1.22
# Poorly-conditioned matrix
bad_matrix = np.array([
[1, 1],
[1, 1.0001]
])
print(f"Ill-conditioned: {np.linalg.cond(bad_matrix):.2f}") # ~40000
Output:
Well-conditioned: 1.22
Ill-conditioned: 40002.00
Interpreting Condition Numbers
| Condition Number | Status | Reliability |
|---|---|---|
| ~1 | ✅ Ideal | Maximum precision |
| < 100 | ✅ Well-conditioned | Results reliable |
| 100 - 1,000 | ⚠️ Moderate | Some precision loss |
| > 1,000,000 | ❌ Ill-conditioned | Results unreliable |
| ∞ (inf) | ❌ Singular | No unique solution |
import numpy as np
def assess_condition(matrix):
"""Evaluate matrix conditioning."""
cond = np.linalg.cond(matrix)
if np.isinf(cond):
return f"Condition: ∞ - Singular matrix (no inverse)"
elif cond < 100:
return f"Condition: {cond:.2f} - Well-conditioned ✅"
elif cond < 10000:
return f"Condition: {cond:.2f} - Moderately conditioned ⚠️"
else:
return f"Condition: {cond:.2e} - Ill-conditioned ❌"
# Test various matrices
matrices = [
np.eye(3), # Identity
np.array([[1, 2], [3, 4]]), # Normal
np.array([[1, 2], [2, 4.0001]]), # Nearly singular
np.array([[1, 2], [2, 4]]) # Singular
]
for m in matrices:
print(assess_condition(m))
Output:
Condition: 1.00 - Well-conditioned ✅
Condition: 14.93 - Well-conditioned ✅
Condition: 2.50e+05 - Ill-conditioned ❌
Condition: 2.52e+16 - Ill-conditioned ❌
Examples of Ill-Conditioned Matrices
Nearly Proportional Rows
import numpy as np
# Rows are almost multiples of each other
nearly_singular = np.array([
[1, 2, 3],
[2, 4, 6.0001], # Almost 2× row 1
[1, 1, 1]
])
print(f"Condition: {np.linalg.cond(nearly_singular):.2e}")
# Condition: 4.67e+05
# Very large - matrix is ill-conditioned
Hilbert Matrix (Classic Example)
import numpy as np
from scipy.linalg import hilbert
# Hilbert matrices are notoriously ill-conditioned
for n in [3, 5, 7, 10]:
H = hilbert(n)
cond = np.linalg.cond(H)
print(f"Hilbert {n}×{n}: condition = {cond:.2e}")
Output:
Hilbert 3×3: condition = 5.24e+02
Hilbert 5×5: condition = 4.77e+05
Hilbert 7×7: condition = 4.75e+08
Hilbert 10×10: condition = 1.60e+13
Hilbert matrices demonstrate how condition numbers can explode. A 10×10 Hilbert matrix has a condition number around 10¹³, meaning you'd lose about 13 digits of precision in calculations.
Using Different Norms
The condition number depends on the matrix norm used:
import numpy as np
matrix = np.array([
[4, 2],
[3, 1]
])
# 2-norm (default): based on singular values
cond_2 = np.linalg.cond(matrix)
print(f"2-norm condition: {cond_2:.4f}")
# Infinity norm: max absolute row sum
cond_inf = np.linalg.cond(matrix, p=np.inf)
print(f"∞-norm condition: {cond_inf:.4f}")
# 1-norm: max absolute column sum
cond_1 = np.linalg.cond(matrix, p=1)
print(f"1-norm condition: {cond_1:.4f}")
# Frobenius norm
cond_fro = np.linalg.cond(matrix, p='fro')
print(f"Frobenius condition: {cond_fro:.4f}")
Output:
2-norm condition: 14.9330
∞-norm condition: 21.0000
1-norm condition: 21.0000
Frobenius condition: 15.0000
Norm Options
Parameter p | Norm Type | Formula |
|---|---|---|
None or 2 | 2-norm (default) | σ_max / σ_min |
np.inf | Infinity norm | max row sum |
1 | 1-norm | max column sum |
-np.inf | Negative infinity | min row sum |
'fro' | Frobenius | √(Σ |
Practical Example: Linear System Solving
Check conditioning before solving systems:
import numpy as np
def safe_solve(A, b):
"""Solve Ax = b with condition check."""
cond = np.linalg.cond(A)
if np.isinf(cond):
raise ValueError("Matrix is singular - no unique solution")
if cond > 1e10:
print(f"Warning: High condition number ({cond:.2e})")
print("Solution may be inaccurate!")
x = np.linalg.solve(A, b)
# Verify solution
residual = np.linalg.norm(A @ x - b)
print(f"Condition: {cond:.2e}, Residual: {residual:.2e}")
return x
# Well-conditioned system
A_good = np.array([[4, 1], [1, 3]])
b = np.array([1, 2])
x = safe_solve(A_good, b)
print(f"Solution: {x}\n")
# Ill-conditioned system
A_bad = np.array([[1, 1], [1, 1.00000001]])
x = safe_solve(A_bad, b)
print(f"Solution: {x}")
Output:
Condition: 1.94e+00, Residual: 0.00e+00
Solution: [0.09090909 0.63636364]
Condition: 4.00e+08, Residual: 0.00e+00
Solution: [-9.99999996e+07 1.00000001e+08]
Relationship to Singular Values
The 2-norm condition number is the ratio of largest to smallest singular values:
import numpy as np
matrix = np.array([
[4, 2],
[3, 1]
])
# Using singular values directly
U, s, Vt = np.linalg.svd(matrix)
cond_from_svd = s[0] / s[-1] # max / min singular value
# Using cond function
cond_direct = np.linalg.cond(matrix)
print(f"From SVD: {cond_from_svd:.4f}")
print(f"From cond: {cond_direct:.4f}")
# Both should match
Output:
From SVD: 14.9330
From cond: 14.9330
When the smallest singular value approaches zero, the condition number approaches infinity, indicating a near-singular matrix.
Checking Before Matrix Inversion
Always check conditioning before computing inverses:
import numpy as np
def safe_inverse(matrix):
"""Compute matrix inverse with stability check."""
cond = np.linalg.cond(matrix)
# Rule of thumb: lose log10(cond) digits of precision
digits_lost = np.log10(cond)
available_digits = 15 # float64 precision
print(f"Condition number: {cond:.2e}")
print(f"Digits of precision lost: ~{digits_lost:.1f}")
print(f"Reliable digits remaining: ~{available_digits - digits_lost:.1f}")
if cond > 1e12:
print("⚠️ Matrix is too ill-conditioned for reliable inversion")
return None
return np.linalg.inv(matrix)
# Test
matrix = np.array([[1, 2], [2, 4.001]])
inv = safe_inverse(matrix)
Output:
Condition number: 2.50e+04
Digits of precision lost: ~4.4
Reliable digits remaining: ~10.6
Summary
| Function | Purpose |
|---|---|
np.linalg.cond(A) | 2-norm condition number |
np.linalg.cond(A, p=1) | 1-norm condition number |
np.linalg.cond(A, p=np.inf) | Infinity-norm condition number |
Best Practice: Always check np.linalg.cond() before matrix inversion or solving linear systems. Condition numbers above 10⁶ suggest results may be unreliable, and values approaching machine epsilon reciprocal (~10¹⁶ for float64) indicate the matrix is effectively singular.