{ "cells": [ { "cell_type": "markdown", "id": "12d9be42", "metadata": {}, "source": [ "# Guppy to QIR (Experimental)" ] }, { "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 (Heirarchical 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-QIS package requires the user first generate HUGR, before transpiling to QIR. HUGR-generated QIR is verified for backwards compatability 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.html#qir-specifications-for-quantinuum-systems). Not all Guppy features are available in the full QIR adaptive profile (specified in the linked table). In addition to this, HUGR-QIS does not support conversion of the following Guppy features:\n", "\n", "* Functions returing collections over Qubits;\n", "* Unbounded Loops;\n", "* RNG;\n", "* Dynamic Qubit Allocation." ] }, { "cell_type": "markdown", "id": "5675fc8d", "metadata": {}, "source": [ "## Installation\n", "\n", "HUGR-QIR is available on PyPi and requires Python 3.10. The corresponding GitHub repository is available at [https://github.com/CQCL/hugr-qir.git](https://github.com/CQCL/hugr-qir.git).\n", "\n", "```{code} bash\n", "pip install hugr-qir\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": 7, "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": 5, "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 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__qis__read_result__body(%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__qis__read_result__body(%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__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\"=\"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", "* Foor Loops\n", "\n", "All hardware native gate operations are supported on System Model H2. Users cannnot call the *General $SU(4)$ Entangler* gate using Guppy. Additionally, Random Number Generator (RNG) conversion is not supported (see [here](#use-case-3-random-number-generator-rng)). For a list of supported gate operations see [here](index.ipynb#available-qis-functions)." ] }, { "cell_type": "code", "execution_count": 3, "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 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__qis__read_result__body(%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__qis__read_result__body(%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__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\"=\"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": [ "Interations are compiled to a non-compact representation using forward branching for compliance with H2." ] }, { "cell_type": "code", "execution_count": 4, "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": 9, "id": "bea01150", "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 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__qis__read_result__body(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %0, label %21, label %cond_374_case_0\n", "\n", "cond_374_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__qis__read_result__body(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %1, label %2, label %cond_374_case_0.1\n", "\n", "2: ; preds = %cond_374_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_374_case_0.1\n", "\n", "cond_374_case_0.1: ; preds = %2, %cond_374_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__qis__read_result__body(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %3, label %4, label %cond_374_case_0.2\n", "\n", "4: ; preds = %cond_374_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_374_case_0.2\n", "\n", "cond_374_case_0.2: ; preds = %4, %cond_374_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__qis__read_result__body(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %5, label %6, label %cond_374_case_0.3\n", "\n", "6: ; preds = %cond_374_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_374_case_0.3\n", "\n", "cond_374_case_0.3: ; preds = %6, %cond_374_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__qis__read_result__body(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %7, label %8, label %cond_374_case_0.4\n", "\n", "8: ; preds = %cond_374_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_374_case_0.4\n", "\n", "cond_374_case_0.4: ; preds = %8, %cond_374_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__qis__read_result__body(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %9, label %10, label %cond_374_case_0.5\n", "\n", "10: ; preds = %cond_374_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_374_case_0.5\n", "\n", "cond_374_case_0.5: ; preds = %10, %cond_374_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__qis__read_result__body(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %11, label %12, label %cond_374_case_0.6\n", "\n", "12: ; preds = %cond_374_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_374_case_0.6\n", "\n", "cond_374_case_0.6: ; preds = %12, %cond_374_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__qis__read_result__body(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %13, label %14, label %cond_374_case_0.7\n", "\n", "14: ; preds = %cond_374_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_374_case_0.7\n", "\n", "cond_374_case_0.7: ; preds = %14, %cond_374_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__qis__read_result__body(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %15, label %16, label %cond_374_case_0.8\n", "\n", "16: ; preds = %cond_374_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_374_case_0.8\n", "\n", "cond_374_case_0.8: ; preds = %16, %cond_374_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__qis__read_result__body(%Result* nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %17, label %18, label %cond_374_case_0.9\n", "\n", "18: ; preds = %cond_374_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_374_case_0.9\n", "\n", "cond_374_case_0.9: ; preds = %18, %cond_374_case_0.8\n", " tail call void @__quantum__qis__mz__body(%Qubit* null, %Result* null)\n", " %19 = tail call i1 @__quantum__qis__read_result__body(%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__qis__read_result__body(%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_374_case_0\n", "}\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__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": "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": 15, "id": "91626f83", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Validation failed as expected:\n", "Found loop in CFG containing the block: cond_exit_176\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: Qubit Arrays\n", "\n", "A program using Arrays over qubits leads to invalid QIR." ] }, { "cell_type": "code", "execution_count": 21, "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": 23, "id": "01928989", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Validation failed as expected:\n", "Unknown type: 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": "markdown", "id": "fd45a277", "metadata": {}, "source": [ "### Use Case 3: Random Number Generator (RNG)\n", "\n", "Conversion of the Guppy integrated RNG to QIR leads to a validation error. If users wish to use the RNG capability with QIR, see the Quantinuum QIR extension for RNG [here](index.html#rng). " ] }, { "cell_type": "code", "execution_count": 35, "id": "1080969b", "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(0)\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": 36, "id": "ac70305d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Validation failed as expected:\n", "Unknown type: context\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)" ] } ], "metadata": { "kernelspec": { "display_name": ".venv", "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.8" } }, "nbformat": 4, "nbformat_minor": 5 }