monoid-delooping#

We give an example of a custom monoidal category in discopy, with a monoidal functor into it.

The monoidal category is given by the delooping of a commutative monoid, see the nLab. It has one object, arrows given by the monoid elements, composition and tensor both given by the monoid product.

We encode a context-free grammar as a free monoidal category, then taking monoidal functors into the delooping of the nonnegative reals yields a weighted context-free grammar.

[1]:
from discopy import Ty, Box, Id, Functor
[2]:
# The CFG's non-terminals are types.

s, n, tv, itv = Ty('s'), Ty('n'), Ty('tv'), Ty('itv')

# The CFG's production rules are boxes.

r0 = Box('r0', s, n @ itv)
r1 = Box('r1', itv, tv @ n)

# We model the words as effects, i.e. boxes with empty codomains.

Alice = Box('Alice', n, Ty())
loves = Box('loves', tv, Ty())
Bob = Box('Bob', n, Ty())

# A syntax tree is a diagram!

syntax_tree = r0 >> Id(n) @ r1
sentence = syntax_tree >> Alice @ loves @ Bob
sentence.draw(aspect='auto')
../_images/notebooks_monoid-delooping_2_0.png
[3]:
"""
We define monoid elements as a subclass of Box.
It is enough to override __init__, __repr__, then, tensor and id.
"""

class Monoid(Box):
    def __init__(self, m):
        self.m = m
        super().__init__(m, Ty(), Ty())

    def __repr__(self):
        return "Monoid({})".format(self.m)

    def then(self, other):
        if not isinstance(other, Monoid):
            import pdb; pdb.set_trace()
            raise ValueError
        return Monoid(self.m * other.m)

    def tensor(self, other):
        return self.then(other)

    @staticmethod
    def id(x):
        if x != Ty():
            raise ValueError
        return Monoid(1)

# Now we can take arbitrary diagrams with Monoid boxes.
# Both tensor and composition are given by product, the unit is 1.
Monoid(2) @ Monoid.id(Ty()) >> Monoid(5) @ Monoid(0.1)
[3]:
Monoid(1.0)
[4]:
"""
We define monoid functors as a subclass of monoidal functors.
"""

class MonoidFunctor(Functor):
    def __init__(self, ob, ar):
        super().__init__(ob, ar, ar_factory=Monoid)

ob = {x: Ty() for x in [s, n, tv, itv]}
ar = {r0: Monoid(0.25), r1: Monoid(2.0)}
ar.update({w: Monoid(1) for w in [Alice, loves, Bob]})

F = MonoidFunctor(ob, ar)
F(sentence)
[4]:
Monoid(0.5)