class_docstr: Implementation of VF2 algorithm for matching directed graphs.
  
  Suitable for DiGraph and MultiDiGraph instances.
class_docstr_tok: ["Implementation","of","VF2","algorithm","for","matching","directed","graphs",".","Suitable","for","DiGraph","and","MultiDiGraph","instances","."]
docstr: Returns True if adding (G1_node, G2_node) is syntactically feasible.
  
  This function returns True if it is adding the candidate pair
  to the current partial isomorphism/monomorphism mapping is allowable.
  The addition is allowable if the inclusion of the candidate pair does
  not make it impossible for an isomorphism/monomorphism to be found.
docstr_tok: ["Returns","True","if","adding","(","G1_node",",","G2_node",")","is","syntactically","feasible",".","This","function","returns","True","if","it","is","adding","the","candidate","pair","to","the","current","partial","isomorphism\/monomorphism","mapping","is","allowable",".","The","addition","is","allowable","if","the","inclusion","of","the","candidate","pair","does","not","make","it","impossible","for","an","isomorphism\/monomorphism","to","be","found","."]
code: def syntactic_feasibility(self, G1_node, G2_node):
      """Returns True if adding (G1_node, G2_node) is syntactically feasible.
  
      This function returns True if it is adding the candidate pair
      to the current partial isomorphism/monomorphism mapping is allowable.
      The addition is allowable if the inclusion of the candidate pair does
      not make it impossible for an isomorphism/monomorphism to be found.
      """
  
      # The VF2 algorithm was designed to work with graphs having, at most,
      # one edge connecting any two nodes.  This is not the case when
      # dealing with an MultiGraphs.
      #
      # Basically, when we test the look-ahead rules R_pred and R_succ, we
      # will make sure that the number of edges are checked.  We also add
      # a R_self check to verify that the number of selfloops is acceptable.
  
      # Users might be comparing DiGraph instances with MultiDiGraph
      # instances. So the generic DiGraphMatcher class must work with
      # MultiDiGraphs. Care must be taken since the value in the innermost
      # dictionary is a singlet for DiGraph instances.  For MultiDiGraphs,
      # the value in the innermost dictionary is a list.
  
      ###
      # Test at each step to get a return value as soon as possible.
      ###
  
      # Look ahead 0
  
      # R_self
  
      # The number of selfloops for G1_node must equal the number of
      # self-loops for G2_node. Without this check, we would fail on R_pred
      # at the next recursion level. This should prune the tree even further.
      if self.test == "mono":
          if self.G1.number_of_edges(
              G1_node, G1_node
          ) < self.G2.number_of_edges(G2_node, G2_node):
              return False
      else:
          if self.G1.number_of_edges(
              G1_node, G1_node
          ) != self.G2.number_of_edges(G2_node, G2_node):
              return False
  
      # R_pred
  
      # For each predecessor n' of n in the partial mapping, the
      # corresponding node m' is a predecessor of m, and vice versa. Also,
      # the number of edges must be equal
      if self.test != "mono":
          for predecessor in self.G1.pred[G1_node]:
              if predecessor in self.core_1:
                  if (
                      self.core_1[predecessor]
                      not in self.G2.pred[G2_node]
                  ):
                      return False
                  elif self.G1.number_of_edges(
                      predecessor, G1_node
                  ) != self.G2.number_of_edges(
                      self.core_1[predecessor], G2_node
                  ):
                      return False
  
      for predecessor in self.G2.pred[G2_node]:
          if predecessor in self.core_2:
              if self.core_2[predecessor] not in self.G1.pred[G1_node]:
                  return False
              elif self.test == "mono":
                  if self.G1.number_of_edges(
                      self.core_2[predecessor], G1_node
                  ) < self.G2.number_of_edges(predecessor, G2_node):
                      return False
              else:
                  if self.G1.number_of_edges(
                      self.core_2[predecessor], G1_node
                  ) != self.G2.number_of_edges(predecessor, G2_node):
                      return False
  
      # R_succ
  
      # For each successor n' of n in the partial mapping, the corresponding
      # node m' is a successor of m, and vice versa. Also, the number of
      # edges must be equal.
      if self.test != "mono":
          for successor in self.G1[G1_node]:
              if successor in self.core_1:
                  if self.core_1[successor] not in self.G2[G2_node]:
                      return False
                  elif self.G1.number_of_edges(
                      G1_node, successor
                  ) != self.G2.number_of_edges(
                      G2_node, self.core_1[successor]
                  ):
                      return False
  
      for successor in self.G2[G2_node]:
          if successor in self.core_2:
              if self.core_2[successor] not in self.G1[G1_node]:
                  return False
              elif self.test == "mono":
                  if self.G1.number_of_edges(
                      G1_node, self.core_2[successor]
                  ) < self.G2.number_of_edges(G2_node, successor):
                      return False
              else:
                  if self.G1.number_of_edges(
                      G1_node, self.core_2[successor]
                  ) != self.G2.number_of_edges(G2_node, successor):
                      return False
  
      if self.test != "mono":
          # Look ahead 1
  
          # R_termin
          # The number of predecessors of n that are in T_1^{in} is equal to the
          # number of predecessors of m that are in T_2^{in}.
          num1 = 0
          for predecessor in self.G1.pred[G1_node]:
              if (predecessor in self.in_1) and (
                  predecessor not in self.core_1
              ):
                  num1 += 1
          num2 = 0
          for predecessor in self.G2.pred[G2_node]:
              if (predecessor in self.in_2) and (
                  predecessor not in self.core_2
              ):
                  num2 += 1
          if self.test == "graph":
              if num1 != num2:
                  return False
          else:  # self.test == 'subgraph'
              if not (num1 >= num2):
                  return False
  
          # The number of successors of n that are in T_1^{in} is equal to the
          # number of successors of m that are in T_2^{in}.
          num1 = 0
          for successor in self.G1[G1_node]:
              if (successor in self.in_1) and (
                  successor not in self.core_1
              ):
                  num1 += 1
          num2 = 0
          for successor in self.G2[G2_node]:
              if (successor in self.in_2) and (
                  successor not in self.core_2
              ):
                  num2 += 1
          if self.test == "graph":
              if num1 != num2:
                  return False
          else:  # self.test == 'subgraph'
              if not (num1 >= num2):
                  return False
  
          # R_termout
  
          # The number of predecessors of n that are in T_1^{out} is equal to the
          # number of predecessors of m that are in T_2^{out}.
          num1 = 0
          for predecessor in self.G1.pred[G1_node]:
              if (predecessor in self.out_1) and (
                  predecessor not in self.core_1
              ):
                  num1 += 1
          num2 = 0
          for predecessor in self.G2.pred[G2_node]:
              if (predecessor in self.out_2) and (
                  predecessor not in self.core_2
              ):
                  num2 += 1
          if self.test == "graph":
              if num1 != num2:
                  return False
          else:  # self.test == 'subgraph'
              if not (num1 >= num2):
                  return False
  
          # The number of successors of n that are in T_1^{out} is equal to the
          # number of successors of m that are in T_2^{out}.
          num1 = 0
          for successor in self.G1[G1_node]:
              if (successor in self.out_1) and (
                  successor not in self.core_1
              ):
                  num1 += 1
          num2 = 0
          for successor in self.G2[G2_node]:
              if (successor in self.out_2) and (
                  successor not in self.core_2
              ):
                  num2 += 1
          if self.test == "graph":
              if num1 != num2:
                  return False
          else:  # self.test == 'subgraph'
              if not (num1 >= num2):
                  return False
  
          # Look ahead 2
  
          # R_new
  
          # The number of predecessors of n that are neither in the core_1 nor
          # T_1^{in} nor T_1^{out} is equal to the number of predecessors of m
          # that are neither in core_2 nor T_2^{in} nor T_2^{out}.
          num1 = 0
          for predecessor in self.G1.pred[G1_node]:
              if (predecessor not in self.in_1) and (
                  predecessor not in self.out_1
              ):
                  num1 += 1
          num2 = 0
          for predecessor in self.G2.pred[G2_node]:
              if (predecessor not in self.in_2) and (
                  predecessor not in self.out_2
              ):
                  num2 += 1
          if self.test == "graph":
              if num1 != num2:
                  return False
          else:  # self.test == 'subgraph'
              if not (num1 >= num2):
                  return False
  
          # The number of successors of n that are neither in the core_1 nor
          # T_1^{in} nor T_1^{out} is equal to the number of successors of m
          # that are neither in core_2 nor T_2^{in} nor T_2^{out}.
          num1 = 0
          for successor in self.G1[G1_node]:
              if (successor not in self.in_1) and (
                  successor not in self.out_1
              ):
                  num1 += 1
          num2 = 0
          for successor in self.G2[G2_node]:
              if (successor not in self.in_2) and (
                  successor not in self.out_2
              ):
                  num2 += 1
          if self.test == "graph":
              if num1 != num2:
                  return False
          else:  # self.test == 'subgraph'
              if not (num1 >= num2):
                  return False
  
      # Otherwise, this node pair is syntactically feasible!
      return True
  
imports: import sys
label_desc: Use image node_id 4 for calling the DiGraphMatcher obj's underlying member method code with example usage: obj.syntactic_feasibility(G1_node, G2_node) and returns: True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False