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: Find all subgraph isomorphisms honoring constraints.
code: def _map_nodes(
      self,
      sgn,
      candidates,
      constraints,
      mapping=None,
      to_be_mapped=None,
  ):
      """
      Find all subgraph isomorphisms honoring constraints.
      """
      if mapping is None:
          mapping = {}
      else:
          mapping = mapping.copy()
      if to_be_mapped is None:
          to_be_mapped = set(self.subgraph.nodes)
  
      # Note, we modify candidates here. Doesn't seem to affect results, but
      # remember this.
      # candidates = candidates.copy()
      sgn_candidates = intersect(candidates[sgn])
      candidates[sgn] = frozenset([sgn_candidates])
      for gn in sgn_candidates:
          # We're going to try to map sgn to gn.
          if gn in mapping.values() or sgn not in to_be_mapped:
              # gn is already mapped to something
              continue  # pragma: no cover
  
          # REDUCTION and COMBINATION
          mapping[sgn] = gn
          # BASECASE
          if to_be_mapped == set(mapping.keys()):
              yield {v: k for k, v in mapping.items()}
              continue
          left_to_map = to_be_mapped - set(mapping.keys())
  
          new_candidates = candidates.copy()
          sgn_nbrs = set(self.subgraph[sgn])
          not_gn_nbrs = set(self.graph.nodes) - set(self.graph[gn])
          for sgn2 in left_to_map:
              if sgn2 not in sgn_nbrs:
                  gn2_options = not_gn_nbrs
              else:
                  # Get all edges to gn of the right color:
                  g_edges = self._edges_of_same_color(sgn, sgn2)
                  # FIXME directed graphs
                  # And all nodes involved in those which are connected to gn
                  gn2_options = {
                      n for e in g_edges for n in e if gn in e
                  }
              # Node color compatibility should be taken care of by the
              # initial candidate lists made by find_subgraphs
  
              # Add gn2_options to the right collection. Since new_candidates
              # is a dict of frozensets of frozensets of node indices it's
              # a bit clunky. We can't do .add, and + also doesn't work. We
              # could do |, but I deem union to be clearer.
              new_candidates[sgn2] = new_candidates[sgn2].union(
                  [frozenset(gn2_options)]
              )
  
              if (sgn, sgn2) in constraints:
                  gn2_options = {gn2 for gn2 in self.graph if gn2 > gn}
              elif (sgn2, sgn) in constraints:
                  gn2_options = {gn2 for gn2 in self.graph if gn2 < gn}
              else:
                  continue  # pragma: no cover
              new_candidates[sgn2] = new_candidates[sgn2].union(
                  [frozenset(gn2_options)]
              )
  
          # The next node is the one that is unmapped and has fewest
          # candidates
          next_sgn = min(
              left_to_map, key=lambda n: min(new_candidates[n], key=len)
          )
          yield from self._map_nodes(
              next_sgn,
              new_candidates,
              constraints,
              mapping=mapping,
              to_be_mapped=to_be_mapped,
          )
  
imports: import itertools
  from collections import Counter, defaultdict
  from functools import reduce, wraps
label_desc: Use image node_id 29 for calling the ISMAGS obj's underlying member method code with example usage: obj._map_nodes(sgn, candidates, constraints, mapping, to_be_mapped) without return types