package edu.stanford.nlp.sempre.test; import edu.stanford.nlp.sempre.*; import fig.basic.LogInfo; import org.testng.annotations.Test; import java.util.HashMap; import java.util.Map; import static org.testng.AssertJUnit.assertEquals; /** * Test parsers. * * @author Roy Frostig * @author Percy Liang */ public class ParserTest { // Collects a grammar, and some input/output test pairs public abstract static class ParseTest { public Grammar grammar; ParseTest(Grammar g) { this.grammar = g; } public Parser.Spec getParserSpec() { Executor executor = new JavaExecutor(); FeatureExtractor extractor = new FeatureExtractor(executor); FeatureExtractor.opts.featureDomains.add("rule"); ValueEvaluator valueEvaluator = new ExactValueEvaluator(); return new Parser.Spec(grammar, extractor, executor, valueEvaluator); } public abstract void test(Parser parser); } private static void checkNumDerivations(Parser parser, Params params, String utterance, String targetValue, int numExpected) { Parser.opts.verbose = 5; Example ex = TestUtils.makeSimpleExample(utterance, targetValue != null ? Value.fromString(targetValue) : null); ParserState state = parser.parse(params, ex, targetValue != null); // Debug information for (Derivation deriv : state.predDerivations) { LogInfo.dbg(deriv.getAllFeatureVector()); LogInfo.dbg(params.getWeights()); LogInfo.dbgs("Score %f", deriv.computeScore(params)); } // parser.extractor.extractLocal(); assertEquals(numExpected, ex.getPredDerivations().size()); if (numExpected > 0 && targetValue != null) assertEquals(targetValue, ex.getPredDerivations().get(0).value.toString()); } private static void checkNumDerivations(Parser parser, String utterance, String targetValue, int numExpected) { checkNumDerivations(parser, new Params(), utterance, targetValue, numExpected); } static ParseTest ABCTest() { return new ParseTest(TestUtils.makeAbcGrammar()) { @Override public void test(Parser parser) { checkNumDerivations(parser, "a +", null, 0); checkNumDerivations(parser, "a", "(string a)", 1); checkNumDerivations(parser, "a b", "(string a,b)", 1); checkNumDerivations(parser, "a b c", "(string a,b,c)", 2); checkNumDerivations(parser, "a b c a b c", "(string a,b,c,a,b,c)", 42); } }; } static ParseTest ArithmeticTest() { return new ParseTest(TestUtils.makeArithmeticGrammar()) { @Override public void test(Parser parser) { checkNumDerivations(parser, "1 + ", null, 0); checkNumDerivations(parser, "1 plus 2", "(number 3)", 1); checkNumDerivations(parser, "2 times 3", "(number 6)", 1); checkNumDerivations(parser, "1 plus times 3", null, 0); checkNumDerivations(parser, "times", null, 0); } }; }; // Create parsers @Test public void checkBeamNumDerivationsForABCGrammar() { Parser.opts.coarsePrune = false; ParseTest p; p = ABCTest(); p.test(new BeamParser(p.getParserSpec())); p = ArithmeticTest(); p.test(new BeamParser(p.getParserSpec())); } @Test public void checkCoarseBeamNumDerivations() { Parser.opts.coarsePrune = true; ParseTest p; p = ABCTest(); p.test(new BeamParser(p.getParserSpec())); p = ArithmeticTest(); p.test(new BeamParser(p.getParserSpec())); } @Test(groups = "reinforcement") public void checkReinforcementNumDerivations() { ParseTest p; p = ABCTest(); p.test(new ReinforcementParser(p.getParserSpec())); p = ArithmeticTest(); p.test(new ReinforcementParser(p.getParserSpec())); // TODO(chaganty): test more thoroughly } @Test public void checkFloatingNumDerivations() { // Make it behave like the BeamParser FloatingParser.opts.defaultIsFloating = false; ParseTest p; p = ABCTest(); p.test(new FloatingParser(p.getParserSpec())); p = ArithmeticTest(); p.test(new FloatingParser(p.getParserSpec())); // If floating, should get more hypotheses FloatingParser.opts.defaultIsFloating = true; Parser parser = new FloatingParser(ABCTest().getParserSpec()); FloatingParser.opts.maxDepth = 2; checkNumDerivations(parser, "ignore", null, 3); FloatingParser.opts.maxDepth = 3; checkNumDerivations(parser, "ignore", null, 3 + 3 * 3); } // TODO(chaganty): verify that things are ranked appropriately public void checkRankingArithmetic(Parser parser) { Params params = new Params(); Map<String, Double> features = new HashMap<>(); features.put("rule :: $Operator -> and (ConstantFn (lambda y (lambda x (call + (var x) (var y)))))", 1.0); features.put("rule :: $Operator -> and (ConstantFn (lambda y (lambda x (call * (var x) (var y)))))", -1.0); params.update(features); checkNumDerivations(parser, params, "2 and 3", "(number 5)", 2); params = new Params(); features.put("rule :: $Operator -> and (ConstantFn (lambda y (lambda x (call + (var x) (var y)))))", -1.0); features.put("rule :: $Operator -> and (ConstantFn (lambda y (lambda x (call * (var x) (var y)))))", 1.0); params.update(features); checkNumDerivations(parser, params, "2 and 3", "(number 6)", 2); } @Test void checkRankingSimple() { checkRankingArithmetic(new BeamParser(ArithmeticTest().getParserSpec())); } @Test void checkRankingReinforcement() { checkRankingArithmetic(new ReinforcementParser(ArithmeticTest().getParserSpec())); } @Test void checkRankingFloating() { FloatingParser.opts.defaultIsFloating = false; checkRankingArithmetic(new FloatingParser(ArithmeticTest().getParserSpec())); } // TODO(chaganty): verify the parser gradients }