package edu.stanford.nlp.sempre; import java.util.*; import com.google.common.base.Function; import fig.basic.*; /** * Prune derivations during parsing. * * To add custom pruning criteria, implement a DerivationPruningComputer class, * and put the class name in the |pruningComputers| option. * * @author ppasupat */ public class DerivationPruner { public static class Options { @Option(gloss = "Pruning strategies to use") public List<String> pruningStrategies = new ArrayList<>(); @Option(gloss = "DerivationPruningComputer subclasses to look for pruning strategies") public List<String> pruningComputers = new ArrayList<>(); @Option public int pruningVerbosity = 0; @Option(gloss = "(for tooManyValues) maximum denotation size of the final formula") public int maxNumValues = 10; @Option(gloss = "(for badSummarizerHead) allow count on sets of size 1") public boolean allowCountOne = false; @Option(gloss = "Ensure that deriv.value contains the executed denotation.") public boolean ensureExecuted = true; @Option(gloss = "If false, apply recursive pruning strategies only at the outermost layer of the formula") public boolean recursivePruning = true; } public static Options opts = new Options(); public final Parser parser; public final Example ex; private List<DerivationPruningComputer> pruningComputers = new ArrayList<>(); // If not null, limit the pruning strategies to this list in addition to opts.pruningStrategies. private List<String> customAllowedPruningStrategies; private final Set<String> allStrategyNames; public DerivationPruner(ParserState parserState) { this.parser = parserState.parser; this.ex = parserState.ex; this.pruningComputers.add(new DefaultDerivationPruningComputer(this)); for (String pruningComputer : opts.pruningComputers) { try { Class<?> pruningComputerClass = Class.forName(SempreUtils.resolveClassName(pruningComputer)); pruningComputers.add((DerivationPruningComputer) pruningComputerClass.getConstructor(this.getClass()).newInstance(this)); } catch (ClassNotFoundException e1) { throw new RuntimeException("Illegal pruning computer: " + pruningComputer); } catch (Exception e) { e.printStackTrace(); e.getCause().printStackTrace(); throw new RuntimeException("Error while instantiating pruning computer: " + pruningComputer); } } // Compile the list of all strategies allStrategyNames = new HashSet<>(); for (DerivationPruningComputer computer : pruningComputers) allStrategyNames.addAll(computer.getAllStrategyNames()); for (String strategy : opts.pruningStrategies) { if (!allStrategyNames.contains(strategy)) LogInfo.fails("Pruning strategy '%s' not found!", strategy); } } /** * Set additional restrictions on the pruning strategies. * * If customAllowedPruningStrategies is not null, the pruning strategy must be in both * opts.pruningStrategies and customAllowedPruningStrategies in order to be used. * * Useful when some pruning strategies can break the parsing mechanism. */ public void setCustomAllowedPruningStrategies(List<String> customAllowedPruningStrategies) { this.customAllowedPruningStrategies = customAllowedPruningStrategies; } protected boolean containsStrategy(String name) { return opts.pruningStrategies.contains(name) && (customAllowedPruningStrategies == null || customAllowedPruningStrategies.contains(name)); } public List<DerivationPruningComputer> getPruningComputers() { return new ArrayList<>(pruningComputers); } /** * Return true if the derivation should be pruned. Otherwise, return false. */ public boolean isPruned(Derivation deriv) { if (opts.pruningStrategies.isEmpty() && pruningComputers.isEmpty()) return false; if (isPrunedWithoutExecution(deriv)) return true; if (isPrunedGeneral(deriv)) return true; if (isPrunedRecursive(deriv)) return true; return false; } // Prune without executing the formula boolean isPrunedWithoutExecution(Derivation deriv) { String matchedStrategy; for (DerivationPruningComputer computer : pruningComputers) { if ((matchedStrategy = computer.isPrunedWithoutExecution(deriv)) != null) { if (opts.pruningVerbosity >= 2) LogInfo.logs("PRUNED [%s] %s", matchedStrategy, deriv.formula); return true; } } return false; } // Prune based on the denotation (general) boolean isPrunedGeneral(Derivation deriv) { if (!opts.ensureExecuted) return false; deriv.ensureExecuted(parser.executor, ex.context); String matchedStrategy; for (DerivationPruningComputer computer : pruningComputers) { if ((matchedStrategy = computer.isPrunedGeneral(deriv)) != null) { if (opts.pruningVerbosity >= 2) LogInfo.logs("PRUNED [%s] %s", matchedStrategy, deriv.formula); return true; } } return false; } // Prune based on subformula boolean isPrunedRecursive(Derivation deriv) { if (!opts.recursivePruning) { // If recursivePruning flag is turned off, only look at the outermost layer. if (opts.ensureExecuted) deriv.ensureExecuted(parser.executor, ex.context); String matchedStrategy; for (DerivationPruningComputer computer : pruningComputers) { if ((matchedStrategy = computer.isPrunedRecursive(deriv, deriv.formula, new HashMap<>())) != null) { if (opts.pruningVerbosity >= 2) LogInfo.logs("PRUNED [%s] %s", matchedStrategy, deriv.formula); return true; } } } else { PruningRecurser recurser = new PruningRecurser(deriv); deriv.formula.forEach(recurser); if (recurser.matchedStrategy != null) { if (opts.pruningVerbosity >= 2) LogInfo.logs("PRUNED [%s] %s", recurser.matchedStrategy, deriv.formula); return true; } } return false; } /** * Helper class for traversing the formula recursively. */ class PruningRecurser implements Function<Formula, Boolean> { final Derivation deriv; final Map<String, Object> state = new HashMap<>(); String matchedStrategy = null; public PruningRecurser(Derivation deriv) { this.deriv = deriv; } @Override public Boolean apply(Formula f) { if (matchedStrategy != null) return true; // Don't process further for (DerivationPruningComputer computer : pruningComputers) { if ((matchedStrategy = computer.isPrunedRecursive(deriv, f, state)) != null) return true; // Don't process further } return false; // Recurse } } /** * Run isPruned with a (temporary) custom set of allowed pruning strategies. * If customAllowedPruningStrategies is null, all strategies are allowed. * If customAllowedPruningStrategies is empty, no pruning happens. */ public boolean isPruned(Derivation deriv, List<String> customAllowedPruningStategies) { List<String> old = this.customAllowedPruningStrategies; this.customAllowedPruningStrategies = customAllowedPruningStategies; boolean answer = isPruned(deriv); this.customAllowedPruningStrategies = old; return answer; } }