class_docstr: Class which converts the supplied pytorch model into an equivalent model
  which uses abstract operations
class_docstr_tok: ["Class","which","converts","the","supplied","pytorch","model","into","an","equivalent","model","which","uses","abstract","operations"]
code: def __init__(
      self,
      model: "torch.nn.Module",
      channels_first: bool,
      input_shape: Tuple[int, ...],
  ):
      super().__init__()
      modules = []
      self.device = torch.device(
          "cuda:0" if torch.cuda.is_available() else "cpu"
      )
      self.forward_mode: str
      self.forward_mode = "abstract"
      self.reshape_op_num = -1
  
      # pylint: disable=W0613
      def forward_hook(input_module, hook_input, hook_output):
          modules.append(input_module)
  
      for module in model.children():
          module.register_forward_hook(forward_hook)
  
      if channels_first:
          input_for_hook = torch.rand(input_shape).to(self.device)
      else:
          raise ValueError(
              "Please provide data in channels first format"
          )
  
      input_for_hook = torch.unsqueeze(input_for_hook, dim=0)
      model(
          input_for_hook
      )  # hooks are fired sequentially from model input to the output
  
      self.ops = torch.nn.ModuleList()
      for module in modules:
          if isinstance(module, torch.nn.modules.conv.Conv2d):
              zono_conv = ZonoConv(
                  in_channels=module.in_channels,
                  out_channels=module.out_channels,
                  kernel_size=module.kernel_size,  # type: ignore
                  stride=module.stride,  # type: ignore
                  dilation=module.dilation,  # type: ignore
                  padding=module.padding,  # type: ignore
              )
              zono_conv.conv.weight.data = module.weight.data.to(
                  self.device
              )
              zono_conv.bias.data = module.bias.data.to(self.device)  # type: ignore
              self.ops.append(zono_conv)
  
          elif isinstance(module, torch.nn.modules.linear.Linear):
              zono_dense = ZonoDenseLayer(
                  in_features=module.in_features,
                  out_features=module.out_features,
              )
              zono_dense.weight.data = module.weight.data.to(
                  self.device
              )
              zono_dense.bias.data = module.bias.data.to(self.device)
              self.ops.append(zono_dense)
  
          elif isinstance(module, torch.nn.modules.activation.ReLU):
              self.ops.append(ZonoReLU(device=self.device))
          else:
              raise ValueError(
                  "Supported Operations are Conv2D, Linear, and RelU"
              )
  
      for op_num, op in enumerate(self.ops):
          # as reshapes are not modules we infer when the reshape from convolutional to dense occurs
          if isinstance(op, ZonoDenseLayer):
              # if the preceeding op was a convolution:
              if isinstance(self.ops[op_num - 1], ZonoConv):
                  self.reshape_op_num = op_num
                  print("Inferred reshape on op num", op_num)
              # if the preceeding op was a relu and the one before the activation was a convolution
              if isinstance(
                  self.ops[op_num - 1], ZonoReLU
              ) and isinstance(self.ops[op_num - 2], ZonoConv):
                  self.reshape_op_num = op_num
                  print("Inferred reshape on op num", op_num)
  
imports: from typing import List, Optional, Tuple, Union, Callable, Any, TYPE_CHECKING
  import logging
  import math
  import warnings
  import sys
  import numpy
  import torch
  from art.estimators.certification.deep_z.deep_z import ZonoConv, ZonoDenseLayer, ZonoReLU, ZonoBounds
  from art.estimators.classification.pytorch import PyTorchClassifier
label_desc: Use image node_id 1 to create a new ConvertedModel object from inherited base classes: torch.nn with example: obj = ConvertedModel(model, channels_first, input_shape)