Semantics Exercises
Author: 
Ewan Klein 
Date: 
20080119 
Title:  Semantics Exercises [with Answers] 
The first lot of exercises concentrate on translating simple English expressions into
logical form and checking that they are parsable by the
LogicParser in NLTK. If you have experience of this kind of
translation exercise, you may still find it useful to check that you
understand the syntax expected by the parser.
First, start off Python. Usually, you can just type python at the
prompt in a terminal window. However, you can also type idle,
which will give you the Python editor, and offers a bit more
flexibility. Alternatively, your favourite editor (e.g., emacs or vi)
has almost certainly got a Python mode which will allow you to use
familiar editing commands.
Once you have the Python interpreter running, give it the following instruction:

>>> from nltk.sem import *
>>> lp = LogicParser()


1 Logical Form: Propositions
Translate the following English sentences into propositional logic and
verify that they parse with LogicParser. Provide a key which
shows how the propositional variables in your translation correspond
to expressions of English.
Here's an example to get you started.
(1.1) If Kim sings, it is not the case that Lee sings.
Key: k = 'Kim sings', l = 'Lee sings'

>>> lp.parse('(k implies (not l))')
ApplicationExpression('(implies k)', '(not l)')


This output provides some information about the kind of expression
that the parser has recognized, namely an
ApplicationExpression. In order to suppress this, use print:

>>> print lp.parse('(k implies (not l))')
(implies k (not l))


In order to get the Boolean connectives in infix notation, we can
invoke the infixify() method:

>>> print lp.parse('(k implies (not l))').infixify()
(k implies (not l))


Note
If the parser gives an error or an unexpected result, the most likely
reason is that you have omitted some brackets or added in some
superfluous ones (or , in later examples, omitted a period immediately
after a variablebinding expression).
In some cases, the parser ends
up in a confused state, and will give unpredictable results. If this
happens, you can create a new parser instance with
Alternatively, in place of using lp.parse(s) on a string s, try
LogicParser(s).next().
An alternative English rendering of the meaning in (1.1) might be:
(1.2) Lee doesn't sing if Kim does.
This could receive the same translation, namely
'(k implies (not l))'. There is another possible interpretation
however: '(not (k implies l))'. In other words, the scope of the
negation could either
be restricted to Lee's singing, or else be the whole implication. So
in the case of ambiguity, you can give one or more of several possible
answers.
Now try the following:
(1.3) Fido runs and barks.
Answer:

>>> print lp.parse('(fidorun and fidobark)').infixify()
(fidorun and fidobark)


(1.4) It will snow if it doesn't rain.
Answer:

>>> print lp.parse('((not rain) implies snow)').infixify()
((not rain) implies snow)


(1.5) It's not the case that Suzie will be happy if Peter or Rob comes.
Answer:

>>> print lp.parse('(not ((petecome or robcome) implies suziehappy))').infixify()
(not ((petecome or robcome) implies suziehappy))


(1.6) Kim didn't cough or sneeze.
Answer:

>>> print lp.parse('(not (kimcough or kimsneeze))').infixify()
(not (kimcough or kimsneeze))


(1.7) If you don't come if I call, I won't come if you call.
Answer:

>>> print lp.parse('((not (icall implies youcome)) implies (not (youcall implies icome)))').infixify()
((not (icall implies youcome)) implies (not (youcall implies icome)))


6 Logical Form: Higherorder Abstraction
So far, we have only looked at individual variables being bound by
lambda. However, much of the power of the lambda calculus depends on
being able to bind variables over function expressions. We follow
standard practice in using uppercase letters for variables over
functors. In particular, 'P', 'Q', 'R' are variables over
expressions of type IND → (IND → ... (IND → BOOL)...)). Here's a
simple example:

>>> print lp.parse('(\\P. (P kim fido) love)').simplify()
(love kim fido)


We can loosely paraphrase '\P. (P kim fido)' as 'be a property P
such that P holds between Fido and Kim'. Here's a slightly more
complex example:

