One important limitation of the methods described earlier is that they do not deal with scope ambiguity. Our translation method is syntax-driven, in the sense that the semantic representation is closely coupled with the syntactic analysis, and the scope of the quantifiers in the semantics therefore reflects the relative scope of the corresponding NPs in the syntactic parse tree. Consequently, a sentence like (26), repeated here, will always be translated as (53a), not (53b).
(52) Every girl chases a dog.
(53) a. all x.(girl(x) -> exists y.(dog(y) & chase(x,y))) b. exists y.(dog(y) & all x.(girl(x) -> chase(x,y)))
There are numerous approaches to dealing with scope ambiguity, and we will look very briefly at one of the simplest. To start with, let's briefly consider the structure of scoped formulas. Figure 10-3 depicts the way in which the two readings of (52) differ.
Figure 10-3. Quantifier scopings.
Let's consider the lefthand structure first. At the top, we have the quantifier corresponding to every girl. The 9 can be thought of as a placeholder for whatever is inside the scope of the quantifier. Moving downward, we see that we can plug in the quantifier corresponding to a dog as an instantiation of 9. This gives a new placeholder representing the scope of a dog, and into this we can plug the "core" of the semantics, namely the open sentence corresponding to x chases y. The structure on the righthand side is identical, except we have swapped round the order of the two quantifiers.
In the method known as Cooper storage, a semantic representation is no longer an expression of first-order logic, but instead a pair consisting of a "core" semantic representation plus a list of binding operators. For the moment, think of a binding operator as being identical to the semantic representation of a quantified NP such as (44) or
(45). Following along the lines indicated in Figure 10-3, let's assume that we have constructed a Cooper-storage-style semantic representation of sentence (52), and let's take our core to be the open formula chase(x,y). Given a list of binding operators corresponding to the two NPs in (52), we pick a binding operator off the list, and combine it with the core.
\P.exists y.(dog(y) & P(y))(\z2.chase(z1,z2)) Then we take the result, and apply the next binding operator from the list to it. \P.all x.(girl(x) -> P(x))(\z1.exists x.(dog(x) & chase(z1,x)))
Once the list is empty, we have a conventional logical form for the sentence. Combining binding operators with the core in this way is called S-Retrieval. If we are careful to allow every possible order of binding operators (for example, by taking all permutations of the list; see Section 4.5), then we will be able to generate every possible scope ordering of quantifiers.
The next question to address is how we build up a core+store representation compo-sitionally. As before, each phrasal and lexical rule in the grammar will have a SEM feature, but now there will be embedded features CORE and STORE. To illustrate the machinery, let's consider a simpler example, namely Cyril smiles. Here's a lexical rule for the verb smiles (taken from the grammar storage.fcfg), which looks pretty innocuous: IV[SEM=[CORE=<\x.smile(x)>, STORE=(/)]] -> 'smiles'
The rule for the proper name Cyril is more complex.
NP[SEM=[CORE=<@x>, STORE=(<bo(\P.P(cyril),@x)>)]] -> 'Cyril'
The bo predicate has two subparts: the standard (type-raised) representation of a proper name, and the expression @x, which is called the address of the binding operator. (We'll explain the need for the address variable shortly.) @x is a metavariable, that is, a variable that ranges over individual variables of the logic and, as you will see, also provides the value of core. The rule for VP just percolates up the semantics of the IV, and the interesting work is done by the S rule. VP[SEM=?s] -> IV[SEM=?s]
S[SEM=[CORE=<?vp(?subj)>, STORE=(?bl+?b2)]] ->
NP[SEM=[CORE=?subj, STORE=?bl]] VP[SEM=[core=?vp, store=?b2]]
The core value at the S node is the result of applying the VP's core value, namely \x.smile(x), to the subject NP's value. The latter will not be @x, but rather an instantiation of @x, say, z3. After ^-reduction, <?vp(?subj)> will be unified with <smile(z3)>. Now, when @x is instantiated as part of the parsing process, it will be instantiated uniformly. In particular, the occurrence of @x in the subject NP's STORE will also be mapped to z3, yielding the element bo(\P.P(cyril),z3). These steps can be seen in the following parse tree.
(S[SEM=[CORE=<smile(z3)>, STORE=(bo(\P.P(cyril),z3))]] (NP[SEM=[CORE=<z3>, STORE=(bo(\P.P(cyril),z3))]] Cyril) (vP[SEM=[CORE=<\x.smile(x)>, STORE=()]] (IV[SEM=[CORE=<\x.smile(x)>, STORE=()]] smiles)))
Let's return to our more complex example, (52), and see what the storage style SEM value is, after parsing with grammar storage.fcfg. CORE = <chase(z1,z2)>
STORE = (bo(\P.all x.(girl(x) -> P(x)),z1), bo(\P.exists x.(dog(x) & P(x)),z2))
It should be clearer now why the address variables are an important part of the binding operator. Recall that during S-retrieval, we will be taking binding operators off the STORE list and applying them successively to the CORE. Suppose we start with bo(\P.all x. (girl(x) -> P(x)),z1), which we want to combine with chase(z1,z2). The quantifier part of the binding operator is \P.all x.(girl(x) -> P(x)), and to combine this with chase(z1,z2), the latter needs to first be turned into a A-abstract. How do we know which variable to abstract over? This is what the address z1 tells us, i.e., that every girl has the role of chaser rather than chasee.
The module nltk.sem.cooper_storage deals with the task of turning storage-style semantic representations into standard logical forms. First, we construct a CooperStore instance, and inspect its STORE and CORE.
>>> from nltk.sem import cooper_storage as cs >>> sentence = 'every girl chases a dog'
>>> trees = cs.parse_with_bindops(sentence, grammar='grammars/book_grammars/storage.fcfg')
>>> cs_semrep = cs.CooperStore(semrep)
>>> for bo in cs_semrep.store: ... print bo bo(\P.all x.(girl(x) -> P(x)),z1) bo(\P.exists x.(dog(x) & P(x)),z2)
Finally, we call s_retrieve() and check the readings.
>>> cs_semrep.s_retrieve(trace=True) Permutation 1
(\P.all x.(girl(x) -> P(x)))(\z1.chase(z1,z2)) (\P.exists x.(dog(x) & P(x)))(\z2.all x.(girl(x) -> chase(x,z2))) Permutation 2
(\P.exists x.(dog(x) & P(x)))(\z2.chase(z1,z2))
(\P.all x.(girl(x) -> P(x)))(\z1.exists x.(dog(x) & chase(z1,x)))
>>> for reading in cs_semrep.readings: ... print reading exists x.(dog(x) & all z3.(girl(z3) -> chase(z3,x))) all x.(girl(x) -> exists z4.(dog(z4) & chase(x,z4)))
Was this article helpful?