code: def approximate_gradient(
      decision_function,
      sample,
      num_evals,
      delta,
      constraint,
      shape,
      clip_min,
      clip_max,
  ):
      """Gradient direction estimation"""
      # Generate random vectors.
      noise_shape = [num_evals] + list(shape)
      if constraint == 2:
          rv = torch.randn(noise_shape)
      elif constraint == np.inf:
          rv = -1 + torch.rand(noise_shape) * 2
  
      axis = tuple(range(1, 1 + len(shape)))
      rv = rv / torch.sqrt(torch.sum(rv**2, dim=axis, keepdim=True))
      perturbed = sample + delta * rv.to(sample.device)
      perturbed = torch.clamp(perturbed, clip_min, clip_max)
      rv = (perturbed - sample) / delta
  
      # query the model.
      decisions = decision_function(perturbed).float()
      fval = (
          2.0
          * decisions.view((decisions.shape[0],) + (1,) * len(shape))
          - 1.0
      )
  
      # Baseline subtraction (when fval differs)
      fval_mean = torch.mean(fval)
      if fval_mean == 1.0:  # label changes.
          gradf = torch.mean(rv, dim=0)
      elif fval_mean == -1.0:  # label not change.
          gradf = -torch.mean(rv, dim=0)
      else:
          fval = fval - fval_mean
          gradf = torch.mean(fval * rv, dim=0)
  
      # Get the gradient direction.
      gradf = gradf / torch.norm(gradf, p=2)
  
      return gradf
  
file_path: cleverhans/cleverhans/torch/attacks/hop_skip_jump_attack.py
imports: import numpy
  import torch
label_desc: Use image node_id 3 for calling a global function with example usage: approximate_gradient(decision_function, sample, num_evals, delta, constraint, shape, clip_min, clip_max) and returns: gradf