code: def _fit_projected_gradient(
      X,
      W,
      H,
      tol,
      max_iter,
      nls_max_iter,
      alpha,
      l1_ratio,
      sparseness,
      beta,
      eta,
  ):
      """Compute Non-negative Matrix Factorization (NMF) with Projected Gradient
  
      References
      ----------
      C.-J. Lin. Projected gradient methods for non-negative matrix
      factorization. Neural Computation, 19(2007), 2756-2779.
      http://www.csie.ntu.edu.tw/~cjlin/nmf/
  
      P. Hoyer. Non-negative Matrix Factorization with Sparseness Constraints.
      Journal of Machine Learning Research 2004.
      """
      gradW = np.dot(W, np.dot(H, H.T)) - safe_sparse_dot(
          X, H.T, dense_output=True
      )
      gradH = np.dot(np.dot(W.T, W), H) - safe_sparse_dot(
          W.T, X, dense_output=True
      )
  
      init_grad = squared_norm(gradW) + squared_norm(gradH.T)
      # max(0.001, tol) to force alternating minimizations of W and H
      tolW = max(0.001, tol) * np.sqrt(init_grad)
      tolH = tolW
  
      for n_iter in range(1, max_iter + 1):
          # stopping condition
          # as discussed in paper
          proj_grad_W = squared_norm(
              gradW * np.logical_or(gradW < 0, W > 0)
          )
          proj_grad_H = squared_norm(
              gradH * np.logical_or(gradH < 0, H > 0)
          )
  
          if (proj_grad_W + proj_grad_H) / init_grad < tol**2:
              break
  
          # update W
          W, gradW, iterW = _update_projected_gradient_w(
              X,
              W,
              H,
              tolW,
              nls_max_iter,
              alpha,
              l1_ratio,
              sparseness,
              beta,
              eta,
          )
          if iterW == 1:
              tolW = 0.1 * tolW
  
          # update H
          H, gradH, iterH = _update_projected_gradient_h(
              X,
              W,
              H,
              tolH,
              nls_max_iter,
              alpha,
              l1_ratio,
              sparseness,
              beta,
              eta,
          )
          if iterH == 1:
              tolH = 0.1 * tolH
  
      H[H == 0] = 0  # fix up negative zeros
  
      if n_iter == max_iter:
          W, _, _ = _update_projected_gradient_w(
              X,
              W,
              H,
              tol,
              nls_max_iter,
              alpha,
              l1_ratio,
              sparseness,
              beta,
              eta,
          )
  
      return W, H, n_iter
  
imports: from __future__ import division, print_function
  from math import sqrt
  import warnings
  import numbers
  import numpy
  import scipy.sparse
  from ..base import BaseEstimator, TransformerMixin
  from ..utils import check_random_state, check_array
  from ..utils.extmath import randomized_svd, safe_sparse_dot, squared_norm
  from ..utils.extmath import fast_dot
  from ..utils.validation import check_is_fitted, check_non_negative
  from ..utils import deprecated
  from ..exceptions import ConvergenceWarning
  from .cdnmf_fast import _update_cdnmf_fast
label_desc: Use image node_id 12 for calling a global function with example usage: _fit_projected_gradient(X, W, H, tol, max_iter, nls_max_iter, alpha, l1_ratio, sparseness, beta, eta) and returns: W,  H,  n_iter