code: def hop_skip_jump_attack(
      model_fn,
      x,
      norm,
      y_target=None,
      image_target=None,
      initial_num_evals=100,
      max_num_evals=10000,
      stepsize_search="geometric_progression",
      num_iterations=64,
      gamma=1.0,
      constraint=2,
      batch_size=128,
      verbose=True,
      clip_min=0,
      clip_max=1,
  ):
      """
      PyTorch implementation of HopSkipJumpAttack.
      HopSkipJumpAttack was originally proposed by Chen, Jordan and Wainwright.
      It is a decision-based attack that requires access to output
      labels of a model alone.
      Paper link: https://arxiv.org/abs/1904.02144
      At a high level, this attack is an iterative attack composed of three
      steps: Binary search to approach the boundary; gradient estimation;
      stepsize search. HopSkipJumpAttack requires fewer model queries than
      Boundary Attack which was based on rejective sampling.
  
      :param model_fn: a callable that takes an input tensor and returns the model logits.
      :param x: input tensor with n samples.
      :param norm: The distance to optimize. Possible values: 2 or np.inf.
      :param y_target:  A tensor of shape (n, nb_classes) for target labels.
      Required for targeted attack.
      :param image_target: A tensor of shape (n, **image shape) for initial
      target images. Required for targeted attack.
      :param initial_num_evals: initial number of evaluations for
                                gradient estimation.
      :param max_num_evals: maximum number of evaluations for gradient estimation.
      :param stepsize_search: How to search for stepsize; choices are
                              'geometric_progression', 'grid_search'.
                              'geometric progression' initializes the stepsize
                               by ||x_t - x||_p / sqrt(iteration), and keep
                               decreasing by half until reaching the target
                               side of the boundary. 'grid_search' chooses the
                               optimal epsilon over a grid, in the scale of
                               ||x_t - x||_p.
      :param num_iterations: The number of iterations.
      :param gamma: The binary search threshold theta is gamma / d^{3/2} for
                     l2 attack and gamma / d^2 for linf attack.
      :param batch_size: batch_size for model prediction.
      :param verbose: (boolean) Whether distance at each step is printed.
      :param clip_min: (optional float) Minimum input component value
      :param clip_max: (optional float) Maximum input component value
      """
      shape = (1,) + x.shape[1:]
      if y_target is not None:
          assert (
              image_target is not None
          ), "Require a target image for targeted attack."
      if clip_min is not None and clip_max is not None:
          if clip_min > clip_max:
              raise ValueError(
                  "clip_min must be less than or equal to clip_max, got clip_min={} and clip_max={}".format(
                      clip_min, clip_max
                  )
              )
  
      d = int(np.prod(shape))
  
      if constraint == 2:
          theta = gamma / (np.sqrt(d) * d)
      else:
          theta = gamma / (d * d)
  
      def hsja(sample, target_label, target_image):
          if target_label is None:
              _, original_label = torch.max(model_fn(sample), 1)
  
          def decision_function(images):
              """
              Decision function output 1 on the desired side of the boundary,
              0 otherwise.
              """
              images = torch.clamp(images, clip_min, clip_max)
              prob = []
              for i in range(0, len(images), batch_size):
                  batch = images[i : i + batch_size]
                  prob_i = model_fn(batch)
                  prob.append(prob_i)
              prob = torch.cat(prob, dim=0)
              if target_label is None:
                  return torch.max(prob, dim=1)[1] != original_label
              else:
                  return torch.max(prob, dim=1)[1] == target_label
  
          # Initialize.
          if target_image is None:
              perturbed = initialize(
                  decision_function, sample, shape, clip_min, clip_max
              )
          else:
              perturbed = target_image.to(sample.device)
  
          # Project the initialization to the boundary.
          perturbed, dist_post_update = binary_search_batch(
              sample,
              perturbed,
              decision_function,
              shape,
              constraint,
              theta,
          )
          dist = compute_distance(perturbed, sample, constraint)
  
          for j in np.arange(num_iterations):
              current_iteration = j + 1
  
              # Choose delta.
              delta = select_delta(
                  dist_post_update,
                  current_iteration,
                  clip_max,
                  clip_min,
                  d,
                  theta,
                  constraint,
              )
  
              # Choose number of evaluations.
              num_evals = int(
                  min(
                      [
                          initial_num_evals * np.sqrt(j + 1),
                          max_num_evals,
                      ]
                  )
              )
  
              # approximate gradient.
              gradf = approximate_gradient(
                  decision_function,
                  perturbed,
                  num_evals,
                  delta,
                  constraint,
                  shape[1:],
                  clip_min,
                  clip_max,
              )
              if constraint == np.inf:
                  update = torch.sign(gradf)
              else:
                  update = gradf
  
              # search step size.
              if stepsize_search == "geometric_progression":
                  # find step size.
                  epsilon = geometric_progression_for_stepsize(
                      perturbed,
                      update,
                      dist,
                      decision_function,
                      current_iteration,
                  )
  
                  # Update the sample.
                  perturbed = torch.clamp(
                      perturbed + epsilon * update, clip_min, clip_max
                  )
  
                  # Binary search to return to the boundary.
                  perturbed, dist_post_update = binary_search_batch(
                      sample,
                      perturbed,
                      decision_function,
                      shape,
                      constraint,
                      theta,
                  )
  
              elif stepsize_search == "grid_search":
                  # Grid search for stepsize.
                  epsilons = (
                      torch.from_numpy(
                          np.logspace(-4, 0, num=20, endpoint=True)
                      )
                      .to(perturbed.device)
                      .float()
                      * dist
                  )
                  perturbeds = (
                      perturbed
                      + epsilons.view((20,) + (1,) * (len(shape) - 1))
                      * update
                  )
                  perturbeds = torch.clamp(
                      perturbeds, clip_min, clip_max
                  )
                  idx_perturbed = decision_function(perturbeds)
  
                  if torch.sum(idx_perturbed) > 0:
                      # Select the perturbation that yields the minimum distance # after binary search.
                      perturbed, dist_post_update = binary_search_batch(
                          sample,
                          perturbeds[idx_perturbed],
                          decision_function,
                          shape,
                          constraint,
                          theta,
                      )
  
              # compute new distance.
              dist = compute_distance(perturbed, sample, constraint)
              if verbose:
                  print(
                      "iteration: {:d}, {:s} distance {:.4E}".format(
                          j + 1, str(constraint), dist
                      )
                  )
  
          return perturbed
  
      # Perform attack on one instance at a time
      adv_x = []
      for i, x_ in enumerate(x):
          x_ = x_.unsqueeze(0)
          if y_target is not None:
              # targeted attack that requires target label and image.
              pert = hsja(x_, y_target[i], image_target[i])
          else:
              if image_target is not None:
                  pert = hsja(x_, None, image_target[i])
              else:
                  # untargeted attack without an initialized image.
                  pert = hsja(x_, None, None)
          adv_x.append(pert)
      return torch.cat(adv_x, 0)
  
file_path: cleverhans/cleverhans/torch/attacks/hop_skip_jump_attack.py
imports: import numpy
  import torch
label_desc: Use image node_id 1 for calling a global function with example usage: hop_skip_jump_attack(model_fn, x, norm, y_target, image_target, initial_num_evals, max_num_evals, stepsize_search, num_iterations, gamma, constraint, batch_size, verbose, clip_min, clip_max) and returns: torch, perturbed, unknown, unknown