grammar induction test

This commit is contained in:
Sida Wang 2017-01-07 17:46:07 -08:00
parent 7a10a749dc
commit 3d68330bbe
7 changed files with 180 additions and 49 deletions

1
run
View File

@ -1034,6 +1034,7 @@ o('Learner.maxTrainIters', 1),
o('server', true),
o('Server.numThreads', 8),
o('Server.maxCandidates', 50),
o('Derivation.showTypes', false),
nil) })

View File

@ -101,7 +101,7 @@
####### domain specific floating rules
# red means has color red
(rule $Sets ($Color) (lambda c (color (var c))))
# (rule $Sets ($Color) (lambda c (color (var c))))
# various actions can be performed
# (rule $Action ($Color) (lambda c (: @add (var c) here)))

View File

@ -89,7 +89,7 @@
# Floating rules
#############################
(rule $Action ($Numbers $Actions) (lambda n (lambda a (:loop (var n) (var a)))) (floating 1)) # do add red top 3 times
# (rule $Action ($Numbers $Actions) (lambda n (lambda a (:loop (var n) (var a)))) (floating 1)) # do add red top 3 times
# (rule $Action ($Sets $Actions) (lambda s (lambda a (:foreach (var s) (var a)))) (floating 1))
# (rule $Action ($Actions $Actions) (lambda a1 (lambda a2 (:s (var a1) (var a2)))) (floating 1)) # add red top then remove them
@ -99,12 +99,12 @@
# (rule $ValueSets ($Rel $Sets) (lambda r (lambda s
# ((reverse (var r)) (var s)))) (floating 1))
(rule $Action ($Sets) (lambda s (: @select (var s))) (floating 1))
# (rule $Action ($Sets) (lambda s (: @select (var s))) (floating 1))
# (rule $Set ($Rel $ValueSets) (lambda r (lambda s
# ((var r) (var s)))) (floating 1))
# (rule $Action ($Rel $ValueSets) (lambda r (lambda v (: @update (var r) (var v)))) (floating 1))
# (rule $Action (nothing) (ConstantFn (: @remove)) (floating 1))
(rule $FROOT ($Action) (IdentityFn) (floating 1))
(rule $FROOT ($Actions) (IdentityFn) (floating 1))
# (rule $FROOT ($Action) (IdentityFn) (floating 1))
# (rule $FROOT ($Actions) (IdentityFn) (floating 1))

View File

