NOTE: can change are allowed in this api
ERROR:attacks.attackrlpe:Unexpected error during 'AdversarialPatch': can't convert cuda:0 device type tensor to numpy. Use Tensor.cpu() to copy the tensor to host memory first.
Traceback (most recent call last):
  File "/scratch/hemanth/Hemanth/RLPE/attacks/attackrlpe.py", line 933, in apply_attack
    adversarial_examples = torch.from_numpy(adversarial_examples).to(original_device)
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: expected np.ndarray (got tuple)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/scratch/hemanth/Hemanth/RLPE/rlpe.py", line 111, in <module>
    adversarial_images = apply_attack(
                         ^^^^^^^^^^^^^
  File "/scratch/hemanth/Hemanth/RLPE/attacks/attackrlpe.py", line 939, in apply_attack
    adversarial_examples=attack.generate(x=x, y=y)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/scratch/hemanth/anaconda/envs/rlpe/lib/python3.12/site-packages/art/attacks/evasion/adversarial_patch/adversarial_patch.py", line 173, in generate
    return self._attack.generate(x=x, y=y, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/scratch/hemanth/anaconda/envs/rlpe/lib/python3.12/site-packages/art/attacks/evasion/adversarial_patch/adversarial_patch_pytorch.py", line 507, in generate
    y = check_and_transform_label_format(labels=y, nb_classes=self.estimator.nb_classes)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/scratch/hemanth/anaconda/envs/rlpe/lib/python3.12/site-packages/art/utils.py", line 834, in check_and_transform_label_format
    labels_return = to_categorical(labels, nb_classes)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/scratch/hemanth/anaconda/envs/rlpe/lib/python3.12/site-packages/art/utils.py", line 753, in to_categorical
    labels = np.array(labels, dtype=int)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/scratch/hemanth/anaconda/envs/rlpe/lib/python3.12/site-packages/torch/_tensor.py", line 1194, in __array__
    return self.numpy().astype(dtype, copy=False)
           ^^^^^^^^^^^^
TypeError: can't convert cuda:0 device type tensor to numpy. Use Tensor.cpu() to copy the tensor to host memory first.
(rlpe) (base) hemanth@aics:/scratch/hemanth$ 
def apply_attack(
    attack_name: str,
    classifier: Any,
    x: np.ndarray,
    y: Optional[np.ndarray] = None,
    new_params: Optional[Dict[str, Any]] = None,
) -> np.ndarray:
    """
    Apply a single adversarial attack with the ability to update attack parameters.
    """
    logger.info(f"Initializing attack: {attack_name}")
    try:
        attack_class = get_attack_class(attack_name)
        if not attack_class:
            raise NotImplementedError(f"The attack '{attack_name}' is not supported.")

        params = get_attack_parameters(attack_name)

        if new_params:
            params = update_attack_parameters(params, new_params)

        # Correctly assign the classifier or estimator
        if "estimator" in params:
            params["estimator"] = classifier
        if "classifier" in params:
            params["classifier"] = classifier

        # Handle special parameters if necessary
        if attack_name == "AutoAttack":
            params["estimator_orig"] = classifier
        elif attack_name == "AdversarialPatchPyTorch":
            params["estimator"] = classifier

        # Initialize the attack with the classifier and parameters
        attack = attack_class(**params)
        original_type = 'tensor' if isinstance(x, torch.Tensor) else 'numpy'
        if original_type == 'tensor':
                    x_numpy = x.detach().cpu().numpy()
                    y_numpy = y.detach().cpu().numpy()
        else:
            x_numpy = x
            y_numpy = y
        try:
            # Fit the defense
            adversarial_examples = attack.generate(x=x_numpy, y=y_numpy)
            # Convert back to tensor if input was tensor
            if isinstance(x, torch.Tensor):
                adversarial_examples = torch.from_numpy(adversarial_examples).to(original_device)
                
            logger.info(f"Attack '{attack_name}' performed successfully.")
            return adversarial_examples

        except:
            adversarial_examples=attack.generate(x=x, y=y)
            adversarial_examples

    
    except Exception as e:
        logger.error(f"Unexpected error during '{attack_name}': {e}")
        raise