I'm currently working on kernel methods, and at some point I needed to make a non positive semi-definite matrix (i.e. similarity matrix) into one PSD matrix. I tried this approach:
def makePSD(mat):
#make symmetric
k = (mat+mat.T)/2
#make PSD
min_eig = np.min(np.real(linalg.eigvals(mat)))
e = np.max([0, -min_eig + 1e-4])
mat = k + e*np.eye(mat.shape[0]);
return mat
but it fails if I test the resulting matrix with the following function:
def isPSD(A, tol=1e-8):
E,V = linalg.eigh(A)
return np.all(E >= -tol)
I also tried the approach suggested in other related question (How can I calculate the nearest positive semi-definite matrix?), but the resulting matrix also failed to pass the isPSD test.
Do you have any suggestions on how to correctly make such transformation correctly?
First thing I’d say is don’t use
eigh
for testing positive-definiteness, sinceeigh
assumes the input is Hermitian. That’s probably why you think the answer you reference isn’t working.I didn’t like that answer because it had an iteration (and, I couldn’t understand its example), nor the other answer there it doesn’t promise to give you the best positive-definite matrix, i.e., the one closest to the input in terms of the Frobenius norm (squared-sum of elements). (I have absolutely no idea what your code in your question is supposed to do.)
I do like this Matlab implementation of Higham’s 1988 paper: https://www.mathworks.com/matlabcentral/fileexchange/42885-nearestspd so I ported it to Python:
In addition to just finding the nearest positive-definite matrix, the above library includes
isPD
which uses the Cholesky decomposition to determine whether a matrix is positive-definite. This way, you don’t need any tolerances—any function that wants a positive-definite will run Cholesky on it, so it’s the absolute best way to determine positive-definiteness.It also has a Monte Carlo-based unit test at the end. If you put this in
posdef.py
and runpython posdef.py
, it’ll run a unit-test that passes in ~a second on my laptop. Then in your code you canimport posdef
and callposdef.nearestPD
orposdef.isPD
.The code is also in a Gist if you do that.