code: def _gradient_descent(
      objective,
      p0,
      it,
      n_iter,
      n_iter_check=1,
      n_iter_without_progress=300,
      momentum=0.8,
      learning_rate=200.0,
      min_gain=0.01,
      min_grad_norm=1e-7,
      verbose=0,
      args=None,
      kwargs=None,
  ):
      """Batch gradient descent with momentum and individual gains.
  
      Parameters
      ----------
      objective : callable
          Should return a tuple of cost and gradient for a given parameter
          vector. When expensive to compute, the cost can optionally
          be None and can be computed every n_iter_check steps using
          the objective_error function.
  
      p0 : array-like of shape (n_params,)
          Initial parameter vector.
  
      it : int
          Current number of iterations (this function will be called more than
          once during the optimization).
  
      n_iter : int
          Maximum number of gradient descent iterations.
  
      n_iter_check : int, default=1
          Number of iterations before evaluating the global error. If the error
          is sufficiently low, we abort the optimization.
  
      n_iter_without_progress : int, default=300
          Maximum number of iterations without progress before we abort the
          optimization.
  
      momentum : float within (0.0, 1.0), default=0.8
          The momentum generates a weight for previous gradients that decays
          exponentially.
  
      learning_rate : float, default=200.0
          The learning rate for t-SNE is usually in the range [10.0, 1000.0]. If
          the learning rate is too high, the data may look like a 'ball' with any
          point approximately equidistant from its nearest neighbours. If the
          learning rate is too low, most points may look compressed in a dense
          cloud with few outliers.
  
      min_gain : float, default=0.01
          Minimum individual gain for each parameter.
  
      min_grad_norm : float, default=1e-7
          If the gradient norm is below this threshold, the optimization will
          be aborted.
  
      verbose : int, default=0
          Verbosity level.
  
      args : sequence, default=None
          Arguments to pass to objective function.
  
      kwargs : dict, default=None
          Keyword arguments to pass to objective function.
  
      Returns
      -------
      p : ndarray of shape (n_params,)
          Optimum parameters.
  
      error : float
          Optimum.
  
      i : int
          Last iteration.
      """
      if args is None:
          args = []
      if kwargs is None:
          kwargs = {}
  
      p = p0.copy().ravel()
      update = np.zeros_like(p)
      gains = np.ones_like(p)
      error = np.finfo(float).max
      best_error = np.finfo(float).max
      best_iter = i = it
  
      tic = time()
      for i in range(it, n_iter):
          check_convergence = (i + 1) % n_iter_check == 0
          # only compute the error when needed
          kwargs["compute_error"] = check_convergence or i == n_iter - 1
  
          error, grad = objective(p, *args, **kwargs)
  
          inc = update * grad < 0.0
          dec = np.invert(inc)
          gains[inc] += 0.2
          gains[dec] *= 0.8
          np.clip(gains, min_gain, np.inf, out=gains)
          grad *= gains
          update = momentum * update - learning_rate * grad
          p += update
  
          if check_convergence:
              toc = time()
              duration = toc - tic
              tic = toc
              grad_norm = linalg.norm(grad)
  
              if verbose >= 2:
                  print(
                      "[t-SNE] Iteration %d: error = %.7f,"
                      " gradient norm = %.7f"
                      " (%s iterations in %0.3fs)"
                      % (
                          i + 1,
                          error,
                          grad_norm,
                          n_iter_check,
                          duration,
                      )
                  )
  
              if error < best_error:
                  best_error = error
                  best_iter = i
              elif i - best_iter > n_iter_without_progress:
                  if verbose >= 2:
                      print(
                          "[t-SNE] Iteration %d: did not make any progress "
                          "during the last %d episodes. Finished."
                          % (i + 1, n_iter_without_progress)
                      )
                  break
              if grad_norm <= min_grad_norm:
                  if verbose >= 2:
                      print(
                          "[t-SNE] Iteration %d: gradient norm %f. Finished."
                          % (i + 1, grad_norm)
                      )
                  break
  
      return p, error, i
  
imports: from numbers import Integral, Real
  from time import time
  import numpy
  from scipy import linalg
  from scipy.sparse import csr_matrix, issparse
  from scipy.spatial.distance import pdist, squareform
  from ..base import BaseEstimator, ClassNamePrefixFeaturesOutMixin, TransformerMixin, _fit_context
  from ..decomposition import PCA
  from ..metrics.pairwise import _VALID_METRICS, pairwise_distances
  from ..neighbors import NearestNeighbors
  from ..utils import check_random_state
  from ..utils._openmp_helpers import _openmp_effective_n_threads
  from ..utils._param_validation import Interval, StrOptions, validate_params
  from ..utils.validation import _num_samples, check_non_negative
  from .None import _barnes_hut_tsne, _utils
label_desc: Use image node_id 5 for calling a global function with example usage: _gradient_descent(objective, p0, it, n_iter, n_iter_check, n_iter_without_progress, momentum, learning_rate, min_gain, min_grad_norm, verbose, args, kwargs) and returns: p,  error,  i