code: def _jit_pass_lower_graph(graph, torchscript):
      """
      This graph pass does a similar thing as torch._C._jit_pass_lower_graph does.
      It does three things:
      1. Rename getattr nodes which produce a torch tensor to match the keys in torch model's state_dict
      2. Construct the params_dict, with the keys similar to state_dict
      3. Get the named_buffer dict in torch model
  
      To be more specific, this graph pass traces down series of GetAttr ops, and rename the final node to match the torch model state_dict.
      It also replaces the node inputs by the first created tensor node with the same name.
  
      Example:
      Input graph:
      graph(%self.1 : __torch__.torch.nn.modules.Sequential, %input.1 : Tensor):
      %2 : prim::GetAttr[name="linear"](%self.1)
      %3 : prim::GetAttr[name="weight"](%2)
      %4 : prim::GetAttr[name="bias"](%2)
      %5 : prim::GetAttr[name="bias"](%2) # duplicated node
      %6 : conv(%input.1, %3, %4)
      %7 : add(%input.1, %5)
      return (%6, %7)
  
      Output graph:
      graph(%self.1 : __torch__.torch.nn.modules.Sequential, %input.1 : Tensor):
      %2 : prim::GetAttr[name="linear"](%self.1)
      %linear.weight : prim::GetAttr[name="weight"](%2)
      %linear.bias : prim::GetAttr[name="bias"](%2)
      %5 : prim::GetAttr[name="bias"](%2) # duplicated node, it is not used now
      %6 : conv(%input.1, %linear.weight, %linear.bias)
      %7 : add(%input.1, %linear.bias) # the second input is replaced
      return (%6, %7)
  
      And a dictionary {"linear.weight": ..., "linear.bias": ...} is returned, to record the parameters values.
      Note that, those GetAttr nodes are still in the torch ir graph, but they would be removed in a latter
      graph pass in the coremltools torch internal graph
  
      """
  
      """
      Each getattr node corresponds to a torch object in the torch IR,
      it could be either:
      1. torch.nn.modules: submodule in a torch model. For instance, a linear layer in a MLP network.
      2. torch.Tensor: torch model parameters. For instance, weight for a conv layer.
      3. torch._C.ScriptObject: quantized torch model parameters.
      For example, in the graph above, %2 is pointing to the __torch__.torch.nn.modules.Sequential.linear torch submodule.
      node_to_module_map tracks these mapping.
  
      node_to_prefic_map track the name for each module,
      for example, %2 has the prefix name linear and %3 is linear.weight.
      These names are also keys in the state_dict
      """
      node_to_module_map = {}
      node_to_prefix_map = {}
      first_node_with_prefix = {}
      replace_input = {}
  
      base_module_node = list(graph.inputs())[0]
      node_to_module_map[base_module_node] = torchscript
      node_to_prefix_map[base_module_node] = ""
  
      """
      params_dict will be contructed in this graph pass. It contains all const tensors needed for the graph computation.
      And the value is validated against the state_dict if the key is presented in both dictionaries.
      In some rare cases, state_dict lacks parameters / buffers, so we still need to go through the while graph ourselves.
      """
      params_dict = {}
      state_dict = torchscript.state_dict(keep_vars=True)
      buffer_dict = {k: v for k, v in torchscript.named_buffers()}
  
      def _check_is_tensor(node, module):
          if not isinstance(module, torch.Tensor):
              return False
          if str(node.output().type()) not in (
              "Tensor",
              "Optional[Tensor]",
          ):
              raise TypeError(
                  f'Type "{node.output().type()}" not supported'
              )
          return True
  
      def _check_is_quantized_tensor(node, module):
          if not isinstance(module, torch._C.ScriptObject):
              return False
          # We only support ScriptObjects that correspond to quantized packed params.
          assert "PackedParams" in node.output().type().name()
          return True
  
      def _lower_graph_block(graph):
          for node in list(graph.nodes()):
              for block in node.blocks():
                  _lower_graph_block(block)
  
              for idx, _input in enumerate(list(node.inputs())):
                  if _input in replace_input:
                      node.replaceInput(idx, replace_input[_input])
  
              kind = node.kind().split("::")[1].lower()
              if kind != "getattr":
                  continue
  
              _input = node.input()
              _output = node.output()
              attr_name = getattr(node, node.kindOf("name"))("name")
  
              module = getattr(node_to_module_map[_input], attr_name)
              node_to_module_map[_output] = module
  
              input_prefix = node_to_prefix_map[_input]
              prefix = (
                  input_prefix + "." + attr_name
                  if input_prefix != ""
                  else attr_name
              )
              node_to_prefix_map[_output] = prefix
  
              is_tensor = _check_is_tensor(node, module)
              is_quantized_tensor = _check_is_quantized_tensor(
                  node, module
              )
  
              if is_tensor or is_quantized_tensor:
                  if is_tensor and prefix in state_dict:
                      assert torch.equal(
                          module.cpu(), state_dict[prefix].cpu()
                      ), "tensor value not consistent between torch ir and state_dict"
                  if prefix in params_dict:
                      assert torch.equal(
                          module.cpu(), params_dict[prefix].cpu()
                      )
                      replace_input[_output] = first_node_with_prefix[
                          prefix
                      ]
                  else:
                      params_dict[prefix] = module
                      first_node_with_prefix[prefix] = _output
                      _output.setDebugName(prefix)
  
      _lower_graph_block(graph)
      return graph, params_dict, buffer_dict
  
imports: import torch
  from coremltools._deps import version_lt
  from coremltools.converters.mil.mil import types
label_desc: Use image node_id 1 for calling a global function with example usage: _jit_pass_lower_graph(graph, torchscript) and returns: graph,  params_dict,  buffer_dict, True, True, False, False