package edu.stanford.nlp.sempre.tables; import java.util.*; import edu.stanford.nlp.sempre.*; import edu.stanford.nlp.sempre.AggregateFormula.Mode; import edu.stanford.nlp.sempre.tables.lambdadcs.LambdaDCSException; import fig.basic.LogInfo; public class TableDerivationPruningComputer extends DerivationPruningComputer { public TableDerivationPruningComputer(DerivationPruner pruner) { super(pruner); } public static final String lambdaDCSError = "lambdaDCSError"; public static final String emptyDenotation = DefaultDerivationPruningComputer.emptyDenotation; public static final String badSummarizerHead = DefaultDerivationPruningComputer.badSummarizerHead; public static final String sameMark = "sameMark"; public static final String forwardBackward = "forwardBackward"; public static final String doubleNext = "doubleNext"; public static final String doubleCompares = "doubleCompares"; public static final String emptyJoin = "emptyJoin"; public static final String subsetMerge = "subsetMerge"; public static final String typeRowMerge = "typeRowMerge"; public static final String aggregateInfinite = "aggregateInfinite"; public static final String aggregateUncomparable = "aggregateUncomparable"; public static final String aggregateVariable = "aggregateVariable"; public static final String superlativeIdentity = "superlativeIdentity"; @Override public Collection<String> getAllStrategyNames() { return Arrays.asList( lambdaDCSError, emptyDenotation, badSummarizerHead, sameMark, forwardBackward, doubleNext, doubleCompares, emptyJoin, subsetMerge, typeRowMerge, aggregateInfinite, aggregateUncomparable, aggregateVariable, superlativeIdentity); } private static final String NEXT = TableTypeSystem.ROW_NEXT_VALUE.id, PREV = "!" + NEXT; private static final ValueFormula<Value> STAR = new ValueFormula<>(new NameValue("*")); private final Formula TYPE_ROW = Formula.fromString("(fb:type.object.type fb:type.row)"); private final Formula IDENTITY = Formula.fromString("(reverse (lambda x (var x)))"); @Override public String isPrunedGeneral(Derivation deriv) { // lambdaDCSError: Prune unrecoverable LambdaDCSException if (containsStrategy(lambdaDCSError)) { if (deriv.value instanceof ErrorValue && LambdaDCSException.isUnrecoverable(((ErrorValue) deriv.value).type)) { if (DerivationPruner.opts.pruningVerbosity >= 5) LogInfo.logs("lambdaDCSError: %s => %s", deriv.formula, deriv.value); return lambdaDCSError; } } // emptyDenotation: Prune if the denotation is empty (for ScopedValue) if (containsStrategy(emptyDenotation)) { if (deriv.value instanceof PairListValue && ((PairListValue) deriv.value).pairs.isEmpty()) { return emptyDenotation; } if (deriv.value instanceof ScopedValue) { Value head = ((ScopedValue) deriv.value).head, relation = ((ScopedValue) deriv.value).relation; if ((head instanceof ListValue && ((ListValue) head).values.isEmpty()) || (relation instanceof PairListValue && ((PairListValue) relation).pairs.isEmpty())) return emptyDenotation; } } // badSummarizerHead: Prune if the head of a ScopedValue is empty or is a single object if (containsStrategy(badSummarizerHead)) { if (deriv.value instanceof ScopedValue) { Value head = ((ScopedValue) deriv.value).head; if ((head instanceof ListValue) && ((ListValue) head).values.size() == 1) return badSummarizerHead; } } // sameMark: Prune if mark does not filter out anything if (containsStrategy(sameMark)) { if (deriv.formula instanceof MergeFormula) { MergeFormula merge = (MergeFormula) deriv.formula; if (merge.mode == MergeFormula.Mode.and) { Value head = null; if (merge.child1 instanceof MarkFormula) { head = pruner.parser.executor.execute(merge.child2, pruner.ex.context).value; } else if (merge.child2 instanceof MarkFormula) { head = pruner.parser.executor.execute(merge.child1, pruner.ex.context).value; } if (head != null && head.equals(deriv.value)) { return sameMark; } } } } return null; } @Override public String isPrunedRecursive(Derivation deriv, Formula subformula, Map<String, Object> state) { // Prune JoinFormulas if (subformula instanceof JoinFormula) { JoinFormula join1 = (JoinFormula) subformula; if (join1.child instanceof JoinFormula) { JoinFormula join2 = (JoinFormula) join1.child; String rid1 = Formulas.getBinaryId(join1.relation), rid2 = Formulas.getBinaryId(join2.relation); if (rid1 != null && rid2 != null) { // forwardBackward: Prune (!relation (relation (...))) if (containsStrategy(forwardBackward) && (rid1.equals("!" + rid2) || rid2.equals("!" + rid1))) return forwardBackward; // doubleNext: Prune (next (next (...))) if (containsStrategy(doubleNext) && (rid1.equals(NEXT) || rid1.equals(PREV)) && (rid2.equals(NEXT) || rid2.equals(PREV))) return doubleNext; // doubleCompares: Prune (< (> ( ...))) if (containsStrategy(doubleCompares) && CanonicalNames.COMPARATORS.contains(rid1) && CanonicalNames.COMPARATORS.contains(rid2)) return doubleCompares; // emptyJoin: prune if the composition two consecutive joins always produce an empty set if (containsStrategy(emptyJoin)) { Formula test = new JoinFormula(rid1, new JoinFormula(rid2, STAR)); try { Value value = pruner.parser.executor.execute(test, pruner.ex.context).value; if (value instanceof ListValue && ((ListValue) value).values.isEmpty()) return emptyJoin; } catch (RuntimeException e) { // Do nothing. Don't prune the formula. } } } } } // Prune merge formulas else if (subformula instanceof MergeFormula) { MergeFormula merge = (MergeFormula) subformula; Formula child1 = merge.child1, child2 = merge.child2; // subsetMerge: Prune merge formulas where one child is a subset of the other if (containsStrategy(subsetMerge)) { Value d1 = pruner.parser.executor.execute(child1, pruner.ex.context).value; Value d2 = pruner.parser.executor.execute(child2, pruner.ex.context).value; if (d1 instanceof ListValue && d2 instanceof ListValue) { Set<Value> v1 = new HashSet<>(((ListValue) d1).values); Set<Value> v2 = new HashSet<>(((ListValue) d2).values); if ((v1.size() >= v2.size() && v1.containsAll(v2)) || (v2.size() > v1.size() && v2.containsAll(v1))) return subsetMerge; } } // typeRowMerge: Prune merge formulas where one child is (@type @row) [generally redundant] if (containsStrategy(typeRowMerge) && ((TYPE_ROW.equals(merge.child1) && !(merge.child2 instanceof MarkFormula)) || (TYPE_ROW.equals(merge.child2) && !(merge.child1 instanceof MarkFormula)))) return typeRowMerge; } // Prune aggregate formulas else if (subformula instanceof AggregateFormula) { AggregateFormula aggregate = (AggregateFormula) subformula; Formula child = aggregate.child; // aggregateInfinite: Prune aggregates when the child is an infinite set if (containsStrategy(aggregateInfinite) && child instanceof JoinFormula) { String rid = Formulas.getBinaryId(((JoinFormula) child).relation); if (CanonicalNames.COMPARATORS.contains(rid) || "!=".equals(rid)) return aggregateInfinite; } // aggregateUncomparable: Prune aggregates when the child's type is not number or date if (containsStrategy(aggregateUncomparable) && aggregate.mode != AggregateFormula.Mode.count) { SemType type = TypeInference.inferType(child, true); if (!type.meet(SemType.numberOrDateType).isValid() || (!type.meet(SemType.numberType).isValid() && (aggregate.mode == Mode.sum || aggregate.mode == Mode.avg))) return aggregateUncomparable; } // aggregateVariable: Prune aggregates when the child is variable if (containsStrategy(aggregateVariable) && child instanceof VariableFormula) { return aggregateVariable; } } // Prune superlative formulas else if (subformula instanceof SuperlativeFormula) { SuperlativeFormula superlative = (SuperlativeFormula) subformula; Formula relation = superlative.relation; // superlativeIdentity: Prune superlatives when the relation is exactly (lambda x (var x)) if (containsStrategy(superlativeIdentity) && IDENTITY.equals(relation)) { return superlativeIdentity; } } // For ScopedFormula if (!DerivationPruner.opts.recursivePruning && subformula instanceof ScopedFormula) { Formula relation = ((ScopedFormula) subformula).relation; if (relation instanceof LambdaFormula) { relation = ((LambdaFormula) relation).body; String matchedStrategy; for (DerivationPruningComputer computer : pruner.getPruningComputers()) { if ((matchedStrategy = computer.isPrunedRecursive(deriv, relation, new HashMap<>())) != null) { return matchedStrategy; } } } } return null; } }