Arrays¶
These examples show the current boundary for array-like programming for H-Series compatible Guppy.
The important theme is that compile-time arrays can be useful as a structuring tool, but runtime array values and helpers that depend on borrowed array representations are not yet supported by hugr-qir.
Supported: compile-time arrays¶
Source file: guppy_examples/guppy-features/supported/comptime-array.py
from __future__ import annotations
import sys
from typing import TYPE_CHECKING, no_type_check
from guppylang import guppy
from guppylang.std.builtins import array, qubit, result
from guppylang.std.quantum import cx, measure, x
if TYPE_CHECKING:
from guppylang.std.lang import owned
@no_type_check
def create_steane(name: str) -> tuple[str, array[qubit, 7]]:
return name, [qubit() for _ in range(7)]
def steane_x(stq: list[qubit]) -> None:
for q in stq:
x(q)
def steane_cx(
stq1: list[qubit],
stq2: list[qubit],
) -> None:
num_q = len(stq1)
for i in range(num_q):
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]) for i in range(7)])
@no_type_check
def steane_measure_result(
stq1: tuple[str, list[qubit]] @ owned,
) -> None:
name, qbs = stq1
res = steane_measure(qbs)
for i in range(len(res)):
result(f"{name}_{i}", res[i])
@guppy.comptime
@no_type_check
def main() -> None:
steane_q1 = create_steane("q1")
steane_q2 = create_steane("q2")
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 works because the array manipulation happens under @guppy.comptime, so the loop structure and indexing can be resolved before QIR emission.
Unsupported: runtime arrays¶
Source file: guppy_examples/guppy-features/unsupported/runtime-array.py
import sys
from typing import no_type_check
from guppylang import guppy
from guppylang.std.builtins import array, qubit, result
from guppylang.std.quantum import cx, h, measure
@guppy
@no_type_check
def create_steane() -> array[qubit, 7]:
return array(qubit() for _ in range(7))
@guppy
@no_type_check
def steane_h(stq: array[qubit, 7]) -> None:
for i in range(7):
h(stq[i])
@guppy
@no_type_check
def steane_cx(
stq1: array[qubit, 7],
stq2: array[qubit, 7],
) -> None:
for i in range(7):
cx(stq1[i], stq2[i])
@guppy
@no_type_check
def main() -> None:
steane_q1 = create_steane()
steane_q2 = create_steane()
steane_h(steane_q1)
steane_h(steane_q2)
steane_cx(steane_q1, steane_q2)
q1_0, q1_1, q1_2, q1_3, q1_4, q1_5, q1_6 = steane_q1
q2_0, q2_1, q2_2, q2_3, q2_4, q2_5, q2_6 = steane_q2
result("st1_0", measure(q1_0))
result("st1_1", measure(q1_1))
result("st1_2", measure(q1_2))
result("st1_3", measure(q1_3))
result("st1_4", measure(q1_4))
result("st1_5", measure(q1_5))
result("st1_6", measure(q1_6))
result("st2_0", measure(q2_0))
result("st2_1", measure(q2_1))
result("st2_2", measure(q2_2))
result("st2_3", measure(q2_3))
result("st2_4", measure(q2_4))
result("st2_5", measure(q2_5))
result("st2_6", measure(q2_6))
if __name__ == "__main__":
sys.stdout.buffer.write(main.compile().to_bytes())
This version looks structurally similar to the compile-time example above, but it keeps the arrays in ordinary @guppy functions and therefore requires runtime array support during lowering.
Expected error:
QIR generation failed. This may be the result of a bug but can also happen when trying to convert a feature in HUGR/Guppylang which is not supported in QIR. Error details: Failed to emit LLVM for function guppy_example_mod.main at node Node(1)
Caused by:
0: Failed to emit LLVM for node N<CFG:Node(4)> with optype CFG
1: Unknown type: borrow_array(7, qubit)
Unsupported: measure_array¶
Source file: guppy_examples/guppy-features/unsupported/measure-array.py
import sys
from typing import no_type_check
from guppylang import guppy
from guppylang.std.builtins import array, qubit, result
from guppylang.std.quantum import cx, h, measure_array
@guppy.comptime
@no_type_check
def main() -> None:
qbs = array(qubit() for _ in range(8)) # comptime array is ok
for i in range(8):
if i % 2 == 0:
h(qbs[i])
else:
cx(qbs[i - 1], qbs[i])
result("qbs", measure_array(qbs))
if __name__ == "__main__":
sys.stdout.buffer.write(main.compile().to_bytes())
This fails because measure_array relies on borrowed array types that are not currently emitted by the backend.
Expected error:
QIR generation failed. This may be the result of a bug but can also happen when trying to convert a feature in HUGR/Guppylang which is not supported in QIR. Error details: Failed to emit LLVM for function main at node Node(1)
Caused by:
Unknown type: borrow_array(8, qubit)
Unsupported: discard_array¶
Source file: guppy_examples/guppy-features/unsupported/discard-array.py
import sys
from typing import no_type_check
from guppylang import guppy
from guppylang.std.builtins import array, qubit, result
from guppylang.std.quantum import cx, discard_array, h, measure
@guppy.comptime
@no_type_check
def main() -> None:
qbs = array(qubit() for _ in range(8)) # comptime array is ok
for i in range(8):
if i % 2 == 0:
h(qbs[i])
else:
cx(qbs[i - 1], qbs[i])
measure_q, *discard_qs = qbs
result("qb0", measure(measure_q))
discard_array(discard_qs)
if __name__ == "__main__":
sys.stdout.buffer.write(main.compile().to_bytes())
Like measure_array, this path depends on array borrowing support that is not yet available in QIR emission.
Expected error:
QIR generation failed. This may be the result of a bug but can also happen when trying to convert a feature in HUGR/Guppylang which is not supported in QIR. Error details: Failed to emit LLVM for function main at node Node(1)
Caused by:
Unknown type: borrow_array(7, qubit)