argument querying in copular transfer eval
All checks were successful
studiorailgun/trpg/pipeline/head This commit looks good

This commit is contained in:
austin 2024-12-31 11:58:35 -05:00
parent 4c75cdc07a
commit 05122a844e
11 changed files with 258 additions and 41 deletions

View File

@ -1,20 +1,18 @@
package org.studiorailgun.ai.conversation.evaluators.transfer;
import org.studiorailgun.Globals;
import org.studiorailgun.ai.conversation.evaluators.query.NounStack;
import org.studiorailgun.ai.conversation.parser.NLPDependencies;
import org.studiorailgun.ai.conversation.parser.PennTreebankTagSet;
import org.studiorailgun.ai.conversation.parser.depend.Argument;
import org.studiorailgun.ai.conversation.parser.depend.Clause;
import org.studiorailgun.ai.conversation.parser.depend.Predicate;
import org.studiorailgun.ai.conversation.tracking.Conversation;
import org.studiorailgun.ai.conversation.tracking.Quote;
import org.studiorailgun.ai.conversation.tracking.Sentence;
import org.studiorailgun.ai.conversation.web.ArgumentQuery;
import org.studiorailgun.ai.knowledge.Node;
import org.studiorailgun.ai.knowledge.query.NodePropQuery;
import edu.stanford.nlp.ling.IndexedWord;
import edu.stanford.nlp.semgraph.SemanticGraph;
import edu.stanford.nlp.trees.GrammaticalRelation;
/**
* Evaluates a transfer statement
@ -35,21 +33,22 @@ public class TransferEval {
throw new UnsupportedOperationException(message);
}
IndexedWord root = semanticGraph.getFirstRoot();
Clause mainClause = sentence.getMainClause();
Predicate pred = mainClause.getPredicate();
if(PennTreebankTagSet.isNoun(root.tag())){
if(TransferEval.isCopularStatement(semanticGraph)){
NounStack noun1 = NounStack.parse(semanticGraph, root);
NounStack noun2 = null;
for(IndexedWord child : semanticGraph.getChildList(root)){
if(PennTreebankTagSet.isNoun(child.tag())){
noun2 = NounStack.parse(semanticGraph, child);
break;
}
if(TransferEval.isCopularStatement(pred)){
//this is a copular predicate
Argument argument1 = mainClause.getSubject();
Argument argument2 = mainClause.getPredicate().getCopularArg();
if(argument1 == null || argument2 == null){
String message = "Failed to parse arguments for copular statement! \n" +
argument1 + " " + argument2 + "\n" +
sentence.getRaw() + "\n" +
semanticGraph;
throw new Error(message);
}
if(noun2 == null){
throw new Error("Failed to parse second noun! " + semanticGraph);
}
TransferEval.evaluateCopularStatement(conversation, quote, sentence, noun1, null);
TransferEval.evaluateCopularStatement(conversation, quote, sentence, argument1, argument2);
} else {
String message = "Unsupported root type!\n" +
"\"" + sentence.getRaw() + "\"\n" +
@ -72,11 +71,11 @@ public class TransferEval {
* @param noun1 The first noun stack
* @param noun2 The second noun stack
*/
private static void evaluateCopularStatement(Conversation conversation, Quote quote, Sentence sentence, NounStack noun1, NounStack noun2){
private static void evaluateCopularStatement(Conversation conversation, Quote quote, Sentence sentence, Argument subject, Argument object){
SemanticGraph graph = sentence.getGraph();
IndexedWord verb = NLPDependencies.getCopular(graph);
if(PennTreebankTagSet.isBe(verb.tag())){
TransferEval.evaluateAssignmentStatement(conversation, quote, sentence, noun1, noun2);
TransferEval.evaluateAssignmentStatement(conversation, quote, sentence, subject, object);
} else {
throw new Error("Unsupported verb type! " + graph);
}
@ -88,18 +87,20 @@ public class TransferEval {
* @param quote The quote
* @param sentence The sentence
*/
private static void evaluateAssignmentStatement(Conversation conversation, Quote quote, Sentence sentence, NounStack noun1, NounStack noun2){
private static void evaluateAssignmentStatement(Conversation conversation, Quote quote, Sentence sentence, Argument subjectArg, Argument objectArg){
//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 subjectNode = ArgumentQuery.getArgument(quote, subjectArg);
if(subjectNode == null){
throw new Error("Error querying subject!");
}
Node subject = ArgumentQuery.getArgument(quote, mainClause.getSubject());
// Node object = ArgumentQuery.getArgument(quote, mainClause.getDirectObject());
//make an equivalence link here
Node objectNode = ArgumentQuery.getArgument(quote, objectArg);
if(objectNode == null){
throw new Error("Error querying object!");
}
//make an equivalence link here if it doesn't exist already
//for the moment, assume the other noun is a concept
@ -107,18 +108,11 @@ public class TransferEval {
/**
* Checks if this is a copular statement
* @param graph The graph
* @param pred The predicate of the sentence
* @return true if it is a copular statement, false otherwise
*/
private static boolean isCopularStatement(SemanticGraph graph){
IndexedWord root = graph.getFirstRoot();
for(IndexedWord child : graph.getChildList(root)){
GrammaticalRelation relation = graph.reln(root,child);
if(NLPDependencies.isCopular(relation.getLongName())){
return true;
}
}
return false;
private static boolean isCopularStatement(Predicate pred){
return pred.getCopular() != null;
}
}

View File

@ -79,6 +79,23 @@ public class PennTreebankTagSet {
}
}
/**
* Checks if this is a proper noun or not
* @param tag The tag
* @return true if it is a proper noun, false otherwise
*/
public static boolean isCommonNoun(String tag){
switch(tag){
case "NN":
case "NNS": {
return true;
}
default: {
return false;
}
}
}
/**
* Checks if this tag is a proper noun or not
* @param tag The tag
@ -87,7 +104,8 @@ public class PennTreebankTagSet {
public static boolean isProperNoun(String tag){
switch(tag){
case "NP":
case "NPS": {
case "NPS":
case "NNP": {
return true;
}
default: {

View File

@ -123,6 +123,21 @@ public class Argument {
rVal.adverb = child;
} break;
//this will have already been parsed at the clause level, want to ignore now
case "advcl_preposition":
//this will have already been parsed at the clause level, want to ignore now
case "parataxis":
//this will have already been parsed at the clause level, want to ignore now
case "punctuation":
//this will have already been parsed at the clause level, want to ignore now
case "copula":
//this can occur when we are parsing the root nominal of a non-verbal copular sentence -- we would expect the root to point at the subject of the copular and we don't want that subject to be a part of this argument
case "nominal subject": {
//cases to ignore
continue;
}
//unhandled cases
default: {
throw new Error("Unsupported relation type! " + relation.getLongName() + "\n" + "for " + child.originalText() + "\n" + graph);

View File

@ -131,6 +131,8 @@ public class Clause {
case "copula": {
//this means the root is a noun, but this related word is turning it into a copular predicate
pred.setCopular(child);
Argument copularArg = Argument.parse(graph, pred.getRoot(), ArgumentType.NOMINAL);
pred.setCopularArg(copularArg);
} break;
//a prepositional adjunct (oblique)

View File

@ -87,6 +87,11 @@ public class Predicate {
*/
IndexedWord copularVerb;
/**
* The argument that this predicate is storing given that it is a copular predicate
*/
Argument copularArg;
/**
* Constructor
* @param root The root of the predicate
@ -135,6 +140,22 @@ public class Predicate {
return this.copularVerb;
}
/**
* Gets the copular argument for this predicate
* @param arg The copular argument
*/
public void setCopularArg(Argument arg){
this.copularArg = arg;
}
/**
* Gets the copular argument for this predicate
* @return The copular argument
*/
public Argument getCopularArg(){
return copularArg;
}
/**
* Sets the existential status of the predicate
* @param existential true if existential, false otherwise

View File

@ -84,6 +84,14 @@ public class Quote {
public void setNode(Node node){
this.node = node;
}
/**
* Gets the node corresponding to this quote
* @return The node
*/
public Node getNode(){
return node;
}
}

View File

@ -1,8 +1,17 @@
package org.studiorailgun.ai.conversation.web;
import java.util.List;
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.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.types.RelationTypes;
import org.studiorailgun.ai.linguistics.ProperNounQuery;
import org.studiorailgun.ai.philosophy.ConceptQuery;
/**
@ -18,19 +27,22 @@ public class ArgumentQuery {
*/
public static Node getArgument(Quote quote, Argument argument){
String argumentRoot = argument.getRoot().originalText();
String argumentTag = argument.getRoot().tag();
//the root node which then had modifiers applied to it
Node rootNode = null;
Node conceptNode = ConceptQuery.getConcept(argumentRoot);
if(conceptNode != null){
if(PennTreebankTagSet.isCommonNoun(argumentTag)){
Node conceptNode = ConceptQuery.getConcept(argumentRoot);
rootNode = conceptNode;
//apply possessive modifier
if(argument.getPossessiveModifier() != null){
rootNode = ArgumentQuery.getPossessionModified(quote, rootNode, argument);
}
} else if(PennTreebankTagSet.isProperNoun(argumentTag)) {
rootNode = ProperNounQuery.getProperNoun(argumentRoot);
} else {
throw new Error("TODO: solve for proper nouns");
throw new Error("Undefined case! " + argumentTag);
}
return rootNode;
@ -47,7 +59,21 @@ public class ArgumentQuery {
String possessionModifier = argument.getPossessiveModifier().originalText().toLowerCase();
switch(possessionModifier){
case "my": {
List<Node> instances = InstanceQuery.getInstances(rootNode);
List<Node> possessedInstances = instances.stream().filter(node -> {
List<Relation> relations = Globals.web.getRelationsOfChildNode(node);
List<Relation> possessionRelationsToRoot = relations.stream().filter(relation -> relation.getName().equals(RelationTypes.POSSESSION_OF) && relation.getParent().equals(rootNode)).collect(Collectors.toList());
return possessionRelationsToRoot.size() > 0;
}).collect(Collectors.toList());
if(possessedInstances.size() == 1){
//already exists, return it
rVal = possessedInstances.get(0);
} else if(possessedInstances.size() > 1) {
//already exists, return it
throw new Error("Can't currently handle plural instances!");
} else {
//does not already exist, see if we can generate a node to encapsulate it
}
} break;
default: {
throw new Error("Unsupported possession modifier! " + possessionModifier);

View File

@ -1,7 +1,11 @@
package org.studiorailgun.ai.conversation.web;
import java.util.List;
import java.util.stream.Collectors;
import org.studiorailgun.Globals;
import org.studiorailgun.ai.knowledge.Node;
import org.studiorailgun.ai.knowledge.Relation;
import org.studiorailgun.ai.knowledge.types.RelationTypes;
/**
@ -27,4 +31,18 @@ public class QuoteQuery {
Globals.web.createRelation(RelationTypes.MEMBER_OF, conversation, quote);
}
/**
* Gets the speaker of a quote
* @param quote The quote
* @return The speaker of the quote
*/
public static Node getSpeaker(Node quote){
List<Relation> relations = Globals.web.getRelationsOfChildNode(quote);
List<Node> speakers = relations.stream().filter(relation -> relation.getName().equals(RelationTypes.QUOTE_OF)).map(relation -> relation.getParent()).collect(Collectors.toList());
if(speakers.size() != 1){
throw new Error("Invalid number of speakers! " + speakers.size());
}
return speakers.get(0);
}
}

View File

@ -254,6 +254,14 @@ public class KnowledgeWeb {
return nodes.values();
}
/**
* Gets the list of all nodes
* @return The collection
*/
public List<Node> getNodesList() {
return new LinkedList<Node>(nodes.values());
}
/**
* Gets the collection of all relations
* @return The collection of all relations

View File

@ -0,0 +1,74 @@
package org.studiorailgun.ai.linguistics;
import java.util.List;
import java.util.stream.Collectors;
import org.studiorailgun.Globals;
import org.studiorailgun.ai.conversation.parser.depend.Argument;
import org.studiorailgun.ai.conversation.tracking.Quote;
import org.studiorailgun.ai.conversation.web.QuoteQuery;
import org.studiorailgun.ai.knowledge.Node;
import org.studiorailgun.ai.knowledge.Relation;
import org.studiorailgun.ai.knowledge.query.InstanceQuery;
import org.studiorailgun.ai.knowledge.types.RelationTypes;
/**
* Queries related to possession
*/
public class PosessionQuery {
/**
* 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
*/
public static Node getPossessionModified(Quote quote, Node rootNode, Argument argument){
Node rVal = rootNode;
String possessionModifier = argument.getPossessiveModifier().originalText().toLowerCase();
switch(possessionModifier){
case "my": {
List<Node> instances = InstanceQuery.getInstances(rootNode);
List<Node> possessedInstances = instances.stream().filter((Node node) -> {
List<Relation> relations = Globals.web.getRelationsOfChildNode(node);
List<Relation> possessionRelationsToRoot = relations.stream().filter(relation -> relation.getName().equals(RelationTypes.POSSESSION_OF) && relation.getParent().equals(rootNode)).collect(Collectors.toList());
return possessionRelationsToRoot.size() > 0;
}).collect(Collectors.toList());
if(possessedInstances.size() == 1){
//already exists, return it
rVal = possessedInstances.get(0);
} else if(possessedInstances.size() > 1) {
//already exists, return it
throw new Error("Can't currently handle plural instances!");
} else {
rVal = PosessionQuery.generatePossessionModified(quote, rootNode, argument);
}
} break;
default: {
throw new Error("Unsupported possession modifier! " + possessionModifier);
}
}
return rVal;
}
/**
* Generates a node to fill in the instance of the possessor owning an instance of the root node
* @param quote The quote
* @param rootNode The root node
* @param argument The argument
* @return The generated node
*/
private static Node generatePossessionModified(Quote quote, Node rootNode, Argument argument){
Node possession = Globals.web.createNode("possession(inst)");
//attach to concept
Globals.web.createRelation(RelationTypes.INSTANCE_OF, rootNode, possession);
//attach to speaker
Node possessor = QuoteQuery.getSpeaker(quote.getNode());
Globals.web.createRelation(RelationTypes.POSSESSION_OF, possessor, possession);
return possession;
}
}

View File

@ -0,0 +1,33 @@
package org.studiorailgun.ai.linguistics;
import java.util.List;
import java.util.stream.Collectors;
import org.studiorailgun.Globals;
import org.studiorailgun.ai.knowledge.Node;
/**
* Queries related to proper nouns
*/
public class ProperNounQuery {
/**
* Tries to locate a proper noun
* @param properNoun The proper noun
* @return The node representing the proper noun
*/
public static Node getProperNoun(String properNoun){
Node rVal = null;
List<Node> nodes = Globals.web.getNodesList().stream().filter(node -> node.getName().equals(properNoun)).collect(Collectors.toList());
if(nodes.size() < 1){
//create a name node to represent this name
rVal = Globals.web.createNode(properNoun);
} else if(nodes.size() == 1){
rVal = nodes.get(0);
} else {
throw new Error("More than one proper match found! " + nodes.size());
}
return rVal;
}
}