Repository: tensorly/tensorly

Issue:
Add a deprecation for tl.partial_svd to suggest tl.tenalg.svd_interface
The issue in the tests comes from the fact that we changed the SVD interface in #429, the best would be to use `tensorly.tenalg.svd_interface`. @aarmey perhaps we should deprecate tl.partial_svd or at least raise an error if users try to use it, to let them know they should switch to the new interface?

_Originally posted by @JeanKossaifi in https://github.com/tensorly/tensorly/issues/411#issuecomment-1217122493_

Comments:

Patch:
diff --git a/doc/modules/api.rst b/doc/modules/api.rst
index ad8c2a1..d6004d6 100644
--- a/doc/modules/api.rst
+++ b/doc/modules/api.rst
@@ -328,6 +328,10 @@ Note that these are currently experimental and may change in the future.
     Parafac2
     SymmetricCP
     ConstrainedCP
+    TensorTrain
+    TensorRing
+    TensorTrainMatrix
+
 
 Functions
 ---------
@@ -352,6 +356,7 @@ Functions
     robust_pca
     tensor_train
     tensor_train_matrix
+    tensor_ring
     parafac2
     constrained_parafac
 
diff --git a/tensorly/backend/__init__.py b/tensorly/backend/__init__.py
index 35b44f5..326dde0 100644
--- a/tensorly/backend/__init__.py
+++ b/tensorly/backend/__init__.py
@@ -105,6 +105,7 @@ class BackendManager(types.ModuleType):
         "asinh",
         "acosh",
         "atanh",
+        "partial_svd",
     ]
     _attributes = [
         "int64",
diff --git a/tensorly/backend/core.py b/tensorly/backend/core.py
index fb43e7e..7b4a1e1 100644
--- a/tensorly/backend/core.py
+++ b/tensorly/backend/core.py
@@ -1262,3 +1262,11 @@ class Backend(object):
     def atanh(self, x):
         """Return the arctanh of x."""
         return self.arctanh(x)
+
+    def partial_svd(self, *args, **kwargs):
+        msg = (
+            "partial_svd is no longer used. "
+            "Please use tensorly.tenalg.svd_interface instead, "
+            "it provides a unified interface to all available SVD implementations."
+        )
+        raise NotImplementedError(msg)
diff --git a/tensorly/decomposition/__init__.py b/tensorly/decomposition/__init__.py
index 61332d3..7998a94 100644
--- a/tensorly/decomposition/__init__.py
+++ b/tensorly/decomposition/__init__.py
@@ -13,8 +13,9 @@ from ._tucker import (
     Tucker,
 )
 from .robust_decomposition import robust_pca
-from ._tt import TensorTrain, tensor_train, tensor_train_matrix
-from ._tr import tensor_ring
+from ._tt import tensor_train, tensor_train_matrix
+from ._tt import TensorTrain, TensorTrainMatrix
+from ._tr import tensor_ring, TensorRing
 from ._parafac2 import parafac2, Parafac2
 from ._symmetric_cp import (
     symmetric_parafac_power_iteration,
diff --git a/tensorly/decomposition/_tt.py b/tensorly/decomposition/_tt.py
index 961c516..9929193 100644
--- a/tensorly/decomposition/_tt.py
+++ b/tensorly/decomposition/_tt.py
@@ -136,6 +136,40 @@ def tensor_train_matrix(tensor, rank, svd="truncated_svd", verbose=False):
 
 
 class TensorTrain(DecompositionMixin):
+    """Decompose a tensor into a matrix in tt-format
+
+    Parameters
+    ----------
+    tensor : tensorized matrix
+        if your input matrix is of size (4, 9) and your tensorized_shape (2, 2, 3, 3)
+        then tensor should be tl.reshape(matrix, (2, 2, 3, 3))
+    rank : 'same', float or int tuple
+        - if 'same' creates a decomposition with the same number of parameters as `tensor`
+        - if float, creates a decomposition with `rank` x the number of parameters of `tensor`
+        - otherwise, the actual rank to be used, e.g. (1, rank_2, ..., 1) of size tensor.ndim//2. Note that boundary conditions dictate that the first rank = last rank = 1.
+    svd : str, default is 'truncated_svd'
+        function to use to compute the SVD, acceptable values in tensorly.SVD_FUNS
+    verbose : boolean, optional
+            level of verbosity
+
+    Returns
+    -------
+    tt_matrix
+    """
+
+    def __init__(self, rank, svd="truncated_svd", verbose=False):
+        self.rank = rank
+        self.svd = svd
+        self.verbose = verbose
+
+    def fit_transform(self, tensor):
+        self.decomposition_ = tensor_train(
+            tensor, rank=self.rank, svd=self.svd, verbose=self.verbose
+        )
+        return self.decomposition_
+
+
+class TensorTrainMatrix(DecompositionMixin):
     """TT decomposition via recursive SVD
 
         Decomposes `input_tensor` into a sequence of order-3 tensors (factors)
@@ -169,7 +203,7 @@ class TensorTrain(DecompositionMixin):
         self.verbose = verbose
 
     def fit_transform(self, tensor):
-        self.decomposition_ = tensor_train(
+        self.decomposition_ = tensor_train_matrix(
             tensor, rank=self.rank, svd=self.svd, verbose=self.verbose
         )
         return self.decomposition_


Test patch:
diff --git a/tensorly/decomposition/tests/test_tt_decomposition.py b/tensorly/decomposition/tests/test_tt_decomposition.py
index 48675da..1bf7322 100644
--- a/tensorly/decomposition/tests/test_tt_decomposition.py
+++ b/tensorly/decomposition/tests/test_tt_decomposition.py
@@ -1,6 +1,6 @@
 import pytest
 import tensorly as tl
-from .._tt import tensor_train, tensor_train_matrix, TensorTrain
+from .._tt import tensor_train, tensor_train_matrix, TensorTrain, TensorTrainMatrix
 from ...tt_matrix import tt_matrix_to_tensor
 from ...random import random_tt
 from ...testing import (
@@ -89,10 +89,14 @@ def test_tensor_train(monkeypatch):
 
 # TODO: Remove once MXNet supports transpose for > 6th order tensors
 @skip_mxnet
-def test_tensor_train_matrix():
+def test_tensor_train_matrix(monkeypatch):
     """Test for tensor_train_matrix decomposition"""
     tensor = random_tt((2, 2, 2, 3, 3, 3), rank=2, full=True)
     tt = tensor_train_matrix(tensor, 10)
 
     tt_rec = tt_matrix_to_tensor(tt)
     assert_array_almost_equal(tensor, tt_rec, decimal=4)
+
+    assert_class_wrapper_correctly_passes_arguments(
+        monkeypatch, tensor_train_matrix, TensorTrainMatrix, ignore_args={}, rank=3
+    )