diff --git a/current_goal.txt b/current_goal.txt index 7ced37e..53a6090 100644 --- a/current_goal.txt +++ b/current_goal.txt @@ -5,14 +5,7 @@ sitting in a tavern by a fireplace -Use arguments from clause parsing to perform queries when answering question - - use subject to find some node - - use interrogative argument to apply filters to the lookup - - -Comprehend the sentence "My name is ${name}" - - Transfer statement eval - +Parse the sentence "I am John" diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml index 89d568d..527501a 100644 --- a/dependency-reduced-pom.xml +++ b/dependency-reduced-pom.xml @@ -30,6 +30,7 @@ 17 + 4.5.6 17 UTF-8 1.0.0-M2 diff --git a/src/main/java/org/studiorailgun/ai/conversation/evaluators/transfer/TransferEval.java b/src/main/java/org/studiorailgun/ai/conversation/evaluators/transfer/TransferEval.java index 9bcf7f9..de9117a 100644 --- a/src/main/java/org/studiorailgun/ai/conversation/evaluators/transfer/TransferEval.java +++ b/src/main/java/org/studiorailgun/ai/conversation/evaluators/transfer/TransferEval.java @@ -12,6 +12,7 @@ import org.studiorailgun.ai.conversation.web.ArgumentQuery; import org.studiorailgun.ai.conversation.web.MetaQuery; import org.studiorailgun.ai.knowledge.Node; import org.studiorailgun.ai.knowledge.cleaning.CollapseRelations; +import org.studiorailgun.ai.linguistics.NameQuery; import edu.stanford.nlp.ling.IndexedWord; import edu.stanford.nlp.semgraph.SemanticGraph; @@ -93,12 +94,12 @@ public class TransferEval { //if there is no node with this name already, create one System.out.println(sentence.getGraph()); - Node subjectNode = ArgumentQuery.getArgument(quote, subjectArg); + Node subjectNode = ArgumentQuery.getArgument(conversation, quote, subjectArg); if(subjectNode == null){ throw new Error("Error querying subject!"); } - Node objectNode = ArgumentQuery.getArgument(quote, objectArg); + Node objectNode = ArgumentQuery.getArgument(conversation, quote, objectArg); if(objectNode == null){ throw new Error("Error querying object!"); } @@ -107,6 +108,11 @@ public class TransferEval { MetaQuery.equate(subjectNode, objectNode); CollapseRelations.collapse(subjectNode); CollapseRelations.collapse(objectNode); + } else if(NameQuery.isName(objectNode)) { + //because this is to-be verb, can explicitly check if the object is a name and assign name relation accordingly + NameQuery.attachName(objectNode, subjectNode); + CollapseRelations.collapse(subjectNode); + CollapseRelations.collapse(objectNode); } else { String message = "Information already stored?\n" + sentence.getGraph(); diff --git a/src/main/java/org/studiorailgun/ai/conversation/parser/PennTreebankTagSet.java b/src/main/java/org/studiorailgun/ai/conversation/parser/PennTreebankTagSet.java index e6fc130..3a856d4 100644 --- a/src/main/java/org/studiorailgun/ai/conversation/parser/PennTreebankTagSet.java +++ b/src/main/java/org/studiorailgun/ai/conversation/parser/PennTreebankTagSet.java @@ -114,4 +114,21 @@ public class PennTreebankTagSet { } } + /** + * Checks if this tag is a pronoun or not + * @param tag The tag + * @return true if it is a pronoun, false otherwise + */ + public static boolean isPronoun(String tag){ + switch(tag){ + case "PRP": + case "PRP$": { + return true; + } + default: { + return false; + } + } + } + } diff --git a/src/main/java/org/studiorailgun/ai/conversation/web/ArgumentQuery.java b/src/main/java/org/studiorailgun/ai/conversation/web/ArgumentQuery.java index 49051bb..2415b1f 100644 --- a/src/main/java/org/studiorailgun/ai/conversation/web/ArgumentQuery.java +++ b/src/main/java/org/studiorailgun/ai/conversation/web/ArgumentQuery.java @@ -6,12 +6,14 @@ import java.util.stream.Collectors; import org.studiorailgun.Globals; import org.studiorailgun.ai.conversation.parser.PennTreebankTagSet; import org.studiorailgun.ai.conversation.parser.depend.Argument; +import org.studiorailgun.ai.conversation.tracking.Conversation; import org.studiorailgun.ai.conversation.tracking.Quote; import org.studiorailgun.ai.knowledge.Node; import org.studiorailgun.ai.knowledge.Relation; import org.studiorailgun.ai.knowledge.query.InstanceQuery; import org.studiorailgun.ai.knowledge.query.PossessionQuery; import org.studiorailgun.ai.knowledge.types.RelationTypes; +import org.studiorailgun.ai.linguistics.PronounQuery; import org.studiorailgun.ai.linguistics.ProperNounQuery; import org.studiorailgun.ai.philosophy.ConceptQuery; @@ -22,11 +24,12 @@ public class ArgumentQuery { /** * Gets the node representing a linguistic argument + * @param conversation The conversation the quote is in context of * @param quote The quote the argument is originating from -- used for some lookups (ie possession) * @param argument The argument * @return The node */ - public static Node getArgument(Quote quote, Argument argument){ + public static Node getArgument(Conversation conversation, Quote quote, Argument argument){ String argumentRoot = argument.getRoot().originalText(); String argumentTag = argument.getRoot().tag(); @@ -40,8 +43,10 @@ public class ArgumentQuery { if(argument.getPossessiveModifier() != null){ rootNode = ArgumentQuery.getPossessionModified(quote, rootNode, argument); } - } else if(PennTreebankTagSet.isProperNoun(argumentTag)) { + } else if(PennTreebankTagSet.isProperNoun(argumentTag)){ rootNode = ProperNounQuery.getProperNoun(argumentRoot); + } else if(PennTreebankTagSet.isPronoun(argumentTag)){ + rootNode = PronounQuery.getPronoun(conversation, quote, argumentRoot); } else { throw new Error("Undefined case! " + argumentTag); } diff --git a/src/main/java/org/studiorailgun/ai/linguistics/NameCollapse.java b/src/main/java/org/studiorailgun/ai/linguistics/NameCollapse.java index 5e5af74..f642912 100644 --- a/src/main/java/org/studiorailgun/ai/linguistics/NameCollapse.java +++ b/src/main/java/org/studiorailgun/ai/linguistics/NameCollapse.java @@ -53,29 +53,32 @@ public class NameCollapse { * @param node The node */ private static void attemptPossessionSubstitution(Node node){ - Node nameConcept = ConceptQuery.getConcept("name"); - if(nameConcept == null){ - throw new Error("Name concept undefined!"); - } - boolean shouldAttemptPossessionSubstitution = false; + boolean equalToName = false; + boolean isPossession = false; for(Relation childRelation : Globals.web.getRelationsOfChildNode(node)){ switch(childRelation.getName()){ - case RelationTypes.INSTANCE_OF: { - /** - * Collapse names - * person(inst) <--possessionOf-- name(inst) --equals--> "John" - * collapses to - * person(inst) <-- nameOf -- "John" - */ - if(childRelation.getParent().equals(nameConcept)){ - shouldAttemptPossessionSubstitution = true; + case RelationTypes.EQUALS: { + if(NameQuery.isName(childRelation.getParent())){ + equalToName = true; } } break; + case RelationTypes.POSSESSION_OF: { + isPossession = true; + } break; default: { } break; } } + /** + * Collapse names + * person(inst) <--possessionOf-- name(inst) --equals--> "John" + * collapses to + * person(inst) <-- nameOf -- "John" + */ + if(equalToName && isPossession){ + shouldAttemptPossessionSubstitution = true; + } if(shouldAttemptPossessionSubstitution){ Node properNoun = null; Node possessor = null; diff --git a/src/main/java/org/studiorailgun/ai/linguistics/NameQuery.java b/src/main/java/org/studiorailgun/ai/linguistics/NameQuery.java index 761ee7c..09962b2 100644 --- a/src/main/java/org/studiorailgun/ai/linguistics/NameQuery.java +++ b/src/main/java/org/studiorailgun/ai/linguistics/NameQuery.java @@ -7,6 +7,7 @@ import org.studiorailgun.Globals; import org.studiorailgun.ai.knowledge.Node; import org.studiorailgun.ai.knowledge.Relation; import org.studiorailgun.ai.knowledge.types.RelationTypes; +import org.studiorailgun.ai.philosophy.ConceptQuery; /** * Queries related to the linguistic concept of a name @@ -30,4 +31,20 @@ public class NameQuery { Globals.web.createRelation(RelationTypes.NAME_OF, properNoun, child); } + /** + * Checks if this node is a name or not + * @param node The node + * @return true if it is a name, false otherwise + */ + public static boolean isName(Node node){ + Node nameConcept = ConceptQuery.getConcept("name"); + List childRelations = Globals.web.getRelationsOfChildNode(node); + List nameSearch = childRelations.stream() + .filter(relation -> relation.getName().equals(RelationTypes.INSTANCE_OF)) + .map(relation -> relation.getParent()) + .filter(instParent -> instParent.equals(nameConcept)) + .collect(Collectors.toList()); + return nameSearch.size() > 0; + } + } diff --git a/src/main/java/org/studiorailgun/ai/linguistics/PronounQuery.java b/src/main/java/org/studiorailgun/ai/linguistics/PronounQuery.java new file mode 100644 index 0000000..2475cdb --- /dev/null +++ b/src/main/java/org/studiorailgun/ai/linguistics/PronounQuery.java @@ -0,0 +1,33 @@ +package org.studiorailgun.ai.linguistics; + +import org.studiorailgun.ai.conversation.tracking.Conversation; +import org.studiorailgun.ai.conversation.tracking.Quote; +import org.studiorailgun.ai.conversation.web.QuoteQuery; +import org.studiorailgun.ai.knowledge.Node; + +/** + * Queries related to pronouns + */ +public class PronounQuery { + + /** + * Tries to locate a pronoun + * @param conversation The conversation + * @param quote The quote + * @param pronounText The pronoun text itself + * @return The node representing the pronoun + */ + public static Node getPronoun(Conversation conversation, Quote quote, String pronounText){ + Node rVal = null; + switch(pronounText){ + case "I": { + rVal = QuoteQuery.getSpeaker(quote.getNode()); + } break; + default: { + throw new Error("Unhandled pronoun type " + pronounText); + } + } + return rVal; + } + +} diff --git a/src/main/java/org/studiorailgun/ai/linguistics/ProperNounQuery.java b/src/main/java/org/studiorailgun/ai/linguistics/ProperNounQuery.java index 4f5a366..a14eace 100644 --- a/src/main/java/org/studiorailgun/ai/linguistics/ProperNounQuery.java +++ b/src/main/java/org/studiorailgun/ai/linguistics/ProperNounQuery.java @@ -5,6 +5,8 @@ import java.util.stream.Collectors; import org.studiorailgun.Globals; import org.studiorailgun.ai.knowledge.Node; +import org.studiorailgun.ai.knowledge.query.InstanceQuery; +import org.studiorailgun.ai.philosophy.ConceptQuery; /** * Queries related to proper nouns @@ -22,6 +24,9 @@ public class ProperNounQuery { if(nodes.size() < 1){ //create a name node to represent this name rVal = Globals.web.createNode(properNoun); + //make this an instance of a name (it is a proper noun, by definition it is a name) + Node nameConcept = ConceptQuery.getConcept("name"); + InstanceQuery.setInstance(nameConcept, rVal); } else if(nodes.size() == 1){ rVal = nodes.get(0); } else { diff --git a/src/test/java/org/studiorailgun/transfer/TransferEvaluationTests.java b/src/test/java/org/studiorailgun/transfer/TransferEvaluationTests.java index b1dd372..6a7336c 100644 --- a/src/test/java/org/studiorailgun/transfer/TransferEvaluationTests.java +++ b/src/test/java/org/studiorailgun/transfer/TransferEvaluationTests.java @@ -34,4 +34,20 @@ public class TransferEvaluationTests { assertEquals(names.get(0).getName(),"John"); } + @Test + public void testEval2(){ + Globals.init("./data/webs/test/web.json"); + Quote input = new Quote("I am John."); + NLPParser.parse(input); + + Globals.conversation.addQuote(ConversationQuery.getOtherParticipant(), input); + + TransferEval.evaluate(Globals.conversation, input, input.getSentences().get(0)); + + //search for the name being assigned in the knowledge web + Node otherParticipant = ConversationQuery.getOtherParticipant(); + List names = NameQuery.getNames(otherParticipant); + assertEquals(names.get(0).getName(),"John"); + } + } diff --git a/src/test/java/org/studiorailgun/transfer/TransferParsingTests.java b/src/test/java/org/studiorailgun/transfer/TransferParsingTests.java index 3443f0e..e8e741d 100644 --- a/src/test/java/org/studiorailgun/transfer/TransferParsingTests.java +++ b/src/test/java/org/studiorailgun/transfer/TransferParsingTests.java @@ -29,4 +29,18 @@ public class TransferParsingTests { assertEquals(mainClause.getSubject().getPossessiveModifier().originalText(), "My"); } + @Test + public void testParse2(){ + Globals.init("./data/webs/test/web.json"); + Quote input = new Quote("I am John."); + NLPParser.parse(input); + Sentence sentence = input.getSentences().get(0); + Clause mainClause = sentence.getMainClause(); + + //has a copular verb + assertEquals(mainClause.getPredicate().getRoot().originalText(),"John"); + assertEquals(mainClause.getPredicate().getCopular().originalText(),"am"); + assertEquals(mainClause.getSubject().getRoot().originalText(), "I"); + } + }