INFO:__main__:Initializing defense: GaussianNoise
ERROR:__main__:Defense 'GaussianNoise' type is not supported.
ERROR:__main__:An error occurred while applying defense 'GaussianNoise': Defense 'GaussianNoise' type is not supported.
Traceback (most recent call last):
  File "c:\Users\heman\Desktop\Notes\2024notes\Learning\test\deftest1.py", line 548, in apply_defense
    raise TypeError(error_msg)
TypeError: Defense 'GaussianNoise' type is not supported.
Failed to apply defense: An error occurred while applying defense 'GaussianNoise': Defense 'GaussianNoise' type is not supported.    
INFO:__main__:Initializing defense: Rounded
ERROR:__main__:Defense 'Rounded' type is not supported.
ERROR:__main__:An error occurred while applying defense 'Rounded': Defense 'Rounded' type is not supported.
Traceback (most recent call last):
  File "c:\Users\heman\Desktop\Notes\2024notes\Learning\test\deftest1.py", line 548, in apply_defense
    raise TypeError(error_msg)
TypeError: Defense 'Rounded' type is not supported.
Failed to apply postprocessor: An error occurred while applying defense 'Rounded': Defense 'Rounded' type is not supported.

code:
from __future__ import absolute_import, division, print_function, unicode_literals, annotations

import abc
import logging
from typing import Any, Dict, List, Optional, Type, Union

import numpy as np

# Configure the logger
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Assume the following imports are from the 'art.defences' package
# These should be replaced with actual imports based on the installed package
from art.defences.postprocessor import (
    ClassLabels,
    GaussianNoise,
    HighConfidence,
    Postprocessor,
    ReverseSigmoid,
    Rounded,
)
from art.defences.preprocessor import (
    CutMix,
    CutMixPyTorch,
    CutMixTensorFlowV2,
    Cutout,
    CutoutPyTorch,
    CutoutTensorFlowV2,
    FeatureSqueezing,
    GaussianAugmentation,
    DefenseGAN,
    InverseGAN,
    JpegCompression,
    LabelSmoothing,
    Mixup,
    MixupPyTorch,
    MixupTensorFlowV2,
    Mp3Compression,
    Mp3CompressionPyTorch,
    PixelDefend,
    Preprocessor,
    Resample,
    SpatialSmoothing,
)
from art.defences.trainer import (
    AdversarialTrainer,
    AdversarialTrainerCertifiedPytorch,
    AdversarialTrainerCertifiedIBPPyTorch,
    AdversarialTrainerMadryPGD,
    AdversarialTrainerFBF,
    AdversarialTrainerFBFPyTorch,
    AdversarialTrainerTRADESPyTorch,
    AdversarialTrainerTRADES,
    AdversarialTrainerAWP,
    AdversarialTrainerAWPPyTorch,
    AdversarialTrainerOAAT,
    AdversarialTrainerOAATPyTorch,
    DPInstaHideTrainer,
)

# Example base classes (from user's provided code)
class Postprocessor(abc.ABC):
    params: List[str] = []

    def __init__(
        self,
        is_fitted: bool = False,
        apply_fit: bool = True,
        apply_predict: bool = True,
    ) -> None:
        self._is_fitted = bool(is_fitted)
        self._apply_fit = bool(apply_fit)
        self._apply_predict = bool(apply_predict)
        self._check_params()

    @property
    def is_fitted(self) -> bool:
        return self._is_fitted

    @property
    def apply_fit(self) -> bool:
        return self._apply_fit

    @property
    def apply_predict(self) -> bool:
        return self._apply_predict

    @abc.abstractmethod
    def __call__(self, preds: np.ndarray) -> np.ndarray:
        raise NotImplementedError

    def fit(self, preds: np.ndarray, **kwargs) -> None:
        pass

    def set_params(self, **kwargs) -> None:
        for key, value in kwargs.items():
            if key in self.params:
                setattr(self, key, value)
        self._check_params()

    def _check_params(self) -> None:
        pass


