code: def sparse_nnls(
      y,
      X,
      momentum=1,
      step_size=0.01,
      non_neg=True,
      check_error_iter=10,
      max_error_checks=10,
      converge_on_sse=0.99,
  ):
      """
      Solve y=Xh for h, using gradient descent, with X a sparse matrix.
  
      Parameters
      ----------
      y : 1-d array of shape (N)
          The data. Needs to be dense.
  
      X : ndarray. May be either sparse or dense. Shape (N, M)
         The regressors
  
      momentum : float, optional (default: 1).
          The persistence of the gradient.
  
      step_size : float, optional (default: 0.01).
          The increment of parameter update in each iteration
  
      non_neg : Boolean, optional (default: True)
          Whether to enforce non-negativity of the solution.
  
      check_error_iter : int (default:10)
          How many rounds to run between error evaluation for
          convergence-checking.
  
      max_error_checks : int (default: 10)
          Don't check errors more than this number of times if no improvement in
          r-squared is seen.
  
      converge_on_sse : float (default: 0.99)
        a percentage improvement in SSE that is required each time to say
        that things are still going well.
  
      Returns
      -------
      h_best : The best estimate of the parameters.
  
      """
      num_regressors = X.shape[1]
      # Initialize the parameters at the origin:
      h = np.zeros(num_regressors)
      # If nothing good happens, we'll return that:
      h_best = h
      iteration = 1
      ss_residuals_min = (
          np.inf
      )  # This will keep track of the best solution
      sse_best = (
          np.inf
      )  # This will keep track of the best performance so far
      count_bad = 0  # Number of times estimation error has gone up.
      error_checks = 0  # How many error checks have we done so far
  
      while 1:
          if iteration > 1:
              # The gradient is (Kay 2008 supplemental page 27):
              gradient = spdot(X.T, spdot(X, h) - y)
              gradient += momentum * gradient
              # Normalize to unit-length
              unit_length_gradient = gradient / np.sqrt(
                  np.dot(gradient, gradient)
              )
              # Update the parameters in the direction of the gradient:
              h -= step_size * unit_length_gradient
              if non_neg:
                  # Set negative values to 0:
                  h[h < 0] = 0
  
          # Every once in a while check whether it's converged:
          if np.mod(iteration, check_error_iter):
              # This calculates the sum of squared residuals at this point:
              sse = np.sum((y - spdot(X, h)) ** 2)
              # Did we do better this time around?
              if sse < ss_residuals_min:
                  # Update your expectations about the minimum error:
                  ss_residuals_min = sse
                  h_best = (
                      h  # This holds the best params we have so far
                  )
                  # Are we generally (over iterations) converging on
                  # sufficient improvement in r-squared?
                  if sse < converge_on_sse * sse_best:
                      sse_best = sse
                      count_bad = 0
                  else:
                      count_bad += 1
              else:
                  count_bad += 1
  
              if count_bad >= max_error_checks:
                  return h_best
              error_checks += 1
          iteration += 1
  
imports: import abc
  import warnings
  import numpy
  import scipy.sparse
  import scipy.optimize
  from scipy.optimize import minimize
  from dipy.utils.optpkg import optional_package
label_desc: Use image node_id 2 for calling a global function with example usage: sparse_nnls(y, X, momentum, step_size, non_neg, check_error_iter, max_error_checks, converge_on_sse) and returns: h_best