code: def test_builder_to_backend_cast_ios17(
      self, compute_unit, backend, src_dtype, dst_dtype
  ):
      _SUPPORTED_IO_DTYPES = {types.fp16, types.fp32, types.int32}
      x = np.array([[1, 2, 3], [4, 5, 6]], dtype=src_dtype)
      src_builtin_dtype = numpy_type_to_builtin_type(src_dtype)
      dst_builtin_dtype = numpy_type_to_builtin_type(dst_dtype)
      expected_res = x.astype(dtype=np.float16)
  
      expected_cast_num = 1
      if src_builtin_dtype not in _SUPPORTED_IO_DTYPES:
          # A cast will be inserted for unsupported dtypes inputs.
          expected_cast_num += 1
  
      # As CoreML IO only allows fp16/32 and int32, the output will be further cast.
      expected_res_builtin_dtype = dst_builtin_dtype
      if dst_builtin_dtype not in _SUPPORTED_IO_DTYPES:
          expected_res_builtin_dtype = (
              types.int32
              if types.is_int(dst_builtin_dtype)
              else types.fp32
          )
          expected_cast_num += 1
  
      def build(x):
          return mb.cast(
              x=x, dtype=types.builtin_to_string(dst_builtin_dtype)
          )
  
      with patch.object(
          Var, "_is_nonreplaceable_var"
      ) as mocked_is_nonreplaceable_var:
          # Mock that the cast is non-replaceable, to make sure it's kept in the graph.
          mocked_is_nonreplaceable_var.side_effect = (
              lambda var: var.op and var.op.op_type == "cast"
          )
          # Remove the cast optimization pass to make sure all cast are kept in the graph.
          pass_pipeline: PassPipeline = PassPipeline.DEFAULT
          pass_pipeline.remove_passes(
              [
                  "common::cast_optimization",
                  "common::topological_reorder",
              ]
          )
          mlmodel = run_compare_builder(
              build,
              {
                  "x": mb.placeholder(
                      shape=x.shape, dtype=src_builtin_dtype
                  )
              },
              input_values={"x": x},
              expected_output_types=x.shape
              + (expected_res_builtin_dtype,),
              expected_outputs=expected_res,
              compute_unit=compute_unit,
              backend=backend,
              pass_pipeline=pass_pipeline,
          )
          prog = mlmodel._mil_program
          cast_ops = prog["main"].find_ops(op_type="cast")
          assert len(cast_ops) == expected_cast_num
  
file_path: coremltools/coremltools/converters/mil/mil/ops/tests/iOS17/test_elementwise_unary.py
imports: import itertools
  from unittest.mock import patch
  import numpy
  import pytest
  import coremltools
  from coremltools.converters.mil.mil import Builder
  from coremltools.converters.mil.mil import types
  from coremltools.converters.mil.mil.ops.tests.iOS14.test_elementwise_unary import TestElementwiseUnary
  from coremltools.converters.mil.mil.ops.tests.iOS17 import backends
  from coremltools.converters.mil.mil.ops.tests.testing_utils import run_compare_builder
  from coremltools.converters.mil.mil.passes.pass_pipeline import PassPipeline
  from coremltools.converters.mil.mil.types.type_mapping import numpy_type_to_builtin_type
  from coremltools.converters.mil.mil.var import Var
  from coremltools.converters.mil.testing_reqs import compute_units
  from coremltools.converters.mil.testing_utils import get_op_types_in_program
label_desc: Use image node_id 2 for calling the TestElementwiseUnary obj's underlying member method code with example usage: obj.test_builder_to_backend_cast_ios17(compute_unit, backend, src_dtype, dst_dtype) and returns: mb