-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsemantics.py
executable file
·194 lines (170 loc) · 6.35 KB
/
semantics.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File: semantics.py
# Template file for Informatics 2A Assignment 2:
# 'A Natural Language Query System in Python/NLTK'
# John Longley, November 2012
# Revised November 2013 and November 2014 with help from Nikolay Bogoychev
# Revised October 2015 by Toms Bergmanis
# Revised October 2017 by Chunchuan Lyu with help from Kiniorski Filip
# PART D: Semantics for the Query Language.
from agreement import *
def sem(tr):
"""translates a syntax tree into a logical lambda expression (in string form)"""
rule = top_level_rule(tr)
if (tr.label() == 'P'):
return tr[0][0]
elif (tr.label() == 'N'):
return '(\\x.' + tr[0][0] + '(x))' # \\ is escape sequence for \
elif (tr.label() == 'I'):
return '(\\x.' + tr[0][0] + '(x))'
elif (tr.label() == 'A'):
return '(\\x.' + tr[0][0] + '(x))'
elif (tr.label() == 'T'):
return '(\\x.(\\y.' + tr[0][0] + '(x,y)))'
#AN
elif (rule == 'AN -> A AN'):
return '(\\x.(' + sem(tr[0]) + '(x) & ' + sem(tr[1]) + '(x)))'
elif (rule == 'AN -> N'):
return sem(tr[0])
#NP
elif (rule == 'NP -> P'):
return '\\x.(x=' + sem(tr[0]) + ')'
elif (rule == 'NP -> AR Nom'):
return sem(tr[1])
elif (rule == 'NP -> Nom'):
return sem(tr[0])
#S
elif (rule == 'S -> WHO QP QM'):
return sem(tr[1])
elif (rule == 'S -> WHICH Nom QP QM'):
return '(\\x.(' + sem(tr[1]) + '(x) & ' + sem(tr[2]) + '(x)))'
#QP
elif rule == 'QP -> VP':
return '(\\x.(' + sem(tr[0]) + '(x)))'
elif rule == 'QP -> DO NP T':
return '(\\x. (exists y.(' + sem(tr[1]) + '(y) & ' + sem(tr[2])+'(y,x))))'
#VP
elif (rule == 'VP -> T NP'):
return '(\\x.exist y.(' + sem(tr[1]) + '(y) & ' + sem(tr[0]) + '(x)(y)))'
elif (rule == 'VP -> BE A' or rule == 'VP -> BE NP'):
return sem(tr[1])
elif (rule == 'VP -> VP AND VP'):
return '(\\x.(' + sem(tr[0]) + '(x) & ' + sem(tr[2]) + '(x)))'
elif (rule == 'VP -> I'):
return '(\\x.(' + sem(tr[0]) + '(x)))'
#Nom
elif (rule == 'Nom -> AN Rel'):
return '(\\x.(' + sem(tr[0]) + '(x) & ' + sem(tr[1]) + '(x)))'
elif (rule == 'Nom -> AN'):
return sem(tr[0])
#Rel
elif (rule == 'Rel -> WHO VP'):
return sem(tr[1])
elif (rule == 'Rel -> NP T'):
return 'exist y.(' + sem(tr[0]) + '(x) & ' + sem(tr[1]) + '(y)(x))'
# Logic parser for lambda expressions
from nltk.sem.logic import LogicParser
lp = LogicParser()
# Lambda expressions can now be checked and simplified as follows:
# A = lp.parse('(\\x.((\\P.P(x,x))(loves)))(John)')
# B = lp.parse(sem(tr)) # for some tree tr
# A.simplify()
# B.simplify()
# Model checker
from nltk.sem.logic import *
# Can use: A.variable, A.term, A.term.first, A.term.second, A.function, A.args
def interpret_const_or_var(s,bindings,entities):
if (s in entities): # s a constant
return s
else: # s a variable
return [p[1] for p in bindings if p[0]==s][0] # finds most recent binding
def model_check (P,bindings,entities,fb):
if (isinstance (P,ApplicationExpression)):
if (len(P.args)==1):
pred = P.function.__str__()
arg = interpret_const_or_var(P.args[0].__str__(),bindings,entities)
return fb.queryUnary(pred,arg)
else:
pred = P.function.function.__str__()
arg0 = interpret_const_or_var(P.args[0].__str__(),bindings,entities)
arg1 = interpret_const_or_var(P.args[1].__str__(),bindings,entities)
return fb.queryBinary(pred,arg0,arg1)
elif (isinstance (P,EqualityExpression)):
arg0 = interpret_const_or_var(P.first.__str__(),bindings,entities)
arg1 = interpret_const_or_var(P.second.__str__(),bindings,entities)
return (arg0 == arg1)
elif (isinstance (P,AndExpression)):
return (model_check (P.first,bindings,entities,fb) and
model_check (P.second,bindings,entities,fb))
elif (isinstance (P,ExistsExpression)):
v = str(P.variable)
P1 = P.term
for e in entities:
bindings1 = [(v,e)] + bindings
if (model_check (P1,bindings1,entities,fb)):
return True
return False
def find_all_solutions (L,entities,fb):
v = str(L.variable)
P = L.term
return [e for e in entities if model_check(P,[(v,e)],entities,fb)]
# Interactive dialogue session
def fetch_input():
s = raw_input('$$ ')
while (s.split() == []):
s = raw_input('$$ ')
return s
def output(s):
print (' '+s)
def dialogue():
lx = Lexicon()
fb = FactBase()
output('')
s = fetch_input()
while (s.split() == []):
s = raw_input('$$ ')
while (s != 'exit'):
if (s[-1]=='?'):
sent = s[:-1] + ' ?' # tolerate absence of space before '?'
if len(sent) == 0:
output ("Eh??")
else:
wds = sent.split()
trees = all_valid_parses(lx,wds)
if (len(trees)==0):
output ("Eh??")
elif (len(trees)>1):
output ("Ambiguous!")
else:
tr = restore_words (trees[0],wds)
lam_exp = lp.parse(sem(tr))
L = lam_exp.simplify()
# print L # useful for debugging
entities = lx.getAll('P')
results = find_all_solutions (L,entities,fb)
if (results == []):
if (wds[0].lower() == 'who'):
output ("No one")
else:
output ("None")
else:
buf = ''
for r in results:
buf = buf + r + ' '
output (buf)
elif (s[-1]=='.'):
s = s[:-1] # tolerate final full stop
if len(s) == 0:
output ("Eh??")
else:
wds = s.split()
msg = process_statement(lx,wds,fb)
if (msg == ''):
output ("OK.")
else:
output ("Sorry - " + msg)
else:
output ("Please end with \".\" or \"?\" to avoid confusion.")
s = fetch_input()
if __name__ == "__main__":
dialogue()
# End of PART D.