Skip to main content

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 NumberStatusReliability
~1✅ IdealMaximum precision
< 100✅ Well-conditionedResults reliable
100 - 1,000⚠️ ModerateSome precision loss
> 1,000,000❌ Ill-conditionedResults unreliable
∞ (inf)❌ SingularNo 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
warning

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 pNorm TypeFormula
None or 22-norm (default)σ_max / σ_min
np.infInfinity normmax row sum
11-normmax column sum
-np.infNegative infinitymin 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
note

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

FunctionPurpose
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.