class_docstr: Generates model graph for pytorch models in protobuf, this implementation
  is borrowed from pytorch v1.4.0, and fixed following issues:
  https://github.com/pytorch/pytorch/issues/33691
  https://github.com/pytorch/pytorch/issues/33670
docstr: This method parses an optimized PyTorch model graph and produces
  a list of nodes and node stats for eventual conversion to TensorBoard
  protobuf format.
  
  Args:
  graph (PyTorch module): The model graph to be parsed.
  trace (PyTorch JIT TracedModule): The model trace to be parsed.
  args (tuple): input tensor[s] for the model.
  omit_useless_nodes (boolean): Whether to remove nodes from the graph.
code: def parse(self, graph, trace, args=None, omit_useless_nodes=True):
      """This method parses an optimized PyTorch model graph and produces
      a list of nodes and node stats for eventual conversion to TensorBoard
      protobuf format.
  
      Args:
      graph (PyTorch module): The model graph to be parsed.
      trace (PyTorch JIT TracedModule): The model trace to be parsed.
      args (tuple): input tensor[s] for the model.
      omit_useless_nodes (boolean): Whether to remove nodes from the graph.
      """
      nodes_py = GraphPy()
      for node in graph.inputs():
          if omit_useless_nodes:
              if (
                  not node.uses()
              ):  # number of user of the node (= number of outputs/ fanout)
                  continue
  
          if node.type().kind() != CLASSTYPE_KIND:
              nodes_py.append(NodePyIO(node, "input"))
  
      attr_to_scope = dict()
  
      def node_to_name(d):
          return str(d).split(":")[0].strip()
  
      for node in graph.nodes():
          if node.kind() == GETATTR_KIND:
              attr_name = node.s("name")
              node_name = node_to_name(node)
              parent = node.input().node()
              # If the parent node is not the top-level "self" node
              if parent.kind() == GETATTR_KIND:
                  parent_scope = attr_to_scope[node_to_name(parent)]
                  attr_scope = parent_scope.split("/")[-1]
                  attr_to_scope[node_name] = "{}/{}.{}".format(
                      parent_scope, attr_scope, attr_name
                  )
              else:
                  attr_to_scope[node_name] = "__module.{}".format(
                      attr_name
                  )
              # We don't need classtype nodes; scope will provide this information
              if node.output().type().kind() != CLASSTYPE_KIND:
                  node_py = NodePyOP(node)
                  node_py.scopeName = attr_to_scope[node_name]
                  nodes_py.append(node_py)
          else:
              nodes_py.append(NodePyOP(node))
  
      # Create sink nodes for output ops
      for i, node in enumerate(graph.outputs()):
          node_py = NodePyIO(node, "output")
          node_py.debugName = "output.{}".format(i + 1)
          node_py.inputs = [node.debugName()]
          nodes_py.append(node_py)
  
      alias_to_name = dict()
      base_name = parse_traced_name(trace._name)
      for name, module in trace.named_modules(prefix="__module"):
          mod_name = parse_traced_name(module._name)
          attr_name = name.split(".")[-1]
          alias_to_name[name] = "{}[{}]".format(mod_name, attr_name)
  
      for node in nodes_py.nodes_op:
          module_aliases = node.scopeName.split("/")[-1].split(".")
          module_name = ""
          for i, alias in enumerate(module_aliases):
              if i == 0:
                  module_name = alias
                  node.scopeName = base_name
              else:
                  module_name += "." + alias
                  node.scopeName += "/" + (
                      alias_to_name[module_name]
                      if module_name in alias_to_name
                      else alias
                  )
  
      nodes_py.populate_namespace_from_OP_to_IO()
      return nodes_py.to_proto()
  
imports: import logging
  import queue
  import re
  from collections import defaultdict
  import torch
  from torch.utils.tensorboard._pytorch_graph import NodePy, NodePyIO, NodePyOP, GraphPy
label_desc: Use image node_id 2 for calling the TorchProtoGraph obj's underlying member method code with example usage: obj.parse(graph, trace, args, omit_useless_nodes) and returns: nodes_py, str