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)]])