class_docstr: Implementation of Clean-Label Backdoor Attack introduced in Turner et al., 2018.
  
  Applies a number of backdoor perturbation functions and does not change labels.
  
  | Paper link: https://people.csail.mit.edu/madry/lab/cleanlabel.pdf
docstr: Calls perturbation function on input x and returns the perturbed input and poison labels for the data.
  
  :param x: An array with the points that initialize attack points.
  :param y: The target labels for the attack.
  :param broadcast: whether or not to broadcast single target label
  :return: An tuple holding the `(poisoning_examples, poisoning_labels)`.
code: def poison(  # pylint: disable=W0221
      self,
      x: np.ndarray,
      y: Optional[np.ndarray] = None,
      broadcast: bool = True,
      **kwargs,
  ) -> Tuple[np.ndarray, np.ndarray]:
      """
      Calls perturbation function on input x and returns the perturbed input and poison labels for the data.
  
      :param x: An array with the points that initialize attack points.
      :param y: The target labels for the attack.
      :param broadcast: whether or not to broadcast single target label
      :return: An tuple holding the `(poisoning_examples, poisoning_labels)`.
      """
      data = np.copy(x)
      estimated_labels = (
          self.proxy_classifier.predict(data)
          if y is None
          else np.copy(y)
      )
  
      # Selected target indices to poison
      all_indices = np.arange(len(data))
      target_indices = all_indices[
          np.all(estimated_labels == self.target, axis=1)
      ]
      num_poison = int(self.pp_poison * len(target_indices))
      selected_indices = np.random.choice(target_indices, num_poison)
  
      # Run untargeted PGD on selected points, making it hard to classify correctly
      perturbed_input = self.attack.generate(data[selected_indices])
      no_change_detected = np.array(
          [
              np.all(
                  data[selected_indices][poison_idx]
                  == perturbed_input[poison_idx]
              )
              for poison_idx in range(len(perturbed_input))
          ]
      )
  
      if any(no_change_detected):  # pragma: no cover
          logger.warning(
              "Perturbed input is the same as original data after PGD. Check params."
          )
          idx_no_change = np.arange(len(no_change_detected))[
              no_change_detected
          ]
          logger.warning(
              "%d indices without change: %s",
              len(idx_no_change),
              idx_no_change,
          )
  
      # Add backdoor and poison with the same label
      poisoned_input, _ = self.backdoor.poison(
          perturbed_input, self.target, broadcast=broadcast
      )
      data[selected_indices] = poisoned_input
  
      return data, estimated_labels
  
file_path: adversarial-robustness-toolbox/art/attacks/poisoning/clean_label_backdoor_attack.py
imports: from __future__ import absolute_import, division, print_function, unicode_literals
  import logging
  from typing import Optional, Tuple, TYPE_CHECKING, Union
  import numpy
  from art.attacks.attack import PoisoningAttackBlackBox
  from art.attacks.evasion.projected_gradient_descent.projected_gradient_descent import ProjectedGradientDescent
  from art.attacks.poisoning.backdoor_attack import PoisoningAttackBackdoor
label_desc: Use image node_id 2 for calling the PoisoningAttackCleanLabelBackdoor obj's underlying member method code with example usage: obj.poison(x, y, broadcast) and returns: data,  estimated_labels