class Preprocessor(abc.ABC):
    params: List[str] = []

    def __init__(
        self,
        is_fitted: bool = False,
        apply_fit: bool = True,
        apply_predict: bool = True,
    ) -> None:
        self._is_fitted = bool(is_fitted)
        self._apply_fit = bool(apply_fit)
        self._apply_predict = bool(apply_predict)

    @property
    def is_fitted(self) -> bool:
        return self._is_fitted

    @property
    def apply_fit(self) -> bool:
        return self._apply_fit

    @property
    def apply_predict(self) -> bool:
        return self._apply_predict

    @abc.abstractmethod
    def __call__(
        self, x: np.ndarray, y: Optional[Any] = None
    ) -> tuple[np.ndarray, Optional[Any]]:
        raise NotImplementedError

    def fit(
        self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs: Any
    ) -> None:
        pass

    def estimate_gradient(
        self, x: np.ndarray, grad: np.ndarray
    ) -> np.ndarray:
        return grad

    def set_params(self, **kwargs: Any) -> None:
        for key, value in kwargs.items():
            if key in self.params:
                setattr(self, key, value)
        self._check_params()

    def _check_params(self) -> None:
        pass


class Trainer(abc.ABC):
    def __init__(self, classifier: Any) -> None:
        self._classifier = classifier

    @abc.abstractmethod
    def fit(
        self, x: np.ndarray, y: np.ndarray, **kwargs: Any
    ) -> None:
        raise NotImplementedError

    @property
    def classifier(self) -> Any:
        return self._classifier

    def get_classifier(self) -> Any:
        return self._classifier

