def __init__(
        self,
        model: str | "VisionTransformer" | "torch.nn.Module",
        loss: "torch.nn.modules.loss._Loss",
        input_shape: tuple[int, ...],
        nb_classes: int,
        ablation_size: int,
        algorithm: str = "salman2021",
        ablation_type: str = "column",
        replace_last_layer: bool | None = None,
        drop_tokens: bool = True,
        load_pretrained: bool = True,
        optimizer: "torch.optim.Optimizer" | None = None,
        optimizer_params: dict | None = None,
        channels_first: bool = True,
        threshold: float | None = None,
        logits: bool | None = True,
        clip_values: "CLIP_VALUES_TYPE" | None = None,
        preprocessing_defences: "Preprocessor" | list["Preprocessor"] | None = None,
        postprocessing_defences: "Postprocessor" | list["Postprocessor"] | None = None,
        preprocessing: "PREPROCESSING_TYPE" = (0.0, 1.0),
        device_type: str = "gpu",
        verbose: bool = True,
        **kwargs,
    ):
        """
        Create a smoothed classifier.

        :param model: Either a CNN or a VIT. For a ViT supply a string specifying which ViT architecture to load from
                      the ViT library, or a vision transformer already created with the
                      Pytorch Image Models (timm) library. To run Levine et al. (2020) provide a regular pytorch model.
        :param loss: The loss function for which to compute gradients for training. The target label must be raw
                     categorical, i.e. not converted to one-hot encoding.
        :param input_shape: The shape of one input instance.
        :param nb_classes: The number of classes of the model.
        :param ablation_size: The size of the data portion to retain after ablation.
        :param algorithm: Either 'salman2021' or 'levine2020'. For salman2021 we support ViTs and CNNs. For levine2020
                          there is only CNN support.
        :param replace_last_layer: ViT-specific. If to replace the last layer of the ViT with a fresh layer
                                   matching the number of classes for the dataset to be examined.
                                   Needed if going from the pre-trained imagenet models to fine-tune
                                   on a dataset like CIFAR.
        :param drop_tokens: ViT-specific. If to drop the fully ablated tokens in the ViT
        :param load_pretrained: ViT-specific. If to load a pretrained model matching the ViT name.
                                Will only affect the ViT if a string name is passed to model rather than a ViT directly.
        :param optimizer: The optimizer used to train the classifier.
        :param ablation_type: The type of ablation to perform. Either "column", "row", or "block"
        :param threshold: Specific to Levine et al. The minimum threshold to count a prediction.
        :param logits: Specific to Levine et al. If the model returns logits or normalized probabilities
        :param channels_first: Set channels first or last.
        :param clip_values: Tuple of the form `(min, max)` of floats or `np.ndarray` representing the minimum and
               maximum values allowed for features. If floats are provided, these will be used as the range of all
               features. If arrays are provided, each value will be considered the bound for a feature, thus
               the shape of clip values needs to match the total number of features.
        :param preprocessing_defences: Preprocessing defence(s) to be applied by the classifier.
        :param postprocessing_defences: Postprocessing defence(s) to be applied by the classifier.
        :param preprocessing: Tuple of the form `(subtrahend, divisor)` of floats or `np.ndarray` of values to be
               used for data preprocessing. The first value will be subtracted from the input. The input will then
               be divided by the second one.
        :param device_type: Type of device on which the classifier is run, either `gpu` or `cpu`.
        """

        import torch

        if not channels_first:
            raise ValueError("Channels must be set to first")
        logger.info("Running algorithm: %s", algorithm)

        # Default value for output shape
        output_shape = input_shape
        self.mode = None
        if importlib.util.find_spec("timm") is not None and algorithm == "salman2021":
            from timm.models.vision_transformer import VisionTransformer

            if isinstance(model, (VisionTransformer, str)):
                import timm
                from art.estimators.certification.derandomized_smoothing.vision_transformers.pytorch import (
                    PyTorchVisionTransformer,
                )

                if replace_last_layer is None:
                    raise ValueError("If using ViTs please specify if the last layer should be replaced")

                # temporarily assign the original method to tmp_func
                tmp_func = timm.models.vision_transformer._create_vision_transformer

                # override with ART's ViT creation function
                timm.models.vision_transformer._create_vision_transformer = self.create_vision_transformer
                if isinstance(model, str):
                    model = timm.create_model(
                        model, pretrained=load_pretrained, drop_tokens=drop_tokens, device_type=device_type
                    )
                    if replace_last_layer:
                        model.head = torch.nn.Linear(model.head.in_features, nb_classes)  # type: ignore
                    if isinstance(optimizer, type):
                        if optimizer_params is not None:
                            optimizer = optimizer(model.parameters(), **optimizer_params)
                        else:
                            raise ValueError("If providing an optimiser please also supply its parameters")

                elif isinstance(model, VisionTransformer):
                    pretrained_cfg = model.pretrained_cfg
                    supplied_state_dict = model.state_dict()
                    supported_models = self.get_models()
                    if pretrained_cfg["architecture"] not in supported_models:  # type: ignore
                        raise ValueError(
                            "Architecture not supported. Use PyTorchDeRandomizedSmoothing.get_models() "
                            "to get the supported model architectures."
                        )
                    model = timm.create_model(
                        pretrained_cfg["architecture"], drop_tokens=drop_tokens, device_type=device_type  # type: ignore
                    )
                    model.load_state_dict(supplied_state_dict)  # type: ignore
                    if replace_last_layer:
                        model.head = torch.nn.Linear(model.head.in_features, nb_classes)  # type: ignore

                    if optimizer is not None:
                        if not isinstance(optimizer, torch.optim.Optimizer):
                            raise ValueError("Optimizer error: must be a torch.optim.Optimizer instance")

                        converted_optimizer: torch.optim.Adam | torch.optim.SGD
                        opt_state_dict = optimizer.state_dict()
                        if isinstance(optimizer, torch.optim.Adam):
                            logging.info("Converting Adam Optimiser")
                            converted_optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)  # type: ignore
                        elif isinstance(optimizer, torch.optim.SGD):
                            logging.info("Converting SGD Optimiser")
                            converted_optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)  # type: ignore
                        else:
                            raise ValueError("Optimiser not supported for conversion")
                        converted_optimizer.load_state_dict(opt_state_dict)

                self.to_reshape = False
                if not isinstance(model, PyTorchVisionTransformer):
                    raise ValueError("Vision transformer is not of PyTorchViT. Error occurred in PyTorchViT creation.")

                if model.default_cfg["input_size"][0] != input_shape[0]:  # type: ignore
                    raise ValueError(
                        f'ViT requires {model.default_cfg["input_size"][0]} channel input,'  # type: ignore
                        f" but {input_shape[0]} channels were provided."
                    )

                if model.default_cfg["input_size"] != input_shape:  # type: ignore
                    if verbose:
                        logger.warning(
                            " ViT expects input shape of: (%i, %i, %i) but (%i, %i, %i) specified as the input shape."
                            " The input will be rescaled to (%i, %i, %i)",
                            *model.default_cfg["input_size"],  # type: ignore
                            *input_shape,
                            *model.default_cfg["input_size"],  # type: ignore
                        )

                    self.to_reshape = True
                output_shape = model.default_cfg["input_size"]  # type: ignore

                # set the method back to avoid unexpected side effects later on should timm need to be reused.
                timm.models.vision_transformer._create_vision_transformer = tmp_func
                self.mode = "ViT"
            else:
                if isinstance(model, torch.nn.Module):
                    self.mode = "CNN"
                    output_shape = input_shape
                    self.to_reshape = False

        elif algorithm == "levine2020":
            if ablation_type is None or threshold is None or logits is None:
                raise ValueError(
                    "If using CNN please specify if the model returns logits, "
                    " the prediction threshold, and ablation type"
                )
            self.mode = "CNN"
            # input channels are internally doubled.
            input_shape = (input_shape[0] * 2, input_shape[1], input_shape[2])
            output_shape = input_shape
            self.to_reshape = False

        if optimizer is None or isinstance(optimizer, torch.optim.Optimizer):
            super().__init__(
                model=model,
                loss=loss,
                input_shape=input_shape,
                nb_classes=nb_classes,
                optimizer=optimizer,
                channels_first=channels_first,
                clip_values=clip_values,
                preprocessing_defences=preprocessing_defences,
                postprocessing_defences=postprocessing_defences,
                preprocessing=preprocessing,
                device_type=device_type,
            )
        else:
            raise ValueError("Error occurred in optimizer creation")

        self.threshold = threshold
        self.logits = logits
        self.ablation_size = (ablation_size,)
        self.algorithm = algorithm
        self.ablation_type = ablation_type
        if verbose:
            logger.info(self.model)

        from art.estimators.certification.derandomized_smoothing.ablators.pytorch import (
            ColumnAblatorPyTorch,
            BlockAblatorPyTorch,
        )

        if TYPE_CHECKING:
            self.ablator: ColumnAblatorPyTorch | BlockAblatorPyTorch

        if self.mode is None:
            raise ValueError("Model type not recognized.")

        if ablation_type in {"column", "row"}:
            self.ablator = ColumnAblatorPyTorch(
                ablation_size=ablation_size,
                channels_first=True,
                ablation_mode=ablation_type,
                to_reshape=self.to_reshape,
                original_shape=input_shape,
                output_shape=output_shape,
                device_type=device_type,
                algorithm=algorithm,
                mode=self.mode,
            )
        elif ablation_type == "block":
            self.ablator = BlockAblatorPyTorch(
                ablation_size=ablation_size,
                channels_first=True,
                to_reshape=self.to_reshape,
                original_shape=input_shape,
                output_shape=output_shape,
                device_type=device_type,
                algorithm=algorithm,
                mode=self.mode,
            )
        else:
            raise ValueError(f"ablation_type of {ablation_type} not recognized. Must be either column, row, or block")

        Create a smoothed classifier.

        :param model: Either a CNN or a VIT. For a ViT supply a string specifying which ViT architecture to load from
                      the ViT library, or a vision transformer already created with the
                      Pytorch Image Models (timm) library. To run Levine et al. (2020) provide a regular pytorch model.
        :param loss: The loss function for which to compute gradients for training. The target label must be raw
                     categorical, i.e. not converted to one-hot encoding.
        :param input_shape: The shape of one input instance.
        :param nb_classes: The number of classes of the model.
        :param ablation_size: The size of the data portion to retain after ablation.
        :param algorithm: Either 'salman2021' or 'levine2020'. For salman2021 we support ViTs and CNNs. For levine2020
                          there is only CNN support.
        :param replace_last_layer: ViT-specific. If to replace the last layer of the ViT with a fresh layer
                                   matching the number of classes for the dataset to be examined.
                                   Needed if going from the pre-trained imagenet models to fine-tune
                                   on a dataset like CIFAR.
        :param drop_tokens: ViT-specific. If to drop the fully ablated tokens in the ViT
        :param load_pretrained: ViT-specific. If to load a pretrained model matching the ViT name.
                                Will only affect the ViT if a string name is passed to model rather than a ViT directly.
        :param optimizer: The optimizer used to train the classifier.
        :param ablation_type: The type of ablation to perform. Either "column", "row", or "block"
        :param threshold: Specific to Levine et al. The minimum threshold to count a prediction.
        :param logits: Specific to Levine et al. If the model returns logits or normalized probabilities
        :param channels_first: Set channels first or last.
        :param clip_values: Tuple of the form `(min, max)` of floats or `np.ndarray` representing the minimum and
               maximum values allowed for features. If floats are provided, these will be used as the range of all
               features. If arrays are provided, each value will be considered the bound for a feature, thus
               the shape of clip values needs to match the total number of features.
        :param preprocessing_defences: Preprocessing defence(s) to be applied by the classifier.
        :param postprocessing_defences: Postprocessing defence(s) to be applied by the classifier.
        :param preprocessing: Tuple of the form `(subtrahend, divisor)` of floats or `np.ndarray` of values to be
               used for data preprocessing. The first value will be subtracted from the input. The input will then
               be divided by the second one.
        :param device_type: Type of device on which the classifier is run, either `gpu` or `cpu`.
        
art/estimators/certification/derandomized_smoothing/pytorch.py