Search code examples
pythonnumpymatlabpytorch

SVD with matlab, numpy and pytorch


Matlab

A=[-0.67645626,  0.63071378, -0.27856928;
       -0.02497551,  0.42396278,  2.22727768;
       -0.43153722,  1.20107944,  0.39737373;
       -0.878973  , -1.20829399,  0.40960471]
[x,y,v]=svds(A)

numpy

u_np, s_np, vh_np = np.linalg.svd(a, full_matrices=False, compute_uv=True) 

The result from matlab for right singular vector(v) is

-0.0807 -0.1227 0.9892
0.3219  -0.9424 -0.0906
0.9433  0.3111  0.1155 

The result from numpy for right singular vector(vh_np)T is

array([[ 0.08067148, -0.12268262,  0.98916181],
       [-0.32185978, -0.94243885, -0.09063832],
       [-0.94334426,  0.31105947,  0.11551454]])

Can you please explain, where is there a difference in sign. The result woulld be same if the matrix is square matrix.

Consistent result from numpy (Pytorch preferable) and matlab. I cannot figure out which one is the correct one.


Solution

  • Following numpy.linalg.svd()'s documentation, the rows of vh_np (likewise, the columns of vh_np.T) are right-singular vectors, which are eigenvectors of A^H @ A .

    The sign of an eigenvector is arbitrary (see for example this discussion on Cross Validated), i.e. if v is an eigenvector, so is -v. Note that this is not a property of SVD, but of eigenvectors in general. Consistent with this property, both Matlab and Numpy provide the same correct solutions in your example, as they only differ in the sign of the first vector (ignoring numerical imprecision or round-off error).

    If you want to resolve the ambiguous sign, you need to do that yourself; for example, by choosing it such that the largest vector component is always positive (which might be ambiguous as well, e.g. if multiple vector components have the same absolute value).

    Update: As you also asked for a PyTorch solution: You will achieve the same result using torch.linalg.svd():

    import torch
    
    A = [[-0.67645626,  0.63071378, -0.27856928],
         [-0.02497551,  0.42396278,  2.22727768],
         [-0.43153722,  1.20107944,  0.39737373],
         [-0.878973  , -1.20829399,  0.40960471]]
    
    u_torch, s_torch, vh_torch = torch.linalg.svd(torch.tensor(A))
    print(vh_torch.T)
    # >>> tensor([[-0.0807, -0.1227,  0.9892],
    #             [ 0.3219, -0.9424, -0.0906],
    #             [ 0.9433,  0.3111,  0.1155]])
    

    This solution, again, has the same ambiguity regarding the signs of the vectors, which you will need to resolve yourself.