{ "cells": [ { "cell_type": "markdown", "id": "fbaf9aff", "metadata": {}, "source": [ "# QIR\n", "\n", "The Quantum Intermediate Representation (QIR) is a hardware-agnostic, LLVM-based intermediate representation designed to unify quantum and classical code under a common compilation framework {footcite:p}`qir_alliance_homepage`. Proposed and maintained by the QIR Alliance (including Quantinuum), QIR extends LLVM IR with quantum constructs, such as opaque types and specialized quantum instruction functions, allowing a wide variety of quantum front-end languages to target diverse quantum hardware back-ends, whilst leveraging LLVM's mature optimizations and infrastructure {footcite:p}`LLVM`. QIR programs adhere to different profiles. Each QIR profile is a dialect of QIR and is a feature specification for hardware providers.\n", "\n", "Quantinuum Systems supports execution of native QIR programs. Prior to execution, QIR programs undergo server-side transpilation to Quantinuum’s Quantum Instruction Set (QIS). Quantinuum's QIS is an extended LLVM IR that can represent native quantum instructions and control flow primitives. \n", "\n", "```{figure} ../../../../_static/qir/qir_lifecycle.png\n", ":alt: QIR lifecycle on Quantinuum Systems\n", ":width: 800\n", ":align: center\n", "\n", "Server-side QIR lifecycle (post-submission actions in the hardware stack).\n", "```\n", "\n", "Quantinuum Nexus supports management and submission of QIR programs to Quantinuum systems {footcite:p}`QuantinuumNexus`. A workflow specifying Nexus submission pathway for QIR is available [here](../qir/qir_submission.ipynb). Textual QIR representations must be compiled to QIR bitcode prior to program upload and job submission using a client-side tool, `pyqir` {footcite:p}`PyQIR`. System Model H2 supports the (core) adaptive profile and Quantinuum Helios supports the (full) adaptive profile. The availability of QIR on Quantinuum Systems also enables partner integrations with Nvidia CUDA-Q and Microsoft Q#. Quantinuum's [Guppy](https://docs.quantinuum.com/guppy/) provides a front-end compiler to generate HUGR, a compact graph-based representation. Alternate off-the-shelf languages, such as Nvidia CUDA-Q, must equivalently lower to QIR. \n", "\n", "This document provides a QIR specification for System Model H2 and Quantinuum Helios. Additionally, a comparison is provided with Guppy to distinguish feature gaps." ] }, { "cell_type": "markdown", "id": "b8d0a063", "metadata": {}, "source": [ "## Platform Specifications" ] }, { "cell_type": "markdown", "id": "58d63106", "metadata": {}, "source": [ "QIR is supported on both Helios and H2. However, Helios affords users the ability to use optional QIR specifications, enabling access to Helios features also supported by Guppy. Table 1. provides a QIR specification for Helios and H2. \n", "\n", "Additionally, a side-by-side comparison with Guppy is provided. Whilst Guppy is a human-writeable programming language, QIR is an intermediate representation format generated by front-end compilers. The Guppy compiler’s equivalent of QIR is HUGR.\n", "\n", "```{eval-rst}\n", ".. csv-table::\n", " :file: ../../../../_static/qir/qir_spec.csv\n", " :widths: auto\n", "```\n", "\n", "Guppy compiles to a compact graph-based representation (HUGR) which is lowered to LLVM adhering to Quantinuum’s QIS. QIR is extended LLVM. A soft transpilation pass, applied to the QIR program, enables conversion of quantum operations to QIS. The Full Adaptive QIR profile enables use of new features on Quantinuum Helios. However, the Full Adaptive profile does not capture all of Guppy’s capabilities." ] }, { "cell_type": "markdown", "id": "1089f187", "metadata": {}, "source": [ "### Core Adaptive Profile Specifications\n", "\n", "#### Quantum Transformations\n", "\n", "The set of available instructions that transform the quantum state may vary depending on the targeted backend. The profile specification defines how to leverage and combine the available instructions to express a program. Targeting a program to a specific backend requires choosing a suitable profile and quantum instruction set (QIS). Quantinuum QIS is compatible with the Adaptive Profile. More information on QIS is available [here](#available-qis-functions).\n", "\n", "#### Measurements\n", "\n", "Both H2 and Helios support Measurement operations during and at the end of a program.\n", "\n", "The `irreversible` attribute is required to be flagged, and declared `@__quantum__qis__m_body(%Qubit*, %Result*)` and `@__quantum__qis__reset_body(%Qubit*)` operations must be marked by this attribute.\n", "\n", "```{code} llvm\n", "declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1\n", "declare void @__quantum__qis__reset__body(%Qubit*) #1\n", "\n", "attributes #0 = { \"entry_point\" \"output_labeling_schema\" \"qir_profiles\"=\"adaptive_profile\" \"required_num_qubits\"=\"5\" \"required_num_results\"=\"6\" }\n", "attributes #1 = { \"irreversible\" }\n", "```\n", "\n", "#### Forward Branching\n", "\n", "Both Helios and H2 enables actions based on a measurement result. Specifically, it must be possible to execute subsequent quantum instructions conditionally on the produced `%Result` value. To that end, a runtime function must be provided that converts a `%Result` value into a value of type `i1`. Bounded iterations in a surface-level language, targetting H2, are unrolled at compile time, and upon unrolling, all values used to identify a qubit or result value can be replaced by a constant.\n", "\n", "Programs targetting Helios and H2 are requred to call two different functions to convert `%Result*` into `i1`, respectively. Helios consumes a runtime function, `i1 @__quantum__rt__read_result(%Result* readonly)`. H2 consumes a QIS function, `i1 @__quantum__qis__read_result__body(%Result* readonly)`.\n", "\n", "#### Program Output\n", "\n", "```{note}\n", "For System Model H2, the hardware QIR compiler must use `phi`-nodes to consolidate error codes into a single final return.\n", "```\n", "\n", "QIR and its profiles must clearly express program output. Both the Base and Adaptive Profiles requires the user to specify an ordering over a selection of measurement results. A list of runtime (rt) functions are available [here](#available-rt-functions). Quantinuum Systems uses [Output Schema Labelling](https://github.com/qir-alliance/qir-spec/blob/a67da3a3bd902ba7cd94a98722ca5f8c89f5a81a/specification/under_development/output_schemas/Labeled.md#output-recording-functions) to dictate how results are formatted and identified via a header record {footcite:p}`qiralliance_output_schemas_labeled`. Helios and H2 have distinct header records. Helios supports outputting classical values. \n", "\n", "A QIR program that contains a two calls to the integer output recording function. Globally defined strings with fixed length byte arrays are used to represent the result register. Inline `getelementptr` to retrieve the underlying memmory address of the global structure. This results in a pointer to the first byte of the array, which can then be passed as an argument to functions that expect a `char*`.\n", "\n", "```{code} llvm\n", "@0 = internal constant [3 x i8] c\"0_i\\00\"\n", "\n", "define void @program_main() #0 {\n", "entry:\n", " call void @__quantum__rt__int_record_output(i64 42, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @0, i32 0, i32 0))\n", " ret void\n", "}\n", "declare void @__quantum__rt__int_record_output(i64, i8*)\n", "```\n", "\n", "The output for 3 shots would have the following form (using fabricated METADATA records).\n", "\n", "```{code} bash\n", "HEADER\\tschema_id\\tlabeled\n", "HEADER\\tschema_version\\t1.0\n", "START\n", "METADATA\\tentry_point\n", "METADATA\\tqir_profiles\\tbase_profile\n", "METADATA\\trequired_num_qubits\\t5\n", "METADATA\\trequired_num_results\\t5\n", "METADATA\\toutput_labeling_schema\\tlabeled\n", "OUTPUT\\tINT\\t42\\t0_i\n", "END\\t0\n", "START\n", "OUTPUT\\tINT\\t41\\t0_i\n", "END\\t0\n", "START\n", "OUTPUT\\tINT\\t42\\t0_i\n", "END\\t0\n", "```\n", "\n", "More information on output result records is available [here](#output-schemas)." ] }, { "cell_type": "markdown", "id": "fd850f3a", "metadata": {}, "source": [ "### Optional Adaptive Profile Specifications\n", "\n", "#### Classical Computations\n", "\n", "```{note}\n", "The only classical value types allowed are %Int, %Double, %Result, %Pauli, and tuples of these values.\n", "```\n", "\n", "Quantinuum Helios optionally supports classical computations on atomic data types, including integer and floating-point arithmetic. System Model H2 only support the integer computation capability. The behavior in the case of an overflow or underflow may be undefined. Data consumed in classical computations must be specified in the form of module flags. Support for a classical data type implies that local variables of that data type may exist at any point in the program. Specifically, it is then also permitted to pass such variables as arguments to QIS functions, runtime functions, or IR-defined functions. Passing constant values of any data type as arguments to QIS and runtime functions is always permitted, regardless of whether classical computations on that data type are supported. In addition to local variables, global constants may be defined for any of the supported classical data types.\n", "\n", "The following calls are supported by the integer capability.\n", "\n", "```{eval-rst}\n", ".. csv-table::\n", " :file: ../../../../_static/qir/integer_spec.csv\n", " :widths: auto\n", "```\n", "\n", "The following calls are supported by the floating-point capability.\n", "\n", "```{eval-rst}\n", ".. csv-table::\n", " :file: ../../../../_static/qir/float_point_spec.csv\n", " :widths: auto\n", "```\n", "\n", "#### IR-defined Functions and Function Calls\n", "\n", "IR-defined functions are function implementations defined as part of the program IR. An Adaptive Profile program that includes IR-defined functions must indicate this in the form of a module flags.\n", "\n", "IR-defined functions may take arguments of type `%Qubit*` and `%Result*`, and it must have `void` return type. If classical computations are supported in addition to IR-defined functions, then values of the supported data type(s) may also be passed as arguments to, and returned from, an IR-defined function. Since the adaptive profile does not include support for composite data types, such as tuples and arrays, they cannot be passed to or returned from IR-defined functions.\n", "\n", "The body of an IR-defined function may use any of the available classical instructions. It may call QIS functions and other IR-defined functions. However, any form of direct or indirect recursion is forbidden. In contrast to the entry point function, an IR-defined function may *not* contain any calls to output recording or initialization functions, but it may call other runtime functions. Just like for the entry point function, values of type `%Qubit*` and `%Result*` may only occur in calls to other functions; qubit values of these types that are passed as arguments cannot be assigned to local variables, that is they cannot be aliased.\n", "\n", "#### Backwards Branching\n", "\n", "```{note}\n", "`phi`-nodes in the Static Single Assignment (SSA) graph for a function, are required to reconcile variables from different control paths in hybrid quantum-classical programs. \n", "```\n", "\n", "This specification enables a compact expression of loops within user programs. The Adaptive Profile distinguishes two kinds of loops; iterations over sequences of known length, and conditionally terminating loops. **Iterations** are primarily useful for a more compact representation of the code, since code size may be a limiting factor. Iterations necessarily requires supporting the use of `phi`-nodes to identify a qubit or result value. For both iterations and conditionally exiting a loop, the value of a `phi`-node used to identify a qubit or result value never depends on any quantum measurements. \n", "\n", "For example, the following IR expresses a loop that flips the state of qubit 1 to 4, if qubit 0 is in a non-zero state. The `phi`-node is defined as 1 for the first iteration and updated to an incremented value at the start of each successive iteration. The loop terminates after the fourth iteration. Each iteration applies a CNOT gate between qubit 0 (control) and a target qubit from the range 1 to 4.\n", "\n", "```{code} llvm\n", "define void @simple_loop() {\n", "entry:\n", " br label %loop_body\n", "loop_body: ; preds = %loop_body, %entry\n", " %0 = phi i64 [ 1, %entry ], [ %1, %loop_body ]\n", " %qptr = inttoptr i64 %0 to %Qubit*\n", " call void @__quantum__qis__cnot__body(%Qubit* null, %Qubit* nonnull %qptr)\n", " %1 = add i64 %0, 1\n", " %2 = icmp sle i64 %1, 4\n", " br i1 %2, label %loop_body, label %loop_exit\n", "loop_exit: ; preds = %loop_body\n", "}\n", "```\n", "\n", "#### Multiple Target Branching\n", "\n", "Support for control flow constructs that indicate how a computation can lead to branching to one of *many* different execution paths. Having such a construct exposed in the IR allows for more aggressive optimizations across different blocks without any dependencies between them. A backend may hence opt into supporting `switch` instructions to facilitate such optimizations. An Adaptive Profile program that includes `switch` instructions must indicate this in the form of a module flag.\n", "\n", "For this capability to be practically useful, integer computations must be supported as well. For example, the snippet below causes a jump to a block `onzero`, `onone`, `ontwo`, or `otherwise` respectively, depending on the value of an integer variable `%val`:\n", "\n", "```{code} llvm\n", "switch i32 %val, label %otherwise [ i32 0, label %onzero\n", " i32 1, label %onone\n", " i32 2, label %ontwo ]\n", "```\n", "\n", "The variable `%val` may, for example, depend on measurement results or a global constant. Please see the [LLVM language reference](https://llvm.org/docs/LangRef.html#switch-instruction) for more information about the switch instruction.\n", "\n", "#### Multiple Return Points\n", "\n", "A QIR program targetting Helios can use this specification if the optional IR Functions capability is supported. This specification enables initialization or IR-defined functions to support multiple return points, eliminating the need to create `phi` nodes for the purpose of propagating the computed output to a single final block. Any use of this optional capability must be indicated in module flags.\n", "\n", "A return statement is necessarily always the last statement in a block. For each block that returns a zero exit code in the entry point function, that same block must also contain the necessary calls to output recording functions to ensure the correct program output is recorded. If the block returns a non-zero exit code, calls to these functions may be omitted, implying that no output will be recorded in this case.\n", "\n", "For example, an Adaptive Profile program that uses this optional capability may contain a logic like this:\n", "\n", "```{code} llvm\n", "@0 = internal constant [2 x i8] c\"0\\00\"\n", "\n", "define i64 @main() local_unnamed_addr #0 {\n", "entry:\n", " tail call void @__quantum__qis__mz__body(%Qubit* null, %Result* writeonly null)\n", " %0 = tail call i1 @__quantum__rt__read_result(%Result* readonly null)\n", " br i1 %0, label %error, label %exit\n", "error:\n", " ; qubits should be in a zero state at the end of the program\n", " ret i64 1\n", "exit:\n", " call void @__quantum__rt__result_record_output(%Result* null, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @0, i32 0, i32 0))\n", " ret i64 0\n", "}\n", "```" ] }, { "cell_type": "markdown", "id": "5f519e63", "metadata": {}, "source": [ "### Quantinuum Platform Utilities\n", "\n", "Quantinuum provides additional function calls for users to consume within programs. A list of all function calls is provided in the table below.\n", "\n", "```{eval-rst}\n", ".. csv-table::\n", " :file: ../../../../_static/qir/qntm_ext.csv\n", " :widths: auto\n", "```\n", "\n", "#### RNG Capability\n", "\n", "Quantinuum Systems provides an integrated real-time Random Number Generation (RNG) capability. Only the `void @___random_advance(i64)` is exclusive to Helios. The RNG capability allows a user to instantiate an RNG instance with a specified seed. The RNG instance can be used to generate a 32-bit signed integer, a bounded 32-bit signed integer within the interval `[0, b)`, and a random float value in the interval `[0, 1)`. \n", "\n", "All platform utilities relating to the RNG capability must be declared within the LLVM IR.\n", "\n", "```{code} llvm\n", "declare void @___random_seed(i64)\n", "declare i32 @___random_int()\n", "declare double @___random_float()\n", "declare i32 @___random_int_bounded(i32)\n", "declare void @___random_advance(i64)\n", "```\n", "\n", "#### Shot Index\n", "\n", "Quantinuum provides a utility to return the index of the current shot as an 64-bit signed integer. The function must be declared in the program.\n", "\n", "```{code} llvm\n", "declare i64 @__get_current_shot()\n", "```\n", "\n", "#### WebAssembly Callout\n", "\n", "```{note}\n", "For QIR targetting System Model H2, the `@__get_wasm_context` function is not required.\n", "```\n", "\n", "The WebAssembly (Wasm) context utility enables wasm calls in the user program. `i64 @__get_wasm_context`. Each function defined in the *.wasm binary must be declared as program attributes. Wasm must be declared as a program attribute and specified for each wasm function to be called from *.wasm.\n", "\n", "```{code} llvm\n", "; WASM init function\n", "declare i64 @___get_wasm_context(i64)\n", "\n", "; WASM function declarations\n", "declare i64 @round_to_int(i64, double) #2\n", "declare i64 @abs_i64(i64, i64) #2\n", "declare i64 @sqrt_floor(i64, double) #2\n", "declare i64 @factorial(i64, i64) #2\n", "\n", "; WASM attribute\n", "attributes #2 = { \"wasm\" }\n", "```" ] }, { "cell_type": "markdown", "id": "db0bbb40", "metadata": {}, "source": [ "## Supported QIR Functions\n", "\n", "### Available QIS functions\n", "\n", "The table below specifies the available QIS functions to consume on the H2 and Helios platforms.\n", "\n", "```{eval-rst}\n", ".. csv-table::\n", " :file: ../../../../_static/qir/qis_functions.csv\n", " :widths: auto\n", "```\n", "\n", "The function :code:`void @__quantum__qis__cnot__body(%Qubit*, %Qubit*)` is a synonym for `@__quantum__qis__cnot__body`. \n", "\n", "\n", "### Available RT functions\n", "\n", "The table below specifies the available Runtime (RT) functions to consume on the H2 and Helios platforms.\n", "\n", "```{eval-rst}\n", ".. csv-table::\n", " :file: ../../../../_static/qir/rt_functions.csv\n", " :widths: auto\n", "```\n", "\n", "### Gate-set Decomposition\n", "\n", "```{eval-rst}\n", ".. csv-table::\n", " :file: ../../../../_static/qir/gateset_decomposition.csv\n", " :widths: auto\n", "```\n", "\n", "**Controlled X gate (CX)**\n", "\n", "`__quantum__qis__cx__body(control, target)` is decomposed to\n", "\n", "```{code}llvm\n", "__quantum__qis__rxy__body(-π/2, π/2, target);\n", "__quantum__qis__rzz__body(π/2, control, target);\n", "__quantum__qis__rz__body(-π/2, control);\n", "__quantum__qis__rxy__body(π/2, π, target);\n", "__quantum__qis__rz__body(-π/2, target);\n", "```\n", "\n", "**Controlled Z gate (CZ)**\n", "\n", "`__quantum__qis__cz__body(control, target)` is decomposed to\n", "\n", "```{code}llvm\n", "__quantum__qis__rxy__body(π, π/2, control);\n", "__quantum__qis__rzz__body(π/2, control, target);\n", "__quantum__qis__rxy__body(π, -π/2, control);\n", "__quantum__qis__rz__body(π/2, target);\n", "__quantum__qis__rz__body(π/2, control);\n", "```\n", "\n", "**Toffoli gate (CCX)**\n", "\n", "`__quantum__qis__ccx__body(control1, control2, target)` is decomposed to\n", "\n", "```{code}llvm\n", "__quantum__qis__rxy__body(π, -π/2, target);\n", "__quantum__qis__rzz__body(π/2, control2, target);\n", "__quantum__qis__rxy__body(π/4, π/2, target);\n", "__quantum__qis__rzz__body(π/2, control1, target);\n", "__quantum__qis__rxy__body(π/4, 0, target);\n", "__quantum__qis__rzz__body(π/2, control2, target);\n", "__quantum__qis__rxy__body(π/4, -π/2, target);\n", "__quantum__qis__rzz__body(π/2, control1, target);\n", "__quantum__qis__rxy__body(π, π/4, control1);\n", "__quantum__qis__rxy__body(-3π/4, π, target);\n", "__quantum__qis__rzz__body(π/4, control1, control2);\n", "__quantum__qis__rz__body(π, target);\n", "__quantum__qis__rxy__body(π, -π/4, control1);\n", "__quantum__qis__rz__body(-3π/4, control2);\n", "__quantum__qis__rz__body(π/4, control1);\n", "```" ] }, { "cell_type": "markdown", "id": "cd4b7e90", "metadata": {}, "source": [ "## Output Schema Labelling\n", "\n", "These runtime functions determine how values should be collected to be emitted as output. Each of these functions follow the naming pattern `__quantum__rt__*_record_output` where the initial part indicates the type of output to be recorded.\n", "\n", "### Result\n", "\n", "```{code} llvm\n", "void @__quantum__rt__result_record_output(%Result*, i8*)\n", "```\n", "\n", "Produces output records of the format `\"OUTPUT\\tRESULT\\t0\\tlabel\"` or `\"OUTPUT\\tRESULT\\t1\\tlabel\"`, representing measurement results. The fourth element is a string label associated to the result value which is included in the corresponding output record.\n", "\n", "### Boolean\n", "\n", "```{code} llvm\n", "void @__quantum__rt__bool_record_output(i1, i8*)\n", "```\n", "\n", "Produces output records of the format `\"OUTPUT\\tBOOL\\tfalse\\tlabel\"` or `\"OUTPUT\\tBOOL\\ttrue\\tlabel\"`. The fourth element (`label`) is a string label associated to the Boolean value which is included in the corresponding output record.\n", "\n", "### Integer\n", "\n", "```{code} llvm\n", "void @__quantum__rt__int_record_output(i64, i8*)\n", "```\n", "\n", "Produces output records of the format `\"OUTPUT\\tINT\\tn\\tlabel\"` where `n` is the string representation of the integer value, such as `\"OUTPUT\\tINT\\t42\\tlabel\"`. The fourth element (`label`) is a string label associated to the integer value, which is included in the corresponding output record.\n", "\n", "### Double\n", "\n", "```{code} llvm\n", "void @__quantum__rt__double_record_output(double, i8*)\n", "```\n", "\n", "Produces output records of the format `\"OUTPUT\\tDOUBLE\\td\\tlabel\"` where `d` is the string representation of the double value, such as `\"OUTPUT\\tDOUBLE\\t3.14159\\tlabel\"`. The fourth element (`label`) is a string label associated to the double value which is included in the corresponding output record.\n", "\n", "### Tuple\n", "\n", "```{code} llvm\n", "void @__quantum__rt__tuple_record_output(i64, i8*)\n", "```\n", "\n", "Produces output records of the format `\"OUTPUT\\tTUPLE\\tn\\tlabel\"` where `n` is the string representation of the integer value, such as `\"OUTPUT\\tTUPLE\\t4\\tlabel\"`. The fourth element (`label`) is a string label associated to the tuple which is included in the corresponding output record. This record indicates the existence and length of a tuples.\n", "\n", "A QIR program that returns a tuple of a measurement result and a double value uses the following output recording functions.\n", "\n", "```{code} llvm\n", "@0 = internal constant [4 x i8] c\"0_t\\00\"\n", "@1 = internal constant [6 x i8] c\"1_t0r\\00\"\n", "@2 = internal constant [6 x i8] c\"2_t1d\\00\"\n", "call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i32 0, i32 0))\n", "call void @__quantum__rt__result_record_output(%Result* %2, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i32 0, i32 0))\n", "call void @__quantum__rt__double_record_output(double %3, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i32 0, i32 0))\n", "ret void\n", "```\n", "\n", "The output for `3` shots would have the following form (using fabricated `METADATA` records).\n", "\n", "```{code} log\n", "HEADER\\tschema_id\\tlabeled\n", "HEADER\\tschema_version\\t1.0\n", "START\n", "METADATA\\tentry_point\n", "METADATA\\tqir_profiles\\tbase_profile\n", "METADATA\\trequired_num_qubits\\t5\n", "METADATA\\trequired_num_results\\t5\n", "METADATA\\toutput_labeling_schema\\tlabeled\n", "OUTPUT\\tTUPLE\\t2\\t0_t\n", "OUTPUT\\tRESULT\\t0\\t1_t0r\n", "OUTPUT\\tDOUBLE\\t0.42\\t2_t1d\n", "END\\t0\n", "START\n", "OUTPUT\\tTUPLE\\t2\\t0_t\n", "OUTPUT\\tRESULT\\t1\\t1_t0r\n", "OUTPUT\\tDOUBLE\\t0.42\\t2_t1d\n", "END\\t0\n", "START\n", "OUTPUT\\tTUPLE\\t2\\t0_t\n", "OUTPUT\\tRESULT\\t0\\t1_t0r\n", "OUTPUT\\tDOUBLE\\t0.25\\t2_t1d\n", "END\\t0\n", "```\n", "\n", "### Array\n", "\n", "```{code} llvm\n", "void @__quantum__rt__array_record_output(i64, i8*)\n", "```\n", "\n", "Produces output records of the format `\"OUTPUT\\tARRAY\\tn\\tlabel\"` where `n` is the string representation of the integer value, such as `\"OUTPUT\\tARRAY\\t4\\tlabel\"`. The fourth element (`label`) is a string label associated to the array which is included in the corresponding output record. This record indicates the existence and length of an array. \n", "\n", "For two arrays of measurement results, the QIR program contains array output recording calls where the first argument indicates the length of the array, followed by the corresponding output recording calls that represent each one of the array items (shown with static result allocation):\n", "\n", "```{code} llvm\n", "@0 = internal constant [5 x i8] c\"0_0a\\00\"\n", "@1 = internal constant [7 x i8] c\"1_0a0r\\00\"\n", "@2 = internal constant [5 x i8] c\"2_1a\\00\"\n", "@3 = internal constant [7 x i8] c\"3_1a0r\\00\"\n", "@4 = internal constant [7 x i8] c\"4_1a1r\\00\"\n", "call void @__quantum__rt__array_record_output(i64 1, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @0, i32 0, i32 0))\n", "call void @__quantum__rt__result_record_output(%Result* %2, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @1, i32 0, i32 0))\n", "call void @__quantum__rt__array_record_output(i64 2, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @2, i32 0, i32 0))\n", "call void @__quantum__rt__result_record_output(%Result* nonnull inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([7 x i8], [7 x i8]* @3, i32 0, i32 0))\n", "call void @__quantum__rt__result_record_output(%Result* nonnull inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([7 x i8], [7 x i8]* @4, i32 0, i32 0))\n", "ret void\n", "```\n", "\n", "The output for `3` shots would have the following form (using fabricated `METADATA` records).\n", "\n", "```{code} log\n", "HEADER\\tschema_id\\tlabeled\n", "HEADER\\tschema_version\\t1.0\n", "START\n", "METADATA\\tentry_point\n", "METADATA\\tqir_profiles\\tbase_profile\n", "METADATA\\trequired_num_qubits\\t5\n", "METADATA\\trequired_num_results\\t5\n", "METADATA\\toutput_labeling_schema\\tlabeled\n", "OUTPUT\\tARRAY\\t1\\t0_0a\n", "OUTPUT\\tRESULT\\t0\\t1_0a0r\n", "OUTPUT\\tARRAY\\t2\\t2_1a\n", "OUTPUT\\tRESULT\\t1\\t3_1a0r\n", "OUTPUT\\tRESULT\\t1\\t4_1a1r\n", "END\\t0\n", "START\n", "OUTPUT\\tARRAY\\t1\\t0_0a\n", "OUTPUT\\tRESULT\\t1\\t1_0a0r\n", "OUTPUT\\tARRAY\\t2\\t2_1a\n", "OUTPUT\\tRESULT\\t1\\t3_1a0r\n", "OUTPUT\\tRESULT\\t1\\t4_1a1r\n", "END\\t0\n", "START\n", "OUTPUT\\tARRAY\\t1\\t0_0a\n", "OUTPUT\\tRESULT\\t0\\t1_0a0r\n", "OUTPUT\\tARRAY\\t2\\t2_1a\n", "OUTPUT\\tRESULT\\t1\\t3_1a0r\n", "OUTPUT\\tRESULT\\t1\\t4_1a1r\n", "END\\t0\n", "```" ] }, { "cell_type": "markdown", "id": "a0dbca16", "metadata": {}, "source": [ "## Program Structure\n", "\n", "The program structure is desseminated below for Helios-compliant and H2-compliant QIR programs. \n", "\n", "An Adaptive Profile-compliant program is defined in the form textual LLVM IR. The program structure is demonstrated below for a CX teleportation. This program adheres to the QIR Core Adaptive Profile + Classical Computation Specification.\n", "\n", "**Type Definitions**\n", "\n", "```{code} llvm\n", "; type definitions\n", "\n", "%Result = type opaque\n", "%Qubit = type opaque\n", "```\n", "\n", "**Global Constants**\n", "\n", "global constants that store [string labels](#output-recording) needed for certain output schemas. Optionally, if classical computations are supported, global constants of the supported classical data types should be defined here.\n", "\n", "```{code} llvm\n", "@0 = internal constant [5 x i8] c\"0_t0\\00\"\n", "@1 = internal constant [5 x i8] c\"0_t1\\00\"\n", "```\n", "\n", "**Program Logic**\n", "\n", "QIR programs consist of multiple blocks. If IR-defined functions are supported, additional functions can be called . There are two mandatory block `entry` and `body`. \n", "\n", "The `entry` block initializes the quantum runtime.\n", "\n", "```{code} llvm\n", "define i64 @TeleportChain() local_unnamed_addr #0 {\n", "entry:\n", " ; calls to initialize the execution environment\n", " call void @__quantum__rt__initialize(i8* null)\n", " br label %body\n", "```\n", "\n", "The `body` block consists of quantum QIS and runtime calls. A runtime function is used to convert the `%Result` value into `i1`. This conversion is necessary for forward branching. \n", "\n", "```{code} llvm\n", "body: ; preds = %entry\n", " tail call void @__quantum__qis__h__body(%Qubit* null)\n", " tail call void @__quantum__qis__cnot__body(%Qubit* null, %Qubit* nonnull inttoptr (i64 1 to %Qubit*))\n", " tail call void @__quantum__qis__h__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__cnot__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*), %Qubit* nonnull inttoptr (i64 4 to %Qubit*))\n", " tail call void @__quantum__qis__h__body(%Qubit* nonnull inttoptr (i64 3 to %Qubit*))\n", " tail call void @__quantum__qis__cnot__body(%Qubit* nonnull inttoptr (i64 3 to %Qubit*), %Qubit* nonnull inttoptr (i64 5 to %Qubit*))\n", " tail call void @__quantum__qis__cnot__body(%Qubit* nonnull inttoptr (i64 1 to %Qubit*), %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__h__body(%Qubit* nonnull inttoptr (i64 1 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 1 to %Qubit*), %Result* writeonly null)\n", " tail call void @__quantum__qis__reset__body(%Qubit* nonnull inttoptr (i64 1 to %Qubit*))\n", " %0 = tail call i1 @__quantum__rt__read_result(%Result* readonly null)\n", " br i1 %0, label %then__1, label %continue__1\n", "```\n", "\n", "`then_1` is the first conditional block consisting of 1 quantum gate (only one in this block, but many can appear and the full quantum instruction set should be usable).\n", "\n", "```{code} llvm\n", "then__1: ; preds = %body\n", " tail call void @__quantum__qis__z__body(%Qubit* nonnull inttoptr (i64 4 to %Qubit*))\n", " br label %continue__1\n", "```\n", "\n", "If the conditional block `then_1` completes execution, the `continue_1` block measures and resets qubit with index 2.\n", "\n", "```{code} llvm\n", "continue__1: ; preds = %then__1, %body\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*), %Result* writeonly nonnull inttoptr (i64 1 to %Result*))\n", " tail call void @__quantum__qis__reset__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " %1 = tail call i1 @__quantum__rt__read_result(%Result* readonly nonnull inttoptr (i64 1 to %Result*))\n", " br i1 %1, label %then__2, label %continue__2\n", "```\n", "\n", "The `then_2` block is called conditionally, based on the measurement value from `continue_1`. Completion of `then_2` calls `continue_2`.\n", "\n", "```{code} llvm\n", "then__2: ; preds = %continue__1\n", " tail call void @__quantum__qis__x__body(%Qubit* nonnull inttoptr (i64 4 to %Qubit*))\n", " br label %continue__2\n", "\n", "continue__2: ; preds = %then__2, %continue__1\n", " tail call void @__quantum__qis__cnot__body(%Qubit* nonnull inttoptr (i64 4 to %Qubit*), %Qubit* nonnull inttoptr (i64 3 to %Qubit*))\n", " tail call void @__quantum__qis__h__body(%Qubit* nonnull inttoptr (i64 4 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 4 to %Qubit*), %Result* writeonly nonnull inttoptr (i64 2 to %Result*))\n", " tail call void @__quantum__qis__reset__body(%Qubit* nonnull inttoptr (i64 4 to %Qubit*))\n", " %2 = tail call i1 @__quantum__rt__read_result(%Result* readonly nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %2, label %then__3, label %continue__3\n", "```\n", "\n", "The `then_3` block is called conditionally, based on the measurement value from `continue_3`. Subsequent `then` and `continue` blocks are incrementally called up to the limit of 4.\n", "\n", "```{code} llvm\n", "then__3: ; preds = %continue__2\n", " tail call void @__quantum__qis__z__body(%Qubit* nonnull inttoptr (i64 5 to %Qubit*))\n", " br label %continue__3\n", "\n", "continue__3: ; preds = %then__3, %continue__2\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 3 to %Qubit*), %Result* writeonly nonnull inttoptr (i64 3 to %Result*))\n", " tail call void @__quantum__qis__reset__body(%Qubit* nonnull inttoptr (i64 3 to %Qubit*))\n", " %3 = tail call i1 @__quantum__rt__read_result(%Result* readonly nonnull inttoptr (i64 3 to %Result*))\n", " br i1 %3, label %then__4, label %continue__4\n", "\n", "then__4: ; preds = %continue__3\n", " tail call void @__quantum__qis__x__body(%Qubit* nonnull inttoptr (i64 5 to %Qubit*))\n", " br label %continue__4\n", "\n", "continue__4: ; preds = %continue__3, %then__4\n", " tail call void @__quantum__qis__mz__body(%Qubit* null, %Result* writeonly nonnull inttoptr (i64 4 to %Result*))\n", " tail call void @__quantum__qis__reset__body(%Qubit* null)\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 5 to %Qubit*), %Result* writeonly nonnull inttoptr (i64 5 to %Result*))\n", " tail call void @__quantum__qis__reset__body(%Qubit* nonnull inttoptr (i64 5 to %Qubit*))\n", " br label %exit\n", "```\n", "\n", "The `exit` block calls quantum runtime functions to record measurement results in a specified order.\n", "\n", "```{code} llvm\n", "exit:\n", " call void @__quantum__rt__result_record_output(%Result* nonnull inttoptr (i64 4 to %Result*), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @0, i32 0, i32 0))\n", " call void @__quantum__rt__result_record_output(%Result* nonnull inttoptr (i64 5 to %Result*), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @1, i32 0, i32 0))\n", " ret i64 0\n", "}\n", "```\n", "\n", "**QIS Function Declaration**\n", "\n", "QIS function declaration for functions consumed by the program.\n", "\n", "```{code} llvm\n", "declare void @__quantum__qis__cnot__body(%Qubit*, %Qubit*) local_unnamed_addr\n", "declare void @__quantum__qis__h__body(%Qubit*) local_unnamed_addr\n", "declare void @__quantum__qis__x__body(%Qubit*) local_unnamed_addr\n", "declare void @__quantum__qis__z__body(%Qubit*) local_unnamed_addr\n", "declare void @__quantum__qis__reset__body(%Qubit*) local_unnamed_addr\n", "declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1\n", "```\n", "\n", "**Runtime Function Declaration**\n", "\n", "Runtime function declaration used for initialization and output reordering {footcite:p}`qir-spec-libref`.\n", "\n", "```{code} llvm\n", "declare void @__quantum__rt__initialize(i8*)\n", "declare i1 @__quantum__rt__read_result(%Result* readonly)\n", "declare void @__quantum__rt__result_record_output(%Result*, i8*)\n", "```\n", "\n", "**Attributes**\n", "\n", "One or more attribute groups used to store information about the entry point, and optionally additional information about other function declarations. If Wasm is used, it must be declared as an attribute.\n", "\n", "```{code} llvm\n", "attributes #0 = { \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"schema_id\" \"required_num_qubits\"=\"6\" \"required_num_results\"=\"6\" }\n", "attributes #1 = { \"irreversible\" }\n", "```\n", "\n", "**Module Flags**\n", "\n", "Metadata a compiler or backend may need to process the bitcode, including module flags that indicate which features of the Adaptive Profile are consumed.\n", "\n", "```{code} llvm\n", "!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9}\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", "!4 = !{i32 5, !\"int_computations\", !10}\n", "!5 = !{i32 5, !\"float_computations\", !11}\n", "!6 = !{i32 1, !\"ir_functions\", i1 false}\n", "!7 = !{i32 1, !\"backwards_branching\", i2 0}\n", "!8 = !{i32 1, !\"multiple_target_branching\", i1 false}\n", "!9 = !{i32 1, !\"multiple_return_points\", i1 false}\n", "!10 = !{!\"i32\", !\"i64\"}\n", "!11 = !{!\"float\", !\"double\"}\n", "```" ] }, { "cell_type": "markdown", "id": "749e14ae", "metadata": {}, "source": [ "## Platform Examples\n", "\n", "The LLVM IR files can be downloaded here.\n", "\n", "**Helios**\n", "- [RNG Switch](helios_llvm/rng_switch.ll)\n", "- [Teleported Chain](helios_llvm/teleported_chain.ll)\n", "- [IR functions](helios_llvm/ir_fn.ll)\n", "- [Wasm Callout](helios_llvm/wasm.ll) and [arith.wasm](helios_llvm/arith.wasm)\n", "\n", "**H2**\n", "- [Random Number Generation](h2_llvm/random_numbers.ll)\n", "- [Interations](h2_llvm/iterations.ll)\n", "- [Wasm Callout](h2_llvm/wasm.ll) and [testfile.wasm](h2_llvm/testfile.wasm)" ] }, { "cell_type": "markdown", "id": "edbdda63", "metadata": {}, "source": [ "## BitCode Generation\n", "\n", "One LLVM IR file is loaded into the python session as a string and then converted into QIR bitcode." ] }, { "cell_type": "code", "execution_count": 1, "id": "f0e03685", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "; type definitions\n", "\n", "%Result = type opaque\n", "%Qubit = type opaque\n", "\n", "; global constants (labels for output recording)\n", "\n", "@0 = internal constant [5 x i8] c\"0_t0\\00\"\n", "@1 = internal constant [5 x i8] c\"0_t1\\00\"\n", "\n", "; entry point definition\n", "\n", "define i64 @TeleportChain() local_unnamed_addr #0 {\n", "entry:\n", " ; calls to initialize the execution environment\n", " call void @__quantum__rt__initialize(i8* null)\n", " br label %body\n", "\n", "body: ; preds = %entry\n", " tail call void @__quantum__qis__h__body(%Qubit* null)\n", " tail call void @__quantum__qis__cnot__body(%Qubit* null, %Qubit* nonnull inttoptr (i64 1 to %Qubit*))\n", " tail call void @__quantum__qis__h__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__cnot__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*), %Qubit* nonnull inttoptr (i64 4 to %Qubit*))\n", " tail call void @__quantum__qis__h__body(%Qubit* nonnull inttoptr (i64 3 to %Qubit*))\n", " tail call void @__quantum__qis__cnot__body(%Qubit* nonnull inttoptr (i64 3 to %Qubit*), %Qubit* nonnull inttoptr (i64 5 to %Qubit*))\n", " tail call void @__quantum__qis__cnot__body(%Qubit* nonnull inttoptr (i64 1 to %Qubit*), %Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " tail call void @__quantum__qis__h__body(%Qubit* nonnull inttoptr (i64 1 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 1 to %Qubit*), %Result* writeonly null)\n", " tail call void @__quantum__qis__reset__body(%Qubit* nonnull inttoptr (i64 1 to %Qubit*))\n", " %0 = tail call i1 @__quantum__rt__read_result(%Result* readonly null)\n", " br i1 %0, label %then__1, label %continue__1\n", "\n", "; conditional quantum gate (only one in this block, but many can appear and the full quantum instruction set should be usable)\n", "then__1: ; preds = %body\n", " tail call void @__quantum__qis__z__body(%Qubit* nonnull inttoptr (i64 4 to %Qubit*))\n", " br label %continue__1\n", "\n", "continue__1: ; preds = %then__1, %body\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*), %Result* writeonly nonnull inttoptr (i64 1 to %Result*))\n", " tail call void @__quantum__qis__reset__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*))\n", " %1 = tail call i1 @__quantum__rt__read_result(%Result* readonly nonnull inttoptr (i64 1 to %Result*))\n", " br i1 %1, label %then__2, label %continue__2\n", "\n", "then__2: ; preds = %continue__1\n", " tail call void @__quantum__qis__x__body(%Qubit* nonnull inttoptr (i64 4 to %Qubit*))\n", " br label %continue__2\n", "\n", "continue__2: ; preds = %then__2, %continue__1\n", " tail call void @__quantum__qis__cnot__body(%Qubit* nonnull inttoptr (i64 4 to %Qubit*), %Qubit* nonnull inttoptr (i64 3 to %Qubit*))\n", " tail call void @__quantum__qis__h__body(%Qubit* nonnull inttoptr (i64 4 to %Qubit*))\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 4 to %Qubit*), %Result* writeonly nonnull inttoptr (i64 2 to %Result*))\n", " tail call void @__quantum__qis__reset__body(%Qubit* nonnull inttoptr (i64 4 to %Qubit*))\n", " %2 = tail call i1 @__quantum__rt__read_result(%Result* readonly nonnull inttoptr (i64 2 to %Result*))\n", " br i1 %2, label %then__3, label %continue__3\n", "\n", "then__3: ; preds = %continue__2\n", " tail call void @__quantum__qis__z__body(%Qubit* nonnull inttoptr (i64 5 to %Qubit*))\n", " br label %continue__3\n", "\n", "continue__3: ; preds = %then__3, %continue__2\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 3 to %Qubit*), %Result* writeonly nonnull inttoptr (i64 3 to %Result*))\n", " tail call void @__quantum__qis__reset__body(%Qubit* nonnull inttoptr (i64 3 to %Qubit*))\n", " %3 = tail call i1 @__quantum__rt__read_result(%Result* readonly nonnull inttoptr (i64 3 to %Result*))\n", " br i1 %3, label %then__4, label %continue__4\n", "\n", "then__4: ; preds = %continue__3\n", " tail call void @__quantum__qis__x__body(%Qubit* nonnull inttoptr (i64 5 to %Qubit*))\n", " br label %continue__4\n", "\n", "continue__4: ; preds = %continue__3, %then__4\n", " tail call void @__quantum__qis__mz__body(%Qubit* null, %Result* writeonly nonnull inttoptr (i64 4 to %Result*))\n", " tail call void @__quantum__qis__reset__body(%Qubit* null)\n", " tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 5 to %Qubit*), %Result* writeonly nonnull inttoptr (i64 5 to %Result*))\n", " tail call void @__quantum__qis__reset__body(%Qubit* nonnull inttoptr (i64 5 to %Qubit*))\n", " br label %exit\n", "\n", "exit:\n", " call void @__quantum__rt__result_record_output(%Result* nonnull inttoptr (i64 4 to %Result*), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @0, i32 0, i32 0))\n", " call void @__quantum__rt__result_record_output(%Result* nonnull inttoptr (i64 5 to %Result*), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @1, i32 0, i32 0))\n", " ret i64 0\n", "}\n", "\n", "; declarations of QIS functions\n", "\n", "declare void @__quantum__qis__cnot__body(%Qubit*, %Qubit*) local_unnamed_addr\n", "\n", "declare void @__quantum__qis__h__body(%Qubit*) local_unnamed_addr\n", "\n", "declare void @__quantum__qis__x__body(%Qubit*) local_unnamed_addr\n", "\n", "declare void @__quantum__qis__z__body(%Qubit*) local_unnamed_addr\n", "\n", "declare void @__quantum__qis__reset__body(%Qubit*) local_unnamed_addr\n", "\n", "declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1\n", "\n", "; declarations of runtime functions\n", "\n", "declare void @__quantum__rt__initialize(i8*)\n", "\n", "declare i1 @__quantum__rt__read_result(%Result* readonly)\n", "\n", "declare void @__quantum__rt__result_record_output(%Result*, i8*)\n", "\n", "; attributes\n", "\n", "attributes #0 = { \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"schema_id\" \"required_num_qubits\"=\"6\" \"required_num_results\"=\"6\" }\n", "\n", "attributes #1 = { \"irreversible\" }\n", "\n", "; module flags\n", "\n", "!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9}\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", "!4 = !{i32 5, !\"int_computations\", !10}\n", "!5 = !{i32 5, !\"float_computations\", !11}\n", "!6 = !{i32 1, !\"ir_functions\", i1 false}\n", "!7 = !{i32 1, !\"backwards_branching\", i2 0}\n", "!8 = !{i32 1, !\"multiple_target_branching\", i1 false}\n", "!9 = !{i32 1, !\"multiple_return_points\", i1 false}\n", "!10 = !{!\"i32\", !\"i64\"}\n", "!11 = !{!\"float\", !\"double\"}\n" ] } ], "source": [ "with open(\"teleported_cx.ll\", \"r\") as file_io:\n", " llvm_ir = file_io.read()\n", "\n", "print(llvm_ir)" ] }, { "cell_type": "markdown", "id": "6eb33ac8", "metadata": {}, "source": [ "The function {py:class}`pyqir.Module` allows textual QIR to be converted into bitcode. " ] }, { "cell_type": "markdown", "id": "6eb33ac8", "metadata": {}, "source": [ "The function {py:class}`pyqir.Module` allows textual QIR to be converted into bitcode. " ] }, { "cell_type": "markdown", "id": "6eb33ac8", "metadata": {}, "source": [ "The function {py:class}`pyqir.Module` allows textual QIR to be converted into bitcode. " ] }, { "cell_type": "code", "execution_count": 2, "id": "d889a653", "metadata": {}, "outputs": [], "source": [ "import pyqir\n", "\n", "qir_bitcode = pyqir.Module.from_ir(pyqir.Context(), llvm_ir, \"teleported_cx\").bitcode" ] }, { "cell_type": "markdown", "id": "866308ec", "metadata": {}, "source": [ "```{toctree}\n", ":caption: QIR\n", ":hidden: true\n", ":maxdepth: 2\n", "\n", "QIR Specifications \n", "qir_submission.ipynb\n", "guppy_to_qir.ipynb\n", "pytket_to_qir.ipynb\n", "```" ] }, { "cell_type": "markdown", "id": "82eb4429", "metadata": {}, "source": [ "## References\n", "\n", "```{eval-rst}\n", ".. footbibliography::\n", "```" ] } ], "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 }