# Define the parameters dictionary
PARAMETERS: Dict[str, Dict[str, Any]] = {
    'BinaryInputDetector': {'detector': None},
    'BinaryActivationDetector': {'classifier': None, 'detector': None, 'layer': None},
    'SubsetScanningDetector': {
        'classifier': None,
        'bgd_data': None,
        'layer': None,
        'scoring_function': 'BerkJones',
        'verbose': True,
    },
    'ClassLabels': {'apply_fit': False, 'apply_predict': True},
    'GaussianNoise': {'scale': 0.2, 'apply_fit': False, 'apply_predict': True},
    'HighConfidence': {'cutoff': 0.25, 'apply_fit': False, 'apply_predict': True},
    'Postprocessor': {'is_fitted': False, 'apply_fit': True, 'apply_predict': True},
    'ReverseSigmoid': {'beta': 1.0, 'gamma': 0.1, 'apply_fit': False, 'apply_predict': True},
    'Rounded': {'decimals': 3, 'apply_fit': False, 'apply_predict': True},
    'CutMix': {
        'num_classes': None,
        'alpha': 1.0,
        'probability': 0.5,
        'channels_first': False,
        'apply_fit': True,
        'apply_predict': False,
        'verbose': False,
    },
    'CutMixPyTorch': {
        'num_classes': None,
        'alpha': 1.0,
        'probability': 0.5,
        'channels_first': False,
        'apply_fit': True,
        'apply_predict': False,
        'device_type': 'gpu',
        'verbose': False,
    },
    'CutMixTensorFlowV2': {
        'num_classes': None,
        'alpha': 1.0,
        'probability': 0.5,
        'channels_first': False,
        'apply_fit': True,
        'apply_predict': False,
        'verbose': False,
    },
    'Cutout': {
        'length': None,
        'channels_first': False,
        'apply_fit': True,
        'apply_predict': False,
        'verbose': False,
    },
    'CutoutPyTorch': {
        'length': None,
        'channels_first': False,
        'apply_fit': True,
        'apply_predict': False,
        'device_type': 'gpu',
        'verbose': False,
    },
    'CutoutTensorFlowV2': {
        'length': None,
        'channels_first': False,
        'apply_fit': True,
        'apply_predict': False,
        'verbose': False,
    },
    'FeatureSqueezing': {
        'clip_values': None,
        'bit_depth': 8,
        'apply_fit': False,
        'apply_predict': True,
    },
    'GaussianAugmentation': {
        'sigma': 1.0,
        'augmentation': True,
        'ratio': 1.0,
        'clip_values': None,
        'apply_fit': True,
        'apply_predict': False,
    },
    'DefenseGAN': {'sess': None, 'gan': None},
    'InverseGAN': {
        'sess': None,
        'gan': None,
        'inverse_gan': None,
        'apply_fit': False,
        'apply_predict': False,
    },
    'JpegCompression': {
        'clip_values': None,
        'quality': 50,
        'channels_first': False,
        'apply_fit': True,
        'apply_predict': True,
        'verbose': False,
    },
    'LabelSmoothing': {
        'max_value': 0.9,
        'apply_fit': True,
        'apply_predict': False,
    },
    'Mixup': {
        'num_classes': None,
        'alpha': 1.0,
        'num_mix': 2,
        'apply_fit': True,
        'apply_predict': False,
    },
    'MixupPyTorch': {
        'num_classes': None,
        'alpha': 1.0,
        'num_mix': 2,
        'apply_fit': True,
        'apply_predict': False,
        'device_type': 'gpu',
    },
    'MixupTensorFlowV2': {
        'num_classes': None,
        'alpha': 1.0,
        'num_mix': 2,
        'apply_fit': True,
        'apply_predict': False,
    },
    'Mp3Compression': {
        'sample_rate': None,
        'channels_first': False,
        'apply_fit': False,
        'apply_predict': True,
        'verbose': False,
    },
    'Mp3CompressionPyTorch': {
        'sample_rate': None,
        'channels_first': False,
        'apply_fit': False,
        'apply_predict': True,
        'device_type': 'gpu',
        'verbose': False,
    },
    'PixelDefend': {
        'clip_values': (0.0, 1.0),
        'eps': 16,
        'pixel_cnn': None,
        'batch_size': 128,
        'apply_fit': False,
        'apply_predict': True,
        'verbose': False,
    },
    'Preprocessor': {
        'is_fitted': False,
        'apply_fit': True,
        'apply_predict': True,
    },
    'Resample': {
        'sr_original': None,
        'sr_new': None,
        'channels_first': False,
        'apply_fit': False,
        'apply_predict': True,
    },
    'SpatialSmoothing': {
        'window_size': 3,
        'channels_first': False,
        'clip_values': None,
        'apply_fit': False,
        'apply_predict': True,
    },
}

