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: Prepares an ordered list of layers to quantize sequentially. This list has all the layers ordered by their
  topological order in the graph.
  Args:
      recurrent (bool): indication on whether the model might have recurrent connections.
docstr_tok: ["Prepares","an","ordered","list","of","layers","to","quantize","sequentially",".","This","list","has","all","the","layers","ordered","by","their","topological","order","in","the","graph",".","Args",":","recurrent","(","bool",")",":","indication","on","whether","the","model","might","have","recurrent","connections","."]
code: def layers_topological_order(self, recurrent=False):
      """
      Prepares an ordered list of layers to quantize sequentially. This list has all the layers ordered by their
      topological order in the graph.
      Args:
          recurrent (bool): indication on whether the model might have recurrent connections.
      """
      if self._layers_topological_order:
          return self._layers_topological_order
      adj_map = self.adjacency_map()
      ranked_ops = OrderedDict(
          [(k, _OpRank(v, 0)) for k, v in adj_map.items()]
      )
  
      def _recurrent_ancestor(
          ranked_ops_dict, dest_op_name, src_op_name
      ):
          def _is_descendant(parent_op_name, dest_op_name):
              successors_names = [
                  op.name for op in adj_map[parent_op_name].successors
              ]
              if dest_op_name in successors_names:
                  return True
              for succ_name in successors_names:
                  if _is_descendant(succ_name, dest_op_name):
                      return True
              return False
  
          return _is_descendant(dest_op_name, src_op_name) and (
              0
              < ranked_ops_dict[dest_op_name].rank
              < ranked_ops_dict[src_op_name].rank
          )
  
      def rank_op(ranked_ops_dict, op_name, rank):
          ranked_ops_dict[op_name].rank = rank
          for child_op in adj_map[op_name].successors:
              # In recurrent models: if a successor is also an ancestor - we don't increment its rank.
              if not recurrent or not _recurrent_ancestor(
                  ranked_ops_dict, child_op.name, op_name
              ):
                  rank_op(
                      ranked_ops_dict,
                      child_op.name,
                      ranked_ops_dict[op_name].rank + 1,
                  )
  
      roots = [
          k for k, v in adj_map.items() if len(v.predecessors) == 0
      ]
      for root_op_name in roots:
          rank_op(ranked_ops, root_op_name, 0)
  
      # Take only the modules from the original model
      module_dict = dict(self._src_model.named_modules())
      ret = sorted(
          [k for k in ranked_ops.keys() if k in module_dict],
          key=lambda k: ranked_ops[k].rank,
      )
      # Check that only the actual roots have a rank of 0
      assert {k for k in ret if ranked_ops[k].rank == 0} <= set(roots)
      self._layers_topological_order = ret
      return ret
  
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 25 for calling the SummaryGraph obj's underlying member method code with example usage: obj.layers_topological_order(recurrent) and returns: ret, self, unknown, False, True, True