Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions pypfopt/risk_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ def fix_nonpositive_semidefinite(matrix, fix_method="spectral"):
positive semidefinite covariance matrix
"""
if _is_positive_semidefinite(matrix):
try:
cond = np.linalg.cond(matrix)
if cond > 1e10:
warnings.warn(
f"The covariance matrix is positive semidefinite but "
f"ill-conditioned (condition number: {cond:.2e}). This may "
f"lead to numerical instability in optimization.",
RuntimeWarning,
)
except np.linalg.LinAlgError: # pragma: no cover
pass
return matrix

warnings.warn(
Expand Down
19 changes: 19 additions & 0 deletions tests/test_risk_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,25 @@ def test_sample_cov_npd():
risk_models.fix_nonpositive_semidefinite(S, fix_method="blah")


def test_fix_psd_ill_conditioned_warning():
# PSD matrix with a very high condition number (ill-conditioned)
n = 3
matrix = np.eye(n)
matrix[0, 0] = 1e12
assert risk_models._is_positive_semidefinite(matrix)
assert np.linalg.cond(matrix) > 1e10

with pytest.warns(RuntimeWarning, match="ill-conditioned"):
result = risk_models.fix_nonpositive_semidefinite(matrix)
np.testing.assert_array_equal(result, matrix)

# Well-conditioned PSD matrix should NOT warn
good_matrix = np.eye(n)
assert risk_models._is_positive_semidefinite(good_matrix)
result = risk_models.fix_nonpositive_semidefinite(good_matrix)
np.testing.assert_array_equal(result, good_matrix)


def test_fix_npd_different_method():
df = get_data()
S = risk_models.sample_cov(df)
Expand Down