package edu.stanford.nlp.sempre;
import java.util.*;
import java.util.regex.Pattern;
import fig.basic.LogInfo;
import fig.basic.Option;
import fig.basic.Pair;
/**
* A parser that mixes the derivation lists from other parsers.
*
* @author ppasupat
*/
public class MixParser extends Parser {
public static class Options {
@Option(gloss = "verbosity")
public int verbose = 1;
/**
* Syntax: [className]:[options]
* - className also includes the package name (BeamParser, FloatingParser, tables.dpd.DPDParser, ...)
* - options is a comma-separated list of [group] or [group]-[iter1]-[iter2]-...
* [iter1], [iter2], ... are iteration indices (0-based), "xc" (computing expected counts),
* "nxc" (not computing expected counts), index + "xc", or index + "nxc"
*
* Example: Using
* -MixParser.parsers FloatingParser tables.serialize.SerializedParser:train-0-2-3,dev
* will cause derivations from SerializedParser to be mixed in during all dev iterations
* and training iterations 0, 2, and 3.
*/
@Option(gloss = "list of parsers to use along with options")
public List<String> parsers = new ArrayList<>();
}
public static Options opts = new Options();
final List<Pair<Parser, MixParserOption>> parsers;
int iter, numIters;
String group;
public MixParser(Spec spec) {
super(spec);
parsers = new ArrayList<>();
for (String parserAndOptions : opts.parsers) {
if (opts.verbose >= 1)
LogInfo.logs("Adding parser %s", parserAndOptions);
String[] tokens = parserAndOptions.split(":");
if (tokens.length > 2)
throw new RuntimeException("Invalid parser options: " + parserAndOptions);
String parserName = tokens[0];
Parser parser;
try {
Class<?> parserClass = Class.forName(SempreUtils.resolveClassName(parserName));
parser = ((Parser) parserClass.getConstructor(spec.getClass()).newInstance(spec));
} catch (ClassNotFoundException e1) {
throw new RuntimeException("Illegal parser: " + parserName);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Error while instantiating parser: " + parserName + "\n" + e);
}
if (tokens.length > 1)
parsers.add(new Pair<>(parser, new MixParserOption(this, tokens[1])));
else
parsers.add(new Pair<>(parser, new MixParserOption(this)));
}
}
// Don't do it.
@Override protected void computeCatUnaryRules() {
catUnaryRules = Collections.emptyList();
};
@Override
public ParserState newParserState(Params params, Example ex, boolean computeExpectedCounts) {
return new MixParserState(this, params, ex, computeExpectedCounts);
}
@Override
public void onBeginDataGroup(int iter, int numIters, String group) {
this.iter = iter;
this.numIters = numIters;
this.group = group;
}
}
class MixParserOption {
private final MixParser mixParser;
private boolean allowedAll = false;
private List<Pair<String, String>> allowedGroupsAndIter = new ArrayList<>();
public MixParserOption(MixParser mixParser) {
this.mixParser = mixParser;
// Allow in all groups
allowedAll = true;
}
public MixParserOption(MixParser mixParser, String optionString) {
this.mixParser = mixParser;
String[] tokens = optionString.split(",");
for (String option : tokens) {
String[] subtokens = option.split("-");
if (subtokens.length == 1)
allowedGroupsAndIter.add(new Pair<>(subtokens[0], "all"));
else
for (int i = 1; i < subtokens.length; i++) {
if (!Pattern.matches("^([0-9]*(n?xc)?)$", subtokens[i]))
throw new RuntimeException("Invalid iteration options: " + subtokens[i]);
allowedGroupsAndIter.add(new Pair<>(subtokens[0], subtokens[i]));
}
}
}
public boolean isAllowed(boolean computeExpectedCounts) {
if (allowedAll) return true;
String xcFlag = computeExpectedCounts ? "xc" : "nxc";
return allowedGroupsAndIter.contains(new Pair<>(mixParser.group, "all"))
|| allowedGroupsAndIter.contains(new Pair<>(mixParser.group, "" + mixParser.iter))
|| allowedGroupsAndIter.contains(new Pair<>(mixParser.group, xcFlag))
|| allowedGroupsAndIter.contains(new Pair<>(mixParser.group, "" + mixParser.iter + xcFlag));
}
}
class MixParserState extends ParserState {
public MixParserState(Parser parser, Params params, Example ex, boolean computeExpectedCounts) {
super(parser, params, ex, computeExpectedCounts);
}
@Override
public void infer() {
for (Pair<Parser, MixParserOption> pair : ((MixParser) parser).parsers) {
if (!pair.getSecond().isAllowed(computeExpectedCounts)) {
if (MixParser.opts.verbose >= 1)
LogInfo.logs("Skipping %s", pair.getFirst().getClass().getSimpleName());
continue;
}
if (MixParser.opts.verbose >= 1)
LogInfo.begin_track("Using %s", pair.getFirst().getClass().getSimpleName());
ParserState parserState = pair.getFirst().newParserState(params, ex, false);
parserState.infer();
predDerivations.addAll(parserState.predDerivations);
if (MixParser.opts.verbose >= 1) {
LogInfo.logs("Number of derivations: %d", parserState.predDerivations.size());
LogInfo.end_track();
}
}
ensureExecuted();
if (computeExpectedCounts) {
expectedCounts = new HashMap<>();
ParserState.computeExpectedCounts(predDerivations, expectedCounts);
}
}
}