diff --git a/data/webs/philosophy/knowledge/knowledge.json b/data/webs/philosophy/knowledge/knowledge.json index 7a09d6b..e692ac6 100644 --- a/data/webs/philosophy/knowledge/knowledge.json +++ b/data/webs/philosophy/knowledge/knowledge.json @@ -18,6 +18,7 @@ } }, "dependencies" : [ - "./data/webs/philosophy/knowledge/ambiguity.json" + "./data/webs/philosophy/knowledge/ambiguity.json", + "./data/webs/philosophy/knowledge/learnable.json" ] } \ No newline at end of file diff --git a/data/webs/philosophy/knowledge/learnable.json b/data/webs/philosophy/knowledge/learnable.json new file mode 100644 index 0000000..23b9da7 --- /dev/null +++ b/data/webs/philosophy/knowledge/learnable.json @@ -0,0 +1,13 @@ +{ + "tag" : "learnable", + "nodes" : { + "0" : { + "comment" : "Used for flagging a node as being possible to learn", + "id" : 0, + "name" : "learnable" + } + }, + "anchors": { + "learnable" : 0 + } +} \ No newline at end of file diff --git a/src/main/java/org/studiorailgun/conversation/evaluators/EvaluationTree.java b/src/main/java/org/studiorailgun/conversation/evaluators/EvaluationTree.java index 1438372..b00e990 100644 --- a/src/main/java/org/studiorailgun/conversation/evaluators/EvaluationTree.java +++ b/src/main/java/org/studiorailgun/conversation/evaluators/EvaluationTree.java @@ -10,6 +10,7 @@ import org.studiorailgun.conversation.parser.NLPParser; import org.studiorailgun.conversation.tracking.Conversation; import org.studiorailgun.conversation.tracking.Quote; import org.studiorailgun.conversation.tracking.Sentence; +import org.studiorailgun.conversation.web.ConversationQuery; /** * Evaluates a sentence based on data about the sentence @@ -30,8 +31,8 @@ public class EvaluationTree { //perform NLP evaluation of the quote NLPParser.parse(quote); - //add the quote to the conversation - conversation.addQuote(quote); + //add the evaluated quote to the conversation + conversation.addQuote(ConversationQuery.getOtherParticipant(), quote); //evaluate each sentence for(Sentence sentence : quote.getSentences()){ @@ -73,6 +74,9 @@ public class EvaluationTree { } } + //add the synthesized quote to the conversation + conversation.addQuote(Globals.web.getAnchors().getSelfNode(), response); + return response; } diff --git a/src/main/java/org/studiorailgun/conversation/evaluators/transfer/Interrogative.java b/src/main/java/org/studiorailgun/conversation/evaluators/transfer/Interrogative.java index 233961f..092fc44 100644 --- a/src/main/java/org/studiorailgun/conversation/evaluators/transfer/Interrogative.java +++ b/src/main/java/org/studiorailgun/conversation/evaluators/transfer/Interrogative.java @@ -14,6 +14,7 @@ import org.studiorailgun.knowledge.query.InstanceQuery; import org.studiorailgun.knowledge.query.QualityQuery; import org.studiorailgun.knowledge.query.filter.InstanceQueryFilter; import org.studiorailgun.knowledge.query.filter.PossessionQueryFilter; +import org.studiorailgun.philosophy.ConceptQuery; /** * Interrogatives available @@ -32,7 +33,7 @@ public class Interrogative { //get the thing that has the quality if(QualityQuery.isQualityClarificationQuery(interrogative)){ - Node finalQualifier = InstanceQuery.getConcept(items.getIndexedWord().originalText()); + Node finalQualifier = ConceptQuery.getConcept(items.getIndexedWord().originalText()); if(items.getPossessive() != null){ List qualifierInstances = InstanceQuery.getInstances(finalQualifier); finalQualifier = PossessionQueryFilter.withPossessor(qualifierInstances, Globals.web.getAnchors().getSelfNode()); @@ -66,7 +67,7 @@ public class Interrogative { if(QualityQuery.isQualityClarificationQuery(interrogative)){ //get the thing that has the quality - Node finalQualifier = InstanceQuery.getConcept(items.getIndexedWord().originalText()); + Node finalQualifier = ConceptQuery.getConcept(items.getIndexedWord().originalText()); if(items.getPossessive() != null){ List qualifierInstances = InstanceQuery.getInstances(finalQualifier); finalQualifier = PossessionQueryFilter.withPossessor(qualifierInstances, Globals.web.getAnchors().getSelfNode()); diff --git a/src/main/java/org/studiorailgun/conversation/evaluators/transfer/TransferEval.java b/src/main/java/org/studiorailgun/conversation/evaluators/transfer/TransferEval.java index 21b6512..b5772ae 100644 --- a/src/main/java/org/studiorailgun/conversation/evaluators/transfer/TransferEval.java +++ b/src/main/java/org/studiorailgun/conversation/evaluators/transfer/TransferEval.java @@ -4,9 +4,12 @@ import org.studiorailgun.Globals; import org.studiorailgun.conversation.evaluators.query.NounStack; import org.studiorailgun.conversation.parser.NLPDependencies; import org.studiorailgun.conversation.parser.PennTreebankTagSet; +import org.studiorailgun.conversation.parser.depend.Clause; import org.studiorailgun.conversation.tracking.Conversation; import org.studiorailgun.conversation.tracking.Quote; import org.studiorailgun.conversation.tracking.Sentence; +import org.studiorailgun.conversation.web.ArgumentQuery; +import org.studiorailgun.knowledge.Node; import org.studiorailgun.knowledge.query.NodePropQuery; import edu.stanford.nlp.ling.IndexedWord; @@ -88,11 +91,18 @@ public class TransferEval { private static void evaluateAssignmentStatement(Conversation conversation, Quote quote, Sentence sentence, NounStack noun1, NounStack noun2){ //if there is no node with this name already, create one System.out.println(sentence.getGraph()); + Clause mainClause = sentence.getMainClause(); String noun1RootText = noun1.getIndexedWord().originalText(); if(NodePropQuery.byName(noun1RootText).size() < 1){ Globals.web.createNode(noun1RootText); } + Node subject = ArgumentQuery.getArgument(quote, mainClause.getSubject()); + // Node object = ArgumentQuery.getArgument(quote, mainClause.getDirectObject()); + //make an equivalence link here + + //for the moment, assume the other noun is a concept + } /** diff --git a/src/main/java/org/studiorailgun/conversation/synthesis/NounStackSynthesizer.java b/src/main/java/org/studiorailgun/conversation/synthesis/NounStackSynthesizer.java index c1cf030..ae4126c 100644 --- a/src/main/java/org/studiorailgun/conversation/synthesis/NounStackSynthesizer.java +++ b/src/main/java/org/studiorailgun/conversation/synthesis/NounStackSynthesizer.java @@ -2,8 +2,8 @@ package org.studiorailgun.conversation.synthesis; import org.studiorailgun.Globals; import org.studiorailgun.knowledge.Node; -import org.studiorailgun.knowledge.query.InstanceQuery; import org.studiorailgun.knowledge.query.PossessionQuery; +import org.studiorailgun.philosophy.ConceptQuery; /** * Synthesizes text for a noun stack @@ -19,7 +19,7 @@ public class NounStackSynthesizer { String rVal = ""; - Node mostSpecificConcept = InstanceQuery.getMostSpecificConcept(node); + Node mostSpecificConcept = ConceptQuery.getMostSpecificConcept(node); rVal = mostSpecificConcept.getName(); if(possessive){ diff --git a/src/main/java/org/studiorailgun/conversation/tracking/Conversation.java b/src/main/java/org/studiorailgun/conversation/tracking/Conversation.java index 902c6eb..6e51996 100644 --- a/src/main/java/org/studiorailgun/conversation/tracking/Conversation.java +++ b/src/main/java/org/studiorailgun/conversation/tracking/Conversation.java @@ -3,8 +3,10 @@ package org.studiorailgun.conversation.tracking; import java.util.LinkedList; import java.util.List; +import org.studiorailgun.Globals; import org.studiorailgun.conversation.evaluators.goal.GoalData; import org.studiorailgun.conversation.evaluators.greet.GreetingData; +import org.studiorailgun.conversation.web.ConversationQuery; import org.studiorailgun.knowledge.KnowledgeWeb; import org.studiorailgun.knowledge.Node; @@ -118,10 +120,14 @@ public class Conversation { /** * Adds a quote to the conversation + * @param speaker The speaker of the quote * @param quote The quote */ - public void addQuote(Quote quote){ + public void addQuote(Node speaker, Quote quote){ this.history.add(quote); + Node quoteNode = Globals.web.createNode(quote.getRaw()); + quote.setNode(quoteNode); + ConversationQuery.addQuote(speaker, quoteNode); } diff --git a/src/main/java/org/studiorailgun/conversation/tracking/Quote.java b/src/main/java/org/studiorailgun/conversation/tracking/Quote.java index eb97239..79be8fc 100644 --- a/src/main/java/org/studiorailgun/conversation/tracking/Quote.java +++ b/src/main/java/org/studiorailgun/conversation/tracking/Quote.java @@ -3,6 +3,8 @@ package org.studiorailgun.conversation.tracking; import java.util.LinkedList; import java.util.List; +import org.studiorailgun.knowledge.Node; + import edu.stanford.nlp.pipeline.CoreDocument; /** @@ -25,6 +27,11 @@ public class Quote { */ List sentences; + /** + * The node representing this quote + */ + Node node; + /** * Constructor * @param input The raw text of the quote @@ -69,6 +76,14 @@ public class Quote { } this.raw = this.raw + sentence.getRaw(); } + + /** + * Sets the node that corresponds to this quote + * @param node The node + */ + public void setNode(Node node){ + this.node = node; + } } diff --git a/src/main/java/org/studiorailgun/conversation/web/ArgumentQuery.java b/src/main/java/org/studiorailgun/conversation/web/ArgumentQuery.java new file mode 100644 index 0000000..336d8bc --- /dev/null +++ b/src/main/java/org/studiorailgun/conversation/web/ArgumentQuery.java @@ -0,0 +1,59 @@ +package org.studiorailgun.conversation.web; + +import org.studiorailgun.conversation.parser.depend.Argument; +import org.studiorailgun.conversation.tracking.Quote; +import org.studiorailgun.knowledge.Node; +import org.studiorailgun.philosophy.ConceptQuery; + +/** + * Queries related to linguistic arguments + */ +public class ArgumentQuery { + + /** + * Gets the node representing a linguistic argument + * @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){ + String argumentRoot = argument.getRoot().originalText(); + + //the root node which then had modifiers applied to it + Node rootNode = null; + + Node conceptNode = ConceptQuery.getConcept(argumentRoot); + if(conceptNode != null){ + rootNode = conceptNode; + //apply possessive modifier + if(argument.getPossessiveModifier() != null){ + rootNode = ArgumentQuery.getPossessionModified(quote, rootNode, argument); + } + } else { + throw new Error("TODO: solve for proper nouns"); + } + + return rootNode; + } + + /** + * Gets the root node with the possession modifier applied + * @param rootNode The root node + * @param argument The argument + * @return The node that represents the possession modifier applied to the root node + */ + private static Node getPossessionModified(Quote quote, Node rootNode, Argument argument){ + Node rVal = rootNode; + String possessionModifier = argument.getPossessiveModifier().originalText().toLowerCase(); + switch(possessionModifier){ + case "my": { + + } break; + default: { + throw new Error("Unsupported possession modifier! " + possessionModifier); + } + } + return rVal; + } + +} diff --git a/src/main/java/org/studiorailgun/conversation/web/ConversationQuery.java b/src/main/java/org/studiorailgun/conversation/web/ConversationQuery.java index 657a92f..5d171a7 100644 --- a/src/main/java/org/studiorailgun/conversation/web/ConversationQuery.java +++ b/src/main/java/org/studiorailgun/conversation/web/ConversationQuery.java @@ -11,6 +11,15 @@ import org.studiorailgun.knowledge.query.filter.NameQueryFilter; * Queries for nodes related to conversations */ public class ConversationQuery { + + /** + * Gets the current conversation + * @return The current conversation + */ + public static Node getCurrentConversation(){ + List nodes = new LinkedList(Globals.web.getNodes()); + return NameQueryFilter.withNodeName(nodes, "CurrentConversation"); + } /** * Gets the node for the other participant in the conversation @@ -21,4 +30,16 @@ public class ConversationQuery { return NameQueryFilter.withNodeName(nodes, "Other"); } + /** + * Adds a quote to the conversation + * @param speaker The speaker of the quote + * @param quote The quote + */ + public static void addQuote(Node speaker, Node quote){ + //relate this node to the speaker + QuoteQuery.assignSpeaker(speaker, quote); + //relate this node to the conversation + QuoteQuery.assignConversation(ConversationQuery.getCurrentConversation(), quote); + } + } diff --git a/src/main/java/org/studiorailgun/conversation/web/QuoteQuery.java b/src/main/java/org/studiorailgun/conversation/web/QuoteQuery.java new file mode 100644 index 0000000..7408547 --- /dev/null +++ b/src/main/java/org/studiorailgun/conversation/web/QuoteQuery.java @@ -0,0 +1,30 @@ +package org.studiorailgun.conversation.web; + +import org.studiorailgun.Globals; +import org.studiorailgun.knowledge.Node; +import org.studiorailgun.knowledge.types.RelationTypes; + +/** + * Queries related to quotes + */ +public class QuoteQuery { + + /** + * Assigns a speaker to a quote + * @param speaker The speaker + * @param quote The quote + */ + public static void assignSpeaker(Node speaker, Node quote){ + Globals.web.createRelation(RelationTypes.QUOTE_OF, speaker, quote); + } + + /** + * Assigns a conversation to a quote + * @param conversation The conversation + * @param quote The quote + */ + public static void assignConversation(Node conversation, Node quote){ + Globals.web.createRelation(RelationTypes.MEMBER_OF, conversation, quote); + } + +} diff --git a/src/main/java/org/studiorailgun/knowledge/KnowledgeWeb.java b/src/main/java/org/studiorailgun/knowledge/KnowledgeWeb.java index 67f49a8..510d231 100644 --- a/src/main/java/org/studiorailgun/knowledge/KnowledgeWeb.java +++ b/src/main/java/org/studiorailgun/knowledge/KnowledgeWeb.java @@ -346,4 +346,17 @@ public class KnowledgeWeb { return node; } + /** + * Creates a new relation between two nodes + * @param name The name of the relation + * @param parent The parent + * @param child The child + * @return The relation + */ + public Relation createRelation(String name, Node parent, Node child){ + Relation relation = new Relation(name, parent, child); + this.relations.put(relation.getId(), relation); + return relation; + } + } diff --git a/src/main/java/org/studiorailgun/knowledge/anchor/AnchorNodes.java b/src/main/java/org/studiorailgun/knowledge/anchor/AnchorNodes.java index eeb400b..d17ef68 100644 --- a/src/main/java/org/studiorailgun/knowledge/anchor/AnchorNodes.java +++ b/src/main/java/org/studiorailgun/knowledge/anchor/AnchorNodes.java @@ -30,6 +30,11 @@ public class AnchorNodes { */ Integer name; + /** + * Used for flagging a node at learnable + */ + Integer learnable; + /** * Gets the self * @return The self node @@ -138,6 +143,33 @@ public class AnchorNodes { return name; } + /** + * Gets the idea of a learnable + * @return The idea of a learnable + */ + public Node getLearnableNode() { + if(learnable == null){ + return null; + } + return Globals.web.getNode(learnable); + } + + /** + * Sets the learnable node + * @param id The learnable node + */ + public void setLearnable(int id){ + this.learnable = id; + } + + /** + * Gets the id for the learnable node + * @return The id for the learnable node + */ + public Integer getLearnable(){ + return learnable; + } + /** * Copies anchor nodes from the child to the parent * @param parentAnchors The parent anchors @@ -156,6 +188,9 @@ public class AnchorNodes { if(childAnchors.getName() != null){ parentAnchors.setName(childAnchors.getName()); } + if(childAnchors.getLearnable() != null){ + parentAnchors.setLearnable(childAnchors.getLearnable()); + } } /** @@ -192,6 +227,12 @@ public class AnchorNodes { newNodeId = crossRelationLookupMap.get(lookupId); this.setName(newNodeId); } + if(this.getLearnable() != null){ + oldId = this.getLearnable(); + lookupId = "node-" + tag + oldId; + newNodeId = crossRelationLookupMap.get(lookupId); + this.setLearnable(newNodeId); + } } catch (NullPointerException ex){ throw new Error("Failed to find node link " + lookupId + " in relation in web " + tag); } diff --git a/src/main/java/org/studiorailgun/knowledge/query/InstanceQuery.java b/src/main/java/org/studiorailgun/knowledge/query/InstanceQuery.java index 9c15ab6..5a1ed29 100644 --- a/src/main/java/org/studiorailgun/knowledge/query/InstanceQuery.java +++ b/src/main/java/org/studiorailgun/knowledge/query/InstanceQuery.java @@ -6,7 +6,6 @@ import java.util.stream.Collectors; import org.studiorailgun.Globals; import org.studiorailgun.knowledge.Node; import org.studiorailgun.knowledge.Relation; -import org.studiorailgun.knowledge.query.filter.NameQueryFilter; import org.studiorailgun.knowledge.types.RelationTypes; /** @@ -25,44 +24,4 @@ public class InstanceQuery { return relations.stream().filter(relation -> relation.getName().equals(RelationTypes.INSTANCE_OF)).map(relation -> relation.getChild()).collect(Collectors.toList()); } - /** - * Gets a concept by its name - * @param web The web - * @param conceptName The name of the concept - * @return The concept - */ - public static Node getConcept(String conceptName){ - Node concept = Globals.web.getAnchors().getConceptNode(); - List concepts = InstanceQuery.getInstances(concept); - return NameQueryFilter.withNodeName(concepts, conceptName); - } - - /** - * Gets the most specific concept that the child is an instance of - * @param child The child - * @return The most specific concept that the child is an instance of - */ - public static Node getMostSpecificConcept(Node child){ - //get all concepts - Node concept = Globals.web.getAnchors().getConceptNode(); - List concepts = InstanceQuery.getInstances(concept); - - //get the objects the child is an instance of - List relations = Globals.web.getRelationsOfChildNode(child); - - - //filter to most specific concept - List conceptParents = relations.stream() - .filter(relation -> relation.getName().equals(RelationTypes.INSTANCE_OF)) - .filter(relation -> concepts.contains(relation.getParent())) - .map(relation -> relation.getParent()).collect(Collectors.toList()); - - if(conceptParents.size() < 1){ - throw new Error("Node somehow has no concept that it is an instance of! " + concept.getName()); - } - - //TODO: get most specific -- defaulting to first currently - return conceptParents.get(0); - } - } diff --git a/src/main/java/org/studiorailgun/knowledge/types/RelationTypes.java b/src/main/java/org/studiorailgun/knowledge/types/RelationTypes.java index 0868a64..57633ae 100644 --- a/src/main/java/org/studiorailgun/knowledge/types/RelationTypes.java +++ b/src/main/java/org/studiorailgun/knowledge/types/RelationTypes.java @@ -26,4 +26,14 @@ public class RelationTypes { */ public static final String NAME_OF = "nameOf"; + /** + * A quote attributed to another node + */ + public static final String QUOTE_OF = "quoteOf"; + + /** + * A member of a collection + */ + public static final String MEMBER_OF = "memberOf"; + } diff --git a/src/main/java/org/studiorailgun/philosophy/ConceptQuery.java b/src/main/java/org/studiorailgun/philosophy/ConceptQuery.java new file mode 100644 index 0000000..2c548e1 --- /dev/null +++ b/src/main/java/org/studiorailgun/philosophy/ConceptQuery.java @@ -0,0 +1,67 @@ +package org.studiorailgun.philosophy; + +import java.util.List; +import java.util.stream.Collectors; + +import org.studiorailgun.Globals; +import org.studiorailgun.knowledge.Node; +import org.studiorailgun.knowledge.Relation; +import org.studiorailgun.knowledge.query.InstanceQuery; +import org.studiorailgun.knowledge.query.filter.NameQueryFilter; +import org.studiorailgun.knowledge.types.RelationTypes; + +/** + * Queries related to concepts or the idea of a concept + */ +public class ConceptQuery { + + /** + * Gets the list of all concepts + * @return The list of all concepts + */ + public static List getAllConcepts(){ + Node conceptNode = Globals.web.getAnchors().getConceptNode(); + return InstanceQuery.getInstances(conceptNode); + } + + /** + * Gets a concept by its name + * @param web The web + * @param conceptName The name of the concept + * @return The concept + */ + public static Node getConcept(String conceptName){ + Node concept = Globals.web.getAnchors().getConceptNode(); + List concepts = InstanceQuery.getInstances(concept); + return NameQueryFilter.withNodeName(concepts, conceptName); + } + + /** + * Gets the most specific concept that the child is an instance of + * @param child The child + * @return The most specific concept that the child is an instance of + */ + public static Node getMostSpecificConcept(Node child){ + //get all concepts + Node concept = Globals.web.getAnchors().getConceptNode(); + List concepts = InstanceQuery.getInstances(concept); + + //get the objects the child is an instance of + List relations = Globals.web.getRelationsOfChildNode(child); + + + //filter to most specific concept + List conceptParents = relations.stream() + .filter(relation -> relation.getName().equals(RelationTypes.INSTANCE_OF)) + .filter(relation -> concepts.contains(relation.getParent())) + .map(relation -> relation.getParent()).collect(Collectors.toList()); + + if(conceptParents.size() < 1){ + throw new Error("Node somehow has no concept that it is an instance of! " + concept.getName()); + } + + //TODO: get most specific -- defaulting to first currently + return conceptParents.get(0); + } + +} diff --git a/src/test/java/org/studiorailgun/QueryTests.java b/src/test/java/org/studiorailgun/QueryTests.java index 750a704..5a110dc 100644 --- a/src/test/java/org/studiorailgun/QueryTests.java +++ b/src/test/java/org/studiorailgun/QueryTests.java @@ -12,6 +12,7 @@ import org.studiorailgun.conversation.parser.NLPParser; import org.studiorailgun.conversation.tracking.Conversation; import org.studiorailgun.conversation.tracking.Quote; import org.studiorailgun.conversation.tracking.Sentence; +import org.studiorailgun.conversation.web.ConversationQuery; /** * Query tests @@ -46,7 +47,7 @@ public class QueryTests { } //add the quote to the conversation - conversation.addQuote(quote); + conversation.addQuote(ConversationQuery.getOtherParticipant(), quote); //evaluate each sentence for(Sentence sentence : quote.getSentences()){