def METHOD_NAME(  # 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.METHOD_NAME(perturbed_input, self.target, broadcast=broadcast)
    data[selected_indices] = poisoned_input
    return data, estimated_labels