class_docstr: Riemannian gradient descent.
class_docstr_tok: ["Riemannian","gradient","descent","."]
docstr: Perform gradient descent.
docstr_tok: ["Perform","gradient","descent","."]
code: def minimize(self, space, fun, x0):
      """Perform gradient descent."""
      fun = self._handle_jac(fun)
      vector_transport = self._get_vector_transport(space)
  
      lr = self.init_step_size
  
      intercept_init, coef_init = gs.split(x0, 2)
      intercept_init = gs.reshape(intercept_init, space.shape)
      coef_init = gs.reshape(coef_init, space.shape)
  
      intercept_hat = intercept_hat_new = space.projection(
          intercept_init
      )
      coef_hat = coef_hat_new = space.to_tangent(
          coef_init, intercept_hat
      )
      param = gs.vstack(
          [gs.flatten(intercept_hat), gs.flatten(coef_hat)]
      )
  
      current_loss = math.inf
      current_grad = gs.zeros_like(param)
      current_iter = i = 0
  
      for i in range(self.max_iter):
          loss, grad = fun(param)
          if gs.any(gs.isnan(grad)):
              logging.warning(
                  f"NaN encountered in gradient at iter {current_iter}"
              )
              lr /= 2
              grad = current_grad
          elif loss >= current_loss and i > 0:
              lr /= 2
          else:
              if not current_iter % 5:
                  lr *= 2
              coef_hat = coef_hat_new
              intercept_hat = intercept_hat_new
              current_iter += 1
          if abs(loss - current_loss) < self.tol:
              if self.verbose:
                  logging.info(
                      f"Tolerance threshold reached at iter {current_iter}"
                  )
              break
  
          grad_intercept, grad_coef = gs.split(grad, 2)
          riem_grad_intercept = space.to_tangent(
              gs.reshape(grad_intercept, space.shape), intercept_hat
          )
          riem_grad_coef = space.to_tangent(
              gs.reshape(grad_coef, space.shape), intercept_hat
          )
  
          intercept_hat_new = space.metric.exp(
              -lr * riem_grad_intercept, intercept_hat
          )
          coef_hat_new = vector_transport(
              coef_hat - lr * riem_grad_coef,
              -lr * riem_grad_intercept,
              intercept_hat,
              intercept_hat_new,
          )
  
          param = gs.vstack(
              [gs.flatten(intercept_hat_new), gs.flatten(coef_hat_new)]
          )
  
          current_loss = loss
          current_grad = grad
  
      if self.verbose:
          logging.info(
              f"Number of gradient evaluations: {i}, "
              f"Number of gradient iterations: {current_iter}"
              f" loss at termination: {current_loss}"
          )
  
      return OptimizeResult(fun=loss, x=param, nit=current_iter)
  
imports: import logging
  import math
  from scipy.optimize import OptimizeResult
  from sklearn.base import BaseEstimator
  import geomstats.backend
  import geomstats.errors
  from geomstats.learning.frechet_mean import FrechetMean
  from geomstats.numerics.optimizers import ScipyMinimize
label_desc: Use image node_id 4 for calling the RiemannianGradientDescent obj's underlying member method code with example usage: obj.minimize(space, fun, x0) and returns: OptimizeResult