def armijo_backtracking(self, func, alpha, maxiter=20):
        """
        Returns step and next point, minimizing given functional

        Parameters
        ----------
        func : function
            function to minimize
        x : ManifoldElement
            initial point
        alpha : float
            estimated line search parameter
        direction : TangentVector
            direction to move from initial point
        conj_direction : TangentVector
            conjugated direction

        Returns
        -------
        x_new :
            next point (x + step * direction)
        step : float
            optimal step
        """
        scale = -0.0001 * alpha
        for i in range(maxiter):
            x_new = svd_retraction(self.x + (0.5 ** i * alpha) * self.conj.release(), self.x.r)
            bound = (0.5 ** i * scale) * self.grad.release().scalar_product(self.conj.release())
            if self.cost_raw(self.x) - self.cost_raw(x_new) >= bound:
                return x_new, 0.5 ** i * scale
        return x_new, 0.5 ** maxiter * scale
 def gd_step(self):
     alpha = minimize_scalar(lambda x: self.cost_func(x), bounds=(0.0, 10.0), method="bounded")["x"]
     if alpha is None:
         alpha = 1.0
     print(alpha)
     self.x = svd_retraction(self.x + alpha * self.grad.release(), self.x.r)
     # self.armijo_backtracking(lambda x: self.cost_raw(x), alpha)[0]
     return None
 def cost_func(self, param):
     retracted = svd_retraction(self.x + param * self.grad.release(), self.x.r)
     return self.cost_raw(retracted)