Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Use angle type in quantum operations #467

Merged
merged 1 commit into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this function take an angle?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depends how you interpret things. I always think of the t in e^(-itH) as time so I kept float here. But happy to change the angle if you prefer (will require more explicit angle to float and vice versa conversions though)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

never mind

"""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
Loading