class_docstr: Local Search Attack
  Algorithm 3 in https://arxiv.org/pdf/1612.06299.pdf
  
  :param predict: forward pass function.
  :param clip_min: mininum value per input dimension.
  :param clip_max: maximum value per input dimension.
  :param p: parameter controls pixel complexity
  :param r: perturbation value
  :param loss_fn: loss function
  :param d: the half side length of the neighbourhood square
  :param t: the number of pixels perturbed at each round
  :param k: the threshold for k-misclassification
  :param round_ub: an upper bound on the number of rounds
code: def perturb_single(self, x, y):
      # x shape C * H * W
      rescaled_x = replicate_input(x)
      best_img = None
      best_dist = np.inf
      rescaled_x, lb, ub = self._rescale_to_m0d5_to_0d5(
          rescaled_x, vmin=self.clip_min, vmax=self.clip_max
      )
  
      if self.comply_with_foolbox is True:
          np.random.seed(233333)
          init_rand = np.random.permutation(x.shape[1] * x.shape[2])
      else:
          init_rand = None
  
      # Algorithm 3 in v1
  
      pxy = self._random_sample_seeds(
          x.shape[1],
          x.shape[2],
          seed_ratio=self.seed_ratio,
          max_nb_seeds=self.max_nb_seeds,
          init_rand=init_rand,
      )
      pxy = pxy.to(x.device)
      ii = 0
      if self.comply_with_foolbox:
          adv = rescaled_x
      while ii < self.round_ub:
          if not self.comply_with_foolbox:
              adv = replicate_input(rescaled_x)
          # Computing the function g using the neighbourhood
          if self.comply_with_foolbox:
              rand_np = np.random.permutation(len(pxy))[
                  : self.max_nb_seeds
              ]
              pxy = pxy[
                  torch.from_numpy(rand_np).type(torch.LongTensor)
              ]
          else:
              pxy = pxy[torch.randperm(len(pxy))[: self.max_nb_seeds]]
  
          pert_lst = [
              self._perturb_seed_pixel(adv, self.p, int(row), int(col))
              for row, col in pxy
          ]
          # Compute the score for each pert in the list
          scores, curr_best_img, curr_best_dist = self._rescale_x_score(
              self.predict, pert_lst, y, x, best_dist
          )
          if curr_best_img is not None:
              best_img = curr_best_img
              best_dist = curr_best_dist
          _, indices = torch.sort(scores)
          indices = indices[: self.t]
          pxy_star = pxy[indices.data.cpu()]
          # Generation of the perturbed image adv
          for row, col in pxy_star:
              for b in range(x.shape[0]):
                  adv[b, int(row), int(col)] = self._cyclic(
                      self.r, lb, ub, adv[b, int(row), int(col)]
                  )
          # Check whether the perturbed image is an adversarial image
          revert_adv = self._revert_rescale(adv)
          curr_lb = self._get_predicted_label(revert_adv.unsqueeze(0))
          curr_dist = torch.sum((x - revert_adv) ** 2)
          if (
              is_successful(int(curr_lb), y, self.targeted)
              and curr_dist < best_dist
          ):
              best_img = revert_adv
              best_dist = curr_dist
              return best_img
          elif is_successful(curr_lb, y, self.targeted):
              return best_img
          pxy = [
              (row, col)
              for rowcenter, colcenter in pxy_star
              for row in range(
                  int(rowcenter) - self.d, int(rowcenter) + self.d + 1
              )
              for col in range(
                  int(colcenter) - self.d, int(colcenter) + self.d + 1
              )
          ]
          pxy = list(
              set(
                  (row, col)
                  for row, col in pxy
                  if (0 <= row < x.shape[2] and 0 <= col < x.shape[1])
              )
          )
          pxy = torch.FloatTensor(pxy)
          ii += 1
      if best_img is None:
          return x
      return best_img
  
imports: from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  from __future__ import unicode_literals
  import numpy
  import torch
  import torch.nn
  from advertorch.utils import clamp
  from advertorch.utils import replicate_input
  from .base import Attack
  from .base import LabelMixin
  from .utils import is_successful
label_desc: Use image node_id 2 for calling the LocalSearchAttack obj's underlying member method code with example usage: obj.perturb_single(x, y) and returns: best_img, x, best_img, best_img