discopy / discopy

The Python toolkit for computing with string diagrams.
https://discopy.org
BSD 3-Clause "New" or "Revised" License
349 stars 66 forks source link

Tensor product of pregroup diagrams #56

Open kinianlo opened 2 years ago

kinianlo commented 2 years ago

Currently, in version 0.3.7.2, the tensor product of two pregroup diagrams is not a pregroup diagram. (pregroup diagrams are those with all the words at the top). This behaviour is evident when trying to draw the tensored diagram with discopy.grammar.draw.

Example

from discopy import Word, Cup, Id
from discopy.grammar import draw 

n, s = Ty('n'), Ty('s')
john = Word('John', n)
talks = Word('talks', n.r @ s)
walks = Word('walks', n.r @ s)

sent1 = john @ talks >> Cup(n, n.r) @ Id(S)
sent2 = john @ walks >> Cup(n, n.r) @ Id(S)

draw(sent1 @ sent2)

ouptut:

ValueError: Expected a pregroup diagram of shape`word @ ... @ word >> cups_and_swaps`, use diagram.draw() instead.

The reason is that the method discopy.rigid.Diagram.tensor puts the boxes of sent2 after all the boxes of sent1, so that the word boxes of sent2 will appear after the cups in sent1.

If I am not mistaken, there is currently no function in discopy that would tensor together two pregroup diagrams while making sure the output is also a pregroup diagram.

I'd suggest adding a function discopy.grammar.tensor that have the required behaviour.

toumix commented 2 years ago

It's true we never had to take the tensor of two pregroup diagrams. What is your use case exactly?

When you have two diagrams f0 >> g0 and f1 >> g1 with f1.dom == x1 and g0.cod = y0, DisCoPy defines their tensor (f0 >> g0) @ (f1 >> g1) as first all of the left-hand side then all of the right hand-side.

In this case what you want is f0 @ f1 >> g0 @ g1, so you could do something like:

def words_and_cups(self):
    i = max(i for i, box in enumerate(self.boxes) if isinstance(box, Word))
    return self[:i], self[i:]

def tensor(self, other):
    f0, g0 = words_and_cups(self)
    f1, g1 = words_and_cups(other)
    return f0 @ f1 >> g0 @ g1

Right now there isn't a dedicated pregroup.Diagram class, they're just a special case of rigid.Diagram so we can't change the way tensor is defined. That's also why we have to do pregroup.draw(diagram) rather than just diagram.draw().

It would be a good idea to implement such a class, I'll make sure to add it to the v1.0 features.