def generate_derivation(config, goal_rule, current_rules, verbose=False):
  """Attempt to find derivation of goal_rule given current_rules.

  Args:
    config: Config dict.
    goal_rule: Goal QCFGRule to derive.
    current_rules: A set of QCFGRule instances.
    verbose: Print debug output if True.

  Returns:
    Set of QCFGRule representing a derivation or None.
  """
  # Don't use the goal_rule to derive itself if included in current_rules.
  rules = current_rules - {goal_rule}
  source = " ".join(goal_rule.source)
  target = " ".join(goal_rule.target)

  def node_fn(span_begin, span_end, rule, children):
    node_source = goal_rule.source[span_begin:span_end]
    return ChartNode(node_source, span_begin, span_end, rule, children)

  def postprocess_cell_fn(nodes):
    """Filter and merge generated nodes."""
    targets_to_rules = {}
    for node in nodes:
      # Discard targets that are not substrings of the gold target.
      if node.target_string in target:
        # Keep one arbitrary derivation per target.
        # TODO(petershaw): Revisit this.
        targets_to_rules[node.target_string] = node
    new_nodes = list(targets_to_rules.values())
    return new_nodes

  tokens = source.split(" ")
  max_single_nt_applications = config.get("max_single_nt_applications", 2)
  outputs = qcfg_parser.parse(
      tokens,
      rules,
      verbose=verbose,
      node_fn=node_fn,
      postprocess_cell_fn=postprocess_cell_fn,
      max_single_nt_applications=max_single_nt_applications)

  for output in outputs:
    if output.target_string == target:
      return output.rules

  return None
Attempt to find derivation of goal_rule given current_rules.

  Args:
    config: Config dict.
    goal_rule: Goal QCFGRule to derive.
    current_rules: A set of QCFGRule instances.
    verbose: Print debug output if True.

  Returns:
    Set of QCFGRule representing a derivation or None.
  
language/compgen/csl/induction/derivation_utils.py