package edu.stanford.nlp.sempre.tables.serialize;
import java.io.*;
import java.util.*;
import edu.stanford.nlp.sempre.*;
import edu.stanford.nlp.sempre.tables.TableKnowledgeGraph;
import edu.stanford.nlp.sempre.tables.test.CustomExample;
import fig.basic.*;
import fig.exec.*;
/**
* Dump examples and parsed derivations.
*
* This class can be run on its own, in which case the parser in Builder.parser will be used.
* Or it can be supplied the examples to dump in a streaming fashion.
*
* Syntax of the dumped files:
* - Filename: dumped-[prefix]-[numbering].gz
* - Content:
* - First line: (metadata (size [number_of_examples]))
* - Each example begins with a comment (########## Example [example_id] ##########),
* followed by an example LispTree with the following fields:
* id, utterance, targetFormula, targetValue, context,
* tokens, lemmaTokens, posTags, nerTags, nerValues,
* derivations (one derivation per line)
*
* @author ppasupat
*/
public class SerializedDumper implements Runnable {
public static class Options {
@Option(gloss = "Verbosity") public int verbosity = 0;
@Option(gloss = "Randomly shuffle dumped derivations")
public Random shuffleDerivsRandom = new Random(9);
@Option(gloss = "Skip if the table has more than this number of rows")
public int maxNumRowsToDump = 200;
@Option(gloss = "Number of examples per gzip file (0 = single file)")
public int numExamplesPerFile = 0;
@Option(gloss = "Custom dump file prefixes to use in standalone mode")
public String dumpedFilePrefix = "";
}
public static Options opts = new Options();
String prefix;
String filename;
PrintWriter out;
int numExamples = -1, currentIndex = 0;
public SerializedDumper(String prefix, int numExamples) {
reset(prefix, numExamples);
}
public void reset(String prefix, int numExamples) {
closeFile();
this.currentIndex = 0;
this.prefix = prefix;
this.numExamples = numExamples;
}
public void openFile(String filenameSuffix) {
if (out != null) closeFile();
filename = Execution.getFile("dumped-" + filenameSuffix + ".gz");
LogInfo.logs("Opening %s", filename);
if (new File(filename).exists())
LogInfo.warnings("File %s exists; will overwrite!", filename);
out = IOUtils.openOutHard(filename);
}
public void closeFile() {
if (out != null) {
out.close();
LogInfo.logs("Finished dumping to %s", filename);
out = null;
}
}
public void dumpExample(Example ex) {
dumpExample(ex, ex.predDerivations);
}
public void dumpExample(Example ex, List<Derivation> derivations) {
if (numExamples < 0)
throw new RuntimeException("numExamples must be specified via reset(group, numExamples)");
if (currentIndex >= numExamples)
throw new RuntimeException("current example index exceeds numExamples");
if (opts.numExamplesPerFile == 0) {
if (currentIndex == 0) {
openFile(String.format("%s-%06d", prefix, 0));
writeMetadataLispTree(numExamples);
}
} else {
if (currentIndex % opts.numExamplesPerFile == 0) {
openFile(String.format("%s-%06d", prefix, currentIndex));
writeMetadataLispTree(Math.min(opts.numExamplesPerFile, numExamples - currentIndex));
}
}
out.printf("########## Example %s ##########\n", ex.id);
writeExampleLispTree(exampleToLispTree(ex, derivations));
out.flush();
currentIndex++;
if (currentIndex == numExamples || (opts.numExamplesPerFile > 0 && currentIndex % opts.numExamplesPerFile == 0))
closeFile();
}
// ============================================================
// Stand-alone mode
// ============================================================
private SerializedDumper() { }
public static void main(String[] args) {
Execution.run(args, "SerializedDumperMain", new SerializedDumper(), Master.getOptionsParser());
}
@Override
public void run() {
Builder builder = new Builder();
builder.build();
Dataset dataset = new Dataset();
dataset.read();
if (dataset.groups().size() > 1 && !opts.dumpedFilePrefix.isEmpty()) {
LogInfo.warnings("Cannot use dumpedFilePrefix with more than one group; fall back to group names.");
opts.dumpedFilePrefix = "";
}
for (String group : dataset.groups()) {
reset(opts.dumpedFilePrefix.isEmpty() ? group : opts.dumpedFilePrefix, dataset.examples(group).size());
processExamples(dataset.examples(group), builder);
StopWatchSet.logStats();
}
}
private void processExamples(List<Example> examples, Builder builder) {
Evaluation evaluation = new Evaluation();
if (examples.isEmpty()) return;
final String logPrefix = "iter=0." + prefix;
Execution.putOutput("group", logPrefix);
LogInfo.begin_track_printAll("Processing %s: %s examples", logPrefix, examples.size());
LogInfo.begin_track("Examples");
for (int e = 0; e < examples.size(); e++) {
if (!CustomExample.checkFilterExamples(e)) continue;
Example ex = examples.get(e);
LogInfo.begin_track_printAll("%s: example %s/%s: %s", logPrefix, e, examples.size(), ex.id);
ex.log();
Execution.putOutput("example", e);
StopWatchSet.begin("Parser.parse");
if (((TableKnowledgeGraph) ex.context.graph).numRows() > opts.maxNumRowsToDump) {
LogInfo.logs("SKIPPING Example %s (number of rows = %d > %d)",
ex.id, ((TableKnowledgeGraph) ex.context.graph).numRows(), opts.maxNumRowsToDump);
new DummyParserState(builder.parser, builder.params, ex, false);
} else {
builder.parser.parse(builder.params, ex, false);
}
StopWatchSet.end();
dumpExample(ex);
LogInfo.logs("Current: %s", ex.evaluation.summary());
evaluation.add(ex.evaluation);
LogInfo.logs("Cumulative(%s): %s", logPrefix, evaluation.summary());
LogInfo.end_track();
// Save memory
if (ex.predDerivations != null) {
ex.predDerivations.clear();
System.gc();
}
}
LogInfo.end_track();
LogInfo.logs("Stats for %s: %s", logPrefix, evaluation.summary());
evaluation.logStats(logPrefix);
evaluation.putOutput(logPrefix);
LogInfo.end_track();
}
public static class DummyParserState extends ParserState {
public DummyParserState(Parser parser, Params params, Example ex, boolean computeExpectedCounts) {
super(parser, params, ex, computeExpectedCounts);
ex.predDerivations = new ArrayList<>();
ex.evaluation = new Evaluation();
}
@Override public void infer() { } // Unused.
}
// ============================================================
// Conversion to LispTree
// ============================================================
private LispTree exampleToLispTree(Example ex, List<Derivation> preds) {
LispTree tree = LispTree.proto.newList();
tree.addChild("example");
// Basic information
if (ex.id != null)
tree.addChild(LispTree.proto.newList("id", ex.id));
if (ex.utterance != null)
tree.addChild(LispTree.proto.newList("utterance", ex.utterance));
if (ex.targetFormula != null)
tree.addChild(LispTree.proto.newList("targetFormula", ex.targetFormula.toLispTree()));
if (ex.targetValue != null)
tree.addChild(LispTree.proto.newList("targetValue", ex.targetValue.toLispTree()));
if (ex.context != null)
tree.addChild(ex.context.toLispTree());
// Language info
if (ex.languageInfo != null) {
if (ex.languageInfo.tokens != null)
tree.addChild(LispTree.proto.newList("tokens", LispTree.proto.newList(ex.languageInfo.tokens)));
if (ex.languageInfo.lemmaTokens != null)
tree.addChild(LispTree.proto.newList("lemmaTokens", LispTree.proto.newList(ex.languageInfo.lemmaTokens)));
if (ex.languageInfo.posTags != null)
tree.addChild(LispTree.proto.newList("posTags", LispTree.proto.newList(ex.languageInfo.posTags)));
if (ex.languageInfo.nerTags != null)
tree.addChild(LispTree.proto.newList("nerTags", LispTree.proto.newList(ex.languageInfo.nerTags)));
if (ex.languageInfo.nerValues != null)
tree.addChild(LispTree.proto.newList("nerValues", LispTree.proto.newList(ex.languageInfo.nerValues)));
}
// Derivations
List<LispTree> derivations = new ArrayList<>();
for (int i = 0; i < preds.size(); i++) {
Derivation deriv = preds.get(i);
if (!isPruned(deriv)) {
derivations.add(deriv.toLispTree());
}
}
Collections.shuffle(derivations, opts.shuffleDerivsRandom);
LispTree derivationsTree = LispTree.proto.newList();
derivationsTree.addChild("derivations");
for (LispTree derivation : derivations)
derivationsTree.addChild(derivation);
tree.addChild(derivationsTree);
return tree;
}
// Decide whether we should skip the derivation
private boolean isPruned(Derivation derivation) {
if (!(derivation.value instanceof ListValue)) return true;
ListValue list = (ListValue) derivation.value;
// Check if there is at least one answer
if (list.values.isEmpty()) return true;
return false;
}
// ============================================================
// Writing LispTree to file
// ============================================================
private void writeMetadataLispTree(int size) {
LispTree tree = LispTree.proto.newList();
tree.addChild("metadata");
tree.addChild(LispTree.proto.newList("size", "" + size));
tree.print(out);
out.println();
out.flush();
}
private void writeExampleLispTree(LispTree tree) {
out.println("(example");
for (LispTree subtree : tree.children.subList(1, tree.children.size())) {
if (!subtree.isLeaf() && "derivations".equals(subtree.children.get(0).value)) {
if (subtree.children.size() == 1) {
out.println(" (derivations)");
} else {
out.println(" (derivations");
for (LispTree derivation : subtree.children.subList(1, subtree.children.size())) {
out.write(" ");
derivation.print(Integer.MAX_VALUE, Integer.MAX_VALUE, out);
out.write("\n");
}
out.println(" )");
}
} else {
out.write(" ");
subtree.print(Integer.MAX_VALUE, Integer.MAX_VALUE, out);
out.write("\n");
}
}
out.println(")");
out.flush();
}
}