docs: def _node_isomorphic(a, b, check_varprops=True):
      """
      Two Xmrs objects are isomorphic if they have the same structure as
      determined by variable linkages between preds.
      """
      # first some quick checks
      a_var_refs = sorted(len(vd['refs']) for vd in a._vars.values())
      b_var_refs = sorted(len(vd['refs']) for vd in b._vars.values())
      if a_var_refs != b_var_refs:
          return False
      print()
  
      # these signature: [node] indices are meant to avoid unnecessary
      # comparisons; they also take care of "semantic feasibility"
      # constraints (comparing node values and properties). All that's
      # left is the "syntactic feasibility", or node-edge shapes.
      # nodedicts are {sig: [(id, edges), ...], ...}
      a_nd = _node_isomorphic_build_nodedict(a, check_varprops)
      #print('a', a_nd)
      b_nd = _node_isomorphic_build_nodedict(b, check_varprops)
      #print('b', b_nd)
      #return
  
      a_sigs = {}  # for node -> sig mapping
      # don't recurse when things are unique
      agenda = []
      isomap = {}
      for sig, a_pairs in sorted(a_nd.items(), key=lambda x: len(x[1])):
          b_pairs = b_nd.get(sig, [])
          if len(a_pairs) != len(b_pairs):
              return False
          if len(a_pairs) == 1:
              a_, a_edges = a_pairs[0]
              b_, b_edges = b_pairs[0]
              if len(a_edges) != len(b_edges):
                  return False
              a_sigs[a_] = sig
              isomap[a_] = b_
              for edge, a_tgt in a_edges.items():
                  if edge not in b_edges:
                      return False
                  isomap[a_tgt] = b_edges[edge]
          else:
              for a_, ed in a_pairs:
                  a_sigs[a_] = sig
                  agenda.append((a_, sig, ed))
      #print(agenda)
      #return
      isomaps = _node_isomorphic(agenda, a_sigs, b_nd, isomap, {})
      # for sig, a_candidates in sorted(a_nodes.items(), key=lambda x: len(x[1])):
      #     b_candidates = b_nodes.get(sig, [])
      #     if len(a_candidates) != len(b_candidates): return False
      #     candidates.append((a_candidates, b_candidates))
      #
      # nodemaps = _isomorphic(a, b, candidates, {})
      try:
          next(isomaps)
          return True
      except StopIteration:
          return False