# Mapping of defense names to their corresponding classes
DEFENSE_CLASSES: Dict[str, Type[Any]] = {
    'ClassLabels': ClassLabels,
    'GaussianNoise': GaussianNoise,
    'HighConfidence': HighConfidence,
    'ReverseSigmoid': ReverseSigmoid,
    'Rounded': Rounded,
    'CutMix': CutMix,
    'CutMixPyTorch': CutMixPyTorch,
    'CutMixTensorFlowV2': CutMixTensorFlowV2,
    'Cutout': Cutout,
    'CutoutPyTorch': CutoutPyTorch,
    'CutoutTensorFlowV2': CutoutTensorFlowV2,
    'FeatureSqueezing': FeatureSqueezing,
    'GaussianAugmentation': GaussianAugmentation,
    'DefenseGAN': DefenseGAN,
    'InverseGAN': InverseGAN,
    'JpegCompression': JpegCompression,
    'LabelSmoothing': LabelSmoothing,
    'Mixup': Mixup,
    'MixupPyTorch': MixupPyTorch,
    'MixupTensorFlowV2': MixupTensorFlowV2,
    'Mp3Compression': Mp3Compression,
    'Mp3CompressionPyTorch': Mp3CompressionPyTorch,
    'PixelDefend': PixelDefend,
    'Preprocessor': Preprocessor,
    'Resample': Resample,
    'SpatialSmoothing': SpatialSmoothing,
    'BinaryInputDetector': None,  # Placeholder
    'BinaryActivationDetector': None,  # Placeholder
    'SubsetScanningDetector': None,  # Placeholder
    'AdversarialTrainer': AdversarialTrainer,
    'AdversarialTrainerCertifiedPytorch': AdversarialTrainerCertifiedPytorch,
    'AdversarialTrainerCertifiedIBPPyTorch': AdversarialTrainerCertifiedIBPPyTorch,
    'AdversarialTrainerMadryPGD': AdversarialTrainerMadryPGD,
    'AdversarialTrainerFBF': AdversarialTrainerFBF,
    'AdversarialTrainerFBFPyTorch': AdversarialTrainerFBFPyTorch,
    'AdversarialTrainerTRADESPyTorch': AdversarialTrainerTRADESPyTorch,
    'AdversarialTrainerTRADES': AdversarialTrainerTRADES,
    'AdversarialTrainerAWP': AdversarialTrainerAWP,
    'AdversarialTrainerAWPPyTorch': AdversarialTrainerAWPPyTorch,
    'AdversarialTrainerOAAT': AdversarialTrainerOAAT,
    'AdversarialTrainerOAATPyTorch': AdversarialTrainerOAATPyTorch,
    'DPInstaHideTrainer': DPInstaHideTrainer,
}

# Placeholder classes for detectors (to be replaced with actual implementations)
class BinaryInputDetector(abc.ABC):
    pass

class BinaryActivationDetector(abc.ABC):
    pass

class SubsetScanningDetector(abc.ABC):
    pass

# Update DEFENSE_CLASSES with detectors
DEFENSE_CLASSES.update({
    'BinaryInputDetector': BinaryInputDetector,
    'BinaryActivationDetector': BinaryActivationDetector,
    'SubsetScanningDetector': SubsetScanningDetector,
})

