class_docstr: Base class for all type casting related quantization, such as fp32->fp16, int32->int16, etc.
  
  For each valid op, if the "op_selector" return True:
  - For each input with dtype `origin_dtype`, inject a "cast" op to change it to `target_dtype`.
  - For each output with dtype `target_dtype`, inject a "cast" op to change it back to `origin_dtype`.
  All child classes need to specify `origin_dtype` and `target_dtype`.
class_docstr_tok: ["Base","class","for","all","type","casting","related","quantization",",","such","as","fp32-",">","fp16",",","int32-",">","int16",",","etc",".","For","each","valid","op",",","if","the","``","op_selector","''","return","True",":","-","For","each","input","with","dtype","`","origin_dtype","`",",","inject","a","``","cast","''","op","to","change","it","to","`","target_dtype","`",".","-","For","each","output","with","dtype","`","target_dtype","`",",","inject","a","``","cast","''","op","to","change","it","back","to","`","origin_dtype","`",".","All","child","classes","need","to","specify","`","origin_dtype","`","and","`","target_dtype","`","."]
docstr: Transform the input(s)/output(s) dtypes of the op.
docstr_tok: ["Transform","the","input","(","s",")","\/output","(","s",")","dtypes","of","the","op","."]
code: def transform_op(self, op) -> None:
      """Transform the input(s)/output(s) dtypes of the op."""
      block = op.enclosing_block
      casted_inputs = {}
      inputs_modified = False
  
      for param, inputs in op.inputs.items():
          if not self.should_cast_parameter(op, param):
              continue
  
          is_list_input = isinstance(inputs, (list, tuple))
          if not is_list_input:
              inputs = [inputs]
  
          casted_inputs[param] = list(inputs[:])
          for i, var in enumerate(inputs):
              if not var.is_tensor_or_scalar_of(
                  dtype=self.origin_dtype
              ):
                  continue
  
              inputs_modified = True
              casted_var_name = f"{var.name}_to_{self.target_dtype}"
              if (
                  len(var._child_ops) > 1
                  and casted_var_name in self.cache_vars
                  and (
                      block.is_var_visible_in_block(
                          self.cache_vars[casted_var_name]
                      )
                  )
              ):
                  casted_inputs[param][i] = self.cache_vars[
                      casted_var_name
                  ]
              else:
                  x = mb.cast(
                      x=var,
                      dtype=self.target_dtype,
                      name=casted_var_name,
                      before_op=op,
                  )
                  if self.target_dtype == "fp16":
                      self._check_underflow_to_zero(x, var)
  
                  casted_inputs[param][i] = x
                  if len(var._child_ops) > 1:
                      self.cache_vars[casted_var_name] = casted_inputs[
                          param
                      ][i]
  
          if not is_list_input:
              casted_inputs[param] = casted_inputs[param][0]
  
      if inputs_modified:
          casted_inputs.update(
              {
                  k: v
                  for k, v in op.inputs.items()
                  if k not in casted_inputs
              }
          )
          casted_inputs["name"] = f"{op.name}_cast_{self.target_dtype}"
          casted_inputs["before_op"] = op
          quant_output = getattr(mb, op.op_type)(**casted_inputs)
  
          if not isinstance(quant_output, (list, tuple)):
              quant_output = [quant_output]
  
          for old_output_var, new_output_var in zip(
              op.outputs, quant_output
          ):
              if old_output_var.is_tensor_or_scalar_of(
                  dtype=self.origin_dtype
              ) and (
                  not new_output_var.is_tensor_or_scalar_of(
                      dtype=self.origin_dtype
                  )
              ):
                  x = mb.cast(
                      x=new_output_var,
                      dtype=self.origin_dtype,
                      name=f"{new_output_var.name}_to_{self.origin_dtype}",
                      before_op=op,
                  )
                  op.enclosing_block.replace_uses_of_var_after_op(
                      anchor_op=op,
                      old_var=old_output_var,
                      new_var=x,
                      force_replace=True,
                  )
              else:
                  op.enclosing_block.replace_uses_of_var_after_op(
                      anchor_op=op,
                      old_var=old_output_var,
                      new_var=new_output_var,
                      force_replace=True,
                  )
  
          block.remove_ops([op])
  
imports: from abc import abstractmethod
  from enum import Enum
  from typing import Set, Text
  import numpy
  from coremltools.converters.mil._deployment_compatibility import AvailableTarget
  from coremltools.converters.mil.mil import Builder
  from coremltools.converters.mil.mil import Operation, types
  from coremltools.converters.mil.mil.block import is_current_opset_version_compatible_with
  from coremltools.converters.mil.mil.ops.registry import SSAOpRegistry
  from coremltools.converters.mil.mil.passes.graph_pass import AbstractGraphPass
  from coremltools.converters.mil.mil.passes.helper import block_context_manager
  from coremltools.converters.mil.mil.passes.pass_registry import register_pass
  from coremltools.converters.mil.mil.program import Program
  from coremltools.converters.mil.mil.types.symbolic import is_symbolic
label_desc: Use image node_id 5 for calling the CastTypeQuantization obj's underlying member method code with example usage: obj.transform_op(op) without return types