Skip to content

Commit

Permalink
When was X born?
Browse files Browse the repository at this point in the history
Added FunctionBase interface
  • Loading branch information
garfix committed Feb 7, 2018
1 parent 0f1b872 commit c21dced
Show file tree
Hide file tree
Showing 20 changed files with 143 additions and 14 deletions.
1 change: 1 addition & 0 deletions doc/todo.md
Expand Up @@ -2,6 +2,7 @@

## Misc

* If a name is ambiguous, show the user a selection of names (requires a big change in architecture)
* grouped relations in matcher and solver: (), and, or, not
* name(A, F, firstName) !name(A, I, insertion) name(A, L, lastName) join(N, ' ', F, L) => name(A, N);
* declaratives and imperatives that update the database
Expand Down
13 changes: 13 additions & 0 deletions lib/central/ProblemSolver.go
Expand Up @@ -44,6 +44,10 @@ func (solver *ProblemSolver) AddFactBase(factBase knowledge.FactBase) {
solver.allKnowledgeBases = append(solver.allKnowledgeBases, factBase)
}

func (solver *ProblemSolver) AddFunctionBase(functionBase knowledge.FunctionBase) {
solver.allKnowledgeBases = append(solver.allKnowledgeBases, functionBase)
}

func (solver *ProblemSolver) AddRuleBase(ruleBase knowledge.RuleBase) {
solver.ruleBases = append(solver.ruleBases, ruleBase)
solver.allKnowledgeBases = append(solver.allKnowledgeBases, ruleBase)
Expand Down Expand Up @@ -152,6 +156,7 @@ func (solver ProblemSolver) solveSingleRelationGroupSingleBinding(relationGroup
knowledgeBase := solver.allKnowledgeBases[relationGroup.KnowledgeBaseIndex]
factBase, isFactBase := knowledgeBase.(knowledge.FactBase)
ruleBase, isRuleBase := knowledgeBase.(knowledge.RuleBase)
functionBase, isFunctionBase := knowledgeBase.(knowledge.FunctionBase)
_, isNestedStructureBase := knowledgeBase.(knowledge.NestedStructureBase)

boundRelations := solver.matcher.BindRelationSetSingleBinding(relationGroup.Relations, binding)
Expand All @@ -172,6 +177,14 @@ func (solver ProblemSolver) solveSingleRelationGroupSingleBinding(relationGroup
newBindings = append(newBindings, combinedBinding)
}

} else if isFunctionBase {

var relation = relationGroup.Relations[0]
resultBinding, functionFound := functionBase.Execute(relation, binding)
if functionFound {
newBindings = append(newBindings, resultBinding)
}

} else if isRuleBase {

newBindings = append(newBindings, solver.SolveSingleRelationSingleBindingSingleRuleBase(boundRelations[0], binding, ruleBase)...)
Expand Down
7 changes: 5 additions & 2 deletions lib/generate/GenerationLexicon.go
Expand Up @@ -47,6 +47,7 @@ func (lexicon *GenerationLexicon) GetLexemeForGeneration(consequent mentalese.Re
}

partOfSpeech := consequent.Predicate
applicableLexemeFound := false

lexemes, found := lexicon.lexemes[partOfSpeech]
if found {
Expand All @@ -64,14 +65,16 @@ func (lexicon *GenerationLexicon) GetLexemeForGeneration(consequent mentalese.Re
resultLexeme.Form = bindings[0]["Name"].TermValue
}

applicableLexemeFound = true

break
}
}
}

lexicon.log.EndDebug("GetLexemeForGeneration", resultLexeme, found)
lexicon.log.EndDebug("GetLexemeForGeneration", resultLexeme, applicableLexemeFound)

return resultLexeme, found
return resultLexeme, applicableLexemeFound
}

func (lexicon *GenerationLexicon) ImportFrom(fromLexicon *GenerationLexicon) {
Expand Down
2 changes: 2 additions & 0 deletions lib/generate/Generator.go
Expand Up @@ -49,6 +49,8 @@ func (generator *Generator) GenerateNode(antecedent mentalese.Relation, antecede
consequentBinding := conditionBinding.Extract(consequent.Arguments[0].TermValue)
words = append(words, generator.generateSingleConsequent(consequent, consequentBinding, sentenceSense)...)
}
} else {
generator.log.AddError("Cannot generate response for syntax node " + antecedent.String())
}

