Repository: tensorly/tensorly

Issue:
[BUG] nan singular vectors in `tl.partial_svd`
Occasionally `tl.partial_svd` returns nans when `max_dim > n_eigenvecs > rank`.
```python3
import tensorly as tl
import numpy as np
np.random.seed(1)

a = tl.dot(tl.randn((4, 2)), tl.randn((2, 4)))
u, s, v = tl.partial_svd(a, n_eigenvecs=3)
print(u)
```
```
array([[-0.56637993, -0.29798997,         nan],
       [ 0.53373738, -0.33025035,         nan],
       [ 0.2412373 , -0.82224119,         nan],
       [-0.57977819, -0.35504382,         nan]])
```

Discussion:

Patch:
diff --git a/tensorly/backend/core.py b/tensorly/backend/core.py
index 560b3b7..ae7e44c 100644
--- a/tensorly/backend/core.py
+++ b/tensorly/backend/core.py
@@ -1052,13 +1052,15 @@ class Backend(object):
                 S, U = scipy.sparse.linalg.eigsh(
                     np.dot(matrix, matrix.T.conj()), k=n_eigenvecs, which='LM', v0=v0
                 )
-                S = np.where(np.abs(S) <= np.finfo(S.dtype).eps, 0, np.sqrt(S))
+                S = np.sqrt(np.clip(S, 0, None))
+                S = np.clip(S, np.finfo(S.dtype).eps, None)  # To avoid divide by zero warning on next line
                 V = np.dot(matrix.T.conj(), U * np.where(np.abs(S) <= np.finfo(S.dtype).eps, 0, 1/S)[None, :])
             else:
                 S, V = scipy.sparse.linalg.eigsh(
                     np.dot(matrix.T.conj(), matrix), k=n_eigenvecs, which='LM', v0=v0
                 )
-                S = np.where(np.abs(S) <= np.finfo(S.dtype).eps, 0, np.sqrt(S))
+                S = np.sqrt(np.clip(S, 0, None))
+                S = np.clip(S, np.finfo(S.dtype).eps, None)
                 U = np.dot(matrix, V) * np.where(np.abs(S) <= np.finfo(S.dtype).eps, 0, 1/S)[None, :]
 
             # WARNING: here, V is still the transpose of what it should be


Test patch:
diff --git a/tensorly/tests/test_backend.py b/tensorly/tests/test_backend.py
index d4ee94d..5de7d82 100644
--- a/tensorly/tests/test_backend.py
+++ b/tensorly/tests/test_backend.py
@@ -173,10 +173,12 @@ def test_svd():
 
         # Test for singular matrices (some eigenvals will be zero)
         # Rank at most 5
-        matrix = T.tensor(np.dot(np.random.random((20, 5)), np.random.random((5, 20))))
-        U, S, V = tl.partial_svd(matrix, n_eigenvecs=n)
+        matrix = tl.dot(tl.randn((20, 5), seed=12), tl.randn((5, 20), seed=23))
+        U, S, V = tl.partial_svd(matrix, n_eigenvecs=6, random_state=0)
         true_rec_error = tl.sum((matrix - tl.dot(U, tl.reshape(S, (-1, 1))*V))**2)
         assert_(true_rec_error <= tol)
+        assert_(np.isfinite(T.to_numpy(U)).all(), msg="Left singular vectors are not finite")
+        assert_(np.isfinite(T.to_numpy(V)).all(), msg="Right singular vectors are not finite")
 
         # Test if partial_svd returns the same result for the same setting
         matrix = T.tensor(np.random.random((20, 5)))