Higher-Order DisCoCat#

(Peirce-Lambek-Montague Semantics)#

arXiv:2311.17813

1) Define Formula as a subclass of frobenius.Diagram#

[1]:

from discopy import frobenius from discopy.tensor import Dim, Tensor from discopy.cat import Category, factory @factory class Formula(frobenius.Diagram): ty_factory = frobenius.PRO # i.e. natural numbers as objects def eval(self, size): return frobenius.Functor( ob=lambda _: Dim(size), ar=lambda box: box.data, cod=Category(Dim, Tensor[bool]))(self) class Cut(frobenius.Bubble, Formula): pass class Ligature(frobenius.Spider, Formula): pass class Predicate(frobenius.Box, Formula): pass Id, Formula.bubble_factory = Formula.id, Cut Tensor[bool].bubble = lambda self, **_: self.map(lambda x: not x)

2) Parse natural language sentences using a categorial grammar#

[2]:
from discopy.grammar.categorial import Ty, Word, Eval

n, p, s = Ty('n'), Ty('p'), Ty('s')  # noun, phrase and sentence

Alice = Word("Alice", p)
big, sleeps = Word("big", n << n), Word("sleeps", p >> s)
man, island = (Word(noun, n) for noun in ("man", "island"))
kills, _is = (Word(verb, (p >> s) << p) for verb in ("kills", "is"))
no, every, some = (Word(det, p << n) for det in ("no", "every", "some"))

Alice_kills_a_mortal = (Alice @ kills @ some @ man
    >> p @ ((p >> s) << p) @ Eval(p << n)
    >> p @ Eval((p >> s) << p) >> Eval(p >> s))
every_big_man_sleeps = (every @ big @ man @ sleeps
    >> ((p << n) @ Eval(n << n) >> Eval(p << n))
    @ (p >> s) >> Eval(p >> s))
no_man_is_an_island = (no @ man @ _is @ some @ island
    >> Eval(p << n) @ ((p >> s) << p) @ Eval(p << n)
    >> p @ Eval((p >> s) << p) >> Eval(p >> s))
[3]:
# Generating a random interpretation to test our model

from random import choice

size = 42
random_bits = lambda n=size: [choice([True, False]) for _ in range(n)]

is_killed_by = [random_bits() for _ in range(size)]
unary_predicates = is_Alice, is_man, is_island, is_big, is_sleeping = [
    random_bits() for _ in range(5)]

K = Predicate("K", 1, 1, data=is_killed_by)
A, M, I, B, S = (Predicate(P, 0, 1, data)
                 for P, data in zip("AMIBS", unary_predicates))

3) Higher-order DisCoCat as a closed functor into Python functions#

[4]:

from typing import Callable from discopy import closed from discopy.python import Function F = closed.Functor( cod=Category(tuple[type, ...], Function), ob={s: Formula, n: Formula, p: Callable[[Formula], Formula]}, ar={Alice: lambda: lambda f: A >> f, sleeps: lambda: lambda P: P(S.dagger()), man: lambda: M, island: lambda: I, big: lambda: lambda f: f @ B >> Ligature(2, 1, frobenius.PRO(1)), _is: lambda: lambda P: lambda Q: Q(P(Id(1)).dagger()), kills: lambda: lambda P: lambda Q: Q(P(K).dagger()), no: lambda: lambda state: lambda effect: (state >> effect).bubble(), some: lambda: lambda state: lambda effect: state >> effect, every: lambda: lambda state: lambda effect: ( state >> effect.bubble()).bubble()}) evaluate = lambda sentence: bool(F(sentence)().eval(size)) assert evaluate(Alice_kills_a_mortal) == any( is_man[y] and is_killed_by[y][x] and is_Alice[x] for x in range(size) for y in range(size)) assert evaluate(every_big_man_sleeps) == all( not (is_big[x] and is_man[x]) or is_sleeping[x] for x in range(size)) assert evaluate(no_man_is_an_island) == all( not is_man[x] or not is_island[x] for x in range(size))