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, output, qubit
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]).read() 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)):
output(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.
Supported: compile-time array copy and unpacking¶
Source file: guppy_examples/guppy-features/supported/comptime-array-copy-unpack.py
from typing import no_type_check
from guppylang import guppy
from guppylang.std.builtins import array, output
@guppy.comptime
@no_type_check
def main() -> None:
values = array(1, 2, 3, 4)
copied = values.copy()
first, *tail = copied
values[0] = 10
output("array_first", first)
output("array_tail_1", tail[1])
output("array_mutated", values[0])
Copying and starred unpacking produce array-like values, so this pattern stays under @guppy.comptime.
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, output, qubit
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
output("st1_0", measure(q1_0).read())
output("st1_1", measure(q1_1).read())
output("st1_2", measure(q1_2).read())
output("st1_3", measure(q1_3).read())
output("st1_4", measure(q1_4).read())
output("st1_5", measure(q1_5).read())
output("st1_6", measure(q1_6).read())
output("st2_0", measure(q2_0).read())
output("st2_1", measure(q2_1).read())
output("st2_2", measure(q2_2).read())
output("st2_3", measure(q2_3).read())
output("st2_4", measure(q2_4).read())
output("st2_5", measure(q2_5).read())
output("st2_6", measure(q2_6).read())
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:
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, output, qubit
from guppylang.std.quantum import collect_measurements, 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])
results = collect_measurements(measure_array(qbs))
output("qbs", results)
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, Bool)
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, output, qubit
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
output("qb0", measure(measure_q).read())
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)
Unsupported: array-backed collections¶
Stack, Queue, and PriorityQueue are higher-level collection APIs, but they are implemented with runtime arrays internally. That means they currently hit the same QIR lowering boundary as direct runtime arrays.
Source file: guppy_examples/guppy-features/unsupported/collections-stack.py
import sys
from typing import no_type_check
from guppylang import guppy
from guppylang.std.builtins import output
from guppylang.std.collections.stack import empty_stack
@guppy
@no_type_check
def main() -> None:
# Stack is backed by a runtime array, which is not supported by QIR lowering.
stack = empty_stack[int, 4]()
stack.push(3)
stack.push(5)
output("stack_len", len(stack))
output("stack_top", stack.pop())
output("stack_next", stack.pop())
stack.discard_empty()
if __name__ == "__main__":
sys.stdout.buffer.write(main.compile().to_bytes())
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:
Unknown type: borrow_array(4, []+[int(6)])
Source file: guppy_examples/guppy-features/unsupported/collections-queue.py
import sys
from typing import no_type_check
from guppylang import guppy
from guppylang.std.builtins import output
from guppylang.std.collections.queue import empty_queue
@guppy
@no_type_check
def main() -> None:
# Queue is backed by a runtime array, which is not supported by QIR lowering.
queue = empty_queue[int, 4]()
queue.push(3)
queue.push(5)
output("queue_len", len(queue))
output("queue_front", queue.pop())
output("queue_next", queue.pop())
queue.discard_empty()
if __name__ == "__main__":
sys.stdout.buffer.write(main.compile().to_bytes())
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:
Unknown type: borrow_array(4, []+[int(6)])
Source file: guppy_examples/guppy-features/unsupported/collections-priority-queue.py
import sys
from typing import no_type_check
from guppylang import guppy
from guppylang.std.builtins import output
from guppylang.std.collections.priority_queue import empty_priority_queue
@guppy
@no_type_check
def main() -> None:
# PriorityQueue is backed by a runtime array, which is not supported by QIR
# lowering.
queue = empty_priority_queue[int, 4]()
queue.push(30, 3)
queue.push(10, 1)
queue.push(20, 2)
output("priority_queue_len", len(queue))
first_priority, first_value = queue.pop()
second_priority, second_value = queue.pop()
output("priority_queue_first_priority", first_priority)
output("priority_queue_first_value", first_value)
output("priority_queue_second_priority", second_priority)
output("priority_queue_second_value", second_value)
_, final_value = queue.pop()
output("priority_queue_final_value", final_value)
queue.discard_empty()
if __name__ == "__main__":
sys.stdout.buffer.write(main.compile().to_bytes())
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:
Unknown type: borrow_array(4, []+[[int(6), int(6)]])