@ -38,6 +38,8 @@ public class Derivation implements SemanticFn.Callable, HasScore {
public boolean showRules = false;
@Option(gloss = "When printing derivations, to show canonical utterance")
public boolean showUtterance = false;
@Option(gloss = "When printing derivations, show the category")
public boolean showCat = false;
@Option(gloss = "When executing, show formulae (for debugging)")
public boolean showExecutions = false;
@Option(gloss = "changes ScoredDerivationComparator to use pragmatic_score instead")
@ -347,6 +349,9 @@ public class Derivation implements SemanticFn.Callable, HasScore {
if (opts.showUtterance && canonicalUtterance != null) {
tree.addChild(LispTree.proto.newList("canonicalUtterance", canonicalUtterance));
}
if (opts.showCat && cat != null) {
tree.addChild(LispTree.proto.newList("cat", cat));
}
return tree;
}

View File

@ -135,6 +135,13 @@ public final class InteractiveUtils {
return new Rule("$Action", Lists.newArrayList("$Action", "$Action"), b);
}
public static Derivation combine(List<Derivation> children, ActionFormula.Mode mode) {
// stop double blocking
if (children.size() == 1) {
ActionFormula.Mode cmode = ((ActionFormula)children.get(0).formula).mode;
if (cmode == mode) {
return children.get(0);
}
}
Formula f = new ActionFormula(mode,
children.stream().map(d -> d.formula).collect(Collectors.toList()));
Derivation res = new Derivation.Builder()

View File

@ -8,7 +8,10 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.testng.collections.Sets;
import com.beust.jcommander.internal.Lists;
import com.google.common.base.Function;
import edu.stanford.nlp.sempre.ConstantFn;
import edu.stanford.nlp.sempre.Derivation;
@ -57,23 +60,28 @@ public class GrammarInducer {
id = origEx.id;
head = origEx;
head.predDerivations = Lists.newArrayList(def);
LogInfo.logs("chartList.size = %d", chartList.size());
tokens = origEx.getTokens();
int numTokens = origEx.numTokens();
addMatches(def, makeChartMap(chartList));
List<Derivation> bestPacking = bestPackingDP(this.matches, numTokens);
LogInfo.logs("BestPacking: %s", bestPacking);
HashMap<String, String> formulaToCat = new HashMap<>();
bestPacking.forEach(d -> formulaToCat.put(catFormulaKey(d), varName(d)));
LogInfo.logs("chartList.size = %d", chartList.size());
LogInfo.logs("BestPacking: %s", bestPacking);
LogInfo.logs("Matches: %s", this.matches);
LogInfo.logs("formulaToCat: %s", formulaToCat);
buildFormula(def, formulaToCat);
def.grammarInfo.start = 0;
def.grammarInfo.end = tokens.size();
inducedRules = new ArrayList<>(induceRules(bestPacking, def));
}
private Map<String, List<Derivation>> makeChartMap(List<Derivation> chartList) {
@ -90,16 +98,18 @@ public class GrammarInducer {
// this is used to test for matches, same cat, same formula
// maybe cat needs to be more flexible
private String catFormulaKey(Derivation d) {
// return d.formula.toString();
return getNormalCat(d) + "::" + d.formula.toString();
}
private String varName(Derivation anchored) {
return getNormalCat(anchored) + anchored.start + "_" + anchored.end;
}
private String getNormalCat(Derivation def) {
return def.getCat();
// if (cat.endsWith("s"))
// return cat.substring(0, cat.length()-1);
// else return cat;
// return def.cat;
String cat = def.getCat();
if (cat.endsWith("s"))
return cat.substring(0, cat.length()-1);
else return cat;
}
//label the derivation tree with what it matches in chartList
@ -176,7 +186,7 @@ public class GrammarInducer {
}
private List<Rule> induceRules(List<Derivation> packings, Derivation defDeriv) {
LogInfo.dbgs("packings: %s", packings);
List<Rule> inducedRules = new ArrayList<>();
List<String> RHS = getRHS(defDeriv, packings);
SemanticFn sem = getSemantics(defDeriv, packings);
@ -195,9 +205,9 @@ public class GrammarInducer {
// populate grammarInfo.formula, replacing everything that can be replaced
private void buildFormula(Derivation deriv, Map<String, String> replaceMap){
//LogInfo.log("building " + deriv.grammarInfo.formula);
if (deriv.grammarInfo.formula != null) return;
// LogInfo.logs("BUILDING %s at (%d,%d) %s", deriv, deriv.start, deriv.end, catFormulaKey(deriv));
if (replaceMap.containsKey(catFormulaKey(deriv))) {
// LogInfo.logs("Found match %s, %s, %s", catFormulaKey(deriv), replaceMap, deriv);
deriv.grammarInfo.formula = new VariableFormula(replaceMap.get(catFormulaKey(deriv)));
return;
}
@ -219,7 +229,10 @@ public class GrammarInducer {
for (Derivation arg : args) {
if (!(f instanceof LambdaFormula))
throw new RuntimeException("Expected LambdaFormula, but got " + f);
f = Formulas.lambdaApply((LambdaFormula)f, arg.grammarInfo.formula);
Formula after = renameBoundVars(f, new HashSet<>());
// LogInfo.logs("renameBoundVar %s === %s", after, f);
f = Formulas.lambdaApply((LambdaFormula)after, arg.grammarInfo.formula);
}
deriv.grammarInfo.formula = f;
} else if (rule.sem instanceof IdentityFn) {
@ -230,10 +243,33 @@ public class GrammarInducer {
} else {
deriv.grammarInfo.formula = deriv.formula;
}
//LogInfo.log("built " + deriv.grammarInfo.formula);
// LogInfo.logs("BUILT %s for %s", deriv.grammarInfo.formula, deriv.formula);
// LogInfo.log("built " + deriv.grammarInfo.formula);
}
private String newName(String s) {return s.endsWith("_")? s : s + "_";}
private Formula renameBoundVars(Formula formula, Set<String> boundvars) {
if (formula instanceof LambdaFormula) {
LambdaFormula f = (LambdaFormula)formula;
boundvars.add(f.var);
return new LambdaFormula(newName(f.var), renameBoundVars(f.body, boundvars));
} else {
Formula after = formula.map(
new Function<Formula, Formula>() {
public Formula apply(Formula formula) {
if (formula instanceof VariableFormula) { // Replace variable
String name = ((VariableFormula) formula).name;
if (boundvars.contains(name)) return new VariableFormula(newName(name));
else return formula;
}
return null;
}
});
return after;
}
}
private SemanticFn getSemantics(final Derivation def, List<Derivation> packings) {
Formula baseFormula = def.grammarInfo.formula;
if (packings.size() == 0) {
@ -260,7 +296,7 @@ public class GrammarInducer {
List<String> rhs = new ArrayList<>(tokens);
for (Derivation deriv : packings) {
// LogInfo.logs("got (%d,%d):%s:%s", deriv.start, deriv.end, deriv.formula, deriv.cat);
rhs.set(deriv.start, deriv.cat);
rhs.set(deriv.start, getNormalCat(deriv));
for (int i = deriv.start + 1; i < deriv.end; i++) {
rhs.set(i, null);
}

View File

@ -22,14 +22,19 @@ import org.testng.collections.Lists;
* @author Sida Wang
*/
public class GrammarInducerTest {
static Assertion A = new Assertion();
// static Assertion A = new SoftAssert();
Assertion hard = new Assertion();
Assertion soft = new SoftAssert();
private static Spec defaultSpec() {
FloatingParser.opts.defaultIsFloating = true;
ActionExecutor.opts.convertNumberValues = true;
ActionExecutor.opts.printStackTrace = true;
ActionExecutor.opts.FlatWorldType = "BlocksWorld";
Derivation.opts.showTypes = false;
Derivation.opts.showRules = false;
Derivation.opts.showCat = true;
LanguageAnalyzer.opts.languageAnalyzer = "interactive.actions.ActionLanguageAnalyzer";
Grammar.opts.inPaths = Lists.newArrayList("./shrdlurn/blocksworld.grammar");
Grammar.opts.useApplyFn = "interactive.ApplyFn";
@ -68,7 +73,7 @@ public class GrammarInducerTest {
induced.forEach(r -> InteractiveUtils.addRuleInteractive(r, parser));
LogInfo.logs("Defining %s := %s, added %s", head, def, induced);
}
public boolean found(String head, String def) {
public boolean match(String head, String def) {
Example.Builder b = new Example.Builder();
b.setUtterance(head);
Example exHead = b.createExample();
@ -89,6 +94,10 @@ public class GrammarInducerTest {
ind++;
}
printAllRules();
if (!found) {
LogInfo.logs("Did not find %s", defDeriv.formula);
LogInfo.log(exHead.predDerivations);
}
return found;
}
@ -102,56 +111,129 @@ public class GrammarInducerTest {
@Test public void simpleTest() {
LogInfo.begin_track("test simple substitutions");
ParseTester T = new ParseTester();
Assertion A = hard;
T.def("add red twice", d("add red top","add red top"));
A.assertTrue(T.found("add blue twice", d("add blue top","add blue top")));
A.assertTrue(T.match("add blue twice", d("add blue top","add blue top")));
T.def("add red 3 times", d("repeat 3 [add red top]"));
A.assertTrue(T.found("add yellow 5 times", d("repeat 5 [add yellow top]")));
T.def("add red 3 times", d("repeat 3 [add red]"));
A.assertTrue(T.match("add yellow 5 times", d("repeat 5 [add yellow]")));
T.def("add red 3 ^ 2 times", d("repeat 3 {[repeat 3 [add red]]}"));
A.assertTrue(T.match("add blue 2 ^ 2 times", d("repeat 2 {[repeat 2 [add blue]]}")));
T.def("add a red block to the left", d("add red left"));
A.assertTrue(T.found("add a yellow block to the right", d("add yellow right")));
A.assertTrue(T.match("add a yellow block to the right", d("add yellow right")));
T.def("remove the leftmost red block", d("remove very left of has color red"));
A.assertTrue(T.found("remove the leftmost yellow block", d("remove very left of has color yellow")));
A.assertTrue(T.match("remove the leftmost yellow block", d("remove very left of has color yellow")));
T.def("add red to both sides", d("add red left", "add red right"));
T.def("add red to both sides", d("add red back", "add red front"));
A.assertTrue(T.found("add green to both sides", d("add green left", "add green right")));
A.assertTrue(T.found("add green to both sides", d("add green back", "add green front")));
A.assertFalse(T.found("add green to both sides", d("add green left", "add green back")));
A.assertTrue(T.match("add green to both sides", d("add green left", "add green right")));
A.assertTrue(T.match("add green to both sides", d("add green back", "add green front")));
A.assertFalse(T.match("add green to both sides", d("add green left", "add green back")));
T.def("select all but yellow", d("select not has color yellow"));
A.assertTrue(T.found("select all but yellow", d("select not has color yellow")));
A.assertTrue(T.match("select all but yellow", d("select not has color yellow")));
T.def("add a yellow block on top of red blocks", d("select has color red", "add yellow top"));
A.assertTrue(T.found("add a green block on top of blue blocks", d("select has color blue", "add green top")));
A.assertTrue(T.match("add a green block on top of blue blocks", d("select has color blue", "add green top")));
T.def("add a yellow block on top of red blocks", d("select has color red; add yellow top"));
A.assertTrue(T.found("add a green block on top of blue blocks", d("select has color blue ; add green top")));
A.assertTrue(T.match("add a green block on top of blue blocks", d("select has color blue ; add green top")));
//T.printAllRules();
//A.assertAll();
LogInfo.end_track();
}
//
// @Test public void basicTest() {
// LogInfo.begin_track("test Grammar");
// induce("add red twice","[[\"add red top\",\"(: add red top)\"],[\"add red top\",\"(: add red top)\"]]");
// induce("add red thrice", "[[\"[add red top] 3 times\",\"(:loop (number 3) (: add red top))\"]]");
// induce("add a red block to the left", "[[\"add red left\",\"(: add red left)\"]]");
// induce("remove card", "[[\"remove has color red\",\"(:foreach (color red) (: remove))\"]]");
// induce("delete card", "[[\"remove has color red\",\"(:foreach (color red) (: remove))\"]]");
// induce("remove the leftmost red block", "[[\"remove very left of has color red\",\"?\"]]");
// induce("add red to both sides", "[[\"add red left\",\"(: add red left)\"],[\"add red right\",\"(: add red right)\"]]");
// induce("add a yellow block next to brown",
// "[[\"select brown\",\"(:foreach (color brown) (: select))\"],[\"add yellow right\",\"(: add yellow right)\"]]");
// induce("select all but yellow",
// "[[\"select not has color yellow\",\"?\"]]");
// LogInfo.end_track();
// }
//
@Test public void actionTest() {
LogInfo.begin_track("test some action substitutions");
ParseTester T = new ParseTester();
Assertion A = soft;
T.def("add red twice", d("repeat 2 [add red]"));
A.assertTrue(T.match("add blue top twice", d("repeat 2 [add blue top]")));
T.def("add red 3 times", d("repeat 3 [add red]"));
A.assertTrue(T.match("add yellow top 5 times", d("repeat 5 [add yellow top]")));
T.def("add red twice", d("add red; add red top"));
A.assertTrue(T.match("add yellow twice", d("add yellow; add yellow top")));
T.def("add red then add yellow left", d("add red; add yellow left"));
A.assertTrue(T.match("remove red then add brown top", d("remove red; add brown top")));
//T.printAllRules();
//A.assertAll();
LogInfo.end_track();
}
@Test public void learnCatTest() {
LogInfo.begin_track("test the learning via alignment");
ParseTester T = new ParseTester();
Assertion A = soft;
T.def("add cardinal", d("add red"));
A.assertTrue(T.match("select has color cardinal", d("select has color red")));
T.def("move up", d("move top"));
A.assertTrue(T.match("select up", d("select top")));
A.assertTrue(T.match("add red up", d("add red top")));
T.def("select the highest of has color red", d("select very top of has color red"));
A.assertTrue(T.match("remove the highest of all", d("remove very top of all")));
T.def("select the top most of has color red", d("select very top of has color red"));
A.assertTrue(T.match("select the bot most of all", d("select the very bot most of all")));
//T.printAllRules();
//A.assertAll();
LogInfo.end_track();
}
@Test public void cubeTest() {
LogInfo.begin_track("cubeTest");
ParseTester T = new ParseTester();
Assertion A = hard;
T.def("red stick of size 3", d("{repeat 3 [add red; select top]}"));
T.def("red plate of size 3", d("{repeat 3 [red stick of size 3; select left]}"));
T.def("red cube of size 3", d("{repeat 3 [red plate of size 3; select back]}"));
A.assertTrue(T.match("blue stick of size 4", d("{repeat 4 [add blue; select top]}")));
A.assertTrue(T.match("blue plate of size 4", d("{repeat 4 [blue stick of size 4; select left]}")));
A.assertTrue(T.match("blue cube of size 5", d("{repeat 5 [blue plate of size 5; select back]}")));
//T.printAllRules();
//A.assertAll();
LogInfo.end_track();
}
@Test public void rectTest() {
LogInfo.begin_track("rectTest");
ParseTester T = new ParseTester();
Assertion A = hard;
T.def("red stick of size 4", d("{repeat 4 [add red; select top]}"));
T.def("red plate of size 3 by 4", d("{repeat 3 [red stick of size 4; select left]}"));
T.def("red cube of size 2 by 3 by 4", d("{repeat 2 [red plate of size 3 by 4; select back]}"));
A.assertTrue(T.match("red plate of size 1 by 2", d("{repeat 1 [red stick of size 2; select left]}")));
A.assertTrue(T.match("red cube of size 1 by 2 by 3", d("{repeat 1 [red plate of size 2 by 3; select back]}")));
//T.printAllRules();
//A.assertAll();
LogInfo.end_track();
}
// @Test public void learnSets() {
// LogInfo.begin_track("test Grammar");
// induce("remove those red blocks", "[[\"remove has color red\",\"(:foreach (color red) (: remove))\"]]");