code: def parafac2(
      tensor_slices,
      rank,
      n_iter_max=2000,
      init="random",
      svd="truncated_svd",
      normalize_factors=False,
      tol=1e-8,
      absolute_tol=1e-13,
      nn_modes=None,
      random_state=None,
      verbose=False,
      return_errors=False,
      n_iter_parafac=5,
      linesearch=True,
  ):
      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
  
      .. 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::
  
          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.
  
  
      An alternative formulation of the PARAFAC2 decomposition is that the tensor element
      :math:`X_{ijk}` is given by
  
      .. math::
  
          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, optional
          (Default: 2000) Maximum number of iteration
  
          .. versionchanged:: 0.6.1
  
              Previously, the default maximum number of iterations was 100.
      init : {'svd', 'random', CPTensor, Parafac2Tensor}
          Type of factor matrix initialization. See `initialize_factors`.
      svd : str, default is 'truncated_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 decrease tolerance. The
          algorithm is considered to have converged when
          :math:`\left|\| X - \hat{X}_{n-1} \|^2 - \| X - \hat{X}_{n} \|^2\right| < \epsilon \| X - \hat{X}_{n-1} \|^2`.
          That is, when the relative change in sum of squared error is less
          than the tolerance.
  
          .. versionchanged:: 0.6.1
  
              Previously, the stopping condition was
              :math:`\left|\| X - \hat{X}_{n-1} \| - \| X - \hat{X}_{n} \|\right| < \epsilon`.
      absolute_tol : float, optional
          (Default: 1e-13) Absolute reconstruction error tolerance. The algorithm
          is considered to have converged when
          :math:`\left|\| X - \hat{X}_{n-1} \|^2 - \| X - \hat{X}_{n} \|^2\right| < \epsilon_\text{abs}`.
          That is, when the relative sum of squared error is less than the specified tolerance.
          The absolute tolerance is necessary for stopping the algorithm when used on noise-free
          data that follows the PARAFAC2 constraint.
  
          If None, then the machine precision + 1000 will be used.
      nn_modes: None, 'all' or array of integers
          (Default: None) Used to specify which modes to impose non-negativity constraints on.
          We cannot impose non-negativity constraints on the the B-mode (mode 1) with the ALS
          algorithm, so if this mode is among the constrained modes, then a warning will be shown
          (see notes for more info).
      random_state : {None, int, np.random.RandomState}
      verbose : int, optional
          Level of verbosity
      return_errors : bool, optional
          Activate return of iteration errors
      n_iter_parafac : int, optional
          Number of PARAFAC iterations to perform for each PARAFAC2 iteration
      linesearch : bool, default is False
          Whether to perform line search as proposed by Bro in his PhD dissertation [2]_
          (similar to the PLSToolbox line search described in [3]_).
  
      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.
  
      errors : list
          A list of reconstruction errors at each iteration of the algorithms.
  
      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.
      .. [2] R. Bro, "Multi-Way Analysis in the Food Industry: Models, Algorithms, and
              Applications", PhD., University of Amsterdam, 1998
      .. [3] H. Yu, D. Augustijn, R. Bro, "Accelerating PARAFAC2 algorithms for non-negative
              complex tensor decomposition." Chemometrics and Intelligent Laboratory Systems 214
              (2021): 104312.
  
      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. This change allows the function to accept
      both lists of matrices and a single nd-array as input without any mode reordering.
  
      Because of the reformulation above, :math:`B_i = P_i B`, the :math:`B_i` matrices
      cannot be constrained to be non-negative with ALS. If this mode is constrained to be
      non-negative, then :math:`B` will be non-negative, but not the orthogonal `P_i` matrices.
      Consequently, the `B_i` matrices are unlikely to be non-negative.
      """
      weights, factors, projections = initialize_decomposition(
          tensor_slices,
          rank,
          init=init,
          svd=svd,
          random_state=random_state,
      )
      factors = list(factors)
  
      rec_errors = []
      norm_tensor = tl.sqrt(
          sum(
              tl.norm(tensor_slice, 2) ** 2
              for tensor_slice in tensor_slices
          )
      )
  
      if absolute_tol is None:
          absolute_tol = tl.eps(factors[0].dtype) * 1000
  
      if linesearch and not isinstance(
          linesearch, _BroThesisLineSearch
      ):
          linesearch = _BroThesisLineSearch(
              norm_tensor, svd, verbose=verbose, nn_modes=nn_modes
          )
  
      # If nn_modes is set, we use HALS, otherwise, we use the standard parafac implementation.
      if nn_modes is None:
  
          def parafac_updates(X, w, f):
              return parafac(
                  X,
                  rank,
                  n_iter_max=n_iter_parafac,
                  init=(w, f),
                  svd=svd,
                  orthogonalise=False,
                  verbose=verbose,
                  return_errors=False,
                  normalize_factors=False,
                  mask=None,
                  random_state=random_state,
                  tol=1e-100,
              )[1]
  
      else:
          if nn_modes == "all" or 1 in nn_modes:
              warn(
                  "Mode `1` of PARAFAC2 fitted with ALS cannot be constrained to be truly non-negative. See the documentation for more info."
              )
  
          def parafac_updates(X, w, f):
              return non_negative_parafac_hals(
                  X,
                  rank,
                  n_iter_max=n_iter_parafac,
                  init=(w, f),
                  svd=svd,
                  nn_modes=nn_modes,
                  verbose=verbose,
                  return_errors=False,
                  tol=1e-100,
              )[1]
  
      for iteration in range(n_iter_max):
          if verbose:
              print("Starting iteration", iteration)
  
          factors[1] = factors[1] * T.reshape(weights, (1, -1))
          weights = T.ones(
              weights.shape, **tl.context(tensor_slices[0])
          )
  
          # Will we be performing a line search iteration?
          if linesearch and iteration % 2 == 0 and iteration > 5:
              line_iter = True
              factors_last = [tl.copy(f) for f in factors]
          else:
              line_iter = False
  
          projections = _compute_projections(
              tensor_slices, factors, svd
          )
          projected_tensor = _project_tensor_slices(
              tensor_slices, projections
          )
          factors = parafac_updates(projected_tensor, weights, factors)
  
          # Start line search if requested.
          if line_iter:
              (
                  factors,
                  projections,
                  rec_errors[-1],
              ) = linesearch.line_step(
                  iteration,
                  tensor_slices,
                  factors_last,
                  weights,
                  factors,
                  projections,
                  rec_errors[-1],
              )
  
          if normalize_factors:
              weights, factors = cp_normalize((weights, factors))
  
          if tol and not line_iter:
              rec_error = _parafac2_reconstruction_error(
                  tensor_slices,
                  (weights, factors, projections),
                  norm_tensor,
                  projected_tensor,
              )
              rec_error /= norm_tensor
              rec_errors.append(rec_error)
  
          if tol:
              if iteration >= 1:
                  if verbose:
                      print(
                          f"PARAFAC2 reconstruction error={rec_errors[-1]}, variation={rec_errors[-2] - rec_errors[-1]}."
                      )
  
                  if (
                      abs(rec_errors[-2] ** 2 - rec_errors[-1] ** 2)
                      < (tol * rec_errors[-2] ** 2)
                      or rec_errors[-1] ** 2 < absolute_tol
                  ):
                      if verbose:
                          print(f"converged in {iteration} iterations.")
                      break
              else:
                  if verbose:
                      print(
                          f"PARAFAC2 reconstruction error={rec_errors[-1]}"
                      )
  
      parafac2_tensor = Parafac2Tensor((weights, factors, projections))
  
      if return_errors:
          return parafac2_tensor, rec_errors
      else:
          return parafac2_tensor
  
imports: from warnings import warn
  from typing import Iterable
  import tensorly
  from ._base_decomposition import DecompositionMixin
  from tensorly.random import random_parafac2
  from tensorly import backend
  from .None import parafac, non_negative_parafac_hals
  from ..parafac2_tensor import Parafac2Tensor, _validate_parafac2_tensor
  from ..cp_tensor import CPTensor, cp_normalize
  from ..tenalg.svd import svd_interface
label_desc: Use image node_id 5 for calling a global function with example usage: parafac2(tensor_slices, rank, n_iter_max, init, svd, normalize_factors, tol, absolute_tol, nn_modes, random_state, verbose, return_errors, n_iter_parafac, linesearch) and returns: parafac2_tensor,  rec_errors, parafac2_tensor, parafac, non_negative_parafac_hals