def select_action(self, sampled_rule, current_rules):
    """Select action that maximizes improvememt in objective."""
    current_rules = set(current_rules)
    if self.verbose:
      logging.info("Sampled rule: %s, current rules: %d", str(sampled_rule),
                   len(current_rules))

    # Can optionally skip identity rules.
    can_split_seed_rules = self.config.get("can_split_seed_rules", False)
    if (sampled_rule in self.seed_rules and not can_split_seed_rules):
      return None

    # Check if this rule can be derived by the current set of rules.
    derivation_rules = derivation_utils.generate_derivation(
        self.config, sampled_rule, current_rules)
    if derivation_rules:
      action = action_utils.Action(
          rules_to_add=set(), rules_to_remove={sampled_rule})
      return action

    # The "delta" refers to the improvement in grammar induction objective.
    # If no action has a positive delta, then return None.
    max_action = None
    max_delta = 0.0

    # Determine candidate rules to add.
    candidates = _find_possible_candidates(
        sampled_rule,
        current_rules,
        self.config,
    )

    if candidates:
      # Map of rule candidates to set of rules that they enable removing.
      candidates_to_rules_to_remove = collections.defaultdict(set)

      # Dict of rule candidates to the change in objective.
      candidates_to_delta = {}

      for candidate_rule, _ in candidates:
        # Check whether candidate's target can be derived by target grammar.
        if self.target_checker:
          if not self.target_checker.can_parse(candidate_rule.target):
            if self.verbose:
              logging.info("Skipped rule not in target grammar: %s.",
                           str(candidate_rule))
            continue
        candidates_to_rules_to_remove[candidate_rule].add(sampled_rule)
        # For each candidate, attempt to find any additional rules that can be
        # removed.
        additional_rules = _find_additional_rules_to_remove(
            self.seed_rules, current_rules, candidate_rule, self.config)
        candidates_to_rules_to_remove[candidate_rule] |= additional_rules

        # Compute the first term in the objective, the change in grammar size.
        candidates_to_delta[
            candidate_rule] = self.objective_calculator.get_candidate_size_delta(
                candidate_rule, candidates_to_rules_to_remove[candidate_rule])

      # Sort candidates by size reduction prior to computing the
      # delta for rule penalities, which is relatively more
      # expensive. Use lexical ordering to break ties.
      candidates_to_delta_sorted = sorted(
          candidates_to_delta.items(), key=lambda x: (-x[1], x[0]))

      # For debugging, log up to the top 15 candidates.
      if self.verbose:
        logging.info("Candidate rules:")
        for rule, delta in candidates_to_delta_sorted[:15]:
          logging.info("%s (%s)", rule, delta)

      for candidate_rule, delta in candidates_to_delta_sorted:
        if delta <= max_delta:
          # Because the penalty delta is <= 0, we can safely break here as
          # there is no way for delta to increase above max_delta.
          break
        rules_to_remove = candidates_to_rules_to_remove[candidate_rule]
        penalty_delta = 0.0
        if self.config.get("use_rule_penalty", True):
          penalty_delta = self.objective_calculator.get_candidate_penalty_delta(
              candidate_rule, rules_to_remove)
          if self.verbose:
            logging.info("Penalty delta for %s: %s", candidate_rule,
                         penalty_delta)

        # Now consider the full delta in the objective.
        delta += penalty_delta
        # Check if delta is greater than any previous action considered.
        if delta > max_delta:
          max_delta = delta
          max_action = action_utils.Action(
              rules_to_add={candidate_rule}, rules_to_remove=rules_to_remove)
    return max_action
Select action that maximizes improvememt in objective.
language/compgen/csl/induction/greedy_policy.py