fix the in the api itself
ERROR:attacks.attackrlpe:Attack generation failed: expected np.ndarray (got tuple)
ERROR:attacks.attackrlpe:Type error while initializing 'AdversarialPatch': expected np.ndarray (got tuple). Check parameter compatibility.
Traceback (most recent call last):
  File "/scratch/hemanth/Hemanth/RLPE/rlpe.py", line 106, in <module>
    print(model_output.shape)
             ^^^^^^^^^^^^^^^^^
  File "/scratch/hemanth/Hemanth/RLPE/attacks/attackrlpe.py", line 943, in apply_attack
    adversarial_examples = torch.from_numpy(adversarial_examples).to(original_device)
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: expected np.ndarray (got tuple)

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)
        
        # Ensure inputs are on CPU and in numpy format
        if isinstance(x, torch.Tensor):
            original_device = x.device  # Store the original device
            x = x.detach().cpu()
        if isinstance(y, torch.Tensor):
            y = y.detach().cpu()
            
        # Convert to numpy arrays
        x_numpy = x.numpy() if isinstance(x, torch.Tensor) else x
        y_numpy = y.numpy() if isinstance(y, torch.Tensor) else y
        
        # Ensure labels are in the correct format (integers)
        if y_numpy is not None:
            y_numpy = y_numpy.astype(np.int64)
            
        try:
            # Generate adversarial examples
            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 Exception as e:
            logger.error(f"Attack generation failed: {str(e)}")
            raise

    except ValueError as ve:
        logger.error(f"Value error: {ve}")
        raise
    except TypeError as te:
        logger.error(
            f"Type error while initializing '{attack_name}': {te}. Check parameter compatibility."
        )
        raise
    except NotImplementedError as nie:
        logger.warning(str(nie))
        raise
    except Exception as e:
        logger.error(f"Unexpected error during '{attack_name}': {e}")
        raise