class_docstr: Implements the ISMAGS subgraph matching algorithm. [1]_ ISMAGS stands for
  "Index-based Subgraph Matching Algorithm with General Symmetries". As the
  name implies, it is symmetry aware and will only generate non-symmetric
  isomorphisms.
  
  Notes
  -----
  The implementation imposes additional conditions compared to the VF2
  algorithm on the graphs provided and the comparison functions
  (:attr:`node_equality` and :attr:`edge_equality`):
  
   - Node keys in both graphs must be orderable as well as hashable.
   - Equality must be transitive: if A is equal to B, and B is equal to C,
     then A must be equal to C.
  
  Attributes
  ----------
  graph: networkx.Graph
  subgraph: networkx.Graph
  node_equality: collections.abc.Callable
      The function called to see if two nodes should be considered equal.
      It's signature looks like this:
      ``f(graph1: networkx.Graph, node1, graph2: networkx.Graph, node2) -> bool``.
      `node1` is a node in `graph1`, and `node2` a node in `graph2`.
      Constructed from the argument `node_match`.
  edge_equality: collections.abc.Callable
      The function called to see if two edges should be considered equal.
      It's signature looks like this:
      ``f(graph1: networkx.Graph, edge1, graph2: networkx.Graph, edge2) -> bool``.
      `edge1` is an edge in `graph1`, and `edge2` an edge in `graph2`.
      Constructed from the argument `edge_match`.
  
  References
  ----------
  .. [1] M. Houbraken, S. Demeyer, T. Michoel, P. Audenaert, D. Colle,
     M. Pickavet, "The Index-Based Subgraph Matching Algorithm with General
     Symmetries (ISMAGS): Exploiting Symmetry for Faster Subgraph
     Enumeration", PLoS One 9(5): e97896, 2014.
     https://doi.org/10.1371/journal.pone.0097896
class_docstr_tok: ["Implements","the","ISMAGS","subgraph","matching","algorithm",".","[","1","]","_","ISMAGS","stands","for","''","Index-based","Subgraph","Matching","Algorithm","with","General","Symmetries","''",".","As","the","name","implies",",","it","is","symmetry","aware","and","will","only","generate","non-symmetric","isomorphisms",".","Notes","--","--","-","The","implementation","imposes","additional","conditions","compared","to","the","VF2","algorithm","on","the","graphs","provided","and","the","comparison","functions","(",":","attr",":","`","node_equality","`","and",":","attr",":","`","edge_equality","`",")",":","-","Node","keys","in","both","graphs","must","be","orderable","as","well","as","hashable",".","-","Equality","must","be","transitive",":","if","A","is","equal","to","B",",","and","B","is","equal","to","C",",","then","A","must","be","equal","to","C.","Attributes","--","--","--","--","--","graph",":","networkx.Graph","subgraph",":","networkx.Graph","node_equality",":","collections.abc.Callable","The","function","called","to","see","if","two","nodes","should","be","considered","equal",".","It","'s","signature","looks","like","this",":","``","f","(","graph1",":","networkx.Graph",",","node1",",","graph2",":","networkx.Graph",",","node2",")","-",">","bool","``",".","`","node1","`","is","a","node","in","`","graph1","`",",","and","`","node2","`","a","node","in","`","graph2","`",".","Constructed","from","the","argument","`","node_match","`",".","edge_equality",":","collections.abc.Callable","The","function","called","to","see","if","two","edges","should","be","considered","equal",".","It","'s","signature","looks","like","this",":","``","f","(","graph1",":","networkx.Graph",",","edge1",",","graph2",":","networkx.Graph",",","edge2",")","-",">","bool","``",".","`","edge1","`","is","an","edge","in","`","graph1","`",",","and","`","edge2","`","an","edge","in","`","graph2","`",".","Constructed","from","the","argument","`","edge_match","`",".","References","--","--","--","--","--","..","[","1","]","M.","Houbraken",",","S.","Demeyer",",","T.","Michoel",",","P.","Audenaert",",","D.","Colle",",","M.","Pickavet",",","``","The","Index-Based","Subgraph","Matching","Algorithm","with","General","Symmetries","(","ISMAGS",")",":","Exploiting","Symmetry","for","Faster","Subgraph","Enumeration","''",",","PLoS","One","9","(","5",")",":","e97896",",","2014.","https",":","\/\/doi.org\/10.1371\/journal.pone.0097896"]
docstr: Processes ordered pair partitions as per the reference paper. Finds and
  returns all permutations and cosets that leave the graph unchanged.
code: def _process_ordered_pair_partitions(
      self,
      graph,
      top_partitions,
      bottom_partitions,
      edge_colors,
      orbits=None,
      cosets=None,
  ):
      """
      Processes ordered pair partitions as per the reference paper. Finds and
      returns all permutations and cosets that leave the graph unchanged.
      """
      if orbits is None:
          orbits = [{node} for node in graph.nodes]
      else:
          # Note that we don't copy orbits when we are given one. This means
          # we leak information between the recursive branches. This is
          # intentional!
          orbits = orbits
      if cosets is None:
          cosets = {}
      else:
          cosets = cosets.copy()
  
      assert all(
          len(t_p) == len(b_p)
          for t_p, b_p in zip(top_partitions, bottom_partitions)
      )
  
      # BASECASE
      if all(len(top) == 1 for top in top_partitions):
          # All nodes are mapped
          permutations = self._find_permutations(
              top_partitions, bottom_partitions
          )
          self._update_orbits(orbits, permutations)
          if permutations:
              return [permutations], cosets
          else:
              return [], cosets
  
      permutations = []
      unmapped_nodes = {
          (node, idx)
          for idx, t_partition in enumerate(top_partitions)
          for node in t_partition
          if len(t_partition) > 1
      }
      node, pair_idx = min(unmapped_nodes)
      b_partition = bottom_partitions[pair_idx]
  
      for node2 in sorted(b_partition):
          if len(b_partition) == 1:
              # Can never result in symmetry
              continue
          if node != node2 and any(
              node in orbit and node2 in orbit for orbit in orbits
          ):
              # Orbit prune branch
              continue
          # REDUCTION
          # Couple node to node2
          partitions = self._couple_nodes(
              top_partitions,
              bottom_partitions,
              pair_idx,
              node,
              node2,
              graph,
              edge_colors,
          )
          for opp in partitions:
              new_top_partitions, new_bottom_partitions = opp
  
              (
                  new_perms,
                  new_cosets,
              ) = self._process_ordered_pair_partitions(
                  graph,
                  new_top_partitions,
                  new_bottom_partitions,
                  edge_colors,
                  orbits,
                  cosets,
              )
              # COMBINATION
              permutations += new_perms
              cosets.update(new_cosets)
  
      mapped = {
          k
          for top, bottom in zip(top_partitions, bottom_partitions)
          for k in top
          if len(top) == 1 and top == bottom
      }
      ks = {k for k in graph.nodes if k < node}
      # Have all nodes with ID < node been mapped?
      find_coset = ks <= mapped and node not in cosets
      if find_coset:
          # Find the orbit that contains node
          for orbit in orbits:
              if node in orbit:
                  cosets[node] = orbit.copy()
      return permutations, cosets
  
imports: import itertools
  from collections import Counter, defaultdict
  from functools import reduce, wraps
label_desc: Use image node_id 35 for calling the ISMAGS obj's underlying member method code with example usage: obj._process_ordered_pair_partitions(graph, top_partitions, bottom_partitions, edge_colors, orbits, cosets) and returns: permutations,  cosets, list,  cosets, list,  cosets