Dagger

with dagger: applies the inverse of its body. If the body performs \(U_1\) followed by \(U_2\), the dagger block performs \(U_2^\dagger\) followed by \(U_1^\dagger\).

from guppylang import guppy
from guppylang.std.builtins import control, dagger
from guppylang.std.quantum import h, qubit, s, x

@guppy
def sx_dg(q: qubit) -> None:
    with dagger:
        s(q)
        h(q)

sx_dg.check()

For instance, the previous program corresponds to the sequence of operations is equivalent to:

sdg q;
h q;

Forbidden operations in dagger blocks

Dagger blocks have the same qubit-operation restrictions as control blocks and cannot contain control flow.

@guppy
def branch_in_dagger(q: qubit, flag: bool) -> None:
    with dagger:
        if flag:
            h(q)

branch_in_dagger.check()
Error: Invalid expression in dagger (at <In[2]>:4:8)
  | 
2 | def branch_in_dagger(q: qubit, flag: bool) -> None:
3 |     with dagger:
4 |         if flag:
  |         ^^^^^^^^
5 |             h(q)
  | ^^^^^^^^^^^^^^^^ Branch found in a dagger context

Note:
  | 
2 | def branch_in_dagger(q: qubit, flag: bool) -> None:
3 |     with dagger:
  |          ------ dagger modifier is used here

Help: Control flow statements (e.g. loops and branches) are not allowed in
daggered contexts.

Guppy compilation failed due to 1 previous error
@guppy
def loop_in_dagger(q: qubit) -> None:
    with dagger:
        for _ in range(2):
            h(q)

loop_in_dagger.check()
Error: Invalid expression in dagger (at <In[3]>:4:8)
  | 
2 | def loop_in_dagger(q: qubit) -> None:
3 |     with dagger:
4 |         for _ in range(2):
  |         ^^^^^^^^^^^^^^^^^^
5 |             h(q)
  | ^^^^^^^^^^^^^^^^ Loop found in a dagger context

Note:
  | 
2 | def loop_in_dagger(q: qubit) -> None:
3 |     with dagger:
  |          ------ dagger modifier is used here

Help: Control flow statements (e.g. loops and branches) are not allowed in
daggered contexts.

Guppy compilation failed due to 1 previous error

They also cannot perform observable classical effects such as output, panic, or exit: reversing the quantum operations must not change when those effects occur.

from guppylang.std.builtins import output

@guppy
def output_in_dagger(value: bool) -> None:
    with dagger:
        output("value", value)

output_in_dagger.check()
Error: Dagger constraint violation (at <In[4]>:6:8)
  | 
4 | def output_in_dagger(value: bool) -> None:
5 |     with dagger:
6 |         output("value", value)
  |         ^^^^^^^^^^^^^^^^^^^^^^ This function cannot be called in a daggerable context
  |                                because it is not daggerable itself

Guppy compilation failed due to 1 previous error

Combining dagger and controls

Modifiers compose. This example is a doubly controlled inverse of h followed by s:

@guppy
def doubly_controlled_inverse(c0: qubit, c1: qubit, q: qubit) -> None:
    with control(c0):
        with dagger:
            with control(c1):
                h(q)
                s(q)

doubly_controlled_inverse.check()

Resolving one modifier at a time gives this sequence:

Source:              h q;                    s q;
Push control c1:     ctrl @ h c1, q;         ctrl @ s c1, q;
Resolve dagger:      ctrl @ sdg c1, q;      ctrl @ h c1, q;
Push control c0:     ctrl(2) @ sdg c0, c1, q;
                     ctrl(2) @ h c0, c1, q;