package edu.stanford.nlp.sempre.tables.features; import java.util.*; import edu.stanford.nlp.sempre.*; import edu.stanford.nlp.sempre.tables.*; import fig.basic.*; /** * Represents a predicate in the formula. * * Also contains additional information such as type and original string. * * @author ppasupat */ public class PredicateInfo { public static class Options { @Option(gloss = "Allow repreated predicates") public boolean allowRepeats = false; @Option(gloss = "Maximum length of predicate string") public int maxPredicateLength = 40; } public static Options opts = new Options(); static enum PredicateType { KEYWORD, ENTITY, BINARY }; public final String predicate; public final String originalString; public final PredicateType type; public PredicateInfo(String predicate, ContextValue context) { this.predicate = predicate; this.type = inferType(predicate); String s = getOriginalString(predicate, context); this.originalString = (s == null) ? null : s.toLowerCase(); } public static PredicateType inferType(String predicate) { if (predicate.charAt(0) == '!') predicate = predicate.substring(1); if (predicate.startsWith(CanonicalNames.PREFIX)) { if (CanonicalNames.isUnary(predicate)) { return PredicateType.ENTITY; } else if (CanonicalNames.isBinary(predicate)) { return PredicateType.BINARY; } else { throw new RuntimeException("Unrecognized predicate: " + predicate); } } else { return PredicateType.KEYWORD; } } @Override public String toString() { return predicate + "(" + originalString + ")"; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || !(o instanceof PredicateInfo)) return false; return predicate.equals(((PredicateInfo) o).predicate); } @Override public int hashCode() { return predicate.hashCode(); } // ============================================================ // Get original strings and lemmas // ============================================================ // Lemma cache private static final Map<String, String> lemmaCache = new HashMap<>(); // Helper function: get lemma form public static synchronized String getLemma(String s) { if (s == null || s.trim().isEmpty()) return null; String lemma = lemmaCache.get(s); if (lemma == null) { LanguageInfo langInfo = LanguageAnalyzer.getSingleton().analyze(s); lemma = (langInfo.numTokens() == 0) ? "" : langInfo.lemmaPhrase(0, langInfo.numTokens()); lemmaCache.put(s, lemma); } return lemma; } // Helper function: get original string from the table public static String getOriginalString(String predicate, Example ex) { return getOriginalString(predicate, ex.context); } //Helper function: get original string from the table public static String getOriginalString(String predicate, ContextValue context) { if (context == null || context.graph == null || !(context.graph instanceof TableKnowledgeGraph)) return null; return getOriginalString(predicate, (TableKnowledgeGraph) context.graph); } // Helper function: get original string from the table public static String getOriginalString(String predicate, TableKnowledgeGraph graph) { String s = graph.getOriginalString(predicate); s = getLemma(s); if (s != null && s.trim().isEmpty()) s = null; return s; } // ============================================================ // Get the list of all PredicateInfos // ============================================================ public static List<PredicateInfo> getPredicateInfos(Example ex, Derivation deriv) { Collection<PredicateInfo> predicates; Formula formula = deriv.formula; FormulaTraverser traverser = new FormulaTraverser(ex); traverser.traverse(formula); predicates = traverser.predicates; List<PredicateInfo> answer = new ArrayList<>(); for (PredicateInfo p : predicates) { if (p.originalString == null || p.originalString.length() <= opts.maxPredicateLength) answer.add(p); } return answer; } private static class FormulaTraverser { public final Collection<PredicateInfo> predicates; private final ContextValue context; public FormulaTraverser(Example ex) { this.predicates = opts.allowRepeats ? new ArrayList<>() : new HashSet<>(); this.context = ex.context; } public void traverse(Formula formula) { if (formula instanceof ValueFormula) { Value value = ((ValueFormula<?>) formula).value; if (value instanceof NumberValue) { NumberValue number = (NumberValue) value; predicates.add(new PredicateInfo("number", context)); predicates.add(new PredicateInfo(Fmt.D(number.value), context)); } else if (value instanceof DateValue) { DateValue date = (DateValue) value; predicates.add(new PredicateInfo("date", context)); // Use prefixes to distinguish from numbers predicates.add(new PredicateInfo("y:" + Fmt.D(date.year), context)); predicates.add(new PredicateInfo("m:" + Fmt.D(date.month), context)); predicates.add(new PredicateInfo("d:" + Fmt.D(date.day), context)); } else if (value instanceof StringValue) { StringValue string = (StringValue) value; predicates.add(new PredicateInfo("string", context)); predicates.add(new PredicateInfo(string.value, context)); } else if (value instanceof NameValue) { NameValue name = (NameValue) value; String id = name.id; predicates.add(new PredicateInfo(id, context)); } } else if (formula instanceof JoinFormula) { JoinFormula join = (JoinFormula) formula; traverse(join.relation); traverse(join.child); } else if (formula instanceof ReverseFormula) { ReverseFormula reverse = (ReverseFormula) formula; if (reverse.child instanceof ValueFormula<?> && ((ValueFormula<?>) reverse.child).value instanceof NameValue) { String id = ((NameValue) ((ValueFormula<?>) reverse.child).value).id; id = id.startsWith("!") ? id.substring(1) : ("!" + id); traverse(new ValueFormula<>(new NameValue(id))); } else { predicates.add(new PredicateInfo("reverse", context)); traverse(reverse.child); } } else if (formula instanceof MergeFormula) { MergeFormula merge = (MergeFormula) formula; predicates.add(new PredicateInfo(merge.mode.toString(), context)); traverse(merge.child1); traverse(merge.child2); } else if (formula instanceof AggregateFormula) { AggregateFormula aggregate = (AggregateFormula) formula; predicates.add(new PredicateInfo(aggregate.mode.toString(), context)); traverse(aggregate.child); } else if (formula instanceof SuperlativeFormula) { SuperlativeFormula superlative = (SuperlativeFormula) formula; predicates.add(new PredicateInfo(superlative.mode.toString(), context)); // Skip the "(number 1) (number 1)" part traverse(superlative.head); traverse(superlative.relation); } else if (formula instanceof ArithmeticFormula) { ArithmeticFormula arithmetic = (ArithmeticFormula) formula; predicates.add(new PredicateInfo(arithmetic.mode.toString(), context)); traverse(arithmetic.child1); traverse(arithmetic.child2); } else if (formula instanceof VariableFormula) { // Skip variables } else if (formula instanceof MarkFormula) { MarkFormula mark = (MarkFormula) formula; predicates.add(new PredicateInfo("mark", context)); // Skip variable traverse(mark.body); } else if (formula instanceof LambdaFormula) { LambdaFormula lambda = (LambdaFormula) formula; predicates.add(new PredicateInfo("lambda", context)); // Skip variable traverse(lambda.body); } else if (formula instanceof ScopedFormula) { ScopedFormula scoped = (ScopedFormula) formula; traverse(scoped.head); Formula relation = scoped.relation; if (relation instanceof LambdaFormula) relation = ((LambdaFormula) relation).body; traverse(relation); } else { throw new RuntimeException("[PredicateInfo] Cannot handle formula " + formula); } } } }