class_docstr: Implement the CutMix data augmentation defence approach.
  
  | Paper link: https://arxiv.org/abs/1905.04899
  
  | Please keep in mind the limitations of defences. For more information on the limitations of this defence,
      see https://arxiv.org/abs/1803.09868 . For details on how to evaluate classifier security in general, see
      https://arxiv.org/abs/1902.06705
docstr: Apply CutMix data augmentation to sample `x`.
  
  :param x: Sample to augment with shape of `NCHW`, `NHWC`, `NCFHW` or `NFHWC`.
  :param y: Labels of `x` either one-hot or multi-hot encoded of shape `(nb_samples, nb_classes)`
            or class indices of shape `(nb_samples,)`.
  :return: Data augmented sample. The returned labels will be probability vectors of shape
           `(nb_samples, nb_classes)`.
code: def __call__(
      self, x: np.ndarray, y: Optional[np.ndarray] = None
  ) -> Tuple[np.ndarray, Optional[np.ndarray]]:
      """
      Apply CutMix data augmentation to sample `x`.
  
      :param x: Sample to augment with shape of `NCHW`, `NHWC`, `NCFHW` or `NFHWC`.
      :param y: Labels of `x` either one-hot or multi-hot encoded of shape `(nb_samples, nb_classes)`
                or class indices of shape `(nb_samples,)`.
      :return: Data augmented sample. The returned labels will be probability vectors of shape
               `(nb_samples, nb_classes)`.
      """
      if y is None:
          raise ValueError("Labels `y` cannot be None.")
  
      # convert labels to one-hot encoding
      if len(y.shape) == 2:
          y_one_hot = y
      elif len(y.shape) == 1:
          y_one_hot = to_categorical(y, self.num_classes)
      else:
          raise ValueError(
              "Shape of labels not recognised. "
              "Please provide labels in shape (nb_samples,) or (nb_samples, nb_classes)"
          )
  
      x_ndim = len(x.shape)
  
      # NCHW/NHWC/NCFHW --> NFHWC
      if x_ndim == 4:
          if self.channels_first:
              # NCHW --> NHWC --> NFHWC
              x_nhwc = np.transpose(x, (0, 2, 3, 1))
              x_nfhwc = np.expand_dims(x_nhwc, axis=1)
          else:
              # NHWC --> NFHWC
              x_nfhwc = np.expand_dims(x, axis=1)
      elif x_ndim == 5:
          if self.channels_first:
              # NCFHW --> NFHWC
              x_nfhwc = np.transpose(x, (0, 2, 3, 4, 1))
          else:
              # NFHWC
              x_nfhwc = x
      else:
          raise ValueError(
              "Unrecognized input dimension. CutMix can only be applied to image and video data."
          )
  
      n, _, height, width, _ = x_nfhwc.shape
      x_aug = x_nfhwc.copy()
      y_aug = y_one_hot.copy()
  
      # sample the combination ratio from the Beta distribution
      lmb = np.random.beta(self.alpha, self.alpha)
      cut_ratio = np.sqrt(1.0 - lmb)
      cut_height = int(height * cut_ratio)
      cut_width = int(width * cut_ratio)
  
      # randomly choose indices for samples to mix
      indices = np.random.permutation(n)
  
      # generate a random bounding box per image
      for idx1, idx2 in enumerate(
          tqdm(indices, desc="CutMix", disable=not self.verbose)
      ):
          prob = np.random.rand()
          if prob < self.probability:
              # uniform sampling
              center_y = np.random.randint(height)
              center_x = np.random.randint(width)
              bby1 = np.clip(center_y - cut_height // 2, 0, height)
              bbx1 = np.clip(center_x - cut_width // 2, 0, width)
              bby2 = np.clip(center_y + cut_height // 2, 0, height)
              bbx2 = np.clip(center_x + cut_width // 2, 0, width)
  
              # insert image bounding box
              x_aug[idx1, :, bbx1:bbx2, bby1:bby2, :] = x_nfhwc[
                  idx2, :, bbx1:bbx2, bby1:bby2, :
              ]
              # mix labels
              y_aug[idx1] = (
                  lmb * y_aug[idx1] + (1.0 - lmb) * y_one_hot[idx2]
              )
  
      x_nfhwc = x_aug
  
      # NCHW/NHWC/NCFHW <-- NFHWC
      if x_ndim == 4:
          if self.channels_first:
              # NHWC <-- NCHW <-- NFHWC
              x_nhwc = np.squeeze(x_nfhwc, axis=1)
              x_aug = np.transpose(x_nhwc, (0, 3, 1, 2))
          else:
              # NHWC <-- NFHWC
              x_aug = np.squeeze(x_nfhwc, axis=1)
      elif x_ndim == 5:  # lgtm [py/redundant-comparison]
          if self.channels_first:
              # NCFHW <-- NFHWC
              x_aug = np.transpose(x_nfhwc, (0, 4, 1, 2, 3))
          else:
              # NFHWC
              x_aug = x_nfhwc
  
      return x_aug, y_aug
  
file_path: adversarial-robustness-toolbox/art/defences/preprocessor/cutmix/cutmix.py
imports: from __future__ import absolute_import, division, print_function, unicode_literals
  import logging
  from typing import Optional, Tuple
  import numpy
  from tqdm.auto import tqdm
  from art.defences.preprocessor.preprocessor import Preprocessor
  from art.utils import to_categorical
label_desc: Use image node_id 2 for calling the CutMix obj's underlying member method code with example usage: obj.__call__(x, y) and returns: x_aug,  y_aug