text: import torch
  
  def conjugate_gradient(A, b, max_iters, res_tol=1e-10):
      x = torch.zeros_like(b)
      r = b - A(x)
      p = r
  
      rTr = r.T @ r
  
      for _ in range(max_iters):
          Ap = A(p)
          alpha = rTr / (p.T @ Ap)
          x = x + alpha * p
  
          r = r - alpha * Ap
          if torch.norm(r) < res_tol:
              break
  
          rTrnew = r.T @ r
          beta = rTrnew / rTr
          p = r + beta * p
          rTr = rTrnew
  
      return x
  
  def line_search(f, x0, dx, g0, alpha, condition, max_steps=10, c1=0.1):
      assert 0 < alpha < 1
      
      f0 = f(x0)
      for _ in range(max_steps):
          x = x0 + dx
  
          if (f(x) > f0 + c1 * g0.T @ dx) and condition(x):
              return x
          
          dx *= alpha
      
      print('Line search failed, returning x0')
      return x0
  