Skip to content

Commit

Permalink
feat!: Use angle type in quantum operations (#467)
Browse files Browse the repository at this point in the history
Closes #240

BREAKING CHANGE: Quantum operations `rx`, `rz`, `phased_x`, and `zz_max`
use the `angle` type instead of floats.
  • Loading branch information
mark-koch authored Sep 11, 2024
1 parent 4309059 commit ce0f746
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 39 deletions.
10 changes: 6 additions & 4 deletions examples/random_walk_qpe.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
import guppylang.prelude.quantum as quantum
from guppylang.decorator import guppy
from guppylang.module import GuppyModule
from guppylang.prelude.angles import angle
from guppylang.prelude.builtins import py, result
from guppylang.prelude.quantum import cx, discard, h, measure, qubit, rz, x

module = GuppyModule("test")
module.load_all(quantum)
module.load(angle)

sqrt_e = math.sqrt(math.e)
sqrt_e_div = math.sqrt((math.e - 1) / math.e)
Expand Down Expand Up @@ -46,7 +48,7 @@ def random_walk_phase_estimation(
while i < num_iters:
aux = h(qubit())
t = 1 / sigma
aux = rz(h(aux), (sigma - mu) * t)
aux = rz(h(aux), angle((sigma - mu) * t))
aux, tgt = controlled_oracle(aux, tgt, t)
if measure(h(aux)):
mu += sigma / py(sqrt_e)
Expand All @@ -68,10 +70,10 @@ def random_walk_phase_estimation(
def example_controlled_oracle(q1: qubit, q2: qubit, t: float) -> tuple[qubit, qubit]:
"""A controlled e^itH gate for the example Hamiltonian H = -0.5 * Z"""
# This is just a controlled rz gate
angle = -0.5 * t
q2 = rz(q2, angle / 2)
a = angle(-0.5 * t)
q2 = rz(q2, a / 2)
q1, q2 = cx(q1, q2)
q2 = rz(q2, -angle / 2)
q2 = rz(q2, -a / 2)
return cx(q1, q2)


Expand Down
20 changes: 9 additions & 11 deletions examples/t_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from guppylang.decorator import guppy
from guppylang.module import GuppyModule
from guppylang.prelude.angles import angle, pi
from guppylang.prelude.builtins import linst, py
from guppylang.prelude.quantum import (
cz,
Expand All @@ -16,27 +17,24 @@

module = GuppyModule("t_factory")
module.load_all(quantum)
module.load(angle, pi)

phi = np.arccos(1 / 3)
pi = np.pi


@guppy(module)
def ry(q: qubit, theta: float) -> qubit:
q = rx(q, py(pi / 2))
q = rz(q, theta + py(pi))
q = rx(q, py(pi / 2))
return rz(q, py(pi))
def ry(q: qubit, theta: angle) -> qubit:
q = rx(q, pi / 2)
q = rz(q, theta + pi)
q = rx(q, pi / 2)
return rz(q, pi)


# Preparation of approximate T state, from https://arxiv.org/abs/2310.12106
@guppy(module)
def prepare_approx(q: qubit) -> qubit:
phi_ = py(phi)
pi_ = py(pi)

q = ry(q, phi_)
return rz(q, pi_ / 4.0)
q = ry(q, angle(py(phi)))
return rz(q, pi / 4)


# The inverse of the [[5,3,1]] encoder in figure 3 of https://arxiv.org/abs/2208.01863
Expand Down
20 changes: 9 additions & 11 deletions guppylang/prelude/quantum.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
MeasureCompiler,
QAllocCompiler,
)
from guppylang.prelude._internal.util import quantum_op, unsupported_op
from guppylang.prelude._internal.util import quantum_op
from guppylang.prelude.angles import angle

quantum = GuppyModule("quantum")
quantum.load(angle)


@guppy.type(quantum, ht.Qubit, linear=True)
Expand Down Expand Up @@ -74,24 +76,20 @@ def zz_max(q1: qubit, q2: qubit) -> tuple[qubit, qubit]: ...
def measure_return(q: qubit) -> tuple[qubit, bool]: ...


@guppy.hugr_op(
quantum, quantum_op("Rz", ext=HSERIES_EXTENSION)
) # TODO: Use the `tket.quantum` operation once we support angles
def rz(q: qubit, angle: float) -> qubit: ...
@guppy.hugr_op(quantum, quantum_op("Rz"))
def rz(q: qubit, angle: angle) -> qubit: ...


@guppy.hugr_op(
quantum, unsupported_op("Rx")
) # TODO: Use the `tket.quantum` operation once we support angles
def rx(q: qubit, angle: float) -> qubit: ...
@guppy.hugr_op(quantum, quantum_op("Rz"))
def rx(q: qubit, angle: angle) -> qubit: ...


@guppy.hugr_op(quantum, quantum_op("PhasedX", ext=HSERIES_EXTENSION))
def phased_x(q: qubit, angle1: float, angle2: float) -> qubit: ...
def phased_x(q: qubit, angle1: angle, angle2: angle) -> qubit: ...


@guppy.hugr_op(quantum, quantum_op("ZZPhase", ext=HSERIES_EXTENSION))
def zz_phase(q1: qubit, q2: qubit, angle: float) -> tuple[qubit, qubit]: ...
def zz_phase(q1: qubit, q2: qubit, angle: angle) -> tuple[qubit, qubit]: ...


@guppy.hugr_op(quantum, quantum_op("QFree"))
Expand Down
30 changes: 17 additions & 13 deletions tests/integration/test_tket.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from guppylang.decorator import guppy
from guppylang.module import GuppyModule
from guppylang.prelude.angles import pi
from guppylang.prelude.builtins import py
from guppylang.prelude.quantum import qubit, quantum
from guppylang.prelude.quantum import measure, phased_x, rz, zz_max
Expand All @@ -31,23 +32,24 @@ def test_lower_pure_circuit():

module = GuppyModule("test")
module.load_all(quantum)
module.load(pi)

@guppy(module)
def my_func(
q0: qubit,
q1: qubit,
) -> tuple[qubit, qubit]:
q0 = phased_x(q0, py(math.pi / 2), py(-math.pi / 2))
q0 = rz(q0, py(math.pi))
q1 = phased_x(q1, py(math.pi / 2), py(-math.pi / 2))
q1 = rz(q1, py(math.pi))
q0 = phased_x(q0, pi / 2, pi / 2)
q0 = rz(q0, pi)
q1 = phased_x(q1, pi / 2, -pi / 2)
q1 = rz(q1, pi)
q0, q1 = zz_max(q0, q1)
q0 = rz(q0, py(math.pi))
q1 = rz(q1, py(math.pi))
q0 = rz(q0, pi)
q1 = rz(q1, pi)
return (q0, q1)

circ = guppy_to_circuit(my_func)
assert circ.num_operations() == 7
assert circ.num_operations() == 8

tk1 = circ.to_tket1()
assert tk1.n_qubits == 2
Expand All @@ -64,24 +66,26 @@ def test_lower_hybrid_circuit():

module = GuppyModule("test")
module.load_all(quantum)
module.load(pi)

@guppy(module)
def my_func(
q0: qubit,
q1: qubit,
) -> tuple[bool,]:
q0 = phased_x(q0, py(math.pi / 2), py(-math.pi / 2))
q0 = rz(q0, py(math.pi))
q1 = phased_x(q1, py(math.pi / 2), py(-math.pi / 2))
q1 = rz(q1, py(math.pi))
q0 = phased_x(q0, pi / 2, pi / 2)
q0 = rz(q0, pi)
q1 = phased_x(q1, pi / 2, -pi / 2)
q1 = rz(q1, pi)
q0, q1 = zz_max(q0, q1)
_ = measure(q0)
return (measure(q1),)

circ = guppy_to_circuit(my_func)

# The 7 operations in the function, plus two implicit QFree
assert circ.num_operations() == 9
# The 7 operations in the function, plus two implicit QFree, plus one angle
# division op (only counted once since it's in a function?)
assert circ.num_operations() == 10

tk1 = circ.to_tket1()
assert tk1.n_qubits == 2
Expand Down

0 comments on commit ce0f746

Please sign in to comment.