package edu.stanford.nlp.sempre.tables; import java.io.File; import java.util.*; import java.util.concurrent.ExecutionException; import com.google.common.cache.*; import edu.stanford.nlp.sempre.*; import edu.stanford.nlp.sempre.tables.serialize.LazyLoadedExampleList; import edu.stanford.nlp.sempre.tables.serialize.SerializedDataset; import edu.stanford.nlp.sempre.tables.test.CustomExample; import edu.stanford.nlp.sempre.tables.test.TableFormulaCanonicalizer; import edu.stanford.nlp.sempre.tables.test.CustomExample.ExampleProcessor; import fig.basic.*; /** * Evaluate if the predicted formula matches one of the annotated formulas. * * Use example ID to look up the formula annotation. * * The annotationPath option can point to one of the following: * - an annotated LispTree file with targetFormula / alternativeFormula fields * (formula shorthands are allowed) * - a gzip dump file created by tables.serialize.SerializedDumper * - a directory containing gzip dump files created by tables.serialize.SerializedDumper * * @author ppasupat */ public class TableFormulaEvaluator extends TableValueEvaluator { public static class Options { @Option(gloss = "verbosity") public int verbose = 0; @Option(gloss = "Path for formula annotation") public String annotationPath = null; @Option(gloss = "Whether to fall back to ValueEvaluator when the example id does not exist") public boolean fallBackToValueEvaluator = true; } public static Options opts = new Options(); protected Collection<String> availableIds; // Map from ID string to target formulas. // Use only when the file is an annotated LispTree file. protected Map<String, List<Formula>> idToTargetFormulas; // Map from ID string to LazyLoadedExampleList and example index. // Use only when the file is dumped from SerializedDumper protected Map<String, Pair<LazyLoadedExampleList, Integer>> idToSerializedIndex; public TableFormulaEvaluator() { // Load annotation file if (opts.annotationPath == null || opts.annotationPath.isEmpty()) throw new RuntimeException("Annotation file not specified."); // Determine file type if (new File(opts.annotationPath).isDirectory() || opts.annotationPath.endsWith(".gz")) { readSerializedFile(); } else { readAnnotationFile(); } } protected void readSerializedFile() { idToSerializedIndex = new HashMap<>(); SerializedDataset dataset = new SerializedDataset(); if (new File(opts.annotationPath).isDirectory()) { dataset.readDir(opts.annotationPath); } else { dataset.read("annotated", opts.annotationPath); } for (String group : dataset.groups()) { LazyLoadedExampleList examples = dataset.examples(group); List<String> ids = examples.getAllIds(); for (int i = 0; i < ids.size(); i++) idToSerializedIndex.put(ids.get(i), new Pair<>(examples, i)); } availableIds = idToSerializedIndex.keySet(); } protected void readAnnotationFile() { LogInfo.begin_track("Reading annotated examples"); idToTargetFormulas = new HashMap<>(); CustomExample.getDataset(Arrays.asList(new Pair<>("annotated", opts.annotationPath)), new ExampleProcessor() { @Override public void run(CustomExample ex) { List<Formula> targetFormulas = new ArrayList<>(); // Canonicalize formulas if (ex.targetFormula != null) targetFormulas.add(TableFormulaCanonicalizer.canonicalizeFormula(ex.targetFormula)); if (ex.alternativeFormulas != null) { for (Formula alternativeFormula : ex.alternativeFormulas) targetFormulas.add(TableFormulaCanonicalizer.canonicalizeFormula(alternativeFormula)); } idToTargetFormulas.put(ex.id, targetFormulas); } }); availableIds = idToTargetFormulas.keySet(); LogInfo.end_track(); } /** * Get formula compatibility. Fall back to ValueEvaluator if the example * is not in the annotation file. */ public double getCompatibility(Example targetEx, Derivation deriv) { if (availableIds.contains(targetEx.id)) { if (idToTargetFormulas == null) return getCompatibilitySerializedStrict(targetEx, deriv.formula); else return getCompatibilityAnnotationStrict(targetEx, deriv.formula); } else { return opts.fallBackToValueEvaluator ? getCompatibility(targetEx.targetValue, deriv.value) : 0; } } // Add a little cache LoadingCache<Example, List<Formula>> canonicalizedCache = CacheBuilder.newBuilder() .maximumSize(20) .build( new CacheLoader<Example, List<Formula>> () { @Override public List<Formula> load(Example ex) throws Exception { LogInfo.logs("Canonicalizing %s", ex.id); List<Formula> canonicalized = new ArrayList<>(); Pair<LazyLoadedExampleList, Integer> identifier = idToSerializedIndex.get(ex.id); Example annotated = identifier.getFirst().get(identifier.getSecond()); for (Derivation targetDeriv : annotated.predDerivations) { canonicalized.add(TableFormulaCanonicalizer.canonicalizeFormula(targetDeriv.formula)); } LogInfo.logs("Canonicalized %d formulas", canonicalized.size()); return canonicalized; } } ); public double getCompatibilitySerializedStrict(Example targetEx, Formula formula) { try { List<Formula> canonicalized = canonicalizedCache.get(targetEx); formula = TableFormulaCanonicalizer.canonicalizeFormula(formula); for (Formula targetFormula : canonicalized) if (targetFormula.equals(formula)) return 1; return 0; } catch (ExecutionException e) { throw new RuntimeException(e.getCause()); } } public double getCompatibilityAnnotationStrict(Example targetEx, Formula formula) { List<Formula> targetFormulas = idToTargetFormulas.get(targetEx.id); if (targetFormulas == null) throw new RuntimeException("Example ID " + targetEx.id + " not found in annotated data"); formula = TableFormulaCanonicalizer.canonicalizeFormula(formula); for (Formula targetFormula : targetFormulas) if (targetFormula.equals(formula)) return 1; return 0; } public boolean containsId(String id) { return availableIds.contains(id); } // ============================================================ // DEBUG // ============================================================ public void log(Example targetEx, Formula formula) { List<Formula> targetFormulas = idToTargetFormulas.get(targetEx.id); if (targetFormulas.isEmpty()) LogInfo.logs("Gold: NONE"); else for (Formula targetFormula : targetFormulas) LogInfo.logs("Gold: %s", targetFormula); formula = TableFormulaCanonicalizer.canonicalizeFormula(formula); LogInfo.logs("Predicted: %s", formula); } }