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)