code: def non_negative_parafac(
      tensor,
      rank,
      n_iter_max=100,
      init="svd",
      svd="truncated_svd",
      tol=10e-7,
      random_state=None,
      verbose=0,
      normalize_factors=False,
      return_errors=False,
      mask=None,
      cvg_criterion="abs_rec_error",
      fixed_modes=None,
  ):
      """
      Non-negative CP decomposition
  
      Uses multiplicative updates, see [2]_
  
      Parameters
      ----------
      tensor : ndarray
      rank   : int
              number of components
      n_iter_max : int
                   maximum number of iteration
      init : {'svd', 'random'}, optional
      svd : str, default is 'truncated_svd'
          function to use to compute the SVD, acceptable values in tensorly.SVD_FUNS
      tol : float, optional
            tolerance: the algorithm stops when the variation in
            the reconstruction error is less than the tolerance
      random_state : {None, int, np.random.RandomState}
      verbose : int, optional
          level of verbosity
      normalize_factors : if True, aggregate the weights of each factor in a 1D-tensor
          of shape (rank, ), which will contain the norms of the factors
      fixed_modes : list, default is None
          A list of modes for which the initial value is not modified.
          The last mode cannot be fixed due to error computation.
  
      Returns
      -------
      factors : ndarray list
              list of positive factors of the CP decomposition
              element `i` is of shape ``(tensor.shape[i], rank)``
  
      References
      ----------
      .. [2] Amnon Shashua and Tamir Hazan,
             "Non-negative tensor factorization with applications to statistics and computer vision",
             In Proceedings of the International Conference on Machine Learning (ICML),
             pp 792-799, ICML, 2005
      """
      epsilon = tl.eps(tensor.dtype)
      rank = validate_cp_rank(tl.shape(tensor), rank=rank)
  
      weights, factors = initialize_cp(
          tensor,
          rank,
          init=init,
          svd=svd,
          non_negative=True,
          mask=mask,
          random_state=random_state,
          normalize_factors=normalize_factors,
      )
      rec_errors = []
      norm_tensor = tl.norm(tensor, 2)
  
      if fixed_modes is None:
          fixed_modes = []
  
      if tl.ndim(tensor) - 1 in fixed_modes:
          warnings.warn(
              "You asked for fixing the last mode, which is not supported while tol is fixed.\n The last mode will not be fixed. Consider using tl.moveaxis()"
          )
          fixed_modes.remove(tl.ndim(tensor) - 1)
      modes_list = [
          mode
          for mode in range(tl.ndim(tensor))
          if mode not in fixed_modes
      ]
  
      for iteration in range(n_iter_max):
          if verbose > 1:
              print("Starting iteration", iteration + 1)
          for mode in modes_list:
              if verbose > 1:
                  print("Mode", mode, "of", tl.ndim(tensor))
  
              accum = 1
              # khatri_rao(factors).tl.dot(khatri_rao(factors))
              # simplifies to multiplications
              sub_indices = [
                  i for i in range(len(factors)) if i != mode
              ]
              for i, e in enumerate(sub_indices):
                  if i:
                      accum *= tl.dot(
                          tl.transpose(factors[e]), factors[e]
                      )
                  else:
                      accum = tl.dot(
                          tl.transpose(factors[e]), factors[e]
                      )
              accum = (
                  tl.reshape(weights, (-1, 1))
                  * accum
                  * tl.reshape(weights, (1, -1))
              )
              if mask is not None:
                  tensor = tensor * mask + tl.cp_to_tensor(
                      (weights, factors), mask=1 - mask
                  )
  
              mttkrp = unfolding_dot_khatri_rao(
                  tensor, (weights, factors), mode
              )
  
              numerator = tl.clip(mttkrp, a_min=epsilon, a_max=None)
              denominator = tl.dot(factors[mode], accum)
              denominator = tl.clip(
                  denominator, a_min=epsilon, a_max=None
              )
              factor = factors[mode] * numerator / denominator
  
              factors[mode] = factor
              if normalize_factors and mode != modes_list[-1]:
                  weights, factors = cp_normalize((weights, factors))
  
          if tol:
              # ||tensor - rec||^2 = ||tensor||^2 + ||rec||^2 - 2*<tensor, rec>
              factors_norm = cp_norm((weights, factors))
  
              # mttkrp and factor for the last mode. This is equivalent to the
              # inner product <tensor, factorization>
              iprod = tl.sum(tl.sum(mttkrp * factor, axis=0))
              rec_error = (
                  tl.sqrt(
                      tl.abs(
                          norm_tensor**2
                          + factors_norm**2
                          - 2 * iprod
                      )
                  )
                  / norm_tensor
              )
              rec_errors.append(rec_error)
              if iteration >= 1:
                  rec_error_decrease = rec_errors[-2] - rec_errors[-1]
  
                  if verbose:
                      print(
                          f"iteration {iteration}, reconstraction error: {rec_error}, decrease = {rec_error_decrease}"
                      )
  
                  if cvg_criterion == "abs_rec_error":
                      stop_flag = abs(rec_error_decrease) < tol
                  elif cvg_criterion == "rec_error":
                      stop_flag = rec_error_decrease < tol
                  else:
                      raise TypeError("Unknown convergence criterion")
  
                  if stop_flag:
                      if verbose:
                          print(
                              f"PARAFAC converged after {iteration} iterations"
                          )
                      break
              else:
                  if verbose:
                      print(f"reconstruction error={rec_errors[-1]}")
          if normalize_factors:
              weights, factors = cp_normalize((weights, factors))
      cp_tensor = CPTensor((weights, factors))
  
      if return_errors:
          return cp_tensor, rec_errors
      else:
          return cp_tensor
  
file_path: tensorly/tensorly/decomposition/_nn_cp.py
imports: import warnings
  import tensorly
  from ._base_decomposition import DecompositionMixin
  from ._cp import initialize_cp
  from ..tenalg.proximal import hals_nnls
  from ..cp_tensor import CPTensor, unfolding_dot_khatri_rao, cp_norm, cp_normalize, validate_cp_rank
  from ..tenalg.svd import svd_interface
label_desc: Use image node_id 1 for calling a global function with example usage: non_negative_parafac(tensor, rank, n_iter_max, init, svd, tol, random_state, verbose, normalize_factors, return_errors, mask, cvg_criterion, fixed_modes) and returns: cp_tensor,  rec_errors, cp_tensor