code: def non_negative_parafac_hals(
      tensor,
      rank,
      n_iter_max=100,
      init="svd",
      svd="truncated_svd",
      tol=10e-8,
      random_state=None,
      sparsity_coefficients=None,
      fixed_modes=None,
      nn_modes="all",
      exact=False,
      normalize_factors=False,
      verbose=False,
      return_errors=False,
      cvg_criterion="abs_rec_error",
  ):
      """
      Non-negative CP decomposition via HALS
  
      Uses Hierarchical ALS (Alternating Least Squares)
      which updates each factor column-wise (one column at a time while keeping all other columns fixed), see [1]_
  
      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
            Default: 1e-8
      random_state : {None, int, np.random.RandomState}
      sparsity_coefficients: array of float (of length the number of modes)
          The sparsity coefficients on each factor.
          If set to None, the algorithm is computed without sparsity
          Default: None,
      fixed_modes: array of integers (between 0 and the number of modes)
          Has to be set not to update a factor, 0 and 1 for U and V respectively
          Default: None
      nn_modes: None, 'all' or array of integers (between 0 and the number of modes)
          Used to specify which modes to impose non-negativity constraints on.
          If 'all', then non-negativity is imposed on all modes.
          Default: 'all'
      exact: If it is True, the algorithm gives a results with high precision but it needs high computational cost.
          If it is False, the algorithm gives an approximate solution
          Default: False
      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
      verbose: boolean
          Indicates whether the algorithm prints the successive
          reconstruction errors or not
          Default: False
      return_errors: boolean
          Indicates whether the algorithm should return all reconstruction errors
          and computation time of each iteration or not
          Default: False
      cvg_criterion : {'abs_rec_error', 'rec_error'}, optional
          Stopping criterion for ALS, works if `tol` is not None.
          If 'rec_error',  ALS stops at current iteration if ``(previous rec_error - current rec_error) < tol``.
          If 'abs_rec_error', ALS terminates when `|previous rec_error - current rec_error| < tol`.
      sparsity : float or int
      random_state : {None, int, np.random.RandomState}
  
      Returns
      -------
      factors : ndarray list
              list of positive factors of the CP decomposition
              element `i` is of shape ``(tensor.shape[i], rank)``
      errors: list
          A list of reconstruction errors at each iteration of the algorithm.
  
      References
      ----------
      .. [1] N. Gillis and F. Glineur, Accelerated Multiplicative Updates and
             Hierarchical ALS Algorithms for Nonnegative Matrix Factorization,
             Neural Computation 24 (4): 1085-1105, 2012.
      """
  
      weights, factors = initialize_cp(
          tensor,
          rank,
          init=init,
          svd=svd,
          non_negative=True,
          random_state=random_state,
          normalize_factors=normalize_factors,
      )
  
      norm_tensor = tl.norm(tensor, 2)
  
      n_modes = tl.ndim(tensor)
      if sparsity_coefficients is None or isinstance(
          sparsity_coefficients, float
      ):
          sparsity_coefficients = [sparsity_coefficients] * n_modes
  
      if fixed_modes is None:
          fixed_modes = []
  
      if nn_modes == "all":
          nn_modes = set(range(n_modes))
      elif nn_modes is None:
          nn_modes = set()
  
      # Avoiding errors
      for fixed_value in fixed_modes:
          sparsity_coefficients[fixed_value] = None
  
      for mode in range(n_modes):
          if sparsity_coefficients[mode] is not None:
              warnings.warn(
                  "Sparsity coefficient is ignored in unconstrained modes."
              )
      # Generating the mode update sequence
      modes = [
          mode for mode in range(n_modes) if mode not in fixed_modes
      ]
  
      # initialisation - declare local varaibles
      rec_errors = []
  
      # Iteratation
      for iteration in range(n_iter_max):
          # One pass of least squares on each updated mode
          for mode in modes:
              # Computing Hadamard of cross-products
              pseudo_inverse = tl.ones(
                  (rank, rank), **tl.context(tensor)
              )
              for i, factor in enumerate(factors):
                  if i != mode:
                      pseudo_inverse = pseudo_inverse * tl.dot(
                          tl.transpose(factor), factor
                      )
  
              pseudo_inverse = (
                  tl.reshape(weights, (-1, 1))
                  * pseudo_inverse
                  * tl.reshape(weights, (1, -1))
              )
              mttkrp = unfolding_dot_khatri_rao(
                  tensor, (weights, factors), mode
              )
  
              if mode in nn_modes:
                  # Call the hals resolution with nnls, optimizing the current mode
                  nn_factor, _, _, _ = hals_nnls(
                      tl.transpose(mttkrp),
                      pseudo_inverse,
                      tl.transpose(factors[mode]),
                      n_iter_max=100,
                      sparsity_coefficient=sparsity_coefficients[mode],
                      exact=exact,
                  )
                  factors[mode] = tl.transpose(nn_factor)
              else:
                  factor = tl.solve(
                      tl.transpose(pseudo_inverse), tl.transpose(mttkrp)
                  )
                  factors[mode] = tl.transpose(factor)
              if normalize_factors and mode != modes[-1]:
                  weights, factors = cp_normalize((weights, factors))
          if tol:
              factors_norm = cp_norm((weights, factors))
              iprod = tl.sum(tl.sum(mttkrp * factors[-1], 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}, reconstruction 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 2 for calling a global function with example usage: non_negative_parafac_hals(tensor, rank, n_iter_max, init, svd, tol, random_state, sparsity_coefficients, fixed_modes, nn_modes, exact, normalize_factors, verbose, return_errors, cvg_criterion) and returns: cp_tensor,  rec_errors, cp_tensor