class_docstr: Implementation of Bullseye Polytope Attack by Aghakhani, et. al. 2020.
  "Bullseye Polytope: A Scalable Clean-Label Poisoning Attack with Improved Transferability"
  
  This implementation is based on UCSB's original code here: https://github.com/ucsb-seclab/BullseyePoison
  
  | Paper link: https://arxiv.org/abs/2005.00191
class_docstr_tok: ["Implementation","of","Bullseye","Polytope","Attack","by","Aghakhani",",","et",".","al",".","2020",".","``","Bullseye","Polytope",":","A","Scalable","Clean-Label","Poisoning","Attack","with","Improved","Transferability","''","This","implementation","is","based","on","UCSB","'s","original","code","here",":","https",":","\/\/github.com\/ucsb-seclab\/BullseyePoison","|","Paper","link",":","https",":","\/\/arxiv.org\/abs\/2005.00191"]
docstr: Iteratively finds optimal attack points starting at values at x
  
  :param x: The base images to begin the poison process.
  :param y: Target label
  :return: An tuple holding the (poisoning examples, poisoning labels).
docstr_tok: ["Iteratively","finds","optimal","attack","points","starting","at","values","at","x",":","param","x",":","The","base","images","to","begin","the","poison","process",".",":","param","y",":","Target","label",":","return",":","An","tuple","holding","the","(","poisoning","examples",",","poisoning","labels",")","."]
code: def poison(
      self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs
  ) -> Tuple[np.ndarray, np.ndarray]:
      """
      Iteratively finds optimal attack points starting at values at x
  
      :param x: The base images to begin the poison process.
      :param y: Target label
      :return: An tuple holding the (poisoning examples, poisoning labels).
      """
      import torch
  
      class PoisonBatch(torch.nn.Module):
          """
          Implementing this to work with PyTorch optimizers.
          """
  
          def __init__(self, base_list):
              super().__init__()
              base_batch = torch.stack(base_list, 0)
              self.poison = torch.nn.Parameter(base_batch.clone())
  
          def forward(self):
              """Forward method."""
              return self.poison
  
      base_tensor_list = [
          torch.from_numpy(sample).to(self.estimator.device)
          for sample in x
      ]
      poison_batch = PoisonBatch(
          [
              torch.from_numpy(np.copy(sample)).to(
                  self.estimator.device
              )
              for sample in x
          ]
      )
      opt_method = self.opt.lower()
  
      if opt_method == "sgd":  # pragma: no cover
          logger.info("Using SGD to craft poison samples")
          optimizer = torch.optim.SGD(
              poison_batch.parameters(),
              lr=self.learning_rate,
              momentum=self.momentum,
          )
      elif opt_method == "adam":
          logger.info("Using Adam to craft poison samples")
          optimizer = torch.optim.Adam(  # type: ignore
              poison_batch.parameters(),
              lr=self.learning_rate,
              betas=(self.momentum, 0.999),
          )
  
      base_tensor_batch = torch.stack(base_tensor_list, 0)
      base_range01_batch = base_tensor_batch
  
      # Because we have turned on DP for the substitute networks,
      # the target image's feature becomes random.
      # We can try enforcing the convex polytope in one of the multiple realizations of the feature,
      # but empirically one realization is enough.
      target_feat_list = []
      # Coefficients for the convex combination.
      # Initializing from the coefficients of last step gives faster convergence.
      s_init_coeff_list = []
      n_poisons = len(x)
      s_coeff: Union["torch.Tensor", List["torch.Tensor"]]
      for _, net in enumerate(self.subsistute_networks):
          # End to end training
          if self.endtoend:
              if isinstance(self.feature_layer, list):
                  block_feats = []
                  for layer in self.feature_layer:
                      activations = net.get_activations(
                          x,
                          layer=layer,
                          batch_size=self.batch_size,
                          framework=True,
                      )
                      if activations is not None:
                          block_feats += [
                              torch.stack(
                                  [
                                      feat.detach()
                                      for feat in activations
                                  ],
                                  0,
                              )
                          ]
                      else:  # pragma: no cover
                          raise ValueError("Activations are None.")
              else:
                  layer_2: Union[int, str] = self.feature_layer
                  activations = net.get_activations(
                      x,
                      layer=layer_2,
                      batch_size=self.batch_size,
                      framework=True,
                  )
                  if activations is not None:
                      block_feats = [
                          feat.detach() for feat in activations
                      ]
                  else:  # pragma: no cover
                      raise ValueError("Activations are None.")
              target_feat_list.append(block_feats)
              s_coeff = [
                  torch.ones(n_poisons, 1).to(self.estimator.device)
                  / n_poisons
                  for _ in range(len(block_feats))
              ]
          else:  # pragma: no cover
              if isinstance(self.feature_layer, list):
                  raise NotImplementedError
              layer_3: Union[int, str] = self.feature_layer
              activations = net.get_activations(
                  x,
                  layer=layer_3,
                  batch_size=self.batch_size,
                  framework=True,
              )
              if activations is not None:
                  target_feat_list.append(activations.detach())  # type: ignore
              else:
                  raise ValueError("Activations are None.")
              s_coeff = (
                  torch.ones(n_poisons, 1).to(self.estimator.device)
                  / n_poisons
              )
  
          s_init_coeff_list.append(s_coeff)
  
      for ite in trange(self.max_iter):
          if ite % self.decay_iter == 0 and ite != 0:
              for param_group in optimizer.param_groups:
                  param_group["lr"] *= self.decay_coeff
              print(
                  f"{time.strftime('%Y-%m-%d %H:%M:%S')} Iteration {ite}, Adjusted lr to {self.learning_rate:.2e}"
              )
  
          poison_batch.zero_grad()
          total_loss = loss_from_center(
              self.subsistute_networks,
              target_feat_list,
              poison_batch,
              self.net_repeat,
              self.endtoend,
              self.feature_layer,
          )
          total_loss.backward()
          optimizer.step()
  
          # clip the perturbations into the range
          perturb_range01 = torch.clamp(
              (poison_batch.poison.data - base_tensor_batch),
              -self.epsilon,
              self.epsilon,
          )
          perturbed_range01 = torch.clamp(
              base_range01_batch.data + perturb_range01.data,
              self.estimator.clip_values[0],
              self.estimator.clip_values[1],
          )
          poison_batch.poison.data = perturbed_range01
  
      if y is None:  # pragma: no cover
          raise ValueError("You must pass in the target label as y")
  
      return get_poison_tuples(poison_batch, y)
  
file_path: adversarial-robustness-toolbox/art/attacks/poisoning/bullseye_polytope_attack.py
imports: from __future__ import absolute_import, division, print_function, unicode_literals
  import logging
  import time
  from typing import Optional, Tuple, Union, TYPE_CHECKING, List
  import numpy
  from tqdm.auto import trange
  from art.attacks.attack import PoisoningAttackWhiteBox
  from art.estimators import BaseEstimator, NeuralNetworkMixin
  from art.estimators.classification.classifier import ClassifierMixin
  from art.estimators.classification.pytorch import PyTorchClassifier
label_desc: Use image node_id 2 for calling the BullseyePolytopeAttackPyTorch obj's underlying member method code with example usage: obj.poison(x, y) and returns: get_poison_tuples, self