package edu.stanford.nlp.sempre.tables.alter; import java.io.*; import java.util.*; import edu.stanford.nlp.sempre.Derivation; import edu.stanford.nlp.sempre.Value; import edu.stanford.nlp.sempre.Values; import edu.stanford.nlp.sempre.tables.InfiniteListValue; import fig.basic.MapUtils; public class DenotationData { public final int numAlteredTables, numDerivs; private final List<Value> uniqueDenotations = new ArrayList<>(); private final Map<Value, Integer> uniqueDenotationsToId = new HashMap<>(); // derivation -> denotations of that derivation on altered tables private final List<List<Integer>> denotations; // denotations of the annotated formula on altered tables private List<Integer> annotatedDenotations; // denotations on altered tables -> indices of formulas that give those denotations private Map<List<Integer>, List<Integer>> groups; // representative derivation of each group private List<Integer> representativeDerivs; public DenotationData(int numAlteredTables, int numDerivs) { this.numAlteredTables = numAlteredTables; this.numDerivs = numDerivs; denotations = new ArrayList<>(numDerivs); for (int k = 0; k < numDerivs; k++) { List<Integer> denotationsForDeriv = new ArrayList<>(numAlteredTables + 1); for (int j = 0; j < numAlteredTables + 1; j++) denotationsForDeriv.add(null); denotations.add(denotationsForDeriv); } } private int lookup(Value value) { Integer id = uniqueDenotationsToId.get(value); if (id == null) { id = uniqueDenotations.size(); uniqueDenotations.add(value); uniqueDenotationsToId.put(value, id); } return id; } public void addDenotation(int derivIndex, int tableIndex, Value value) { denotations.get(derivIndex).set(tableIndex, lookup(value)); } public Value getDenotation(int derivIndex, int tableIndex) { return uniqueDenotations.get(denotations.get(derivIndex).get(tableIndex)); } public List<Value> getDenotations(int derivIndex) { List<Value> answer = new ArrayList<>(numAlteredTables); for (int x : denotations.get(derivIndex)) answer.add(uniqueDenotations.get(x)); return answer; } public int[][] toArray(List<Integer> derivs) { int[][] answer = new int[derivs.size()][numAlteredTables + 1]; for (int i = 0; i < derivs.size(); i++) { List<Integer> derivDenotationIndices = denotations.get(derivs.get(i)); for (int j = 0; j <= numAlteredTables; j++) answer[i][j] = derivDenotationIndices.get(j); } return answer; } public void addAnnotatedDenotation(int tableIndex, Value value) { if (annotatedDenotations == null) { annotatedDenotations = new ArrayList<>(numAlteredTables + 1); for (int j = 0; j < numAlteredTables + 1; j++) annotatedDenotations.add(null); } annotatedDenotations.set(tableIndex, lookup(value)); } public Value getAnnotatedDenotation(int tableIndex) { return uniqueDenotations.get(annotatedDenotations.get(tableIndex)); } public List<Value> getAnnotatedDenotations() { List<Value> answer = new ArrayList<>(numAlteredTables); for (int x : annotatedDenotations) answer.add(uniqueDenotations.get(x)); return answer; } public boolean isAnnotated() { return annotatedDenotations != null; } public void computeGroups(List<Derivation> derivs) { groups = groupByDenotation(denotations); // Get the representative derivation of each group // Choose the smallest formula representativeDerivs = new ArrayList<>(); for (List<Integer> equivClass : groups.values()) { int bestIndex = 0, bestScore = Integer.MIN_VALUE; for (int index : equivClass) { Derivation deriv = derivs.get(index); int score = -index; try { if (deriv.canonicalUtterance.startsWith("$ROOT:")) score = 100 - Integer.parseInt(deriv.canonicalUtterance.substring("$ROOT:".length())); } catch (NumberFormatException e) { } if (score > bestScore) { bestScore = score; bestIndex = index; } } representativeDerivs.add(bestIndex); } } /** * Helper method: * Group formulas that execute to the same denotation (or tuple of denotations). * * Return a map from denotations to lists of formula indices. */ public static <T> Map<T, List<Integer>> groupByDenotation(List<T> denotations) { Map<T, List<Integer>> groups = new HashMap<>(); for (int i = 0; i < denotations.size(); i++) MapUtils.addToList(groups, denotations.get(i), i); return groups; } public int numClasses() { if (groups == null) throw new RuntimeException("Must call computeGroups(derivs) first"); return groups.size(); } public List<Integer> getRepresentativeIndices() { if (groups == null) throw new RuntimeException("Must call computeGroups(derivs) first"); return representativeDerivs; } public List<Integer> getEquivClass(int representativeIndex) { if (groups == null) throw new RuntimeException("Must call computeGroups(derivs) first"); return groups.get(denotations.get(representativeIndex)); } // ============================================================ // Serialization // ============================================================ public void dump(PrintWriter out) { // # derivations, # altered tables, # unique denotations out.println("" + numDerivs + " " + numAlteredTables + " " + uniqueDenotations.size()); for (Value denotation : uniqueDenotations) out.println(denotation); for (List<Integer> derivDenotationIndices : denotations) { StringBuilder sb = new StringBuilder(); for (Integer derivDenotationIndex : derivDenotationIndices) sb.append(derivDenotationIndex == null ? -1 : derivDenotationIndex).append(" "); out.println(sb.toString().trim()); } } public void dumpAnnotated(PrintWriter out) { for (Integer annotatedDenotationIndex : annotatedDenotations) out.println(annotatedDenotationIndex == null ? null : uniqueDenotations.get(annotatedDenotationIndex)); } public static DenotationData load(BufferedReader in) { try { String line = in.readLine(); String[] tokens = line.split(" "); if (tokens.length != 3) throw new RuntimeException("Expected 3 tokens; got " + tokens.length); int numDerivs = Integer.parseInt(tokens[0]), numAlteredTables = Integer.parseInt(tokens[1]), numUniqueDenotations = Integer.parseInt(tokens[2]); DenotationData denotationData = new DenotationData(numAlteredTables, numDerivs); for (int i = 0; i < numUniqueDenotations; i++) { line = in.readLine(); Value value; if ("ERROR".equals(line)) { value = ValueCanonicalizer.ERROR; } else if ("null".equals(line)) { value = null; } else { try { value = Values.fromString(line); } catch (Exception e) { // Probably InfiniteValue value = new InfiniteListValue(line); } } denotationData.uniqueDenotations.add(value); denotationData.uniqueDenotationsToId.put(value, i); } for (int i = 0; i < numDerivs; i++) { tokens = in.readLine().split(" "); if (tokens.length != numAlteredTables + 1) throw new RuntimeException("Expected " + (numAlteredTables + 1) + " tokens; got " + tokens.length); for (int j = 0; j <= numAlteredTables; j++) { denotationData.denotations.get(i).set(j, Integer.parseInt(tokens[j])); } } return denotationData; } catch (Exception e) { throw new RuntimeException(e); } } }