class DefenseManager:
    """
    Manages the application of various defenses to input data.
    """

    def __init__(self) -> None:
        """
        Initialize the DefenseManager with available defenses and their parameters.
        """
        self.defenses: Dict[str, Type[Any]] = DEFENSE_CLASSES
        self.parameters: Dict[str, Dict[str, Any]] = PARAMETERS
        logger.info("DefenseManager initialized with available defenses.")

    def list_defenses(self) -> List[str]:
        """
        List all available defenses.

        Returns:
            List[str]: A list of available defense names.
        """
        return list(self.defenses.keys())

    def get_defense_parameters(self, defense_name: str) -> Dict[str, Any]:
        """
        Retrieve the default parameters for a given defense.

        Args:
            defense_name (str): The name of the defense.

        Returns:
            Dict[str, Any]: A dictionary of parameters for the defense.

        Raises:
            ValueError: If the defense name is not recognized.
        """
        if defense_name not in self.parameters:
            error_msg = f"No parameters found for defense '{defense_name}'."
            logger.error(error_msg)
            raise ValueError(error_msg)
        return self.parameters[defense_name].copy()

    def apply_defense(
        self,
        defense_name: str,
        x: np.ndarray,
        y: np.ndarray,
        **kwargs: Any
    ) -> np.ndarray:
        """
        Apply the specified defense to the input data.

        Args:
            defense_name (str): The name of the defense to apply.
            x (np.ndarray): Input data with shape (batch_size, channels, height, width).
            y (np.ndarray): True labels corresponding to the input data.
            **kwargs (Any): Additional keyword arguments for specific defenses.

        Returns:
            np.ndarray: The defended input data.

        Raises:
            ValueError: If the defense_name is not recognized or application fails.
            TypeError: If the defense type is not supported.
            RuntimeError: If an unexpected error occurs during defense application.
        """
        if defense_name not in self.defenses:
            available = self.list_defenses()
            error_msg = (
                f"Defense '{defense_name}' is not available. "
                f"Available defenses: {available}"
            )
            logger.error(error_msg)
            raise ValueError(error_msg)

        defense_class = self.defenses[defense_name]
        logger.info(f"Initializing defense: {defense_name}")

        try:
            # Initialize the defense with default and provided parameters
            params = self.get_defense_parameters(defense_name)
            params.update(kwargs)
            defense_instance = defense_class(**params) if params else defense_class()
            logger.debug(
                f"Defense '{defense_name}' initialized with parameters: {params}"
            )

            # Determine the type/category of the defense
            if isinstance(defense_instance, Preprocessor):
                if not defense_instance.apply_predict:
                    logger.warning(
                        f"Defense '{defense_name}' is not set to apply during prediction."
                    )
                logger.info(f"Applying preprocessor defense: {defense_name}")
                defended_x, _ = defense_instance(x=x, y=y)
                logger.debug(
                    f"Preprocessor defense '{defense_name}' applied successfully."
                )
                return defended_x

            elif isinstance(defense_instance, Postprocessor):
                if not defense_instance.apply_predict:
                    logger.warning(
                        f"Defense '{defense_name}' is not set to apply during prediction."
                    )
                logger.info(f"Applying postprocessor defense: {defense_name}")
                defended_y = defense_instance(preds=y)
                logger.debug(
                    f"Postprocessor defense '{defense_name}' applied successfully."
                )
                return defended_y

            elif isinstance(
                defense_instance,
                (BinaryInputDetector, BinaryActivationDetector, SubsetScanningDetector),
            ):
                logger.info(f"Applying evasion detector: {defense_name}")
                # Example: Assuming the detector has a predict method
                detection_result = defense_instance.predict(x)
                if detection_result is None:
                    error_msg = f"Evasion detector '{defense_name}' returned None."
                    logger.error(error_msg)
                    raise ValueError(error_msg)
                if not np.all(detection_result):
                    error_msg = f"Adversarial samples detected by {defense_name}."
                    logger.warning(error_msg)
                    raise ValueError(error_msg)
                logger.debug(
                    f"Evasion detector '{defense_name}' passed without detecting adversarial samples."
                )
                return x

            elif isinstance(defense_instance, Trainer):
                logger.info(f"Applying trainer defense: {defense_name}")
                defense_instance.fit(x, y)
                logger.debug(
                    f"Trainer defense '{defense_name}' trained successfully."
                )
                return x

            else:
                error_msg = f"Defense '{defense_name}' type is not supported."
                logger.error(error_msg)
                raise TypeError(error_msg)

        except Exception as e:
            error_msg = (
                f"An error occurred while applying defense '{defense_name}': {e}"
            )
            logger.exception(error_msg)
            raise RuntimeError(error_msg) from e

# Example usage
if __name__ == "__main__":
    # Initialize DefenseManager
    manager = DefenseManager()

    # List available defenses
    available_defenses = manager.list_defenses()
    print("Available Defenses:", available_defenses)

    # Example input data
    x_example = np.random.rand(10, 3, 224, 224)  # Example image data
    y_example = np.random.randint(0, 10, size=(10,))  # Example labels

    # Apply a defense (e.g., GaussianNoise)
    try:
        defended_x = manager.apply_defense(
            defense_name="GaussianNoise",
            x=x_example,
            y=y_example,
            scale=0.1  # Overriding default scale
        )
        print("Defense applied successfully. Defended data shape:", defended_x.shape)
    except Exception as e:
        print(f"Failed to apply defense: {e}")

    # Apply a postprocessor (e.g., Rounded)
    try:
        defended_y = manager.apply_defense(
            defense_name="Rounded",
            x=x_example,
            y=y_example,
            decimals=2  # Overriding default decimals
        )
        print("Postprocessor applied successfully. Defended labels:", defended_y)
    except Exception as e:
        print(f"Failed to apply postprocessor: {e}")