package edu.stanford.nlp.sempre;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import fig.basic.LogInfo;
/**
* Common pruning strategies that can be used in many semantic parsing tasks.
*
* @author ppasupat
*/
public class DefaultDerivationPruningComputer extends DerivationPruningComputer {
public DefaultDerivationPruningComputer(DerivationPruner pruner) {
super(pruner);
}
public static final String atomic = "atomic";
public static final String emptyDenotation = "emptyDenotation";
public static final String nonLambdaError = "nonLambdaError";
public static final String tooManyValues = "tooManyValues";
public static final String doubleSummarizers = "doubleSummarizers";
public static final String sameMerge = "sameMerge";
public static final String mistypedMerge = "mistypedMerge";
public static final String unsortedMerge = "unsortedMerge";
public static final String badSummarizerHead = "badSummarizerHead";
@Override
public Collection<String> getAllStrategyNames() {
return Arrays.asList(
atomic,
emptyDenotation, nonLambdaError, tooManyValues,
doubleSummarizers, sameMerge, mistypedMerge, unsortedMerge, badSummarizerHead);
}
// ============================================================
// Formula-based pruning
// ============================================================
@Override
public String isPrunedWithoutExecution(Derivation deriv) {
// atomic: Prune atomic formula at root.
// e.g., Prevent "Who was taller, Lincoln or Obama" --> fb:en.lincoln generated from lexicon without any computation
if (containsStrategy(atomic)) {
if (deriv.isRoot(pruner.ex.numTokens()) && deriv.formula instanceof ValueFormula)
return atomic;
}
return null;
}
// ============================================================
// Denotation-based Pruning
// ============================================================
@Override
public String isPrunedGeneral(Derivation deriv) {
// emptyDenotation: Prune if the denotation is empty
if (containsStrategy(emptyDenotation)) {
if (deriv.value instanceof ListValue && ((ListValue) deriv.value).values.isEmpty())
return emptyDenotation;
}
// nonLambdaError: Prune if the denotation is an error and the formula is not a partial formula
if (containsStrategy(nonLambdaError) && !isLambdaFormula(deriv.formula)) {
if (deriv.value instanceof ErrorValue) {
if (DerivationPruner.opts.pruningVerbosity >= 5)
LogInfo.logs("NonLambdaError: %s => %s", deriv.formula, deriv.value);
return nonLambdaError;
}
}
// tooManyValues: Prune if the denotation has too many values (at $ROOT only)
if (containsStrategy(tooManyValues) && deriv.isRoot(pruner.ex.numTokens())) {
if (!(deriv.value instanceof ListValue) ||
((ListValue) deriv.value).values.size() > DerivationPruner.opts.maxNumValues)
return tooManyValues;
}
return null;
}
// Helper function: return true if the result is clearly a binary
private boolean isLambdaFormula(Formula formula) {
if (formula instanceof LambdaFormula) return true;
if (formula instanceof ValueFormula &&
CanonicalNames.isBinary(((ValueFormula<?>) formula).value)) return true;
return false;
}
// ============================================================
// Recursively prune formulas
// ============================================================
@Override
public String isPrunedRecursive(Derivation deriv, Formula subformula, Map<String, Object> state) {
// doubleSummarizers: Prune when two summarizers (aggregate or superlative) are directly nested
// e.g., in (sum (avg ...)) and (min (argmax ...)), the outer operation is redundant
if (containsStrategy(doubleSummarizers)) {
Formula innerFormula = null;
if (subformula instanceof SuperlativeFormula)
innerFormula = ((SuperlativeFormula) subformula).head;
else if (subformula instanceof AggregateFormula)
innerFormula = ((AggregateFormula) subformula).child;
if (innerFormula != null &&
(innerFormula instanceof SuperlativeFormula || innerFormula instanceof AggregateFormula))
return doubleSummarizers;
}
// sameMerge: Prune merge formulas with two identical children
if (containsStrategy(sameMerge) && subformula instanceof MergeFormula) {
MergeFormula merge = (MergeFormula) subformula;
if (merge.child1.equals(merge.child2))
return sameMerge;
}
// mistypedMerge: Prune merge formulas with children of different types
if (containsStrategy(mistypedMerge) && subformula instanceof MergeFormula) {
MergeFormula merge = (MergeFormula) subformula;
SemType type1 = TypeInference.inferType(merge.child1, true);
SemType type2 = TypeInference.inferType(merge.child2, true);
if (!type1.meet(type2).isValid())
return mistypedMerge;
}
// unsortedMerge: Prune merge formulas where the children's string forms are not lexicographically sorted.
// Will remove redundant (and Y X) when (and Y X) is already present.
if (containsStrategy(unsortedMerge) && subformula instanceof MergeFormula) {
MergeFormula merge = (MergeFormula) subformula;
String child1 = merge.child1.toString(), child2 = merge.child2.toString();
if (child1.compareTo(child2) >= 0)
return unsortedMerge;
}
// badSummarizerHead: Prune if the head of a superlative or a non-count aggregate
// is empty or is a single object
if (containsStrategy(badSummarizerHead) && DerivationPruner.opts.ensureExecuted) {
Formula innerFormula = null;
boolean isCount = false;
if (subformula instanceof SuperlativeFormula)
innerFormula = ((SuperlativeFormula) subformula).head;
else if (subformula instanceof AggregateFormula) {
innerFormula = ((AggregateFormula) subformula).child;
if (((AggregateFormula) subformula).mode == AggregateFormula.Mode.count)
isCount = true;
}
if (innerFormula != null) {
try {
TypeInference.inferType(innerFormula);
Value innerValue = pruner.parser.executor.execute(innerFormula, pruner.ex.context).value;
if (innerValue instanceof ListValue) {
int size = ((ListValue) innerValue).values.size();
if (size == 0 || (size == 1 && !(DerivationPruner.opts.allowCountOne && isCount)))
return badSummarizerHead;
}
} catch (Exception e) {
// TypeInference fails; probably because of free variables. No need to do anything.
}
}
}
return null;
}
}