{ "cells": [ { "cell_type": "markdown", "id": "a822a189", "metadata": {}, "source": [ "# Pytket to QIR" ] }, { "cell_type": "markdown", "id": "a7561ec0", "metadata": {}, "source": [ "pytket-qir is an client-side extension (plugin) for pytket, the quantum circuit toolkit & compiler framework by Quantinuum. Its purpose is to provide functionality for 1-way conversion of pytket circuits into the QIR (Quantum Intermediate Representation) format. It allows users of pytket to target QIR-compliant hardware providers or off-the-shelf simulation tool. \n", "\n", "pytket-qir is cross-platform on Windows, Linux and macOS. It is compatible with Python 3.10 - 3.13, and available on PyPi. An API reference is available at [https://docs.quantinuum.com/tket/extensions/pytket-qir/](https://docs.quantinuum.com/tket/extensions/pytket-qir/). The package is opensource (Apache 2.0), available at [https://github.com/quantinuum/pytket-qir.git](https://github.com/quantinuum/pytket-qir.git), and downloadable via PyPI.\n", "\n", "```{code} bash\n", "pip install pytket-qir\n", "```" ] }, { "cell_type": "markdown", "id": "16142376", "metadata": {}, "source": [ "## Basic Usage" ] }, { "cell_type": "code", "execution_count": 1, "id": "e92c9b98", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[ClExpr a[0], a[1], a[2], a[3], a[4], b[0], b[1], b[2], b[3], b[4], c[0], c[1], c[2], c[3], c[4]; H q[0]; H q[1]; H q[2]; ClExpr c[0], c[1], c[2], c[3], c[4], b[0], b[1], b[2], b[3], b[4], d[0], d[1], d[2], d[3], d[4]; H q[0]; IF ([a[4]] == 1) THEN ClExpr c[0], c[1], c[2], c[3], c[4], b[0], b[1], b[2], b[3], b[4], d[0], d[1], d[2], d[3], d[4]; Measure q[2] --> d[2]; Measure q[1] --> d[3]; Measure q[0] --> d[4]; ]" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from pytket.circuit import Circuit, Qubit\n", "from pytket.circuit.clexpr import wired_clexpr_from_logic_exp\n", "\n", "circ = Circuit(3)\n", "a = circ.add_c_register(\"a\", 5)\n", "b = circ.add_c_register(\"b\", 5)\n", "c = circ.add_c_register(\"c\", 5)\n", "d = circ.add_c_register(\"d\", 5)\n", "circ.H(0)\n", "circ.add_clexpr(*wired_clexpr_from_logic_exp(a | b, c)) # type: ignore\n", "circ.add_clexpr(*wired_clexpr_from_logic_exp(c | b, d)) # type: ignore\n", "circ.add_clexpr(*wired_clexpr_from_logic_exp(c | b, d), condition=a[4]) # type: ignore\n", "circ.H(0)\n", "circ.Measure(Qubit(0), d[4])\n", "circ.H(1)\n", "circ.Measure(Qubit(1), d[3])\n", "circ.H(2)\n", "circ.Measure(Qubit(2), d[2])" ] }, { "cell_type": "code", "execution_count": 2, "id": "0115fef5", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "; ModuleID = 'conditional'\n", "source_filename = \"conditional\"\n", "\n", "%Qubit = type opaque\n", "%Result = type opaque\n", "\n", "@0 = internal constant [2 x i8] c\"a\\00\"\n", "@1 = internal constant [2 x i8] c\"b\\00\"\n", "@2 = internal constant [2 x i8] c\"c\\00\"\n", "@3 = internal constant [2 x i8] c\"d\\00\"\n", "\n", "define void @main() #0 {\n", "entry:\n", " call void @__quantum__qis__h__body(%Qubit* null)\n", " call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))\n", " call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*))\n", " call void @__quantum__qis__h__body(%Qubit* null)\n", " br i1 false, label %condb0, label %contb0\n", "\n", "condb0: ; preds = %entry\n", " br label %contb0\n", "\n", "contb0: ; preds = %condb0, %entry\n", " %0 = phi i64 [ 0, %condb0 ], [ 0, %entry ]\n", " call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 2 to %Result*))\n", " %1 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 2 to %Result*))\n", " %2 = zext i1 %1 to i64\n", " %3 = mul i64 %2, 4\n", " %4 = or i64 %3, %0\n", " %5 = sub i64 1, %2\n", " %6 = mul i64 %5, 4\n", " %7 = xor i64 9223372036854775807, %6\n", " %8 = and i64 %7, %4\n", " call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))\n", " %9 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 1 to %Result*))\n", " %10 = zext i1 %9 to i64\n", " %11 = mul i64 %10, 8\n", " %12 = or i64 %11, %8\n", " %13 = sub i64 1, %10\n", " %14 = mul i64 %13, 8\n", " %15 = xor i64 9223372036854775807, %14\n", " %16 = and i64 %15, %12\n", " call void @__quantum__qis__mz__body(%Qubit* null, %Result* null)\n", " %17 = call i1 @__quantum__rt__read_result(%Result* null)\n", " %18 = zext i1 %17 to i64\n", " %19 = mul i64 %18, 16\n", " %20 = or i64 %19, %16\n", " %21 = sub i64 1, %18\n", " %22 = mul i64 %21, 16\n", " %23 = xor i64 9223372036854775807, %22\n", " %24 = and i64 %23, %20\n", " call void @__quantum__rt__int_record_output(i64 0, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @0, i32 0, i32 0))\n", " call void @__quantum__rt__int_record_output(i64 0, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @1, i32 0, i32 0))\n", " call void @__quantum__rt__int_record_output(i64 0, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @2, i32 0, i32 0))\n", " call void @__quantum__rt__int_record_output(i64 %24, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @3, i32 0, i32 0))\n", " ret void\n", "}\n", "\n", "declare i1 @__quantum__rt__read_result(%Result*)\n", "\n", "declare void @__quantum__rt__int_record_output(i64, i8*)\n", "\n", "declare void @__quantum__qis__h__body(%Qubit*)\n", "\n", "declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1\n", "\n", "attributes #0 = { \"entry_point\" \"output_labeling_schema\" \"qir_profiles\"=\"custom\" \"required_num_qubits\"=\"3\" \"required_num_results\"=\"3\" }\n", "attributes #1 = { \"irreversible\" }\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 pytket.qir.conversion.api import QIRFormat, QIRProfile, pytket_to_qir\n", "\n", "\n", "gen_qir_ll = pytket_to_qir(\n", " circ,\n", " name=\"conditional\",\n", " qir_format=QIRFormat.STRING,\n", " profile=QIRProfile.ADAPTIVE,\n", " cut_pytket_register=True,\n", ")\n", "\n", "print(gen_qir_ll)" ] }, { "cell_type": "markdown", "id": "e734b79e", "metadata": {}, "source": [ "## Use Cases" ] }, { "cell_type": "markdown", "id": "87bedf68", "metadata": {}, "source": [ "### Wasm Callouts" ] }, { "cell_type": "code", "execution_count": 3, "id": "7ecabf94", "metadata": {}, "outputs": [ { "ename": "ValueError", "evalue": "wasm file not found at given path", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mValueError\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 5\u001b[39m\n\u001b[32m 2\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpytket\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mcircuit\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m Bit, Circuit, Qubit\n\u001b[32m 3\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpytket\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mwasm\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m WasmFileHandler\n\u001b[32m----> \u001b[39m\u001b[32m5\u001b[39m w = \u001b[43mWasmFileHandler\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mtestfile.wasm\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 6\u001b[39m c = Circuit(\u001b[32m6\u001b[39m, \u001b[32m6\u001b[39m)\n\u001b[32m 7\u001b[39m c0 = c.add_c_register(\u001b[33m\"\u001b[39m\u001b[33mc0\u001b[39m\u001b[33m\"\u001b[39m, \u001b[32m3\u001b[39m)\n", "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\Irfan.Khan\\Projects\\nexus-docs\\.venv\\Lib\\site-packages\\pytket\\wasm\\wasm.py:315\u001b[39m, in \u001b[36mWasmFileHandler.__init__\u001b[39m\u001b[34m(self, filepath, check_file, int_size)\u001b[39m\n\u001b[32m 305\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 306\u001b[39m \u001b[33;03mConstruct a wasm file handler using a filepath to read a wasm module into\u001b[39;00m\n\u001b[32m 307\u001b[39m \u001b[33;03mmemory.\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 312\u001b[39m \u001b[33;03m:param int_size: length of the integer that is used in the wasm file\u001b[39;00m\n\u001b[32m 313\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 314\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m exists(filepath):\n\u001b[32m--> \u001b[39m\u001b[32m315\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mwasm file not found at given path\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 317\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mopen\u001b[39m(filepath, \u001b[33m\"\u001b[39m\u001b[33mrb\u001b[39m\u001b[33m\"\u001b[39m) \u001b[38;5;28;01mas\u001b[39;00m file:\n\u001b[32m 318\u001b[39m \u001b[38;5;28mself\u001b[39m._wasm_file: \u001b[38;5;28mbytes\u001b[39m = file.read()\n", "\u001b[31mValueError\u001b[39m: wasm file not found at given path" ] } ], "source": [ "from pytket import wasm\n", "from pytket.circuit import Bit, Circuit, Qubit\n", "from pytket.wasm import WasmFileHandler\n", "\n", "w = WasmFileHandler(\"testfile.wasm\")\n", "c = Circuit(6, 6)\n", "c0 = c.add_c_register(\"c0\", 3)\n", "c1 = c.add_c_register(\"c1\", 4)\n", "c2 = c.add_c_register(\"c2\", 5)\n", "c.add_wasm_to_reg(\"multi\", w, [c0, c1], [c2])\n", "c.add_wasm_to_reg(\"add_one\", w, [c2], [c2])\n", "c.add_wasm_to_reg(\"no_return\", w, [c2], [])\n", "c.add_wasm_to_reg(\"init\", w, [], [])\n", "c.add_wasm_to_reg(\"no_parameters\", w, [], [c2])" ] }, { "cell_type": "markdown", "id": "09203f95", "metadata": {}, "source": [] }, { "cell_type": "code", "execution_count": null, "id": "b3f41b24", "metadata": {}, "outputs": [ { "ename": "AttributeError", "evalue": "'pytket._tket.unit_id.BitRegister' object has no attribute 'q_registers'", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 5\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpytket\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mqir\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mconversion\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mapi\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m QIRFormat, QIRProfile, pytket_to_qir\n\u001b[32m 3\u001b[39m profile = QIRProfile.ADAPTIVE\n\u001b[32m----> \u001b[39m\u001b[32m5\u001b[39m result = \u001b[43mpytket_to_qir\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 6\u001b[39m \u001b[43m \u001b[49m\u001b[43mc\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 7\u001b[39m \u001b[43m \u001b[49m\u001b[43mname\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43mf\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mpytket_qir_wasm_2-\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43mprofile\u001b[49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 8\u001b[39m \u001b[43m \u001b[49m\u001b[43mqir_format\u001b[49m\u001b[43m=\u001b[49m\u001b[43mQIRFormat\u001b[49m\u001b[43m.\u001b[49m\u001b[43mSTRING\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9\u001b[39m \u001b[43m \u001b[49m\u001b[43mint_type\u001b[49m\u001b[43m=\u001b[49m\u001b[32;43m32\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 10\u001b[39m \u001b[43m \u001b[49m\u001b[43mprofile\u001b[49m\u001b[43m=\u001b[49m\u001b[43mprofile\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 11\u001b[39m \u001b[43m)\u001b[49m\n", "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\Irfan.Khan\\Projects\\nexus-docs\\.venv\\Lib\\site-packages\\pytket\\qir\\conversion\\api.py:117\u001b[39m, in \u001b[36mpytket_to_qir\u001b[39m\u001b[34m(circ, name, qir_format, int_type, cut_pytket_register, profile)\u001b[39m\n\u001b[32m 114\u001b[39m cpass = scratch_reg_resize_pass(int_type)\n\u001b[32m 115\u001b[39m cpass.apply(circ)\n\u001b[32m--> \u001b[39m\u001b[32m117\u001b[39m \u001b[43mcheck_circuit\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcirc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mint_type\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 119\u001b[39m m = tketqirModule(\n\u001b[32m 120\u001b[39m name=name,\n\u001b[32m 121\u001b[39m num_qubits=circ.n_qubits,\n\u001b[32m 122\u001b[39m num_results=circ.n_qubits,\n\u001b[32m 123\u001b[39m )\n\u001b[32m 125\u001b[39m trunc = \u001b[38;5;28;01mFalse\u001b[39;00m\n", "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\Irfan.Khan\\Projects\\nexus-docs\\.venv\\Lib\\site-packages\\pytket\\qir\\conversion\\api.py:230\u001b[39m, in \u001b[36mcheck_circuit\u001b[39m\u001b[34m(circuit, int_type)\u001b[39m\n\u001b[32m 217\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mcheck_circuit\u001b[39m(\n\u001b[32m 218\u001b[39m circuit: Circuit,\n\u001b[32m 219\u001b[39m int_type: \u001b[38;5;28mint\u001b[39m = \u001b[32m64\u001b[39m,\n\u001b[32m 220\u001b[39m ) -> \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 221\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Checks the validity of the circuit.\u001b[39;00m\n\u001b[32m 222\u001b[39m \n\u001b[32m 223\u001b[39m \u001b[33;03m Running this check before conversion is recommended for big circuits that\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 228\u001b[39m \u001b[33;03m :raises ValueError: with a suggestion on how to resolve the problems\u001b[39;00m\n\u001b[32m 229\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m230\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[43mcircuit\u001b[49m\u001b[43m.\u001b[49m\u001b[43mq_registers\u001b[49m) > \u001b[32m1\u001b[39m \u001b[38;5;129;01more\u001b[39;00m (\n\u001b[32m 231\u001b[39m \u001b[38;5;28mlen\u001b[39m(circuit.q_registers) == \u001b[32m1\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m circuit.q_registers[\u001b[32m0\u001b[39m].name != \u001b[33m\"\u001b[39m\u001b[33mq\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 232\u001b[39m ):\n\u001b[32m 233\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[32m 234\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"The circuit that should be converted should only have the default\u001b[39;00m\n\u001b[32m 235\u001b[39m \u001b[33;03m quantum register. You can convert it using the pytket\u001b[39;00m\n\u001b[32m 236\u001b[39m \u001b[33;03m compiler pass `FlattenRelabelRegistersPass`.\"\"\"\u001b[39;00m,\n\u001b[32m 237\u001b[39m )\n\u001b[32m 239\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m int_type \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m {\u001b[32m32\u001b[39m, \u001b[32m64\u001b[39m}:\n", "\u001b[31mAttributeError\u001b[39m: 'pytket._tket.unit_id.BitRegister' object has no attribute 'q_registers'" ] } ], "source": [ "from pytket.qir.conversion.api import QIRFormat, QIRProfile, pytket_to_qir\n", "\n", "profile = QIRProfile.ADAPTIVE\n", "\n", "result = pytket_to_qir(\n", " c,\n", " name=f\"pytket_qir_wasm_2-{profile}\",\n", " qir_format=QIRFormat.STRING,\n", " int_type=32,\n", " profile=profile,\n", ")" ] }, { "cell_type": "markdown", "id": "9c348f3b", "metadata": {}, "source": [ "### RNG Callouts" ] }, { "cell_type": "code", "execution_count": 4, "id": "0a70551d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[SetBits(11) c[3], c[11]; RNGSeed c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8], c[9], c[10], c[11], c[12], c[13], c[14], c[15], c[16], c[17], c[18], c[19], c[20], c[21], c[22], c[23], c[24], c[25], c[26], c[27], c[28], c[29], c[30], c[31], c[32], c[33], c[34], c[35], c[36], c[37], c[38], c[39], c[40], c[41], c[42], c[43], c[44], c[45], c[46], c[47], c[48], c[49], c[50], c[51], c[52], c[53], c[54], c[55], c[56], c[57], c[58], c[59], c[60], c[61], c[62], c[63], _r[0]; ]" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "circ = Circuit()\n", "creg = circ.add_c_register(\"c\", 64)\n", "circ.add_c_setbits([True, True], [creg[3], creg[11]])\n", "circ.set_rng_seed(creg)" ] }, { "cell_type": "markdown", "id": "b9b871f2", "metadata": {}, "source": [] }, { "cell_type": "code", "execution_count": 5, "id": "a52f2a17", "metadata": {}, "outputs": [], "source": [ "from pytket.qir.conversion.api import QIRFormat, QIRProfile, pytket_to_qir\n", "\n", "profile = QIRProfile.ADAPTIVE\n", "\n", "result = pytket_to_qir(\n", " circ,\n", " name=f\"pytket_rng_wasm-{profile}\",\n", " qir_format=QIRFormat.STRING,\n", " int_type=64,\n", " profile=profile,\n", " cut_pytket_register=True\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 }