{ "cells": [ { "cell_type": "markdown", "id": "12d9be42", "metadata": {}, "source": [ "# Guppy to H2" ] }, { "cell_type": "markdown", "id": "4abe570c", "metadata": {}, "source": [ "Guppy is a quantum-first programming language built for Quantinuum Helios. An experimental tool, HUGR-QIR, enables conversion of a Guppy program into QIR. As a result, experimental QIR conversion enables execution on System Model H2 with Guppy programs. QIR is an industry standard tool used to access different hardware platforms.\n", "\n", "HUGR (Hierarchical Unified Graph Representation) is a compact graph-based representation of Guppy programs. HUGR and QIR are equivalents. Whereas opensource off-the-shelf tools (NVIDIA CUDA-Q) generate QIR, HUGR is generated from Guppy source. The HUGR-QIR package requires the user first generate HUGR, before transpiling to QIR. HUGR-generated QIR is verified for backwards compatibility on System Model H2, using the quantinuum qir checker {footcite:p}`quantinuum_qircheck`.\n", "\n", "For Guppy features supported by QIR, please see [here](index.ipynb#platform-specifications). Not all Guppy features are available in the full QIR adaptive profile (specified in the linked table). In addition to this, HUGR-QIR does not support conversion of the following Guppy features:\n", "\n", "* Guppy functions accepting or returning arrays (See [caveats](#use-of-compile-time-arrays))\n", "* Unbounded Loops\n", "* Dynamic Qubit Allocation" ] }, { "cell_type": "markdown", "id": "5675fc8d", "metadata": {}, "source": [ "## Installation\n", "\n", "```{note}\n", "Please note that the hugr-qir compiler is not compatible with Guppy v1. Continue to use the Guppy v0.x line.\n", "```\n", "\n", "HUGR-QIR is available on PyPi and requires Python 3.10. The corresponding GitHub repository is available at [https://github.com/quantinuum/hugr-qir.git](https://github.com/quantinuum/hugr-qir.git).\n", "\n", "```{code} bash\n", "pip install hugr-qir guppylang==0.*\n", "```" ] }, { "cell_type": "markdown", "id": "8ebc5395", "metadata": {}, "source": [ "## User Workflow" ] }, { "cell_type": "code", "execution_count": 1, "id": "4938009e", "metadata": {}, "outputs": [], "source": [ "from typing import no_type_check\n", "\n", "from guppylang import guppy, qubit\n", "from guppylang.std.builtins import result\n", "from guppylang.std.quantum import h, measure\n", "\n", "@guppy\n", "@no_type_check\n", "def main() -> None:\n", " q0 = qubit()\n", " q1 = qubit()\n", "\n", " h(q0)\n", " h(q1)\n", "\n", " b0 = measure(q0)\n", " b1 = measure(q1)\n", " b2 = b0 ^ b1\n", "\n", " result(\"0\", b2)" ] }, { "cell_type": "markdown", "id": "2f0d1f53", "metadata": {}, "source": [ "By default, the function will automatically validate the generated QIR. Using the keyword argument, `output_format=OutputFormat.BITCODE`, returns QIR bitcode." ] }, { "cell_type": "code", "execution_count": 2, "id": "be9c3688", "metadata": {}, "outputs": [], "source": [ "from hugr_qir.hugr_to_qir import hugr_to_qir\n", "from hugr_qir.output import OutputFormat\n", "\n", "guppy_qir_bitcode = hugr_to_qir(main.compile(), output_format=OutputFormat.BITCODE)" ] }, { "cell_type": "markdown", "id": "b6eb89ae", "metadata": {}, "source": [ "Specifying `OutputFormat.LLVM_IR` returns textual QIR." ] }, { "cell_type": "code", "execution_count": 3, "id": "9f5bc36f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "; ModuleID = 'hugr-qir'\n", "source_filename = \"hugr-qir\"\n", "target datalayout = \"e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128\"\n", "target triple = \"aarch64-unknown-linux-gnu\"\n", "\n", "%Qubit = type opaque\n", "%Result = type opaque\n", "\n", "@0 = private unnamed_addr constant [2 x i8] c\"0\\00\", align 1\n", "\n", "define dso_local void @__hugr__.main.1() local_unnamed_addr #0 {\n", "alloca_block:\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__mz__body(%Qubit* null, %Result* null)\n", " %0 = tail call i1 @__quantum__rt__read_result(%Result* null)\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 1 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 1 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 1 to %Qubit*), %Result* nonnull inttoptr (i64 1 to %Result*))\n", " %1 = tail call i1 @__quantum__rt__read_result(%Result* nonnull inttoptr (i64 1 to %Result*))\n", " %2 = xor i1 %0, %1\n", " tail call void @__quantum__rt__bool_record_output(i1 %2, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @0, i64 0, i64 0))\n", " ret void\n", "}\n", "\n", "declare void @__quantum__qis__phasedx__body(double, double, %Qubit*) local_unnamed_addr\n", "\n", "declare void @__quantum__qis__rz__body(double, %Qubit*) local_unnamed_addr\n", "\n", "declare void @__quantum__qis__mz__body(%Qubit*, %Result*) local_unnamed_addr\n", "\n", "declare i1 @__quantum__rt__read_result(%Result*) local_unnamed_addr\n", "\n", "declare void @__quantum__rt__bool_record_output(i1, i8*) local_unnamed_addr\n", "\n", "attributes #0 = { \"entry_point\" \"output_labeling_schema\" \"qir_profiles\"=\"custom\" \"required_num_qubits\"=\"2\" \"required_num_results\"=\"2\" }\n", "\n", "!llvm.module.flags = !{!0, !1, !2, !3}\n", "\n", "!0 = !{i32 1, !\"qir_major_version\", i32 1}\n", "!1 = !{i32 7, !\"qir_minor_version\", i32 0}\n", "!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n", "!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n", "\n" ] } ], "source": [ "guppy_qir_string = hugr_to_qir(main.compile(), output_format=OutputFormat.LLVM_IR)\n", "print(guppy_qir_string)" ] }, { "cell_type": "markdown", "id": "f303d655", "metadata": {}, "source": [ "Only QIR bitcode can be uploaded to Nexus and submitted to Quantinuum Systems. More information on submission is available [here](../qir/qir_submission.ipynb)." ] }, { "cell_type": "markdown", "id": "9854ffdd", "metadata": {}, "source": [ "## Supported on H2\n", "\n", "Supported Actions\n", "* Native gates (see below);\n", "* Measurements\n", "* Result Tagging\n", "* Conditional Branching\n", "* For Loops\n", "* Random Number Generation (RNG)\n", "* Shot number retrieval\n", "\n", "All hardware native gate operations are supported on System Model H2. Users cannot call the *General $SU(4)$ Entangler* gate using Guppy. For a list of supported gate operations see [here](index.ipynb#available-qis-functions)." ] }, { "cell_type": "code", "execution_count": 4, "id": "9a3e22dd", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "; ModuleID = 'hugr-qir'\n", "source_filename = \"hugr-qir\"\n", "target datalayout = \"e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128\"\n", "target triple = \"aarch64-unknown-linux-gnu\"\n", "\n", "%Qubit = type opaque\n", "%Result = type opaque\n", "\n", "@0 = private unnamed_addr constant [2 x i8] c\"0\\00\", align 1\n", "@1 = private unnamed_addr constant [2 x i8] c\"1\\00\", align 1\n", "\n", "define dso_local void @__hugr__.main.1() local_unnamed_addr #0 {\n", "alloca_block:\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__phasedx__body(double 0xBFF921FB54442D18, double 0x3FF921FB54442D18, %Qubit* nonnull inttoptr (i64 1 to %Qubit*))\n", " tail call void @__quantum__qis__rzz__body(double 0x3FF921FB54442D18, %Qubit* null, %Qubit* nonnull inttoptr (i64 1 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0xBFF921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 1 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 1 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* null, %Result* null)\n", " %0 = tail call i1 @__quantum__rt__read_result(%Result* null)\n", " tail call void @__quantum__rt__bool_record_output(i1 %0, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @0, i64 0, i64 0))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 1 to %Qubit*), %Result* nonnull inttoptr (i64 1 to %Result*))\n", " %1 = tail call i1 @__quantum__rt__read_result(%Result* nonnull inttoptr (i64 1 to %Result*))\n", " tail call void @__quantum__rt__bool_record_output(i1 %1, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @1, i64 0, i64 0))\n", " ret void\n", "}\n", "\n", "declare void @__quantum__qis__phasedx__body(double, double, %Qubit*) local_unnamed_addr\n", "\n", "declare void @__quantum__qis__rz__body(double, %Qubit*) local_unnamed_addr\n", "\n", "declare void @__quantum__qis__rzz__body(double, %Qubit*, %Qubit*) local_unnamed_addr\n", "\n", "declare void @__quantum__qis__mz__body(%Qubit*, %Result*) local_unnamed_addr\n", "\n", "declare i1 @__quantum__rt__read_result(%Result*) local_unnamed_addr\n", "\n", "declare void @__quantum__rt__bool_record_output(i1, i8*) local_unnamed_addr\n", "\n", "attributes #0 = { \"entry_point\" \"output_labeling_schema\" \"qir_profiles\"=\"custom\" \"required_num_qubits\"=\"2\" \"required_num_results\"=\"2\" }\n", "\n", "!llvm.module.flags = !{!0, !1, !2, !3}\n", "\n", "!0 = !{i32 1, !\"qir_major_version\", i32 1}\n", "!1 = !{i32 7, !\"qir_minor_version\", i32 0}\n", "!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n", "!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n", "\n" ] } ], "source": [ "from typing import no_type_check\n", "\n", "from guppylang import guppy\n", "from guppylang.std.builtins import result\n", "from guppylang.std.quantum import h, cx, qubit, measure\n", "\n", "from hugr_qir.hugr_to_qir import hugr_to_qir\n", "from hugr_qir.output import OutputFormat\n", "\n", "@guppy\n", "@no_type_check\n", "def main() -> None:\n", " q0 = qubit()\n", " q1 = qubit()\n", "\n", " h(q0)\n", " cx(q0, q1)\n", " \n", " result(\"0\", measure(q0))\n", " result(\"1\", measure(q1))\n", "\n", "hugr = main.compile()\n", "\n", "guppy_llvm_ir = hugr_to_qir(main.compile(), output_format=OutputFormat.LLVM_IR)\n", "print(guppy_llvm_ir)" ] }, { "cell_type": "markdown", "id": "33e5cd9f", "metadata": {}, "source": [ "Iterations are compiled to a non-compact representation using forward branching for compliance with H2." ] }, { "cell_type": "code", "execution_count": 5, "id": "da2ddea3", "metadata": {}, "outputs": [], "source": [ "from typing import no_type_check\n", "\n", "from guppylang import guppy, qubit\n", "from guppylang.std.builtins import result\n", "from guppylang.std.quantum import h, measure\n", "\n", "@guppy\n", "@no_type_check\n", "def main() -> None:\n", " q0 = qubit()\n", " q1 = qubit()\n", "\n", " for _ in range(10):\n", " q3 = qubit()\n", " h(q3)\n", " b = measure(q3)\n", " if b:\n", " h(q0)\n", "\n", " result(\"0\", measure(q0))\n", " result(\"1\", measure(q1))" ] }, { "cell_type": "code", "execution_count": 6, "id": "bea01150", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "; ModuleID = 'hugr-qir'\n", "source_filename = \"hugr-qir\"\n", "target datalayout = \"e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128\"\n", "target triple = \"aarch64-unknown-linux-gnu\"\n", "\n", "%Qubit = type opaque\n", "%Result = type opaque\n", "\n", "@0 = private unnamed_addr constant [2 x i8] c\"0\\00\", align 1\n", "@1 = private unnamed_addr constant [2 x i8] c\"1\\00\", align 1\n", "\n", "define dso_local void @__hugr__.main.1() local_unnamed_addr #0 {\n", "alloca_block:\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*), %Result* nonnull inttoptr (i64 2 to %Result*))\n", " %0 = tail call i1 @__quantum__rt__read_result(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %0, label %21, label %cond_374_case_0\n", "\n", "cond_384_case_0: ; preds = %alloca_block, %21\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*), %Result* nonnull inttoptr (i64 2 to %Result*))\n", " %1 = tail call i1 @__quantum__rt__read_result(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %1, label %2, label %cond_374_case_0.1\n", "\n", "2: ; preds = %cond_384_case_0\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* null)\n", " br label %cond_384_case_0.1\n", "\n", "cond_384_case_0.1: ; preds = %2, %cond_384_case_0\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*), %Result* nonnull inttoptr (i64 2 to %Result*))\n", " %3 = tail call i1 @__quantum__rt__read_result(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %3, label %4, label %cond_374_case_0.2\n", "\n", "4: ; preds = %cond_384_case_0.1\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* null)\n", " br label %cond_384_case_0.2\n", "\n", "cond_384_case_0.2: ; preds = %4, %cond_384_case_0.1\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*), %Result* nonnull inttoptr (i64 2 to %Result*))\n", " %5 = tail call i1 @__quantum__rt__read_result(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %5, label %6, label %cond_374_case_0.3\n", "\n", "6: ; preds = %cond_384_case_0.2\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* null)\n", " br label %cond_384_case_0.3\n", "\n", "cond_384_case_0.3: ; preds = %6, %cond_384_case_0.2\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*), %Result* nonnull inttoptr (i64 2 to %Result*))\n", " %7 = tail call i1 @__quantum__rt__read_result(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %7, label %8, label %cond_374_case_0.4\n", "\n", "8: ; preds = %cond_384_case_0.3\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* null)\n", " br label %cond_384_case_0.4\n", "\n", "cond_384_case_0.4: ; preds = %8, %cond_384_case_0.3\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*), %Result* nonnull inttoptr (i64 2 to %Result*))\n", " %9 = tail call i1 @__quantum__rt__read_result(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %9, label %10, label %cond_374_case_0.5\n", "\n", "10: ; preds = %cond_384_case_0.4\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* null)\n", " br label %cond_384_case_0.5\n", "\n", "cond_384_case_0.5: ; preds = %10, %cond_384_case_0.4\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*), %Result* nonnull inttoptr (i64 2 to %Result*))\n", " %11 = tail call i1 @__quantum__rt__read_result(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %11, label %12, label %cond_374_case_0.6\n", "\n", "12: ; preds = %cond_384_case_0.5\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* null)\n", " br label %cond_384_case_0.6\n", "\n", "cond_384_case_0.6: ; preds = %12, %cond_384_case_0.5\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*), %Result* nonnull inttoptr (i64 2 to %Result*))\n", " %13 = tail call i1 @__quantum__rt__read_result(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %13, label %14, label %cond_374_case_0.7\n", "\n", "14: ; preds = %cond_384_case_0.6\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* null)\n", " br label %cond_384_case_0.7\n", "\n", "cond_384_case_0.7: ; preds = %14, %cond_384_case_0.6\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*), %Result* nonnull inttoptr (i64 2 to %Result*))\n", " %15 = tail call i1 @__quantum__rt__read_result(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %15, label %16, label %cond_374_case_0.8\n", "\n", "16: ; preds = %cond_384_case_0.7\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* null)\n", " br label %cond_384_case_0.8\n", "\n", "cond_384_case_0.8: ; preds = %16, %cond_384_case_0.7\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*), %Result* nonnull inttoptr (i64 2 to %Result*))\n", " %17 = tail call i1 @__quantum__rt__read_result(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %17, label %18, label %cond_374_case_0.9\n", "\n", "18: ; preds = %cond_384_case_0.8\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* null)\n", " br label %cond_384_case_0.9\n", "\n", "cond_384_case_0.9: ; preds = %18, %cond_384_case_0.8\n", " tail call void @__quantum__qis__mz__body(%Qubit* null, %Result* null)\n", " %19 = tail call i1 @__quantum__rt__read_result(%Result* null)\n", " tail call void @__quantum__rt__bool_record_output(i1 %19, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @0, i64 0, i64 0))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 1 to %Qubit*), %Result* nonnull inttoptr (i64 1 to %Result*))\n", " %20 = tail call i1 @__quantum__rt__read_result(%Result* nonnull inttoptr (i64 1 to %Result*))\n", " tail call void @__quantum__rt__bool_record_output(i1 %20, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @1, i64 0, i64 0))\n", " ret void\n", "\n", "21: ; preds = %alloca_block\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* null)\n", " br label %cond_384_case_0\n", "}\n", "\n", "declare void @__quantum__qis__mz__body(%Qubit*, %Result*) local_unnamed_addr\n", "\n", "declare i1 @__quantum__rt__read_result(%Result*) local_unnamed_addr\n", "\n", "declare void @__quantum__rt__bool_record_output(i1, i8*) local_unnamed_addr\n", "\n", "declare void @__quantum__qis__phasedx__body(double, double, %Qubit*) local_unnamed_addr\n", "\n", "declare void @__quantum__qis__rz__body(double, %Qubit*) local_unnamed_addr\n", "\n", "attributes #0 = { \"entry_point\" \"output_labeling_schema\" \"qir_profiles\"=\"custom\" \"required_num_qubits\"=\"3\" \"required_num_results\"=\"3\" }\n", "\n", "!llvm.module.flags = !{!0, !1, !2, !3}\n", "\n", "!0 = !{i32 1, !\"qir_major_version\", i32 1}\n", "!1 = !{i32 7, !\"qir_minor_version\", i32 0}\n", "!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n", "!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n", "\n" ] } ], "source": [ "from hugr_qir.hugr_to_qir import hugr_to_qir\n", "from hugr_qir.output import OutputFormat\n", "\n", "guppy_llvm_ir = hugr_to_qir(main.compile(), output_format=OutputFormat.LLVM_IR)\n", "\n", "print(guppy_llvm_ir)" ] }, { "cell_type": "markdown", "id": "b5c4f7c8-0c96-4ed5-9300-02eca87072a2", "metadata": {}, "source": [ "### Random Number Generator (RNG)\n", "\n", "Conversion of the Guppy integrated RNG to QIR support the constructor with seed, `guppylang.std.qsystem.random.RNG.random_int` and `guppylang.std.qsystem.random.RNG.random_int_bounded(int)`. The following example shows how this can be used:\n" ] }, { "cell_type": "code", "execution_count": 7, "id": "7d229fe8-1402-4b2b-a8c4-c1f5b23ed77e", "metadata": {}, "outputs": [], "source": [ "from guppylang import guppy\n", "from guppylang.std.builtins import result\n", "from guppylang.std.angles import angle\n", "from guppylang.std.quantum import measure, qubit\n", "from guppylang.std.qsystem import phased_x, rz\n", "from guppylang.std.qsystem.random import RNG\n", "\n", "@guppy\n", "def main() -> None:\n", " q = qubit()\n", " rng = RNG(8) # seed=8\n", " index = rng.random_int_bounded(5)\n", " if index == 1:\n", " phased_x(q, angle(1), angle(0))\n", " elif index == 2:\n", " phased_x(q, angle(0), angle(1))\n", " elif index == 3:\n", " rz(q, angle(1))\n", " elif index == 4:\n", " phased_x(q, angle(1), angle(0))\n", " phased_x(q, angle(0), angle(1))\n", " res = measure(q)\n", " result(\"result\", res)\n", " rng.discard()\n", "\n", "hugr = main.compile()" ] }, { "cell_type": "code", "execution_count": 8, "id": "34049b08-0eaa-41c5-b714-1b794eeeaffd", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "; ModuleID = 'hugr-qir'\n", "source_filename = \"hugr-qir\"\n", "target datalayout = \"e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128\"\n", "target triple = \"aarch64-unknown-linux-gnu\"\n", "\n", "%Qubit = type opaque\n", "%Result = type opaque\n", "\n", "@0 = private unnamed_addr constant [7 x i8] c\"result\\00\", align 1\n", "\n", "define dso_local void @__hugr__.main.1() local_unnamed_addr #0 {\n", "alloca_block:\n", " tail call void @___random_seed(i64 8)\n", " %rintb = tail call i32 @___random_int_bounded(i32 5)\n", " br label %NodeBlock554\n", "\n", "NodeBlock554: ; preds = %alloca_block\n", " %Pivot555 = icmp slt i32 %rintb, 3\n", " br i1 %Pivot555, label %NodeBlock, label %NodeBlock552\n", "\n", "NodeBlock552: ; preds = %NodeBlock554\n", " %Pivot553 = icmp slt i32 %rintb, 4\n", " br i1 %Pivot553, label %3, label %LeafBlock550\n", "\n", "LeafBlock550: ; preds = %NodeBlock552\n", " %SwitchLeaf551 = icmp eq i32 %rintb, 4\n", " br i1 %SwitchLeaf551, label %4, label %NewDefault\n", "\n", "NodeBlock: ; preds = %NodeBlock554\n", " %Pivot = icmp slt i32 %rintb, 2\n", " br i1 %Pivot, label %LeafBlock, label %2\n", "\n", "LeafBlock: ; preds = %NodeBlock\n", " %SwitchLeaf = icmp eq i32 %rintb, 1\n", " br i1 %SwitchLeaf, label %1, label %NewDefault\n", "\n", "NewDefault: ; preds = %LeafBlock550, %LeafBlock\n", " br label %cond_318_case_1\n", "\n", "cond_318_case_1: ; preds = %NewDefault, %2, %4, %3, %1\n", " tail call void @__quantum__qis__mz__body(%Qubit* null, %Result* null)\n", " %0 = tail call i1 @__quantum__qis__read_result__body(%Result* null)\n", " tail call void @__quantum__rt__bool_record_output(i1 %0, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @0, i64 0, i64 0))\n", " ret void\n", "\n", "1: ; preds = %LeafBlock\n", " tail call void @__quantum__qis__phasedx__body(double 0x400921FB54442D18, double 0.000000e+00, %Qubit* null)\n", " br label %cond_318_case_1\n", "\n", "2: ; preds = %NodeBlock\n", " tail call void @__quantum__qis__phasedx__body(double 0.000000e+00, double 0x400921FB54442D18, %Qubit* null)\n", " br label %cond_318_case_1\n", "\n", "3: ; preds = %NodeBlock552\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* null)\n", " br label %cond_318_case_1\n", "\n", "4: ; preds = %LeafBlock550\n", " tail call void @__quantum__qis__phasedx__body(double 0x400921FB54442D18, double 0.000000e+00, %Qubit* null)\n", " tail call void @__quantum__qis__phasedx__body(double 0.000000e+00, double 0x400921FB54442D18, %Qubit* null)\n", " br label %cond_318_case_1\n", "}\n", "\n", "declare void @___random_seed(i64) local_unnamed_addr\n", "\n", "declare i32 @___random_int_bounded(i32) local_unnamed_addr\n", "\n", "declare void @__quantum__qis__phasedx__body(double, double, %Qubit*) local_unnamed_addr\n", "\n", "declare void @__quantum__qis__rz__body(double, %Qubit*) local_unnamed_addr\n", "\n", "declare void @__quantum__qis__mz__body(%Qubit*, %Result*) local_unnamed_addr\n", "\n", "declare i1 @__quantum__qis__read_result__body(%Result*) local_unnamed_addr\n", "\n", "declare void @__quantum__rt__bool_record_output(i1, i8*) local_unnamed_addr\n", "\n", "attributes #0 = { \"entry_point\" \"output_labeling_schema\" \"qir_profiles\"=\"custom\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"1\" }\n", "\n", "!llvm.module.flags = !{!0, !1, !2, !3}\n", "\n", "!0 = !{i32 1, !\"qir_major_version\", i32 1}\n", "!1 = !{i32 7, !\"qir_minor_version\", i32 0}\n", "!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n", "!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n", "\n" ] } ], "source": [ "guppy_qir = hugr_to_qir(main.compile(), output_format=OutputFormat.LLVM_IR, validate_qir=True)\n", "print(guppy_qir)\n" ] }, { "cell_type": "markdown", "id": "fb8ade56-566f-4103-8b19-941752128439", "metadata": {}, "source": [ "### Use of compile time arrays\n", "\n", "Guppy arrays within a non-`comptime` context cannot be converted at the moment. The best way to still make use of arrays in guppy and convert them to QIR are by making sure you allocate them within a `@guppy.comptime` context. In this context, arrays are equivalent to python lists. The following rules for writing guppy programs should lead to a smooth conversion to QIR:\n", "* Only allocate arrays within `@guppy.comptime` functions (or alternatively use python lists)\n", " * Even with `@guppy.comptime` functions you should not pass in or return arrays\n", "* Use regular python functions (called from `@guppy.comptime` context) for repeatable operations on qubit arrays/lists at compile time.\n", " * These functions can accept and return qubit lists.\n", "* To implement runtime logic: Use `@guppy` annotated functions, but do not pass or allocate arrays. Only pass/allocate tuples or basic types\n", "* Do not use `measure_array`/`discard_array`, use compile time loops on arrays to do measurements and report results\n", "\n", "One example showing this:" ] }, { "cell_type": "code", "execution_count": 9, "id": "ba7bbc86-be1c-44fe-9f96-e2188356e9f4", "metadata": {}, "outputs": [], "source": [ "from pathlib import Path\n", "from typing import no_type_check\n", "\n", "from guppylang import guppy\n", "from guppylang.defs import GuppyFunctionDefinition\n", "from guppylang.std.angles import angle\n", "from guppylang.std.builtins import array, owned\n", "from guppylang.std.platform import result\n", "from guppylang.std.qsystem.random import RNG\n", "from guppylang.std.quantum import crz, cx, h, measure, qubit\n", "\n", "\n", "# One can define pure python functions that use guppy functions and objects and call them within guppy.comptime functions.\n", "# Examples for this are: cx_registers, measure_qubits_conditionally and record_result_array\n", "# Theses functions can accept and return arrays without breaking QIR generation.\n", "\n", "def cx_registers(qreg1: array[qubit], qreg2: array[qubit]) -> None:\n", " # This function can be called from a guppy comptime function\n", " # it can accept and return qubit lists or qubit arrays.\n", " size = len(qreg1)\n", " for i in range(size):\n", " cx(qreg1[i], qreg2[i])\n", "\n", "def measure_qubits_conditionally(q: list[qubit]) -> list[bool]:\n", " size = len(q)\n", " creg_results: list[bool] = []\n", " \n", " for i in range(0, size - 1, 2):\n", "\n", " # Guppy functions (like conditional_measure) can be called from\n", " # python functions that are later included in a guppy module.\n", " qres_i, qres_ip1 = conditional_measure(q[i], q[i + 1])\n", " \n", " creg_results.append(qres_i)\n", " creg_results.append(qres_ip1)\n", " \n", " return creg_results\n", "\n", "def record_result_array(prefix: str, creg_results: list[bool]) -> None:\n", " # Record all values as individual bools\n", " size = len(creg_results)\n", "\n", " for i in range(size):\n", " result(f\"{prefix}_{i}\", creg_results[i])\n", "\n", "def record_result_array_record_int(prefix: str, creg_results: list[bool]) -> None: \n", " # Combine all bool values into one int for recording. (Consider endianness)\n", " size = len(creg_results)\n", "\n", " integer_value = 0\n", "\n", " for b in creg_results:\n", " integer_value = (integer_value << 1) | int(b) # for big-endian\n", " # integer_value = (integer_value << 1) | int(b) # for little-endian\n", " result(prefix, integer_value)\n", "\n", "@guppy\n", "@no_type_check\n", "def conditional_measure(q0: qubit @ owned, q1: qubit @ owned) -> tuple[bool, bool]:\n", " # This function must be a guppy function, not guppy comptime, because it conditions\n", " # on runtime values (here: measurement results).\n", " # For QIR generation: this type of function cannot use arrays.\n", " q2 = qubit()\n", " h(q2)\n", " c = measure(q2)\n", " if c:\n", " h(q0)\n", " a = measure(q0)\n", " if a:\n", " h(q1)\n", " b = measure(q1)\n", " return a, b\n", "\n", "def main_generator(size: int, theta: float) -> GuppyFunctionDefinition[[None], None]: \n", " # This function returns the guppy main function for a given size\n", " # parameter and a given angle theta.\n", "\n", " # expects an even int for the size\n", " assert size % 2 == 0\n", "\n", " @guppy.comptime # For QIR generation: this function must be comptime since it uses arrays.\n", " @no_type_check\n", " def main() -> None: \n", " q = array(qubit() for _ in range(size))\n", " q2 = array(qubit() for _ in range(size))\n", " h(q[0])\n", " cx_registers(q, q2)\n", " r1 = measure_qubits_conditionally(q)\n", " r2 = measure_qubits_conditionally(q2)\n", " record_result_array(\"q1\", r1)\n", " record_result_array_record_int(\"q2\", r2)\n", "\n", " return main" ] }, { "cell_type": "code", "execution_count": 10, "id": "21bf24db-6482-4569-8f75-ca6d4b2ff745", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "; ModuleID = 'hugr-qir'\n", "source_filename = \"hugr-qir\"\n", "target datalayout = \"e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128\"\n", "target triple = \"aarch64-unknown-linux-gnu\"\n", "\n", "%Qubit = type opaque\n", "%Result = type opaque\n", "\n", "@0 = private unnamed_addr constant [5 x i8] c\"q1_0\\00\", align 1\n", "@1 = private unnamed_addr constant [5 x i8] c\"q1_1\\00\", align 1\n", "@2 = private unnamed_addr constant [5 x i8] c\"q1_2\\00\", align 1\n", "@3 = private unnamed_addr constant [5 x i8] c\"q1_3\\00\", align 1\n", "@4 = private unnamed_addr constant [5 x i8] c\"q1_4\\00\", align 1\n", "@5 = private unnamed_addr constant [5 x i8] c\"q1_5\\00\", align 1\n", "@6 = private unnamed_addr constant [3 x i8] c\"q2\\00\", align 1\n", "\n", "define dso_local void @__hugr__.main.1() local_unnamed_addr #0 {\n", "alloca_block:\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__phasedx__body(double 0xBFF921FB54442D18, double 0x3FF921FB54442D18, %Qubit* nonnull inttoptr (i64 6 to %Qubit*))\n", " tail call void @__quantum__qis__rzz__body(double 0x3FF921FB54442D18, %Qubit* null, %Qubit* nonnull inttoptr (i64 6 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0xBFF921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 6 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 6 to %Qubit*))\n", " tail call void @__quantum__qis__phasedx__body(double 0xBFF921FB54442D18, double 0x3FF921FB54442D18, %Qubit* nonnull inttoptr (i64 7 to %Qubit*))\n", " tail call void @__quantum__qis__rzz__body(double 0x3FF921FB54442D18, %Qubit* nonnull inttoptr (i64 1 to %Qubit*), %Qubit* nonnull inttoptr (i64 7 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 1 to %Qubit*))\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 7 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 7 to %Qubit*))\n", " tail call void @__quantum__qis__phasedx__body(double 0xBFF921FB54442D18, double 0x3FF921FB54442D18, %Qubit* nonnull inttoptr (i64 8 to %Qubit*))\n", " tail call void @__quantum__qis__rzz__body(double 0x3FF921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*), %Qubit* nonnull inttoptr (i64 8 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 8 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 8 to %Qubit*))\n", " tail call void @__quantum__qis__phasedx__body(double 0xBFF921FB54442D18, double 0x3FF921FB54442D18, %Qubit* nonnull inttoptr (i64 9 to %Qubit*))\n", " tail call void @__quantum__qis__rzz__body(double 0x3FF921FB54442D18, %Qubit* nonnull inttoptr (i64 3 to %Qubit*), %Qubit* nonnull inttoptr (i64 9 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 3 to %Qubit*))\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 9 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 9 to %Qubit*))\n", " tail call void @__quantum__qis__phasedx__body(double 0xBFF921FB54442D18, double 0x3FF921FB54442D18, %Qubit* nonnull inttoptr (i64 10 to %Qubit*))\n", " tail call void @__quantum__qis__rzz__body(double 0x3FF921FB54442D18, %Qubit* nonnull inttoptr (i64 4 to %Qubit*), %Qubit* nonnull inttoptr (i64 10 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 4 to %Qubit*))\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 10 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 10 to %Qubit*))\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 12 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 12 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 12 to %Qubit*), %Result* nonnull inttoptr (i64 2 to %Result*))\n", " %0 = tail call i1 @__quantum__qis__read_result__body(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %0, label %4, label %cond_1367_case_1\n", "\n", "cond_1367_case_1: ; preds = %alloca_block, %4\n", " tail call void @__quantum__qis__mz__body(%Qubit* null, %Result* null)\n", " %1 = tail call i1 @__quantum__qis__read_result__body(%Result* null)\n", " br i1 %1, label %5, label %cond_1306_case_1\n", "\n", "cond_1306_case_1: ; preds = %cond_1367_case_1, %5\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 1 to %Qubit*), %Result* nonnull inttoptr (i64 1 to %Result*))\n", " %2 = tail call i1 @__quantum__qis__read_result__body(%Result* nonnull inttoptr (i64 1 to %Result*))\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 13 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 13 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 13 to %Qubit*), %Result* nonnull inttoptr (i64 5 to %Result*))\n", " %3 = tail call i1 @__quantum__qis__read_result__body(%Result* nonnull inttoptr (i64 5 to %Result*))\n", " br i1 %3, label %9, label %cond_1226_case_1\n", "\n", "4: ; preds = %alloca_block\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* null)\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* null)\n", " br label %cond_1367_case_1\n", "\n", "5: ; preds = %cond_1367_case_1\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 1 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 1 to %Qubit*))\n", " br label %cond_1306_case_1\n", "\n", "cond_1226_case_1: ; preds = %cond_1306_case_1, %9\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*), %Result* nonnull inttoptr (i64 3 to %Result*))\n", " %6 = tail call i1 @__quantum__qis__read_result__body(%Result* nonnull inttoptr (i64 3 to %Result*))\n", " br i1 %6, label %10, label %cond_267_case_1\n", "\n", "cond_267_case_1: ; preds = %cond_1226_case_1, %10\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 3 to %Qubit*), %Result* nonnull inttoptr (i64 4 to %Result*))\n", " %7 = tail call i1 @__quantum__qis__read_result__body(%Result* nonnull inttoptr (i64 4 to %Result*))\n", " tail call void @__quantum__qis__phasedx__body(double 0xBFF921FB54442D18, double 0x3FF921FB54442D18, %Qubit* nonnull inttoptr (i64 11 to %Qubit*))\n", " tail call void @__quantum__qis__rzz__body(double 0x3FF921FB54442D18, %Qubit* nonnull inttoptr (i64 5 to %Qubit*), %Qubit* nonnull inttoptr (i64 11 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 5 to %Qubit*))\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 11 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 11 to %Qubit*))\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 14 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 14 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 14 to %Qubit*), %Result* nonnull inttoptr (i64 8 to %Result*))\n", " %8 = tail call i1 @__quantum__qis__read_result__body(%Result* nonnull inttoptr (i64 8 to %Result*))\n", " br i1 %8, label %14, label %cond_1085_case_1\n", "\n", "9: ; preds = %cond_1306_case_1\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " br label %cond_1226_case_1\n", "\n", "10: ; preds = %cond_1226_case_1\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 3 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 3 to %Qubit*))\n", " br label %cond_267_case_1\n", "\n", "cond_1085_case_1: ; preds = %cond_267_case_1, %14\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 4 to %Qubit*), %Result* nonnull inttoptr (i64 6 to %Result*))\n", " %11 = tail call i1 @__quantum__qis__read_result__body(%Result* nonnull inttoptr (i64 6 to %Result*))\n", " br i1 %11, label %15, label %cond_1024_case_1\n", "\n", "cond_1024_case_1: ; preds = %cond_1085_case_1, %15\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 5 to %Qubit*), %Result* nonnull inttoptr (i64 7 to %Result*))\n", " %12 = tail call i1 @__quantum__qis__read_result__body(%Result* nonnull inttoptr (i64 7 to %Result*))\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 15 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 15 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 15 to %Qubit*), %Result* nonnull inttoptr (i64 11 to %Result*))\n", " %13 = tail call i1 @__quantum__qis__read_result__body(%Result* nonnull inttoptr (i64 11 to %Result*))\n", " br i1 %13, label %19, label %cond_944_case_1\n", "\n", "14: ; preds = %cond_267_case_1\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 4 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 4 to %Qubit*))\n", " br label %cond_1085_case_1\n", "\n", "15: ; preds = %cond_1085_case_1\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 5 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 5 to %Qubit*))\n", " br label %cond_1024_case_1\n", "\n", "cond_944_case_1: ; preds = %cond_1024_case_1, %19\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 6 to %Qubit*), %Result* nonnull inttoptr (i64 9 to %Result*))\n", " %16 = tail call i1 @__quantum__qis__read_result__body(%Result* nonnull inttoptr (i64 9 to %Result*))\n", " br i1 %16, label %20, label %cond_883_case_1\n", "\n", "cond_883_case_1: ; preds = %cond_944_case_1, %20\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 7 to %Qubit*), %Result* nonnull inttoptr (i64 10 to %Result*))\n", " %17 = tail call i1 @__quantum__qis__read_result__body(%Result* nonnull inttoptr (i64 10 to %Result*))\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 16 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 16 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 16 to %Qubit*), %Result* nonnull inttoptr (i64 14 to %Result*))\n", " %18 = tail call i1 @__quantum__qis__read_result__body(%Result* nonnull inttoptr (i64 14 to %Result*))\n", " br i1 %18, label %24, label %cond_803_case_1\n", "\n", "19: ; preds = %cond_1024_case_1\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 6 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 6 to %Qubit*))\n", " br label %cond_944_case_1\n", "\n", "20: ; preds = %cond_944_case_1\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 7 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 7 to %Qubit*))\n", " br label %cond_883_case_1\n", "\n", "cond_803_case_1: ; preds = %cond_883_case_1, %24\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 8 to %Qubit*), %Result* nonnull inttoptr (i64 12 to %Result*))\n", " %21 = tail call i1 @__quantum__qis__read_result__body(%Result* nonnull inttoptr (i64 12 to %Result*))\n", " br i1 %21, label %25, label %cond_742_case_1\n", "\n", "cond_742_case_1: ; preds = %cond_803_case_1, %25\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 9 to %Qubit*), %Result* nonnull inttoptr (i64 13 to %Result*))\n", " %22 = tail call i1 @__quantum__qis__read_result__body(%Result* nonnull inttoptr (i64 13 to %Result*))\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 17 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 17 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 17 to %Qubit*), %Result* nonnull inttoptr (i64 17 to %Result*))\n", " %23 = tail call i1 @__quantum__qis__read_result__body(%Result* nonnull inttoptr (i64 17 to %Result*))\n", " br i1 %23, label %33, label %cond_662_case_1\n", "\n", "24: ; preds = %cond_883_case_1\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 8 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 8 to %Qubit*))\n", " br label %cond_803_case_1\n", "\n", "25: ; preds = %cond_803_case_1\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 9 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 9 to %Qubit*))\n", " br label %cond_742_case_1\n", "\n", "cond_662_case_1: ; preds = %cond_742_case_1, %33\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 10 to %Qubit*), %Result* nonnull inttoptr (i64 15 to %Result*))\n", " %26 = tail call i1 @__quantum__qis__read_result__body(%Result* nonnull inttoptr (i64 15 to %Result*))\n", " br i1 %26, label %34, label %cond_287_case_1\n", "\n", "cond_287_case_1: ; preds = %cond_662_case_1, %34\n", " %\"464_0.0\" = phi i64 [ 0, %cond_662_case_1 ], [ 2, %34 ]\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 11 to %Qubit*), %Result* nonnull inttoptr (i64 16 to %Result*))\n", " %27 = tail call i1 @__quantum__qis__read_result__body(%Result* nonnull inttoptr (i64 16 to %Result*))\n", " tail call void @__quantum__rt__bool_record_output(i1 %1, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @0, i64 0, i64 0))\n", " tail call void @__quantum__rt__bool_record_output(i1 %2, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @1, i64 0, i64 0))\n", " tail call void @__quantum__rt__bool_record_output(i1 %6, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @2, i64 0, i64 0))\n", " tail call void @__quantum__rt__bool_record_output(i1 %7, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @3, i64 0, i64 0))\n", " tail call void @__quantum__rt__bool_record_output(i1 %11, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @4, i64 0, i64 0))\n", " tail call void @__quantum__rt__bool_record_output(i1 %12, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @5, i64 0, i64 0))\n", " %\"604_0.0\" = select i1 %16, i64 32, i64 0\n", " %\"569_0.0\" = select i1 %17, i64 16, i64 0\n", " %\"534_0.0\" = select i1 %21, i64 8, i64 0\n", " %\"499_0.0\" = select i1 %22, i64 4, i64 0\n", " %\"429_0.0\" = zext i1 %27 to i64\n", " %28 = or i64 %\"569_0.0\", %\"604_0.0\"\n", " %29 = or i64 %28, %\"534_0.0\"\n", " %30 = or i64 %29, %\"499_0.0\"\n", " %31 = or i64 %30, %\"464_0.0\"\n", " %32 = or i64 %31, %\"429_0.0\"\n", " tail call void @__quantum__rt__int_record_output(i64 %32, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @6, i64 0, i64 0))\n", " ret void\n", "\n", "33: ; preds = %cond_742_case_1\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 10 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 10 to %Qubit*))\n", " br label %cond_662_case_1\n", "\n", "34: ; preds = %cond_662_case_1\n", " tail call void @__quantum__qis__phasedx__body(double 0x3FF921FB54442D18, double 0xBFF921FB54442D18, %Qubit* nonnull inttoptr (i64 11 to %Qubit*))\n", " tail call void @__quantum__qis__rz__body(double 0x400921FB54442D18, %Qubit* nonnull inttoptr (i64 11 to %Qubit*))\n", " br label %cond_287_case_1\n", "}\n", "\n", "declare void @__quantum__qis__phasedx__body(double, double, %Qubit*) local_unnamed_addr\n", "\n", "declare void @__quantum__qis__rz__body(double, %Qubit*) local_unnamed_addr\n", "\n", "declare void @__quantum__qis__rzz__body(double, %Qubit*, %Qubit*) local_unnamed_addr\n", "\n", "declare void @__quantum__qis__mz__body(%Qubit*, %Result*) local_unnamed_addr\n", "\n", "declare i1 @__quantum__qis__read_result__body(%Result*) local_unnamed_addr\n", "\n", "declare void @__quantum__rt__bool_record_output(i1, i8*) local_unnamed_addr\n", "\n", "declare void @__quantum__rt__int_record_output(i64, i8*) local_unnamed_addr\n", "\n", "attributes #0 = { \"entry_point\" \"output_labeling_schema\" \"qir_profiles\"=\"custom\" \"required_num_qubits\"=\"18\" \"required_num_results\"=\"18\" }\n", "\n", "!llvm.module.flags = !{!0, !1, !2, !3}\n", "\n", "!0 = !{i32 1, !\"qir_major_version\", i32 1}\n", "!1 = !{i32 7, !\"qir_minor_version\", i32 0}\n", "!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n", "!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n", "\n" ] } ], "source": [ "from hugr_qir.hugr_to_qir import hugr_to_qir\n", "from hugr_qir.output import OutputFormat\n", "\n", "\n", "guppy_qir = hugr_to_qir(main_generator(size=6, theta=0.3).compile(), output_format=OutputFormat.LLVM_IR, validate_qir=True)\n", "print(guppy_qir)\n" ] }, { "cell_type": "markdown", "id": "46c35ab4", "metadata": {}, "source": [ "## Unsupported Conversions" ] }, { "cell_type": "markdown", "id": "9cd11f6b", "metadata": {}, "source": [ "### Use Case 1: Unbounded Loops\n", "\n", "Constructing a similar example like the one above with a loop which numbers of execution depends on the measurement results inside the loop leads to the generation of invalid QIR." ] }, { "cell_type": "code", "execution_count": 11, "id": "91626f83", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Validation failed as expected:\n", "Found loop in CFG containing the block: cond_exit_182\n" ] } ], "source": [ "from typing import no_type_check\n", "\n", "from guppylang import guppy, qubit\n", "from guppylang.std.builtins import result\n", "from guppylang.std.quantum import h, measure\n", "\n", "@guppy\n", "@no_type_check\n", "def main() -> None:\n", " q0 = qubit()\n", " q1 = qubit()\n", " \n", " i = 0\n", " while i < 10:\n", " q3 = qubit()\n", " h(q3)\n", " b = measure(q3)\n", " if b:\n", " h(q0)\n", " i += 1\n", "\n", " result(\"0\", measure(q0))\n", " result(\"1\", measure(q1))\n", "\n", "hugr = main.compile()\n", "\n", "try:\n", " guppy_qir = hugr_to_qir(main.compile(), output_format=OutputFormat.BITCODE, validate_qir=True)\n", " print(guppy_qir)\n", "except Exception as e:\n", " print('Validation failed as expected:')\n", " print(e)" ] }, { "cell_type": "markdown", "id": "e068faac", "metadata": {}, "source": [ "### Use Case 2: Non Comptime Qubit Arrays\n", "\n", "A program using Arrays over qubits leads to invalid QIR if they are used in non comptime functions. See [here](#use-of-compile-time-arrays) for alternatives." ] }, { "cell_type": "code", "execution_count": 12, "id": "6c5b7537", "metadata": {}, "outputs": [], "source": [ "from guppylang import guppy\n", "from guppylang.std.builtins import array, result\n", "from guppylang.std.quantum import qubit, measure_array, h, cx\n", "\n", "\n", "@guppy\n", "def ghz_state() -> array[qubit, 4]:\n", " qubit_array = array(qubit() for _ in range(4))\n", " h(qubit_array[0])\n", " for i in range(1, 4):\n", " cx(qubit_array[0], qubit_array[i])\n", " return qubit_array\n", "\n", "@guppy\n", "def main() -> None:\n", " qubit_array = ghz_state()\n", " measure_results = measure_array(qubit_array)\n", " res = 0\n", " for i in range(4):\n", " if measure_results[i]:\n", " res += 2**int(i)\n", " result(\"result\", res)\n", "\n", "hugr = main.compile()" ] }, { "cell_type": "code", "execution_count": 13, "id": "01928989", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Validation failed as expected:\n", "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: Unknown type: borrow_array(4, [Bool]+[Future(Bool)])\n" ] } ], "source": [ "try:\n", " guppy_qir = hugr_to_qir(main.compile(), output_format=OutputFormat.BITCODE, validate_qir=True)\n", " print(guppy_qir)\n", "except Exception as e:\n", " print('Validation failed as expected:')\n", " print(e)" ] }, { "cell_type": "code", "execution_count": null, "id": "4c87a83d-190e-4857-a93a-32773a83e39c", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.3" } }, "nbformat": 4, "nbformat_minor": 5 }