generator.log.EndDebug("GenerateNode", words)
Expand Down
2 changes: 2 additions & 0 deletions lib/global/SystemBuilder.go
Expand Up @@ -50,6 +50,8 @@ func (builder systemBuilder) buildFromConfig(system *system, config systemConfig

solver := central.NewProblemSolver(matcher, builder.log)

solver.AddFunctionBase(systemFunctionBase)

systemAggregateBase := knowledge.NewSystemAggregateBase(builder.log)
solver.AddMultipleBindingsBase(systemAggregateBase)

Expand Down
File renamed without changes.
8 changes: 8 additions & 0 deletions lib/knowledge/FunctionBase.go
@@ -0,0 +1,8 @@
package knowledge

import "nli-go/lib/mentalese"

type FunctionBase interface {
KnowledgeBase
Execute(input mentalese.Relation, binding mentalese.Binding) (mentalese.Binding, bool)
}
55 changes: 49 additions & 6 deletions lib/knowledge/SystemFunctionBase.go
Expand Up @@ -2,6 +2,7 @@ package knowledge

import (
"nli-go/lib/mentalese"
"strings"
)

type SystemFunctionBase struct {
Expand All @@ -11,14 +12,53 @@ func NewSystemFunctionBase() *SystemFunctionBase {
return &SystemFunctionBase{}
}

func (base *SystemFunctionBase) Execute(input mentalese.Relation, binding mentalese.Binding) (mentalese.Term, bool) {
func (base *SystemFunctionBase) GetMatchingGroups(set mentalese.RelationSet, knowledgeBaseIndex int) []RelationGroup {

result := mentalese.Term{}
found := true
matchingGroups := []RelationGroup{}
predicates := []string{"join", "split"}

for _, setRelation := range set {
for _, predicate:= range predicates {
if predicate == setRelation.Predicate {
// TODO calculate real cost
matchingGroups = append(matchingGroups, RelationGroup{mentalese.RelationSet{setRelation}, knowledgeBaseIndex, worst_cost})
break
}
}
}

return matchingGroups
}

func (base *SystemFunctionBase) Execute(input mentalese.Relation, binding mentalese.Binding) (mentalese.Binding, bool) {

newBinding := binding
found := false

if input.Predicate == "split" {

sourceValue, sourceFound := binding[input.Arguments[0].TermValue]
if sourceFound {

parts := strings.Split(sourceValue.TermValue, input.Arguments[1].TermValue)
newBinding = binding.Copy()

for i, argument := range input.Arguments[2:] {
variable := argument.TermValue
newTerm := mentalese.Term{}
newTerm.TermType = mentalese.Term_stringConstant
newTerm.TermValue = parts[i]
newBinding[variable] = newTerm
}

found = true
}
}

if input.Predicate == "join" {

sep := ""
result := mentalese.Term{}

for _, argument := range input.Arguments[2:] {
variable := argument.TermValue
Expand All @@ -30,9 +70,12 @@ func (base *SystemFunctionBase) Execute(input mentalese.Relation, binding mental
}
}

} else {
found = false
newBinding = binding.Copy()
newBinding[input.Arguments[0].TermValue] = result

found = true

}

return result, found
return newBinding, found
}
2 changes: 1 addition & 1 deletion lib/mentalese/FunctionBase.go
Expand Up @@ -3,5 +3,5 @@ package mentalese
type FunctionBase interface {

// Performs argument.Predicate on argument.Arguments[1..] and returns the new value of the first argument (0)
Execute(input Relation, binding Binding) (Term, bool)
Execute(input Relation, binding Binding) (Binding, bool)
}
7 changes: 3 additions & 4 deletions lib/mentalese/RelationMatcher.go
Expand Up @@ -94,14 +94,13 @@ func (matcher *RelationMatcher) MatchSequenceToSetWithIndexes(needleSequence Rel
func (matcher *RelationMatcher) MatchRelationToFunction(needleRelation Relation, binding Binding) (Binding, bool) {

newBinding := Binding{}
resultBinding := Binding{}
functionFound := false
returnValue := Term{}

for _, functionBase := range matcher.functionBases {
returnValue, functionFound = functionBase.Execute(needleRelation, binding)
resultBinding, functionFound = functionBase.Execute(needleRelation, binding)
if functionFound {
newBinding = binding.Copy()
newBinding[needleRelation.Arguments[0].TermValue] = returnValue
newBinding = resultBinding
break
}
}
Expand Down
2 changes: 2 additions & 0 deletions resources/dbpedia/dbpedia-ds2generic.transform
Expand Up @@ -3,6 +3,8 @@
have_child(A, B) => isa(P1, have) subject(P1, A) object(P1, B) isa(B, child);
has_father(A, S) => isa(P1, be) subject(P1, S) object(P1, B) possession(A, B) isa(B, father);
has_mother(A, S) => isa(P1, be) subject(P1, S) object(P1, B) possession(A, B) isa(B, mother);
birth_date(S, Day, Month, Year) => isa(P1, bear) tense(P1, past) subject(P1, S)
prepositional_object(P1, Pp1) relation(Pp1, Prep, B1) isa(Prep, on) day(B1, Day) month(B1, Month) year(B1, Year);

name(A, N) => name(A, N);

Expand Down
1 change: 1 addition & 0 deletions resources/dbpedia/dbpedia-generic2ds.transform
@@ -1,6 +1,7 @@
[
isa(P1, marry) subject(P1, A) object(P1, B) => married_to(A, B);
isa(P1, be) subject(P1, A) object(P1, B) => ;
isa(P1, bear) subject(P1, S) isa(W, when) isa(A, be) => birth_date(S, B1);

IF isa(S, child) THEN isa(P1, have) subject(P1, S) object(P1, O) => have_child(O, S);
IF isa(O, child) THEN isa(P1, have) subject(P1, S) object(P1, O) => have_child(S, O);
Expand Down
12 changes: 12 additions & 0 deletions resources/dbpedia/dbpedia.solution
Expand Up @@ -62,4 +62,16 @@ quantification(_, _, _, _) => ;
answer: gender(A, Gender) name(B, N) has_mother(A, B)
}
}

== When was X born? ==
{
condition: question(S, wh_question) birth_date(A, Ymd),
no_results: {
answer: dont_know()
},
some_results: {
preparation: gender(A, Gender) split(Ymd, '-', Year, Month, Day),
answer: gender(A, Gender) birth_date(A, Day, Month, Year)
}
}
]
2 changes: 2 additions & 0 deletions resources/dbpedia/dbpedia/ds2db.map
Expand Up @@ -20,4 +20,6 @@

has_father(A, B) => parent(A, B) gender(B, 'male');
has_mother(A, B) => parent(A, B) gender(B, 'female');

birth_date(P, D) => birth_date(P, D);
]
3 changes: 2 additions & 1 deletion resources/dbpedia/dbpedia/names.json
Expand Up @@ -5,5 +5,6 @@
"birth_name": "http://dbpedia.org/ontology/birthName",
"foaf_name": "http://xmlns.com/foaf/0.1/name",
"gender": "http://xmlns.com/foaf/0.1/gender",
"parent": "http://dbpedia.org/ontology/parent"
"parent": "http://dbpedia.org/ontology/parent",
"birth_date": "http://dbpedia.org/ontology/birthDate"
}
6 changes: 6 additions & 0 deletions resources/dbpedia/en_US/read.grammar
Expand Up @@ -21,12 +21,18 @@
{ rule: wh_adjp(A1) -> wh_word(A1) adjective(A1) }
{ rule: wh_adjp(A1) -> wh_word(A1) }

== how many X had Y ==
{ rule: s_interrogative(S1) -> wh_adjp(Q1) np(R1) aux_verb(S1) np(E2), sense: question(S1, wh_question) quantification(R1, [], Q1, []) subject(S1, R1) object(S1, E2) }

== who married X ==
{ rule: s_interrogative(S1) -> wh_word(E1) vp(S1) np(E2), sense: question(S1, wh_question) subject(S1, E1) object(S1, E2) }

== who was X's mother ==
{ rule: s_interrogative(S1) -> wh_word(E1) aux_verb(S1) np(E2), sense: question(S1, wh_question) subject(S1, E1) object(S1, E2) }

== when was X born ==
{ rule: s_interrogative(S1) -> wh_word(W1) aux_verb(A1) np(E2) vp(S1), sense: question(S1, wh_question) subject(S1, E2) }

{ rule: s_interrogative(S1) -> s_interrogative(S1) question_mark() }

{ rule: s(S1) -> s_interrogative(S1) }
Expand Down
2 changes: 2 additions & 0 deletions resources/dbpedia/en_US/read.lexicon
@@ -1,11 +1,13 @@
[
{ form: 'married', pos: verb, sense: isa(E, marry) tense(E, past) }
{ form: 'born', pos: verb, sense: isa(E, bear) tense(E, past) }
{ form: 'had', pos: aux_verb, sense: isa(E, have) }
{ form: 'has', pos: aux_verb, sense: isa(E, have) }
{ form: 'was', pos: aux_verb, sense: isa(E, be) }

{ form: 'who', pos: wh_word, sense: isa(E, who) }
{ form: 'how', pos: wh_word, sense: isa(E, how) }
{ form: 'when', pos: wh_word, sense: isa(E, when) }

{ form: 'many', pos: adjective, sense: isa(E, many) }

Expand Down
10 changes: 10 additions & 0 deletions resources/dbpedia/en_US/write.grammar
@@ -1,6 +1,12 @@
[
{ rule: s(S1) -> np(E) vp(S1), condition: subject(S1, E) }

{ rule: vp(V1) -> vbar(V1) pp(P1), condition: prepositional_object(V1, P1) }

{ rule: vbar(V1) -> aux_verb_be(V1) vgp(V1), condition: tense(V1, past) }

{ rule: vgp(V1) -> verb(V1) }

{ rule: vp(V) -> verb(V) np(E), condition: object(V, E) }
{ rule: vp(V) -> aux_verb(V) negative(V) verb(V), condition: negation(V) }
{ rule: vp(V) -> verb(V) }
Expand All @@ -22,5 +28,9 @@

{ rule: nbar(E) -> noun(E) }

{ rule: pp(R1) -> preposition(P1) np(E1), condition: relation(R1, P1, E1) }

{ rule: determiner(E1) -> number(N1), condition: isa(E1, N1) }

{ rule: noun(Date) -> month_name(Date) number(D) comma(C) number(Y), condition: day(Date, D) month(Date, M) year(Date, Y) }
]
21 changes: 21 additions & 0 deletions resources/dbpedia/en_US/write.lexicon
@@ -1,21 +1,25 @@
[
== verbs ==
{ form: 'married', pos: verb, condition: isa(E, marry) }
{ form: 'born', pos: verb, condition: isa(E, bear) }
{ form: 'has', pos: verb, condition: isa(E, have) }
{ form: 'was', pos: verb, condition: isa(E, be) }
{ form: 'know', pos: verb, condition: isa(E, know) }

== auxiliaries ==
{ form: 'do', pos: aux_verb }
{ form: 'was', pos: aux_verb_be, condition: tense(E, past) }

== nouns ==
{ form: 'children', pos: noun, condition: isa(E, child) }
{ form: 'father', pos: noun, condition: isa(E, father) }
{ form: 'mother', pos: noun, condition: isa(E, mother) }

== adverbs ==
{ form: 'yes', pos: adverb, condition: isa(E, yes) }
{ form: 'no', pos: adverb, condition: isa(E, no) }

== pronouns ==
{ form: 'he', pos: pronoun, condition: subject(P, S) isa(S, male) }
{ form: 'she', pos: pronoun, condition: subject(P, S) isa(S, female) }
{ form: 'her', pos: pronoun, condition: object(S, O) isa(O, female) }
Expand All @@ -24,11 +28,28 @@
{ form: 'him', pos: pronoun, condition: object(S, O) isa(O, male) }
{ form: 'i', pos: pronoun, condition: subject(P, S) isa(S, i) }

== prepositions ==
{ form: 'on', pos: preposition, condition: isa(E, on) }

{ form: '?', pos: proper_noun, condition: name(E, Name) }

{ form: 'and', pos: conjunction, condition: isa(E, and) }

{ form: 'not', pos: negative }

{ form: ',', pos: comma, condition: isa(E, and) }
{ form: ',', pos: comma }

{ form: 'January', pos: month_name, condition: month(E, '01') }
{ form: 'February', pos: month_name, condition: month(E, '02') }
{ form: 'March', pos: month_name, condition: month(E, '03') }
{ form: 'April', pos: month_name, condition: month(E, '04') }
{ form: 'May', pos: month_name, condition: month(E, '05') }
{ form: 'June', pos: month_name, condition: month(E, '06') }
{ form: 'July', pos: month_name, condition: month(E, '07') }
{ form: 'August', pos: month_name, condition: month(E, '08') }
{ form: 'September', pos: month_name, condition: month(E, '09') }
{ form: 'October', pos: month_name, condition: month(E, '10') }
{ form: 'November', pos: month_name, condition: month(E, '11') }
{ form: 'December', pos: month_name, condition: month(E, '12') }
]
1 change: 1 addition & 0 deletions tests/integration/DBPedia_test.go
Expand Up @@ -29,6 +29,7 @@ func TestDBPedia(t *testing.T) {
{"Who was Ada Lovelace's father?", "Lord Byron was her father"},
{"Who was Ada Lovelace's mother?", "Anne Isabella Byron was her mother"},
{"Who was Percy Florence Shelley's father?", "Percy Bysshe Shelley was his father"},
{"When was Lord Byron born?", "He was born on January 22, 1788"},
}

for _, test := range tests {
Expand Down

0 comments on commit c21dced

Please sign in to comment.