class_docstr: A specialized version of the fundamental rule that operates on
  nonterminals whose symbols are ``FeatStructNonterminal``s.  Rather
  than simply comparing the nonterminals for equality, they are
  unified.  Variable bindings from these unifications are collected
  and stored in the chart using a ``FeatureTreeEdge``.  When a
  complete edge is generated, these bindings are applied to all
  nonterminals in the edge.
  
  The fundamental rule states that:
  
  - ``[A -> alpha \* B1 beta][i:j]``
  - ``[B2 -> gamma \*][j:k]``
  
  licenses the edge:
  
  - ``[A -> alpha B3 \* beta][i:j]``
  
  assuming that B1 and B2 can be unified to generate B3.
class_docstr_tok: ["A","specialized","version","of","the","fundamental","rule","that","operates","on","nonterminals","whose","symbols","are","``","FeatStructNonterminal","``","s",".","Rather","than","simply","comparing","the","nonterminals","for","equality",",","they","are","unified",".","Variable","bindings","from","these","unifications","are","collected","and","stored","in","the","chart","using","a","``","FeatureTreeEdge","``",".","When","a","complete","edge","is","generated",",","these","bindings","are","applied","to","all","nonterminals","in","the","edge",".","The","fundamental","rule","states","that",":","-","``","[","A","-",">","alpha","\\","*","B1","beta","]","[","i",":","j","]","``","-","``","[","B2","-",">","gamma","\\","*","]","[","j",":","k","]","``","licenses","the","edge",":","-","``","[","A","-",">","alpha","B3","\\","*","beta","]","[","i",":","j","]","``","assuming","that","B1","and","B2","can","be","unified","to","generate","B3","."]
code: def apply(self, chart, grammar, left_edge, right_edge):
      # Make sure the rule is applicable.
      if not (
          left_edge.end() == right_edge.start()
          and left_edge.is_incomplete()
          and right_edge.is_complete()
          and isinstance(left_edge, FeatureTreeEdge)
      ):
          return
      found = right_edge.lhs()
      nextsym = left_edge.nextsym()
      if isinstance(right_edge, FeatureTreeEdge):
          if not is_nonterminal(nextsym):
              return
          if left_edge.nextsym()[TYPE] != right_edge.lhs()[TYPE]:
              return
          # Create a copy of the bindings.
          bindings = left_edge.bindings()
          # We rename vars here, because we don't want variables
          # from the two different productions to match.
          found = found.rename_variables(
              used_vars=left_edge.variables()
          )
          # Unify B1 (left_edge.nextsym) with B2 (right_edge.lhs) to
          # generate B3 (result).
          result = unify(nextsym, found, bindings, rename_vars=False)
          if result is None:
              return
      else:
          if nextsym != found:
              return
          # Create a copy of the bindings.
          bindings = left_edge.bindings()
  
      # Construct the new edge.
      new_edge = left_edge.move_dot_forward(right_edge.end(), bindings)
  
      # Add it to the chart, with appropriate child pointers.
      if chart.insert_with_backpointer(new_edge, left_edge, right_edge):
          yield new_edge
  
imports: from time import perf_counter
  from nltk.featstruct import TYPE, FeatStruct, find_variables, unify
  from nltk.grammar import CFG, FeatStructNonterminal, Nonterminal, Production, is_nonterminal, is_terminal
  from nltk.parse.chart import BottomUpPredictCombineRule, BottomUpPredictRule, CachedTopDownPredictRule, Chart, ChartParser, EdgeI, EmptyPredictRule, FundamentalRule, LeafInitRule, SingleEdgeFundamentalRule, TopDownInitRule, TreeEdge
  from nltk.sem import logic
  from nltk.tree import Tree
label_desc: Use image node_id 1 for calling the FeatureFundamentalRule obj's underlying member method code with example usage: obj.apply(chart, grammar, left_edge, right_edge) without return types