Control¶
with control(c): applies the operations in its body only when the control qubit c is in \(\ket{1}\). With controls \(c_1, \ldots, c_n\), it applies the operation when all controls are in \(\ket{1}\):
You can pass individual qubits or an array of qubits. Array elements may also be controls.
from guppylang import array, guppy
from guppylang.std.builtins import control, nat
from guppylang.std.quantum import h, qubit, x
@guppy
def c2x(controls0: qubit, controls1: qubit, target: qubit) -> None:
with control(controls0, controls1):
x(target)
@guppy
def cnx[n: nat](controls: array[qubit, n], target: qubit) -> None:
with control(controls):
x(target)
@guppy
def main(c0: qubit, c1: qubit, t: qubit) -> None:
c2x(c0, c1, t)
controls = array([c0, c1])
t2 = qubit()
cnx(controls, t2)
cnx.check()
Classical control¶
When a controlled block contains classical control flow, the control is pushed to every quantum operation produced by the branch or loop. Evaluating the classical condition and loop bounds is not controlled. For instance, the following two programs are equivalent:
@guppy
def control_if(c: qubit, q: qubit, flag: bool) -> None:
with control(c):
if flag:
h(q)
else:
x(q)
control_if.check()
@guppy
def pushed_control_if(c: qubit, q: qubit, flag: bool) -> None:
if flag:
with control(c):
h(q)
else:
with control(c):
x(q)
pushed_control_if.check()
The same applies to loops:
@guppy
def control_loop(c: qubit, q: qubit) -> None:
with control(c):
for _ in range(2):
h(q)
@guppy
def pushed_control_loop(c: qubit, q: qubit) -> None:
for _ in range(2):
with control(c):
h(q)
control_loop.check()
pushed_control_loop.check()
Classical assignments in control blocks¶
Classical assignments are ignored by the control modifier: no controlled operation is generated for them. Thus the following programs are equivalent.
from guppylang.std.quantum import angle, rx
@guppy
def control_assignment(c: qubit, q: qubit) -> None:
with control(c):
theta = angle(1 / 4)
rx(q, theta)
@guppy
def pushed_control_assignment(c: qubit, q: qubit) -> None:
theta = angle(1 / 4)
with control(c):
rx(q, theta)
control_assignment.check()
pushed_control_assignment.check()
Forbidden operations¶
Control blocks allow classical operations since they can be evaluated without affecting the quantum state. However, they cannot allocate, measure, reset, or discard qubits, since these operations have quantum effects, but they are not controllable.
from guppylang.std.quantum import measure
@guppy
def allocation_in_control(c: qubit) -> None:
with control(c):
q = qubit()
measure(q)
allocation_in_control.check()
Error: Control constraint violation (at <In[6]>:6:12)
|
4 | def allocation_in_control(c: qubit) -> None:
5 | with control(c):
6 | q = qubit()
| ^^^^^^^ This function cannot be called in a controllable context
Note: The function allocates qubits, which is not allowed in a controllable
context
Guppy compilation failed due to 1 previous error