CQCL / lambeq

A high-level Python library for Quantum Natural Language Processing
https://cqcl.github.io/lambeq-docs
Apache License 2.0
451 stars 108 forks source link

Circuit ansatze should be dagger functors #155

Closed kinianlo closed 1 day ago

kinianlo commented 1 week ago

It would be nice to have CircuitAnsatz acting as a dagger functor, that is

ansatz(diagram).dagger() == ansatz(diagram.dagger())

Currently this is not true. For instance:

from lambeq import IQPAnsatz, AtomicType
from lambeq.backend import Word
from lambeq.backend.drawing import draw_equation

ansatz = IQPAnsatz({AtomicType.NOUN: 1}, n_layers=1)
diagram = Word('John', AtomicType.NOUN)

circ1 = ansatz(diagram).dagger()
circ2 = ansatz(diagram.dagger())
draw_equation(circ1, circ2, symbol='!=', figsize=(6, 6))

b13fb54a-38dd-4d41-9e0c-353f6f86a964 Note that the symbols of circ2 are completely different to those in circ1. The ansatz simply takes the dagger as part of the symbol names. This behaviour means that the use of RemoveCupsRewriter would lead to circuits that are outright different to those without the use of the rewriter. This should be undesirable as the role of RemoveCupsRewriter should be merely to improve computational efficiency, while keeping outputs untouched.

The proposed fix

In the _ar method of the base class CircuitAnsat, a check is added to see if the box is daggered. If so, the un-daggered box is passed to the ansatz whose output would be then daggered.

nikhilkhatri commented 1 week ago

Thanks a lot for this PR @kinianlo ! This is definitely something that needs fixing.

Your approach would work for circuits, but could be expanded to extend this behaviour to all functors (not just ansatze).

I'd recommend this be implemented by adding your logic in a apply_functor method added to the Daggered class in grammar.py. Daggered class is here: https://github.com/CQCL/lambeq/blob/main/lambeq/backend/grammar.py#L1547 Example apply_functor from existing Box: https://github.com/CQCL/lambeq/blob/main/lambeq/backend/grammar.py#L447

This would yield the same behaviour for ansatze, and across functors applied to daggered boxes.

kinianlo commented 6 days ago

@nikhilkhatri Thank you for the suggestion! I I’ve tried to implement that but there is a problem at its current state: Diagrammable does not have a dagger() method. Is Diagrammable supposed to have a dagger() method? Otherwise the logic should be applied to specific subclasses of Diagrams with a dagger() defined?

nikhilkhatri commented 5 days ago

@kinianlo I think it makes sense to add a dagger signature to Diagrammable.

Something as simple as the following should work:

    def dagger(self) -> Diagrammable:
        """Implements conjugation of diagrams."""
kinianlo commented 5 days ago

@kinianlo I think it makes sense to add a dagger signature to Diagrammable.

Something as simple as the following should work:


    def dagger(self) -> Diagrammable:

        """Implements conjugation of diagrams."""

Copied and pasted👍