我正在使用python 2,我想测试矩阵是否为正半正定(PSD)。 我已经构建了一个随机矩阵X,我想测试Q = X T X的SDP属性。
为此,我调整了一个测试正定性质的函数,以测试正半正定性质。然后我计算我的矩阵Q = X T X. X T X应该是PSD,因为它是矩阵及其转置的乘积(有关PSD保证的更多细节,请参见Is a matrix multiplied with its transpose something special?)。但是,当我测试Q是否为PSD时,函数返回false。
有谁知道我的代码错在哪里?是我的测试功能不测试PSD属性或其他东西?
这是我的脚本(以比实际更简单的方式):
from scipy.stats import bernoulli
from scipy import linalg
import numpy as np
p = 300
N = 100
np.random.seed(18)
X = bernoulli.rvs(0.5, size=p*N).reshape((N, p))
X = 2 * X - 1* np.ones_like(X)
Q = np.dot(X.T, X)
def is_semi_pos_def(x):
return np.all(np.linalg.eigvals(x) >= 0)
is_semi_pos_def(Q)
它返回:
False
非常感谢您对此的任何帮助。
答案 0 :(得分:3)
由于你在脚本中创建的矩阵Q没有满等级,如果N< p,一些特征值将为0.但是正如评论中所提到的,np.eigvals中的数值误差导致它们变为负数:
P = 5
N = 3
np.random.seed(18)
X = bernoulli.rvs(0.5, size=N*P).reshape((N, P))
X = 2 * X - 1* np.ones_like(X)
Q = np.dot(X.T, X)
np.linalg.eigvals(Q)
返回
[1.24244289e+01, -2.96746135e-16, 2.57557110e+00, 4.23704588e-33, 4.25752762e-18]
您可以通过仅测试特征值是否大于0-E来解释错误,对于某些适当小的E>但是,如果在实践中你知道你的矩阵将是满级,你可以计算Cholesky decomposition这似乎是使用numpy的最快方法。
import numpy as np
P = 300
N = 100
X = bernoulli.rvs(0.5, size=N*P).reshape((N, P))
X = 2 * X - 1* np.ones_like(X)
Q = np.dot(X.T, X)
@timing
def is_semi_pos_def_chol(x):
try:
np.linalg.cholesky(x)
return True
except np.linalg.linalg.LinAlgError:
return False
@timing
def is_semi_pos_def_eigsh(x, epsilon=1e-10):
return np.all(np.linalg.eigvalsh(x) >= -epsilon)
@timing
def is_semi_pos_def_eigs(x, epsilon=1e-10):
return np.all(np.linalg.eigvals(x) >= -epsilon)
print is_semi_pos_def(Q)
print is_semi_pos_def_eigs(Q)
print is_semi_pos_def_eigsh(Q)
返回:
is_semi_pos_def function took 0.001 s
False
is_semi_pos_def_eigs function took 0.073 s
True
is_semi_pos_def_eigsh function took 0.011 s
True
我们可以使用更快的eigvalsh
,因为矩阵是对称的。
请注意,由于Q的非平凡内核,Cholesky分解错误地返回False。但是对于N = P = 300,这不是问题:
is_semi_pos_def_chol function took 0.002 s
True
is_semi_pos_def_eigs function took 0.118 s
True
is_semi_pos_def_eigsh function took 0.023 s
True
如果你想要一个分析上合理的答案,你可以使用sympy来计算没有数值误差的特征值,或者使用sympy计算Cholesky分解,这也可以避免浮点错误。然而,对于大的N和P,计算时间变得过高:
from sympy.mpmath import mp
P = 30
N = 10
X = bernoulli.rvs(0.5, size=N*P).reshape((N, P))
X = 2 * X - 1* np.ones_like(X)
M = Matrix(np.dot(X.T, X))
Q = np.dot(X.T, X)
@timing
def is_semi_pos_def_symbolic(x):
try:
M.cholesky()
return True
except ValueError as e:
print e
return False
print is_semi_pos_def_symbolic(M)
print is_semi_pos_def_chol(Q)
print is_semi_pos_def_eigsh(Q)
注意分析真相的重大代价:
is_semi_pos_def_symbolic function took 0.908 s
True
is_semi_pos_def_chol function took 0.000 s
False
is_semi_pos_def_eigsh function took 0.000 s
True