class_docstr: We use Pytorch's JIT tracer to run a forward pass and generate a trace graph, which
  is an internal representation of the model.  We then use ONNX to "clean" this
  representation.  After builiding a new representation of the graph, we can print
  it to a table, a PNG, add information to nodes, etc.
  
  The trace is a C++ component and the API is not documented, so we need to dig into some
  Pytorch internals code to understand how to get the info we need.
  https://github.com/pytorch/pytorch/blob/master/torch/onnx/__init__.py
  https://github.com/pytorch/pytorch/blob/master/torch/onnx/symbolic.py
  
  We think that using the trace output to generate a representation of the graph, is
  the best method available in Pytorch, due to the module's dynamic nature.
  Pytorch's module API naturally ignores layers/operations which are implemented as
  torch.autograd.Function, without an nn.Module.  For example:
      out = F.relu(self.bn1(self.conv1(x)))
  
  Another case where traversing the nn.Module API is not sufficient to create a
  representation of the graph, is the same nn.Module is used several times in the
  graph.  For example:
  
      out = self.conv1(x)
      out = self.bn1(out)
      out = self.relu(out)    <=== First use of self.relu
  
      out = self.conv2(out)
      out = self.bn2(out)
      out = self.relu(out)    <=== Second use of self.relu
class_docstr_tok: ["We","use","Pytorch","'s","JIT","tracer","to","run","a","forward","pass","and","generate","a","trace","graph",",","which","is","an","internal","representation","of","the","model",".","We","then","use","ONNX","to","``","clean","''","this","representation",".","After","builiding","a","new","representation","of","the","graph",",","we","can","print","it","to","a","table",",","a","PNG",",","add","information","to","nodes",",","etc",".","The","trace","is","a","C++","component","and","the","API","is","not","documented",",","so","we","need","to","dig","into","some","Pytorch","internals","code","to","understand","how","to","get","the","info","we","need",".","https",":","\/\/github.com\/pytorch\/pytorch\/blob\/master\/torch\/onnx\/__init__.py","https",":","\/\/github.com\/pytorch\/pytorch\/blob\/master\/torch\/onnx\/symbolic.py","We","think","that","using","the","trace","output","to","generate","a","representation","of","the","graph",",","is","the","best","method","available","in","Pytorch",",","due","to","the","module","'s","dynamic","nature",".","Pytorch","'s","module","API","naturally","ignores","layers\/operations","which","are","implemented","as","torch.autograd.Function",",","without","an","nn.Module",".","For","example",":","out","=","F.relu","(","self.bn1","(","self.conv1","(","x",")",")",")","Another","case","where","traversing","the","nn.Module","API","is","not","sufficient","to","create","a","representation","of","the","graph",",","is","the","same","nn.Module","is","used","several","times","in","the","graph",".","For","example",":","out","=","self.conv1","(","x",")","out","=","self.bn1","(","out",")","out","=","self.relu","(","out",")","<","===","First","use","of","self.relu","out","=","self.conv2","(","out",")","out","=","self.bn2","(","out",")","out","=","self.relu","(","out",")","<","===","Second","use","of","self.relu"]
docstr: The ONNX trace optimization converts average pool ops to a sequence of 2 operations: pad + pool.
  This "quirk" makes makes it unnecessarily difficult to detect the connectivity between an average pool
  op and its predecessor, and it doesn't serve any purpose in the context of SummaryGraph usages.
  So we get rid of the pad op here.
docstr_tok: ["The","ONNX","trace","optimization","converts","average","pool","ops","to","a","sequence","of","2","operations",":","pad","+","pool",".","This","``","quirk","''","makes","makes","it","unnecessarily","difficult","to","detect","the","connectivity","between","an","average","pool","op","and","its","predecessor",",","and","it","does","n't","serve","any","purpose","in","the","context","of","SummaryGraph","usages",".","So","we","get","rid","of","the","pad","op","here","."]
code: def __merge_pad_avgpool(self):
      """The ONNX trace optimization converts average pool ops to a sequence of 2 operations: pad + pool.
      This "quirk" makes makes it unnecessarily difficult to detect the connectivity between an average pool
      op and its predecessor, and it doesn't serve any purpose in the context of SummaryGraph usages.
      So we get rid of the pad op here.
      """
      pad_op_name = None
      for curr_op_name, curr_op in list(self.ops.items()):
          curr_op_type = curr_op["type"]
          if curr_op_type == "Pad":
              pad_op_name = curr_op_name
          else:
              if pad_op_name and curr_op_type == "AveragePool":
                  pad_op = self.ops[pad_op_name]
                  if pad_op["module-name"] != curr_op["module-name"]:
                      continue
                  merged_op = OrderedDict(curr_op)
                  merged_op["name"] = pad_op_name
                  merged_op["inputs"] = pad_op["inputs"]
                  self.ops[pad_op_name] = merged_op
                  self.ops.pop(curr_op_name)
                  self.module_ops_map[merged_op["module-name"]].remove(
                      curr_op_name
                  )
  
                  sequence_input_idx = pad_op["inputs"][0]
                  first_edge = SummaryGraph.Edge(
                      sequence_input_idx, pad_op_name
                  )
                  idx = self.edges.index(first_edge)
                  del self.edges[idx : idx + 4]
                  self.edges.insert(
                      idx,
                      SummaryGraph.Edge(
                          sequence_input_idx, pad_op_name
                      ),
                  )
                  self.edges.insert(
                      idx + 1,
                      SummaryGraph.Edge(
                          pad_op_name, merged_op["outputs"][0]
                      ),
                  )
  
              pad_op_name = None
  
imports: from .None import distiller
  import re
  import numpy
  import collections
  import torch
  import torch.nn
  import torch.jit
  import logging
  from collections import OrderedDict, defaultdict
  from collections.abc import MutableSequence, Iterable
label_desc: Use image node_id 2 for calling the SummaryGraph obj's underlying member method code with example usage: obj.__merge_pad_avgpool() without return types