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: Returns a mapping from each op in the graph to its immediate predecessors and successors.
  
  The keys in the generated mapping are op names, and the values are instances of AdjacentsEntry.
  
  The op names are "de-normalized", meaning they can be used directly with the underlying model's
  named_modules(), for example.
  
  Args:
      dedicated_modules_only (bool): If set, the generated mapping will not include any ops that can't be
        associated with a dedicated module within the underlying model. Examples of this will be
        functional calls, such as "F.relu()", and tensor operations, such as "t3 = t1 + t2".
docstr_tok: ["Returns","a","mapping","from","each","op","in","the","graph","to","its","immediate","predecessors","and","successors",".","The","keys","in","the","generated","mapping","are","op","names",",","and","the","values","are","instances","of","AdjacentsEntry",".","The","op","names","are","``","de-normalized","''",",","meaning","they","can","be","used","directly","with","the","underlying","model's","named_modules","(",")",",","for","example",".","Args",":","dedicated_modules_only","(","bool",")",":","If","set",",","the","generated","mapping","will","not","include","any","ops","that","ca","n't","be","associated","with","a","dedicated","module","within","the","underlying","model",".","Examples","of","this","will","be","functional","calls",",","such","as","``","F.relu","(",")","''",",","and","tensor","operations",",","such","as","``","t3","=","t1","+","t2","''","."]
code: def adjacency_map(self, dedicated_modules_only=False):
      """Returns a mapping from each op in the graph to its immediate predecessors and successors.
  
      The keys in the generated mapping are op names, and the values are instances of AdjacentsEntry.
  
      The op names are "de-normalized", meaning they can be used directly with the underlying model's
      named_modules(), for example.
  
      Args:
          dedicated_modules_only (bool): If set, the generated mapping will not include any ops that can't be
            associated with a dedicated module within the underlying model. Examples of this will be
            functional calls, such as "F.relu()", and tensor operations, such as "t3 = t1 + t2".
      """
      if self._adj_map and not dedicated_modules_only:
          return self._adj_map
      adj_map = OrderedDict()
  
      for op_name, op in self.ops.items():
  
          def op_meta(n):
              return OpSimpleMetadata(
                  distiller.denormalize_module_name(self._src_model, n),
                  self.ops[n]["type"],
              )
  
          if not self._dedicated_module_check(
              op_name, dedicated_modules_only
          ):
              continue
  
          entry = AdjacentsEntry(op_meta(op_name))
          # Find the immediate preceding and succeeding modules. Depth of 1 gets us the
          # input and output tensors, depth of 2 gets the actual modules
          entry.predecessors = [
              op_meta(n)
              for n in self.predecessors(op, 2, denorm_names=False)
              if self._dedicated_module_check(n, dedicated_modules_only)
          ]
          entry.successors = [
              op_meta(n)
              for n in self.successors(op, 2, denorm_names=False)
              if self._dedicated_module_check(n, dedicated_modules_only)
          ]
  
          adj_map[entry.op_meta.name] = entry
      self._adj_map = adj_map
      return adj_map
  
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 24 for calling the SummaryGraph obj's underlying member method code with example usage: obj.adjacency_map(dedicated_modules_only) and returns: adj_map, self, OpSimpleMetadata