class_docstr: A specialized version of the (cached) top down predict rule that operates
  on nonterminals whose symbols are ``FeatStructNonterminal``.  Rather
  than simply comparing the nonterminals for equality, they are
  unified.
  
  The top down expand rule states that:
  
  - ``[A -> alpha \* B1 beta][i:j]``
  
  licenses the edge:
  
  - ``[B2 -> \* gamma][j:j]``
  
  for each grammar production ``B2 -> gamma``, assuming that B1
  and B2 can be unified.
class_docstr_tok: ["A","specialized","version","of","the","(","cached",")","top","down","predict","rule","that","operates","on","nonterminals","whose","symbols","are","``","FeatStructNonterminal","``",".","Rather","than","simply","comparing","the","nonterminals","for","equality",",","they","are","unified",".","The","top","down","expand","rule","states","that",":","-","``","[","A","-",">","alpha","\\","*","B1","beta","]","[","i",":","j","]","``","licenses","the","edge",":","-","``","[","B2","-",">","\\","*","gamma","]","[","j",":","j","]","``","for","each","grammar","production","``","B2","-",">","gamma","``",",","assuming","that","B1","and","B2","can","be","unified","."]
code: def apply(self, chart, grammar, edge):
      if edge.is_complete():
          return
      nextsym, index = edge.nextsym(), edge.end()
      if not is_nonterminal(nextsym):
          return
  
      # If we've already applied this rule to an edge with the same
      # next & end, and the chart & grammar have not changed, then
      # just return (no new edges to add).
      nextsym_with_bindings = edge.next_with_bindings()
      done = self._done.get(
          (nextsym_with_bindings, index), (None, None)
      )
      if done[0] is chart and done[1] is grammar:
          return
  
      for prod in grammar.productions(lhs=nextsym):
          # If the left corner in the predicted production is
          # leaf, it must match with the input.
          if prod.rhs():
              first = prod.rhs()[0]
              if is_terminal(first):
                  if index >= chart.num_leaves():
                      continue
                  if first != chart.leaf(index):
                      continue
  
          # We rename vars here, because we don't want variables
          # from the two different productions to match.
          if unify(prod.lhs(), nextsym_with_bindings, rename_vars=True):
              new_edge = FeatureTreeEdge.from_production(
                  prod, edge.end()
              )
              if chart.insert(new_edge, ()):
                  yield new_edge
  
      # Record the fact that we've applied this rule.
      self._done[nextsym_with_bindings, index] = (chart, grammar)
  
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 FeatureTopDownPredictRule obj's underlying member method code with example usage: obj.apply(chart, grammar, edge) without return types