code: def eig_one_step(current_vector, learning_rate, vector_prod_fn):
      """Function that performs one step of gd (variant) for min eigen value.
  
      Args:
        current_vector: current estimate of the eigen vector with minimum eigen
          value.
        learning_rate: learning rate.
        vector_prod_fn: function which returns product H*x, where H is a matrix for
          which we computing eigenvector.
  
      Returns:
        updated vector after one step
      """
      grad = 2 * vector_prod_fn(current_vector)
      # Current objective = (1/2)*v^T (2*M*v); v = current_vector
      # grad = 2*M*v
      current_objective = tf.reshape(
          tf.matmul(tf.transpose(current_vector), grad) / 2.0, shape=()
      )
  
      # Project the gradient into the tangent space of the constraint region.
      # This way we do not waste time taking steps that try to change the
      # norm of current_vector
      grad = grad - current_vector * tf.matmul(
          tf.transpose(current_vector), grad
      )
      grad_norm = tf.norm(grad)
      grad_norm_sq = tf.square(grad_norm)
  
      # Computing normalized gradient of unit norm
      norm_grad = grad / grad_norm
  
      # Computing directional second derivative (dsd)
      # dsd = 2*g^T M g, where g is normalized gradient
      directional_second_derivative = tf.reshape(
          2
          * tf.matmul(
              tf.transpose(norm_grad), vector_prod_fn(norm_grad)
          ),
          shape=(),
      )
  
      # Computing grad^\top M grad [useful to compute step size later]
      # Just a rescaling of the directional_second_derivative (which uses
      # normalized gradient
      grad_m_grad = directional_second_derivative * grad_norm_sq / 2
  
      # Directional_second_derivative/2 = objective when vector is norm_grad
      # If this is smaller than current objective, simply return that
      if directional_second_derivative / 2.0 < current_objective:
          return norm_grad
  
      # If curvature is positive, jump to the bottom of the bowl
      if directional_second_derivative > 0.0:
          step = -1.0 * grad_norm / directional_second_derivative
      else:
          # If the gradient is very small, do not move
          if grad_norm_sq <= 1e-16:
              step = 0.0
          else:
              # Make a heuristic guess of the step size
              step = (
                  -2.0
                  * tf.reduce_sum(current_vector * grad)
                  / grad_norm_sq
              )
              # Computing gain using the gradient and second derivative
              gain = -(
                  2 * tf.reduce_sum(current_vector * grad)
                  + (step * step) * grad_m_grad
              )
  
              # Fall back to pre-determined learning rate if no gain
              if gain < 0.0:
                  step = -learning_rate * grad_norm
      current_vector = current_vector + step * norm_grad
      return tf.nn.l2_normalize(current_vector)
  
file_path: cleverhans/cleverhans/experimental/certification/utils.py
imports: from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  import numpy
  import tensorflow
label_desc: Use image node_id 3 for calling a global function with example usage: eig_one_step(current_vector, learning_rate, vector_prod_fn) and returns: tf, norm_grad