code: def convert_main_graph(self, prog, graph):
      func_inputs = {}
      for input_type in self.inputs:
          dtype = input_type.dtype
          # int64 and fp64 are not supported, so they are mapped to int32 / fp32 accordingly
          if dtype == types.fp64:
              dtype = types.fp32
          elif types.is_int(dtype):
              dtype = types.int32
          func_inputs[input_type.name] = mb.placeholder(
              input_type.shape.symbolic_shape, dtype=dtype
          )
      prog.set_main_input_types(self.inputs)
  
      with Function(
          func_inputs, opset_version=self.opset_version
      ) as ssa_func:
          # Get the input Var
          for name in func_inputs.keys():
              input_var = ssa_func.inputs[name]
              if (
                  types.is_tensor(input_var.sym_type)
                  or types.is_scalar(input_var.sym_type)
              ) and input_var.dtype == types.fp16:
                  input_var = mb.cast(
                      x=input_var, dtype="fp32", name=name
                  )
              self.context.add(name, input_var)
          outputs = convert_graph(
              self.context, graph, self.output_names
          )
          ssa_func.set_outputs(outputs)
          prog.add_function("main", ssa_func)
      # check duplicate output
      # Note: sometimes two outputs are pointing to the same Var, we should
      # create mb.identity for those cases
      block = prog["main"]
      with block:
          name_counts = {}
          new_outputs = [output for output in block.outputs]
          for i, v_o in enumerate(block.outputs):
              if v_o.name not in name_counts:
                  name_counts[v_o.name] = 1
              else:
                  name_counts[v_o.name] += 1
                  new_name = (
                      v_o.name
                      + "_duplicate_"
                      + str(name_counts[v_o.name])
                  )
                  x = mb.identity(x=v_o, name=new_name)
                  new_outputs[i] = x
          block.set_outputs(new_outputs)
  
      # Rename outputs to TF's name. This is needed when the last op doesn't
      # generate a new Var (e.g., get_tuple, Identity etc.), and thus the
      # last Var would have a different name than the last TF op's name.
      #
      # Example:
      #
      # TF code:
      #    x = tf.placeholder(tf.float32, shape=(1,))
      #    y = tf.placeholder(tf.float32, shape=(1,))
      #    c = lambda i, j: \
      #            tf.less(tf.math.reduce_mean(i), tf.math.reduce_mean(j))
      #    b = lambda i, j: (tf.add(i, 1), j)
      #    res = tf.while_loop(c, b, [x, y])
      #
      # Resulting nodes (excluding the nodes in while loop cond & body):
      #
      # node name: Placeholder op type: Placeholder inputs: []
      # node name: Placeholder_1 op type: Placeholder inputs: []
      # node name: make_input_0 op type: make_tuple inputs: ['Placeholder',
      #         'Placeholder_1']
      # node name: while_0 op type: while inputs: ['make_input_0']
      # node name: while/Exit op type: get_tuple inputs: ['while_0']
      # node name: while/Exit_1 op type: get_tuple inputs: ['while_0']
      #
      # Observe that return node `while/Exit` is an output from get_tuple,
      # which in our translation simply unpack a python tuple of Vars
      # ('while_0:0', 'while_0:1') returned from while_0 SSA op. We need to
      # rename `while_0:0` to `while/Exit` in order for users to find the
      # output.
      # Note: only rename the output if the output is not Placeholder.
  
      input_names = [x.name for x in self.inputs]
      for v_o, out_name in zip(prog["main"].outputs, self.output_names):
          if v_o.name != out_name and v_o.name not in input_names:
              logger.info(
                  "Renaming output var: '{}' -> '{}'".format(
                      v_o.name, out_name
                  )
              )
              v_o.name = out_name
      self.check_placeholder_output(prog, self.output_names)
  
      # verify that if model output dtypes / names are provided by the user, they are valid
      if self.main_output_types is not None:
          self._validate_and_update_main_output_types(prog)
  
      if self.use_default_fp16_io:
          # get a list of names of fp32 output vars
          fp32_output_var_names = [
              var.name
              for var in prog["main"].outputs
              if var.dtype == types.fp32
          ]
  
          if self.main_output_types is not None:
              # set the dtype default to fp16 if main_output_types is provided
              for val in self.main_output_types:
                  if (
                      val.name in fp32_output_var_names
                      and isinstance(val, TensorType)
                      and val.dtype is None
                  ):
                      val.dtype = types.fp16
          else:
              # otherwise, we construct the main_output_types, to make every fp32
              # output var fp16
              main_output_types = []
              for val in prog["main"].outputs:
                  dtype = (
                      types.fp16
                      if val.name in fp32_output_var_names
                      else None
                  )
                  main_output_types.append(
                      TensorType(name=val.name, dtype=dtype)
                  )
              self.main_output_types = main_output_types
  
      prog.set_main_output_types(self.main_output_types)
  
imports: from coremltools import _logger
  from coremltools.converters._profile_utils import _profile
  from coremltools.converters.mil._deployment_compatibility import AvailableTarget
  from coremltools.converters.mil.input_types import ImageType, InputType, RangeDim
  from coremltools.converters.mil.input_types import Shape
  from coremltools.converters.mil.input_types import TensorType, _get_shaping_class
  from coremltools.converters.mil.mil import Builder
  from coremltools.converters.mil.mil import Function, Program, get_new_symbol, types
  from coremltools.converters.mil.mil.types.symbolic import is_symbolic
  from coremltools.converters.mil.mil.var import Var
  from .._utils import get_output_names
  from .basic_graph_ops import simple_topsort
  from .convert_utils import convert_graph
label_desc: Use image node_id 8 for calling the TFConverter obj's underlying member method code with example usage: obj.convert_main_graph(prog, graph) without return types