input: Write a python function to implement gradient descent optimization.
  assert np.allclose(gradient_descent(lambda x: x**2, lambda x: 2*x, 0.1, 10), 0)
  assert np.allclose(gradient_descent(lambda x: x**2, lambda x: 2*x, 0.01, 5), 0)
  assert np.allclose(gradient_descent(lambda x: (x-3)**2, lambda x: 2*(x-3), 0.1, 0), 3)
output: import numpy as np
  
  def gradient_descent(function, gradient, learning_rate, initial_value, tolerance=1e-7, max_iterations=1000):
      x = initial_value
      for _ in range(max_iterations):
          grad = gradient(x)
          new_x = x - learning_rate * grad
          if abs(new_x - x) < tolerance:
              break
          x = new_x
      return x