>>> print lp.parse('(\\P. all x. (P x kim) love)').simplify()
all x.(love x kim)


In the cases we looked at so far, the result of function application
has been an expression of type BOOL. However, it doesn't have to be
this way. Consider the following example:

>>> e1 = lp.parse('\\P. \\x. (P kim x)')
>>> e2 = lp.parse('chase')
>>> e3 = ApplicationExpression(e1, e2)
>>> print e3.infixify().simplify()
\x.(chase kim x)


Here, the result after betareduction is itself a lambda abstract,
corresponding to the VP chases Kim.
Incidentally, there is a more succinct notation for multiple lambda
abstraction, as shown here:

>>> e1 = lp.parse('\\P x. (P kim x)')


Answer:

>>> e2 = lp.parse('chase')
>>> e3 = ApplicationExpression(e1, e2)
>>> print e3.infixify().simplify()
\x.(chase kim x)


Using lambda abstracts with variables over functions takes us outside firstorder
logic. However, we are going to assume that such lambda abstracts
never enter into interpreted sentences of the semantic
representation. Instead, they are always removed by betareduction,
and serve as a kind of 'glue language' for assembling semantic
representations in a compositional manner.
Now, find an e1 that gives the following results:
Answer:

>>> e1 = lp.parse('\\P. \\x. all y. ((dog y) implies (P kim x))')


(6.1)

>>> e2 = lp.parse('chase')
>>> e3 = ApplicationExpression(e1, e2)
>>> print e3.infixify().simplify()
\x.all y.((dog y) implies (chase kim x))


Answer:

>>> e1 = lp.parse('\\P. \\x. some y. ((dog y) and (P x kim))')


(6.2)

>>> e2 = lp.parse('chase')
>>> e3 = ApplicationExpression(e1, e2)
>>> print e3.infixify().simplify()
\x.some y.((dog y) and (chase x kim))


Answer:

>>> e1 = lp.parse('\\P. \\x0. \\x1. some y. ((present y) and (give x0 y x1))')


(6.3)

>>> e2 = lp.parse('give')
>>> e3 = ApplicationExpression(e1, e2)
>>> print e3.infixify().simplify()
\x0 x1.some y.((present y) and (give x0 y x1))


Let's suppose that we translate the verb barks as the
nonlogical constant 'bark', or equivalently as '\x. (bark x)'.
How are we going to translate every dog in such a way
that the correct translation for every dog barks results from a
function application involving the translations of every dog and
walks? In the tradition of Montague grammar, we do it like
this:

>>> e1 = lp.parse('\\P. all x. ((dog x) implies (P x))')
>>> e2 = lp.parse('bark')
>>> e3 = ApplicationExpression(e1, e2)
>>> print e3.infixify().simplify()
all x.((dog x) implies (bark x))


Now, find e1 such that the following hold:
Answer:

>>> e1 = lp.parse('\\P. some y. ((dog x) and (P x))')


(6.4)

>>> e2 = lp.parse('bark')
>>> e3 = ApplicationExpression(e1, e2)
>>> print e3.infixify().simplify()
some y.((dog x) and (bark x))


Answer:

>>> e1 = lp.parse('\\P. (P fido)')


(6.5)

>>> e2 = lp.parse('bark')
>>> e3 = ApplicationExpression(e1, e2)
>>> print e3.infixify().simplify()
(bark fido)


In this case, we are treating the subject as the functor and the verb
as the argument. Devise a translation for barks that makes it the
functor and the subject the argument. In other words, find an
appropriate value of e1 for the following result to be obtained:
Answer:

>>> e1 = lp.parse('\\X.(X bark)')


(6.1)

>>> e2 = lp.parse('\\P. all x. ((dog x) implies (P x))')
>>> e3 = ApplicationExpression(e1, e2)
>>> print e3.infixify().simplify()
all x.((dog x) implies (bark x))

