This course provides an introduction to what is involved in actually implementing, in a computational sense, a system of compositional semantics of the sort commonly assumed in theoretical linguistics and philosophy (see e.g. Szabó 2017). The target audience is students who have had introductory-level programming experience, as well as basic exposure to linguistic or logical semantics in some form, or have basic compositional semantics experience; it is an introductory course that does not assume deep background knowledge in either area.
This course assumes:
I expect it will lean on 1 a bit more than 2. However:
What is your background?
Sloganized: The meaning of the whole is determined by the meanings of the parts and the ways in which they combine.
Szabo, SEP entry on compositionality def (C): The meaning of a complex expression is determined by its structure and the meanings of its constituents.
(1) a. The chair is under the rug.
b. The rug is under the chair.
Frege's Conjecture (Heim and Kratzer 1998): Semantic composition is function application.
(1') a. under(the_rug)
b. under(the_chair)
(1'') a. (under(the_rug))(the_chair)
b. (under(the_chair))(the_rug)
tree1 = ("S", ("NP", ("D", "the"), ("N", "chair")), ("VP", ("V", "is"),
("PP", ("P", "under"),
("NP", ("D", "the"), ("N", "rug")))))
tree2 = ("S", ("NP", ("D", "the"), ("N", "rug")), ("VP", ("V", "is"),
("PP", ("P", "under"),
("NP", ("D", "the"), ("N", "chair")))))
SideBySide(svgling.draw_tree(tree1), svgling.draw_tree(tree2))
A semantic analysis that gives details is sometimes called an implementation.
Marr 1982 (pp. 24-25):
What is sufficient to count as an implementation? No one answer. Some possibilities for now:
We need to differentiate competence/"proof of concept" implementation vs. performance/human-like implementation. We will not be doing psycholinguistics here...
This course is about what it takes to have an executable, competence-oriented, implementation.
I'm not the only one working in this space, but it's not crowded. A few pointers:
(1) two plus two is four
(2) three times two plus two is eight
...
Straightforward mapping:
# Some rather basic python code
2 + 2 == 4
True
# some very slightly less basic python code
def isV(x): # `is` is a python reserved word
return lambda y: x == y
isV(2+2)(4)
True
# even less basic, but closer to what we want...
def isV(x):
return lambda y: x == y
def plus(x):
return lambda y: y + x
def two():
return 2
def four():
return 4
isV(plus(two())(two()))(four())
True
Alright, but how could you more fully implement arithmeticese as a natural language fragment? Here's some lambda notebook code to do it:
%%lamb
||two|| = 2
||four|| = 4
||isV|| = lambda x_n: lambda y_n: x <=> y
||plus|| = lambda x_n: lambda y_n: x + y
||times|| = lambda x_n: lambda y_n: x * y
(two * (plus * two)).trace()
(Derivation for step 5 is $\Downarrow$)
(two * (plus * two))[0].content.derivation
((two * (plus * two)) * (isV * four)).trace()
(Derivation for step 9 is $\Downarrow$)
((two * (plus * two)) * (isV * four))[0].content.derivation
Ok, that was fun, but what's the point?
A few questions to be answered:
Perhaps worth noting: NLU/NLP is not currently in the business of doing anything like this!
Dall-E mini on doing semantics:
There was a brief window around 2010 where it seemed like mainstream NLP might take on the challenge I am developing here. This wave was crushed by language modeling.
A few high points (for me) if you want to follow up; see the Kornai Sunday bootcamp for a lot more on this topic:
Unfortunately open question.
Certain recent work has benefitted from the overall compositional architecture, mapping object-language to metalanguage. A few highlights that I hope to touch on on Friday:
As noted earlier, this course won't introduce python. But, a quick cheat sheet if you're rusty:
def fname(arguments):
followed by an indented block. There's also a lambda args: returnval
notation for writing "anonymous" functions.==
for checking equality, =
for assignment. and
, or
, not
for boolean operators.[]
, e.g. ['a','b','c']
, tuples use ()
(with at least one comma), sets and dicts (~ hash tables) both use {}
except that dicts have a mapping for each element (e.g. {1: 'a', 2: 'b', 3: 'c'}
)if condition:... elif condition: ... else:
. for x in iter:
, while condition:
try:...except:...
blocks.Jupyter: takes the idea of an interactive interpreter and goes to 11, combining it with techniques for integrating code and documentation.
Note: Google Colab (seen in Khalil Iskarous' course) is running a jupyter-based UI. However, it's (annoyingly) not compatible with lambda notebook frontend code. TBD.
Jupyter breaks code into units of "cells".
None
, it's displayed.)x = "this is a string assigned to a variable x"
x
'this is a string assigned to a variable x'
print("You can also use python `print`, but it doesn't determine the cell output")
2+2
You can also use python `print`, but it doesn't determine the cell output
4
The jupyter frontend can handle displaying many useful things beyond text, including graphical outputs, and this is why Jupyter is fairly popular in data science. It also can be controlled more directly, e.g. via the display
function.
from IPython.display import HTML
import nltk
display(HTML("<b>A table drawn via an HTML object:</b><table><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr><tr><td>5</td><td>6</td></tr></table><br /><b>A tree drawn via an SVG object:</b>"))
display(nltk.Tree.fromstring("(S NP (VP V NP))")) # nltk trees render to a svg object via `svgling`
1 | 2 |
3 | 4 |
5 | 6 |
Jupyter notebooks also support MathJax in markdown (including in markdown cell outputs). Aka latex math mode in the browser:
$\lambda x_e . \mathit{Cat}'(x)$
renders as:
$\lambda x_e . \mathit{Cat}'(x)$
More advanced frontend things: injected javascript code, widgets / notebook extensions, ...
There's so much potential here for interesting interactive tools, teaching aids, etc...largely untapped in linguistics/logic.
Tree diagrams were on this list for a long time until I finally buckled down and wrote a solution myself - svgling.
If you see a %%
at the beginning of a cell, the cell is invoking a magic
. These change the behavior of cells from processing python, in potentially drastic ways.
%
for line magics
.Lambda notebook uses various custom magics (return to this shortly):
%%lamb
||cat|| = lambda x_e : Cat_<e,t>(x)
Behind the scenes, each jupyter notebook makes use of a kernel while running. This is the basic program that executes cells.
If there's one thing that trips people up in Jupyter, it's order of execution.
Tips: be wary of running out of order. Make cells self-contained. Group import
s. Pay attention to input numbers. Use "Run all above selected cell" (and other "Run all" commands). When in doubt, restart the kernel and run all.
Jupyter's best use case is not for developing software that will be called by other notebooks.
Great for:
That's Jupyter in a nutshell -- questions?
Compositional semantics provides a clear starting point for how to approach implementing.
The lambda notebook has 4 parts:
The metalanguage is an extension of a relatively straightforward first-order logic + lambda calculus.
%te
line magic.%te L f_<e,t>: L g_<e,t>: L x_e: f(x) & g(x)
A composition system is a set of composition operations together with a bunch of code to do something with them.
*
operator, the composition system looks for a successful combination.lamb.lang.get_system()
The type system has:
Goal: provide a means of implementing, in python, key elements of Linguistically-focused Compositional Semantics
def isV(x):
return lambda y: x == y
def plus(x):
return lambda y: y + x
def two():
return 2
def four():
return 4
isV(plus(two())(two()))(four())
True