Data Types and Structs¶
These examples show the data-shape patterns that are currently practical for H-Series targets.
Numeric types¶
Source file: guppy_examples/guppy-features/supported/num-types.py
from typing import no_type_check
from guppylang import guppy, qubit
from guppylang.std.angles import angle
from guppylang.std.lang import owned
from guppylang.std.num import nat
from guppylang.std.platform import output
from guppylang.std.quantum import h, measure, rz
@guppy
def plus_or_minus(res: bool) -> int:
if res:
return 1
return -1
@guppy.comptime
@no_type_check
def int_from_reg(
qs: tuple[qubit, qubit, qubit, qubit] @ owned,
) -> tuple[tuple[bool, bool, bool, bool], int]:
res_integer_value = 0
rs = (
measure(qs[0]).read(),
measure(qs[1]).read(),
measure(qs[2]).read(),
measure(qs[3]).read(),
)
for i in range(4):
res_integer_value = (res_integer_value << 1) | int(rs[i])
return rs, res_integer_value
@guppy
@no_type_check
def main() -> None:
qs = qubit(), qubit(), qubit(), qubit()
my_float = 0.4 # constant float is ok, any arithmetic or round(),
rz(qs[0], angle(my_float))
h(qs[1])
h(qs[2])
h(qs[3])
h(qs[3])
h(qs[2])
h(qs[1])
rs, rs_int = int_from_reg(qs)
output("q0", rs[0])
output("q1", rs[1])
output("q2", rs[2])
output("q3", rs[3])
output("big_endian_res", rs_int)
random_sum = 0
random_sum += plus_or_minus(rs[0])
random_sum += plus_or_minus(rs[1])
random_sum += plus_or_minus(rs[2])
random_sum += plus_or_minus(rs[3])
output("random_sum", random_sum)
rsum2 = random_sum * random_sum
six = nat(6)
four = nat(4)
example_result = six + four - rsum2
output("int_res", example_result)
This page is especially useful for the current float caveat: constant float values are fine, but runtime float arithmetic is not supported on H-Series.
Tuples¶
Source file: guppy_examples/guppy-features/supported/guppy-tuple.py
import sys
from typing import no_type_check
from guppylang import guppy
from guppylang.std.builtins import output, qubit
from guppylang.std.lang import owned
from guppylang.std.quantum import cx, measure, x
@guppy
def create_steane() -> tuple[qubit, qubit, qubit, qubit, qubit, qubit, qubit]:
return qubit(), qubit(), qubit(), qubit(), qubit(), qubit(), qubit()
@guppy.comptime
def steane_x(stq: tuple[qubit, qubit, qubit, qubit, qubit, qubit, qubit]) -> None:
for i in range(7):
x(stq[i])
@guppy.comptime
def steane_cx(
stq1: tuple[qubit, qubit, qubit, qubit, qubit, qubit, qubit],
stq2: tuple[qubit, qubit, qubit, qubit, qubit, qubit, qubit],
) -> None:
for i in range(7):
cx(stq1[i], stq2[i])
@no_type_check
def steane_measure(
stq: list[qubit] @ owned,
) -> tuple[bool, bool, bool, bool, bool, bool, bool]:
return tuple([measure(stq[i]).read() for i in range(7)])
@no_type_check
def steane_measure_result(
stq1: tuple[str, tuple[qubit, qubit, qubit, qubit, qubit, qubit, qubit]] @ owned,
) -> None:
name, qbs = stq1
qblist = list(qbs)
res = steane_measure(qblist)
for i in range(7):
output(f"{name}_{i}", res[i])
@guppy.comptime
@no_type_check
def main() -> None:
steane_q1 = (
"q1",
create_steane(),
) # this is a python tuple of a python str and guppy tuple
steane_q2 = "q2", create_steane()
steane_x(steane_q1[1])
steane_x(steane_q2[1])
steane_cx(steane_q1[1], steane_q2[1])
steane_measure_result(steane_q1)
steane_measure_result(steane_q2)
if __name__ == "__main__":
sys.stdout.buffer.write(main.compile().to_bytes())
This example stays within the supported subset by doing tuple-oriented bulk operations under @guppy.comptime.
Tuple unpacking¶
Source file: guppy_examples/guppy-features/supported/tuple-unpack.py
from typing import no_type_check
from guppylang import guppy
from guppylang.std.builtins import output
@guppy
@no_type_check
def split_pair(pair: tuple[int, int]) -> int:
left, right = pair
return left * 10 + right
@guppy
@no_type_check
def main() -> None:
output("split_pair", split_pair((4, 7)))
Fixed-shape tuple unpacking is supported. Starred unpacking is more constrained because the starred part is array-like; see Arrays.
Structs¶
Source file: guppy_examples/guppy-features/supported/guppy-struct.py
from __future__ import annotations
import sys
from typing import TYPE_CHECKING, no_type_check
from guppylang.decorator import guppy
from guppylang.std.builtins import output, qubit
from guppylang.std.quantum import cx, cz, measure, x
if TYPE_CHECKING:
from guppylang.std.lang import owned
@guppy.struct
class SteaneQubit:
pq0: qubit
pq1: qubit
pq2: qubit
pq3: qubit
pq4: qubit
pq5: qubit
pq6: qubit
@guppy
def x_all(self) -> None:
"""Single Steane Qubit op"""
x(self.pq0)
x(self.pq1)
x(self.pq2)
x(self.pq3)
x(self.pq4)
x(self.pq5)
x(self.pq6)
@guppy
def cx_all(self, other: SteaneQubit) -> None:
"""Binary Steane Qubit op"""
cx(self.pq0, other.pq0)
cx(self.pq1, other.pq1)
cx(self.pq2, other.pq2)
cx(self.pq3, other.pq3)
cx(self.pq4, other.pq4)
cx(self.pq5, other.pq5)
cx(self.pq6, other.pq6)
@no_type_check
def steane_measure_result(steane_qb: SteaneQubit @ owned, name: str) -> None:
"""
Measure and record results for all qubits of a SteaneQubit
The only way to get naming to work well is to implement this method as a
pure python function and call it within @guppy.comptime. Otherwise, the
name string passed into the function will be a runtime guppy string, which
cannot be printed at compile time. Also, guppy runtime doesn't have string
operations, so strings can't be modified at runtime, which we would need to
do here.
:param steane_qb: The Steane Qubit to measure
:param name: Name of Steane Qubit for result identification
"""
output(f"{name}_0", measure(steane_qb.pq0).read())
output(f"{name}_1", measure(steane_qb.pq1).read())
output(f"{name}_2", measure(steane_qb.pq2).read())
output(f"{name}_3", measure(steane_qb.pq3).read())
output(f"{name}_4", measure(steane_qb.pq4).read())
output(f"{name}_5", measure(steane_qb.pq5).read())
output(f"{name}_6", measure(steane_qb.pq6).read())
def steane_cz(q1: SteaneQubit, q2: SteaneQubit) -> None:
"""Alternative definition of a binary Steane Qubit op"""
cz(q1.pq0, q2.pq0)
cz(q1.pq1, q2.pq1)
cz(q1.pq2, q2.pq2)
cz(q1.pq3, q2.pq3)
cz(q1.pq4, q2.pq4)
cz(q1.pq5, q2.pq5)
cz(q1.pq6, q2.pq6)
@guppy.comptime
@no_type_check
def main() -> None:
steane = SteaneQubit(*[qubit() for _ in range(7)])
other_steane = SteaneQubit(*[qubit() for _ in range(7)])
x(steane.pq0)
x(steane.pq2)
steane.x_all()
steane.cx_all(other_steane)
steane_cz(steane, other_steane)
steane_measure_result(steane, "q1")
steane_measure_result(other_steane, "q2")
if __name__ == "__main__":
sys.stdout.buffer.write(main.compile().to_bytes())
This example is a good pattern to prefer over runtime arrays when you want a fixed-size register-like object in supported H-Series code.
Enums¶
Source file: guppy_examples/guppy-features/supported/enum-method.py
from typing import no_type_check
from guppylang import guppy
from guppylang.std.builtins import output
@guppy.enum
class Flag:
On = {"value": int} # noqa: RUF012
Off = {} # noqa: RUF012
@guppy
@no_type_check
def score(self: "Flag") -> int:
return 1
@guppy
@no_type_check
def main() -> None:
flag = Flag.On(4)
output("enum_score", flag.score())
Type aliases¶
Source file: guppy_examples/guppy-features/supported/type-alias.py
from typing import no_type_check
from guppylang import guppy
from guppylang.std.builtins import output
Row = guppy.type_alias("Row", "tuple[int, int, int, int]")
T = guppy.type_var("T")
Pair = guppy.type_alias("Pair", "tuple[T, T]", params=[T])
@guppy
@no_type_check
def row_sum(row: Row) -> int:
return row[0] + row[1] + row[2] + row[3]
@guppy
@no_type_check
def pair_first(pair: Pair[int]) -> int:
return pair[0]
@guppy.comptime
@no_type_check
def main() -> None:
output("row_sum", row_sum((1, 2, 3, 4)))
output("pair_first", pair_first((8, 13)))
Type aliases are supported when the expanded type is supported. Aliases that expand to runtime arrays still inherit the runtime-array limitation.
Option, Result, and Either¶
Source file: guppy_examples/guppy-features/supported/option-result-either.py
from typing import no_type_check
from guppylang import guppy
from guppylang.std.builtins import output
from guppylang.std.either import Either, left, right
from guppylang.std.err import Result, ok
from guppylang.std.option import Option, some
@guppy
@no_type_check
def option_value(value: int) -> Option[int]:
return some(value)
@guppy
@no_type_check
def result_value(value: int) -> Result[int, int]:
return ok(value)
@guppy
@no_type_check
def choose(flag: bool) -> Either[int, bool]:
if flag:
return left(7)
return right(False)
@guppy
@no_type_check
def main() -> None:
opt = option_value(5)
res = result_value(9)
either = choose(True)
output("option_is_some", opt.is_some())
output("option_value", opt.unwrap())
output("result_is_ok", res.is_ok())
output("result_value", res.unwrap())
output("either_is_left", either.is_left())
These sum types are supported when their payload types are supported.