Repository: tensorly/tensorly

Issue:
Enhancement: Improve naming consistency
Almost all decomposition implementations are stored in a file with the name `_{{decomposition_name}}`. The exceptions are PARAFAC2 and Robust PCA. Should we rename these modules to `_parafac2.py` and `_robust_decomposition.py`, or is there a reason for these exceptions?

Comments:

Patch:
diff --git a/tensorly/decomposition/__init__.py b/tensorly/decomposition/__init__.py
index d43985d..9b4f4c0 100644
--- a/tensorly/decomposition/__init__.py
+++ b/tensorly/decomposition/__init__.py
@@ -8,7 +8,7 @@ from ._nn_cp import non_negative_parafac
 from ._tucker import tucker, partial_tucker, non_negative_tucker, Tucker
 from .robust_decomposition import robust_pca
 from ._tt import TensorTrain, tensor_train, tensor_train_matrix
-from .parafac2 import parafac2, Parafac2
+from ._parafac2 import parafac2, Parafac2
 from ._symmetric_cp import symmetric_parafac_power_iteration, symmetric_power_iteration, SymmetricCP
 from ._cp_power import parafac_power_iteration, power_iteration, CPPower
 
diff --git a/tensorly/decomposition/parafac2.py b/tensorly/decomposition/_parafac2.py
similarity index 76%
rename from tensorly/decomposition/parafac2.py
rename to tensorly/decomposition/_parafac2.py
index 82fed36..eb0cadf 100644
--- a/tensorly/decomposition/parafac2.py
+++ b/tensorly/decomposition/_parafac2.py
@@ -285,95 +285,94 @@ def parafac2(tensor_slices, rank, n_iter_max=100, init='random', svd='numpy_svd'
 
 
 class Parafac2(DecompositionMixin):
+    r"""PARAFAC2 decomposition [1]_ of a third order tensor via alternating least squares (ALS)
 
-    def __init__(self, rank, n_iter_max=100, init='random', svd='numpy_svd', normalize_factors=False,
-             tol=1e-8, random_state=None, verbose=False, n_iter_parafac=5):
-        r"""PARAFAC2 decomposition [1]_ of a third order tensor via alternating least squares (ALS)
+    Computes a rank-`rank` PARAFAC2 decomposition of the third-order tensor defined by 
+    `tensor_slices`. The decomposition is on the form :math:`(A [B_i] C)` such that the
+    i-th frontal slice, :math:`X_i`, of :math:`X` is given by
 
-        Computes a rank-`rank` PARAFAC2 decomposition of the third-order tensor defined by 
-        `tensor_slices`. The decomposition is on the form :math:`(A [B_i] C)` such that the
-        i-th frontal slice, :math:`X_i`, of :math:`X` is given by
+    .. math::
+    
+        X_i = B_i diag(a_i) C^T,
+    
+    where :math:`diag(a_i)` is the diagonal matrix whose nonzero entries are equal to
+    the :math:`i`-th row of the :math:`I \times R` factor matrix :math:`A`, :math:`B_i` 
+    is a :math:`J_i \times R` factor matrix such that the cross product matrix :math:`B_{i_1}^T B_{i_1}`
+    is constant for all :math:`i`, and :math:`C` is a :math:`K \times R` factor matrix. 
+    To compute this decomposition, we reformulate the expression for :math:`B_i` such that
 
-        .. math::
-        
-            X_i = B_i diag(a_i) C^T,
-        
-        where :math:`diag(a_i)` is the diagonal matrix whose nonzero entries are equal to
-        the :math:`i`-th row of the :math:`I \times R` factor matrix :math:`A`, :math:`B_i` 
-        is a :math:`J_i \times R` factor matrix such that the cross product matrix :math:`B_{i_1}^T B_{i_1}`
-        is constant for all :math:`i`, and :math:`C` is a :math:`K \times R` factor matrix. 
-        To compute this decomposition, we reformulate the expression for :math:`B_i` such that
+    .. math::
 
-        .. math::
+        B_i = P_i B,
 
-            B_i = P_i B,
+    where :math:`P_i` is a :math:`J_i \times R` orthogonal matrix and :math:`B` is a
+    :math:`R \times R` matrix.
 
-        where :math:`P_i` is a :math:`J_i \times R` orthogonal matrix and :math:`B` is a
-        :math:`R \times R` matrix.
+    An alternative formulation of the PARAFAC2 decomposition is that the tensor element
+    :math:`X_{ijk}` is given by
 
-        An alternative formulation of the PARAFAC2 decomposition is that the tensor element
-        :math:`X_{ijk}` is given by
+    .. math::
 
-        .. math::
+        X_{ijk} = \sum_{r=1}^R A_{ir} B_{ijr} C_{kr},
+    
+    with the same constraints hold for :math:`B_i` as above.
+    
 
-            X_{ijk} = \sum_{r=1}^R A_{ir} B_{ijr} C_{kr},
-        
-        with the same constraints hold for :math:`B_i` as above.
+    Parameters
+    ----------
+    tensor_slices : ndarray or list of ndarrays
+        Either a third order tensor or a list of second order tensors that may have different number of rows.
+        Note that the second mode factor matrices are allowed to change over the first mode, not the
+        third mode as some other implementations use (see note below).
+    rank  : int
+        Number of components.
+    n_iter_max : int
+        Maximum number of iteration
+    init : {'svd', 'random', CPTensor, Parafac2Tensor}
+        Type of factor matrix initialization. See `initialize_factors`.
+    svd : str, default is 'numpy_svd'
+        function to use to compute the SVD, acceptable values in tensorly.SVD_FUNS
+    normalize_factors : bool (optional)
+        If True, aggregate the weights of each factor in a 1D-tensor
+        of shape (rank, ), which will contain the norms of the factors. Note that
+        there may be some inaccuracies in the component weights.
+    tol : float, optional
+        (Default: 1e-8) Relative reconstruction error tolerance. The
+        algorithm is considered to have found the global minimum when the
+        reconstruction error is less than `tol`.
+    random_state : {None, int, np.random.RandomState}
+    verbose : int, optional
+        Level of verbosity
+    n_iter_parafac: int, optional
+        Number of PARAFAC iterations to perform for each PARAFAC2 iteration
+
+    Returns
+    -------
+    Parafac2Tensor : (weight, factors, projection_matrices)
+        * weights : 1D array of shape (rank, )
+            all ones if normalize_factors is False (default),
+            weights of the (normalized) factors otherwise
+        * factors : List of factors of the CP decomposition element `i` is of shape
+            (tensor.shape[i], rank)
+        * projection_matrices : List of projection matrices used to create evolving
+            factors.
         
+    References
+    ----------
+    .. [1] Kiers, H.A.L., ten Berge, J.M.F. and Bro, R. (1999),
+            PARAFAC2—Part I. A direct fitting algorithm for the PARAFAC2 model. 
+            J. Chemometrics, 13: 275-294.
 
-        Parameters
-        ----------
-        tensor_slices : ndarray or list of ndarrays
-            Either a third order tensor or a list of second order tensors that may have different number of rows.
-            Note that the second mode factor matrices are allowed to change over the first mode, not the
-            third mode as some other implementations use (see note below).
-        rank  : int
-            Number of components.
-        n_iter_max : int
-            Maximum number of iteration
-        init : {'svd', 'random', CPTensor, Parafac2Tensor}
-            Type of factor matrix initialization. See `initialize_factors`.
-        svd : str, default is 'numpy_svd'
-            function to use to compute the SVD, acceptable values in tensorly.SVD_FUNS
-        normalize_factors : bool (optional)
-            If True, aggregate the weights of each factor in a 1D-tensor
-            of shape (rank, ), which will contain the norms of the factors. Note that
-            there may be some inaccuracies in the component weights.
-        tol : float, optional
-            (Default: 1e-8) Relative reconstruction error tolerance. The
-            algorithm is considered to have found the global minimum when the
-            reconstruction error is less than `tol`.
-        random_state : {None, int, np.random.RandomState}
-        verbose : int, optional
-            Level of verbosity
-        n_iter_parafac: int, optional
-            Number of PARAFAC iterations to perform for each PARAFAC2 iteration
-
-        Returns
-        -------
-        Parafac2Tensor : (weight, factors, projection_matrices)
-            * weights : 1D array of shape (rank, )
-                all ones if normalize_factors is False (default), 
-                weights of the (normalized) factors otherwise
-            * factors : List of factors of the CP decomposition element `i` is of shape
-                (tensor.shape[i], rank)
-            * projection_matrices : List of projection matrices used to create evolving
-                factors.
-            
-        References
-        ----------
-        .. [1] Kiers, H.A.L., ten Berge, J.M.F. and Bro, R. (1999), 
-                PARAFAC2—Part I. A direct fitting algorithm for the PARAFAC2 model. 
-                J. Chemometrics, 13: 275-294.
-
-        Notes
-        -----
-        This formulation of the PARAFAC2 decomposition is slightly different from the one in [1]_.
-        The difference lies in that here, the second mode changes over the first mode, whereas in
-        [1]_, the second mode changes over the third mode. We made this change since that means
-        that the function accept both lists of matrices and a single nd-array as input without
-        any reordering of the modes.
-        """
+    Notes
+    -----
+    This formulation of the PARAFAC2 decomposition is slightly different from the one in [1]_.
+    The difference lies in that here, the second mode changes over the first mode, whereas in
+    [1]_, the second mode changes over the third mode. We made this change since that means
+    that the function accept both lists of matrices and a single nd-array as input without
+    any reordering of the modes.
+    """
+    def __init__(self, rank, n_iter_max=100, init='random', svd='numpy_svd', normalize_factors=False,
+             tol=1e-8, random_state=None, verbose=False, n_iter_parafac=5):
         self.rank = rank
         self.n_iter_max=n_iter_max
         self.init=init


Test patch:
diff --git a/tensorly/decomposition/tests/test_parafac2.py b/tensorly/decomposition/tests/test_parafac2.py
index db925e1..8c6f982 100644
--- a/tensorly/decomposition/tests/test_parafac2.py
+++ b/tensorly/decomposition/tests/test_parafac2.py
@@ -7,7 +7,7 @@ import tensorly as tl
 from ...random import check_random_state, random_parafac2
 from ... import backend as T
 from ...testing import assert_array_equal, assert_
-from ..parafac2 import parafac2, initialize_decomposition, _pad_by_zeros
+from .._parafac2 import parafac2, initialize_decomposition, _pad_by_zeros
 from ...parafac2_tensor import parafac2_to_tensor, parafac2_to_slices