nodes: 0, module; 1, function_definition; 2, function_name:canonical_order; 3, parameters; 4, block; 5, identifier:self; 6, expression_statement; 7, comment:# A) find an appropriate starting vertex.; 8, comment:# Here we take a central vertex that has a minimal number of symmetrical; 9, comment:# equivalents, 'the highest atom number', and the highest fingerprint.; 10, comment:# Note that the symmetrical equivalents are computed from the vertex; 11, comment:# fingerprints, i.e. without the GraphSearch.; 12, expression_statement; 13, comment:# B) sort all vertices based on; 14, comment:#      1) distance from central vertex; 15, comment:#      2) number of equivalent vertices; 16, comment:#      3) vertex string, (higher atom numbers come first); 17, comment:#      4) fingerprint; 18, comment:#      5) vertex index; 19, comment:# The last field is only included to collect the result of the sort.; 20, comment:# The fingerprint on itself would be sufficient, but the three first are; 21, comment:# there to have a naturally appealing result.; 22, expression_statement; 23, expression_statement; 24, comment:# C) The order of some vertices is still not completely set. e.g.; 25, comment:# consider the case of allene. The four hydrogen atoms are equivalent,; 26, comment:# but one can have two different orders: make geminiles consecutive or; 27, comment:# don't. It is more trikcy than one would think at first sight. In the; 28, comment:# case of allene, geminility could easily solve the problem. Consider a; 29, comment:# big flat rotationally symmetric molecule (order 2). The first five; 30, comment:# shells are order 4 and one would just give a random order to four; 31, comment:# segemnts in the first shell. Only when one reaches the outer part that; 32, comment:# has order two, it turns out that the arbitrary choices in the inner; 33, comment:# shell play a role. So it does not help to look at relations with; 34, comment:# vertices at inner or current shells only. One has to consider the; 35, comment:# whole picture. (unit testing reveals troubles like these); 36, comment:# I need some sleep now. The code below checks for potential fuzz and; 37, comment:# will raise an error if the ordering is not fully determined yet. One; 38, comment:# day, I'll need this code more than I do now, and I'll fix things up.; 39, comment:# I know how to do this, but I don't care enough right now.; 40, comment:# -- Toon; 41, for_statement; 42, comment:# D) Return only the vertex indexes.; 43, return_statement; 44, comment:"""The vertices in a canonical or normalized order.
  
             This routine will return a list of vertices in an order that does not
             depend on the initial order, but only depends on the connectivity and
             the return values of the function self.get_vertex_string.
  
             Only the vertices that are involved in edges will be included. The
             result can be given as first argument to self.get_subgraph, with
             reduce=True as second argument. This will return a complete canonical
             graph.
  
             The routine is designed not to use symmetry relations that are
             obtained with the GraphSearch routine. We also tried to create an
             ordering that feels like natural, i.e. starting in the center and
             pushing vertices with few equivalents to the front. If necessary, the
             nature of the vertices and  their bonds to atoms closer to the center
             will also play a role, but only as a last resort.
          """; 45, assignment; 46, assignment; 47, call; 48, identifier:i; 49, call; 50, block; 51, list_comprehension; 52, identifier:starting_vertex; 53, subscript; 54, identifier:l; 55, list_comprehension; 56, attribute; 57, argument_list; 58, identifier:range; 59, argument_list; 60, if_statement; 61, subscript; 62, for_in_clause; 63, call; 64, unary_operator; 65, list; 66, for_in_clause; 67, if_clause; 68, identifier:l; 69, identifier:sort; 70, keyword_argument; 71, integer:1; 72, call; 73, comparison_operator:l[i][:-1] == l[i-1][:-1]; 74, block; 75, identifier:record; 76, unary_operator; 77, identifier:record; 78, identifier:l; 79, identifier:max; 80, generator_expression; 81, integer:1; 82, unary_operator; 83, unary_operator; 84, call; 85, call; 86, identifier:vertex; 87, pattern_list; 88, call; 89, comparison_operator:len(self.neighbors[vertex]) > 0; 90, identifier:reverse; 91, True; 92, identifier:len; 93, argument_list; 94, subscript; 95, subscript; 96, raise_statement; 97, integer:1; 98, tuple; 99, for_in_clause; 100, identifier:distance; 101, call; 102, attribute; 103, argument_list; 104, attribute; 105, argument_list; 106, identifier:vertex; 107, identifier:distance; 108, attribute; 109, argument_list; 110, call; 111, integer:0; 112, identifier:l; 113, subscript; 114, slice; 115, subscript; 116, slice; 117, identifier:NotImplementedError; 118, unary_operator; 119, call; 120, call; 121, identifier:vertex; 122, identifier:vertex; 123, attribute; 124, identifier:len; 125, argument_list; 126, identifier:self; 127, identifier:get_vertex_string; 128, identifier:vertex; 129, subscript; 130, identifier:tobytes; 131, identifier:self; 132, identifier:iter_breadth_first; 133, identifier:starting_vertex; 134, identifier:len; 135, argument_list; 136, identifier:l; 137, identifier:i; 138, unary_operator; 139, identifier:l; 140, binary_operator:i-1; 141, unary_operator; 142, call; 143, attribute; 144, argument_list; 145, attribute; 146, argument_list; 147, identifier:self; 148, identifier:central_vertices; 149, subscript; 150, attribute; 151, identifier:vertex; 152, subscript; 153, integer:1; 154, identifier:i; 155, integer:1; 156, integer:1; 157, identifier:len; 158, argument_list; 159, identifier:self; 160, identifier:get_vertex_string; 161, identifier:vertex; 162, subscript; 163, identifier:tobytes; 164, attribute; 165, identifier:vertex; 166, identifier:self; 167, identifier:vertex_fingerprints; 168, attribute; 169, identifier:vertex; 170, subscript; 171, attribute; 172, identifier:vertex; 173, identifier:self; 174, identifier:equivalent_vertices; 175, identifier:self; 176, identifier:neighbors; 177, attribute; 178, identifier:vertex; 179, identifier:self; 180, identifier:vertex_fingerprints; 181, identifier:self; 182, identifier:equivalent_vertices
code: def canonical_order(self):
          """The vertices in a canonical or normalized order.
  
             This routine will return a list of vertices in an order that does not
             depend on the initial order, but only depends on the connectivity and
             the return values of the function self.get_vertex_string.
  
             Only the vertices that are involved in edges will be included. The
             result can be given as first argument to self.get_subgraph, with
             reduce=True as second argument. This will return a complete canonical
             graph.
  
             The routine is designed not to use symmetry relations that are
             obtained with the GraphSearch routine. We also tried to create an
             ordering that feels like natural, i.e. starting in the center and
             pushing vertices with few equivalents to the front. If necessary, the
             nature of the vertices and  their bonds to atoms closer to the center
             will also play a role, but only as a last resort.
          """
          # A) find an appropriate starting vertex.
          # Here we take a central vertex that has a minimal number of symmetrical
          # equivalents, 'the highest atom number', and the highest fingerprint.
          # Note that the symmetrical equivalents are computed from the vertex
          # fingerprints, i.e. without the GraphSearch.
          starting_vertex = max(
              (
                  -len(self.equivalent_vertices[vertex]),
                  self.get_vertex_string(vertex),
                  self.vertex_fingerprints[vertex].tobytes(),
                  vertex
              ) for vertex in self.central_vertices
          )[-1]
  
          # B) sort all vertices based on
          #      1) distance from central vertex
          #      2) number of equivalent vertices
          #      3) vertex string, (higher atom numbers come first)
          #      4) fingerprint
          #      5) vertex index
          # The last field is only included to collect the result of the sort.
          # The fingerprint on itself would be sufficient, but the three first are
          # there to have a naturally appealing result.
          l = [
              [
                  -distance,
                  -len(self.equivalent_vertices[vertex]),
                  self.get_vertex_string(vertex),
                  self.vertex_fingerprints[vertex].tobytes(),
                  vertex
              ] for vertex, distance in self.iter_breadth_first(starting_vertex)
              if len(self.neighbors[vertex]) > 0
          ]
          l.sort(reverse=True)
  
          # C) The order of some vertices is still not completely set. e.g.
          # consider the case of allene. The four hydrogen atoms are equivalent,
          # but one can have two different orders: make geminiles consecutive or
          # don't. It is more trikcy than one would think at first sight. In the
          # case of allene, geminility could easily solve the problem. Consider a
          # big flat rotationally symmetric molecule (order 2). The first five
          # shells are order 4 and one would just give a random order to four
          # segemnts in the first shell. Only when one reaches the outer part that
          # has order two, it turns out that the arbitrary choices in the inner
          # shell play a role. So it does not help to look at relations with
          # vertices at inner or current shells only. One has to consider the
          # whole picture. (unit testing reveals troubles like these)
  
          # I need some sleep now. The code below checks for potential fuzz and
          # will raise an error if the ordering is not fully determined yet. One
          # day, I'll need this code more than I do now, and I'll fix things up.
          # I know how to do this, but I don't care enough right now.
          # -- Toon
          for i in range(1, len(l)):
              if l[i][:-1] == l[i-1][:-1]:
                  raise NotImplementedError
  
          # D) Return only the vertex indexes.
          return [record[-1] for record in l]