1295 lines
41 KiB
Plaintext
1295 lines
41 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "57a88d32-f7b1-4bd9-9677-d172af205376",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Wire cutting for expectation values estimation\n",
|
|
"*Usage estimate: 1 minute on IBM Brisbane (NOTE: This is an estimate only. Your runtime may vary.)*"
|
|
]
|
|
},
|
|
{
|
|
"attachments": {},
|
|
"cell_type": "markdown",
|
|
"id": "4baa77ce-fb6a-4316-8226-a9940bf42014",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Background\n",
|
|
"\n",
|
|
"Circuit-knitting is an umbrella term which encapsulates various methods of partitioning a circuit in multiple smaller subcircuits involving fewer gates and/or qubits. Each of the subcircuits can be executed independently and the final result is obtained via some classical postprocessing over the outcome of each subcircuit. This technique is accessible in the [circuit cutting Qiskit addon](https://qiskit.github.io/qiskit-addon-cutting/index.html), a detailed explaination of the technique is given in the [docs](https://qiskit.github.io/qiskit-addon-cutting/explanation/index.html) along with other [introductory material](https://qiskit.github.io/qiskit-addon-cutting/tutorials/index.html).\n",
|
|
"\n",
|
|
"This notebook deals with a method called <b>wire cutting</b> where the circuit is partitioned along the wire [\\[1\\], \\[2\\]](#references). Note that, partitioning is simple in classical circuits since the outcome at the point of partition can be determined deterministically, and is either 0 or 1. However, the state of the qubit at the point of the cut is, in general, a mixed state. Therefore, each subcircuit needs to be measured multiple times in different basis (usually a tomographically complete set of basis such as the Pauli basis [\\[3\\], \\[4\\]](#references) and correspondingly prepared in its eigenstate. The Figure below (<i>courtesy: PhD Thesis, Ritajit Majumdar</i>) shows an example of wire cutting for a 4-qubit GHZ state into three subcircuits. Here $M_j$ denote a set of basis (usually Pauli X, Y and Z) and $P_i$ denote a set of eigenstates (usually $|0\\rangle$, $|1\\rangle$, $|+\\rangle$ and $|+i\\rangle$).\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Since each subcircuit has fewer qubits and/or gates, they are expected to be less amenable to noise. This notebook shows an example where this method can be used to effectively suppress the noise in the system."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "8f692382",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Requirements\n",
|
|
"Before starting this tutorial, be sure you have the following installed:\n",
|
|
"\n",
|
|
"- Qiskit SDK v1.0 or later, with visualization support ( `pip install 'qiskit[visualization]'` )\n",
|
|
"- Qiskit Runtime 0.22 or later ( `pip install qiskit-ibm-runtime` )\n",
|
|
"- Circuit cutting Qiskit addon 0.9.0 or later (`pip install qiskit-addon-cutting`)\n",
|
|
"\n",
|
|
"We shall consider a Many Body Localization (MBL) circuit for this notebook. The MBL circuit is a hardware-efficient circuit and is parameterized by two parameters $\\theta$ and $\\vec{\\phi}$. When $\\theta$ is set to $0$ and the initial state is prepared in $|0\\rangle$ for all the qubits, the ideal expectation value of $\\langle Z_i \\rangle$ is $+1$ for every qubit site $i$ irrespective of the values of $\\vec{\\phi}$. You can check more details on MBL circuits in <a href=\"https://arxiv.org/abs/2307.07552\">this paper</a>."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "fd0848a8",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Setup"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "36fb62a7",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import numpy as np\n",
|
|
"import matplotlib.pyplot as plt\n",
|
|
"\n",
|
|
"\n",
|
|
"from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit\n",
|
|
"from qiskit.quantum_info import PauliList, SparsePauliOp\n",
|
|
"from qiskit.transpiler import generate_preset_pass_manager\n",
|
|
"from qiskit.result import sampled_expectation_value\n",
|
|
"\n",
|
|
"from qiskit_addon_cutting.instructions import CutWire\n",
|
|
"from qiskit_addon_cutting import (\n",
|
|
" cut_wires,\n",
|
|
" expand_observables,\n",
|
|
" partition_problem,\n",
|
|
" generate_cutting_experiments,\n",
|
|
" reconstruct_expectation_values,\n",
|
|
")\n",
|
|
"\n",
|
|
"from qiskit_ibm_runtime import QiskitRuntimeService\n",
|
|
"from qiskit_ibm_runtime import SamplerV2, Batch\n",
|
|
"\n",
|
|
"\n",
|
|
"class MBLChainCircuit(QuantumCircuit):\n",
|
|
" def __init__(\n",
|
|
" self, num_qubits: int, depth: int, use_cut: bool = False\n",
|
|
" ) -> None:\n",
|
|
" super().__init__(\n",
|
|
" num_qubits, name=f\"MBLChainCircuit<{num_qubits}, {depth}>\"\n",
|
|
" )\n",
|
|
" evolution = MBLChainEvolution(num_qubits, depth, use_cut)\n",
|
|
" self.compose(evolution, inplace=True)\n",
|
|
"\n",
|
|
"\n",
|
|
"class MBLChainEvolution(QuantumCircuit):\n",
|
|
" def __init__(self, num_qubits: int, depth: int, use_cut) -> None:\n",
|
|
" super().__init__(\n",
|
|
" num_qubits, name=f\"MBLChainEvolution<{num_qubits}, {depth}>\"\n",
|
|
" )\n",
|
|
"\n",
|
|
" theta = Parameter(\"θ\")\n",
|
|
" phis = ParameterVector(\"φ\", num_qubits)\n",
|
|
"\n",
|
|
" for layer in range(depth):\n",
|
|
" layer_parity = layer % 2\n",
|
|
" # print(\"layer parity\", layer_parity)\n",
|
|
" for qubit in range(layer_parity, num_qubits - 1, 2):\n",
|
|
" # print(qubit)\n",
|
|
" self.cz(qubit, qubit + 1)\n",
|
|
" self.u(theta, 0, np.pi, qubit)\n",
|
|
" self.u(theta, 0, np.pi, qubit + 1)\n",
|
|
" if (\n",
|
|
" use_cut\n",
|
|
" and layer_parity == 0\n",
|
|
" and (\n",
|
|
" qubit == num_qubits // 2 - 1\n",
|
|
" or qubit == num_qubits // 2\n",
|
|
" )\n",
|
|
" ):\n",
|
|
" self.append(CutWire(), [num_qubits // 2])\n",
|
|
" if use_cut and layer < depth - 1 and layer_parity == 1:\n",
|
|
" if qubit == num_qubits // 2:\n",
|
|
" self.append(CutWire(), [qubit])\n",
|
|
" for qubit in range(num_qubits):\n",
|
|
" self.p(phis[qubit], qubit)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "2c0e1ce3",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Part I. Small scale example"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "a9c3e7a2-49ef-4510-abd1-e1707070ca44",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Step 1: Map classical inputs to a quantum problem"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "215f9315-431d-4e60-b5b2-f7b36b830efa",
|
|
"metadata": {},
|
|
"source": [
|
|
"Initially we build a template circuit without any specific parameter values. We also provide placeholders, called `CutWire`, to annotate the position of cuts. For the small scale example we consider a 10-qubit MBL circuit."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 68,
|
|
"id": "9c7939a1-6b70-4dad-b873-5f34d67551c4",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/9c7939a1-6b70-4dad-b873-5f34d67551c4-0.avif\" alt=\"Output of the previous code cell\" />"
|
|
]
|
|
},
|
|
"execution_count": 68,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"num_qubits = 10\n",
|
|
"depth = 2\n",
|
|
"mbl = MBLChainCircuit(num_qubits, depth)\n",
|
|
"mbl.draw(\"mpl\", fold=-1)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "ed373c57-5037-41ad-a992-4f9b6c0663a1",
|
|
"metadata": {},
|
|
"source": [
|
|
"Recall that we aim to find the expectation value of the observable $\\frac{1}{n}\\sum_{i=1} ^n Z_i$ when $\\theta=0$. We shall put some random values for the parameter $\\vec{\\phi}$."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 69,
|
|
"id": "eab4ce0a-89a1-4c3d-b949-eadac29bb035",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"[0,\n",
|
|
" 0.2376615174332788,\n",
|
|
" 0.28244289857682414,\n",
|
|
" 0.019248960591717768,\n",
|
|
" 0.46140600996102477,\n",
|
|
" 0.31408025180068433,\n",
|
|
" 0.718184005135733,\n",
|
|
" 0.991153920182475,\n",
|
|
" 0.09289485768301442,\n",
|
|
" 0.8857848280067783,\n",
|
|
" 0.6177529765767047]"
|
|
]
|
|
},
|
|
"execution_count": 69,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"phis = list(np.random.rand(mbl.num_parameters - 1))\n",
|
|
"theta = [0]\n",
|
|
"params = theta + phis\n",
|
|
"params"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "ad6708aa-e6e8-42c2-a4b9-a8a805651222",
|
|
"metadata": {},
|
|
"source": [
|
|
"Now we annotate the circuit for cutting by inserting proper **CutWire** to create two roughly equal cuts. We set `use_cut=True` in the function, and allow it to annotate after $\\frac{n}{2}$ qubits, $n$ being the number of qubits in the original circuit."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 70,
|
|
"id": "31844134-514b-46ea-85f9-133e432f053f",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/31844134-514b-46ea-85f9-133e432f053f-0.avif\" alt=\"Output of the previous code cell\" />"
|
|
]
|
|
},
|
|
"execution_count": 70,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)\n",
|
|
"mbl_cut.assign_parameters(params, inplace=True)\n",
|
|
"mbl_cut.draw(\"mpl\", fold=-1)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "043b3cb2-25db-486c-afba-8968a7d9b4ba",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Step 2: Optimize problem for quantum hardware execution"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "93fbe6b3-e1ef-43d2-bf80-4ff56a861ae5",
|
|
"metadata": {},
|
|
"source": [
|
|
"Next we cut the circuit into two smaller subcircuits. For this example, we stick to only 2 subcircuits. For this, we use the <a href=\"https://qiskit.github.io/qiskit-addon-cutting/\">Qiskit Addon: Circuit Cutting</a>."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "fc73dbb9-6eeb-4117-80b0-690e30d8a543",
|
|
"metadata": {},
|
|
"source": [
|
|
"#### Cut the circuit into smaller subcircuits"
|
|
]
|
|
},
|
|
{
|
|
"attachments": {},
|
|
"cell_type": "markdown",
|
|
"id": "34b09a7d-c82c-41e3-8538-42f562e1b1eb",
|
|
"metadata": {},
|
|
"source": [
|
|
"Cutting the wire at a point increases the qubit count by one. Apart from the original qubit, there is now an extra qubit as a placeholder to the circuit after cutting. The following image gives a representation:\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"This Addon uses the function `cut_wires` to account for the extra qubits arising due to cutting."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "e5c6638d-8c37-40ef-902c-ee9661f95399",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"mbl_move = cut_wires(mbl_cut)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "784e19b1-6df5-4c91-99ee-7580da82d09d",
|
|
"metadata": {},
|
|
"source": [
|
|
"#### Create and expand the observables\n",
|
|
"\n",
|
|
"Now we construct the observable $M_z = \\frac{1}{n}\\sum_{i=1}^n \\langle Z_i \\rangle$. Since the ideal outcome of $\\langle Z_i \\rangle$ for each $i$ is $+1$, the ideal outcome of $M_z$ is also $+1$."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "74eadae9-5443-465d-8c87-9f34a77bf6ab",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"PauliList(['ZIIIIIIIII', 'IZIIIIIIII', 'IIZIIIIIII', 'IIIZIIIIII',\n",
|
|
" 'IIIIZIIIII', 'IIIIIZIIII', 'IIIIIIZIII', 'IIIIIIIZII',\n",
|
|
" 'IIIIIIIIZI', 'IIIIIIIIIZ'])"
|
|
]
|
|
},
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"observable = PauliList(\n",
|
|
" [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)]\n",
|
|
")\n",
|
|
"observable"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "28c2d715-adb9-4218-aed5-96ebb1520756",
|
|
"metadata": {},
|
|
"source": [
|
|
"However, note that the number of qubits in the circuit has increased after inserting the virtual 2-qubit `Move` operations after cutting. Therefore, we need to expand the observables as well by inserting identities to assert to the current circuit."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "0e2eabac-b224-4933-9567-7d218c53bc02",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"PauliList(['ZIIIIIIIIII', 'IZIIIIIIIII', 'IIZIIIIIIII', 'IIIZIIIIIII',\n",
|
|
" 'IIIIZIIIIII', 'IIIIIIZIIII', 'IIIIIIIZIII', 'IIIIIIIIZII',\n",
|
|
" 'IIIIIIIIIZI', 'IIIIIIIIIIZ'])"
|
|
]
|
|
},
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"new_obs = expand_observables(observable, mbl, mbl_move)\n",
|
|
"new_obs"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "b0f6d41d-2cd5-43b3-8cd4-afdefac65879",
|
|
"metadata": {},
|
|
"source": [
|
|
"Note that each observable has now expanded to accommodate 7 qubits, as in the circuit with `Move` operation, instead of the original 6 qubits. Now we shall partition the circuit into two subcircuits."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "07651125-238c-4e7d-82c5-3ca8d9ba6fc8",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "e6ac68da-71ba-4344-9b87-fdb776f720b7",
|
|
"metadata": {},
|
|
"source": [
|
|
"Let us visualize the subcircuits"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"id": "265a60eb-50e8-47be-99f0-931fa876644d",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"subcircuits = partitioned_problem.subcircuits"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"id": "c10af39c-88fe-4605-975c-5bf0e21ee4c4",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/c10af39c-88fe-4605-975c-5bf0e21ee4c4-0.avif\" alt=\"Output of the previous code cell\" />"
|
|
]
|
|
},
|
|
"execution_count": 10,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"subcircuits[0].draw(\"mpl\", fold=-1)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 11,
|
|
"id": "35920640-76e8-4af6-a252-ee6a22e9c26a",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/35920640-76e8-4af6-a252-ee6a22e9c26a-0.avif\" alt=\"Output of the previous code cell\" />"
|
|
]
|
|
},
|
|
"execution_count": 11,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"subcircuits[1].draw(\"mpl\", fold=-1)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "0204a26d-5775-4604-8809-3a3b194c710e",
|
|
"metadata": {},
|
|
"source": [
|
|
"The observables have been partitioned as well to fit the subcircuits"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 12,
|
|
"id": "3e892c89-3f46-44d2-a8f6-154daac0415b",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"{0: PauliList(['IIIIII', 'IIIIII', 'IIIIII', 'IIIIII', 'IIIIII', 'IZIIII',\n",
|
|
" 'IIZIII', 'IIIZII', 'IIIIZI', 'IIIIIZ']),\n",
|
|
" 1: PauliList(['ZIIII', 'IZIII', 'IIZII', 'IIIZI', 'IIIIZ', 'IIIII', 'IIIII',\n",
|
|
" 'IIIII', 'IIIII', 'IIIII'])}"
|
|
]
|
|
},
|
|
"execution_count": 12,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"subobservables = partitioned_problem.subobservables\n",
|
|
"subobservables"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "f7204362-5168-4e9e-a2b7-20313b89949a",
|
|
"metadata": {},
|
|
"source": [
|
|
"Note that each subcircuit leads to a number of samples. The reconstruction takes into account the outcome of each of these samples. Each of these samples is termed a `subexperiment`."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "76a69768-e612-43f0-b21e-748bcbf94d43",
|
|
"metadata": {},
|
|
"source": [
|
|
"Extending the observable using the `Move` operation requires a `PauliList` data structure. We can also create the $M_z$ observable in the more generic `SparsePauliOp` data structure which will be useful later during reconstruction of the subexperiments."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "320484d1-7be2-48fa-809f-66a54394b4f4",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"SparsePauliOp(['ZIIIIIIIII', 'IZIIIIIIII', 'IIZIIIIIII', 'IIIZIIIIII', 'IIIIZIIIII', 'IIIIIZIIII', 'IIIIIIZIII', 'IIIIIIIZII', 'IIIIIIIIZI', 'IIIIIIIIIZ'],\n",
|
|
" coeffs=[0.1+0.j, 0.1+0.j, 0.1+0.j, 0.1+0.j, 0.1+0.j, 0.1+0.j, 0.1+0.j, 0.1+0.j,\n",
|
|
" 0.1+0.j, 0.1+0.j])"
|
|
]
|
|
},
|
|
"execution_count": 13,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"M_z = SparsePauliOp(\n",
|
|
" [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)],\n",
|
|
" coeffs=[1 / num_qubits] * num_qubits,\n",
|
|
")\n",
|
|
"M_z"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "e3c96631-4b8c-4026-95ba-c95026c13956",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"subexperiments, coefficients = generate_cutting_experiments(\n",
|
|
" circuits=subcircuits,\n",
|
|
" observables=subobservables,\n",
|
|
" num_samples=np.inf,\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "1bd19292-79a0-4402-b09d-baada463b616",
|
|
"metadata": {},
|
|
"source": [
|
|
"Let us see two examples where the cut qubits are measured in two different basis. First, it is measured in normal Z basis, and next it is measured in X basis."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 15,
|
|
"id": "749e8f27-9c83-48d8-bcf6-635c967bf10b",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/749e8f27-9c83-48d8-bcf6-635c967bf10b-0.avif\" alt=\"Output of the previous code cell\" />"
|
|
]
|
|
},
|
|
"execution_count": 15,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"subexperiments[0][6].draw(\"mpl\", fold=-1)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 16,
|
|
"id": "987547e4-296a-41e4-ad82-41f4139a87a0",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/987547e4-296a-41e4-ad82-41f4139a87a0-0.avif\" alt=\"Output of the previous code cell\" />"
|
|
]
|
|
},
|
|
"execution_count": 16,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"subexperiments[0][2].draw(\"mpl\", fold=-1)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "8178f3dc-f23f-4e0f-9fd5-f8895bb61dbb",
|
|
"metadata": {},
|
|
"source": [
|
|
"#### Transpile each subexperiment\n",
|
|
"\n",
|
|
"Currently we need to transpile our circuits before submitting them for execution. Therefore, we shall transpile each circuit in the subexperiments first."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "12f869b8-f27d-4764-9064-da40977d1b3a",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"service = QiskitRuntimeService()\n",
|
|
"backend = service.least_busy(\n",
|
|
" operational=True, simulator=False, min_num_qubits=127\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "64bdf404-91ad-4119-9e51-2a34bfe876aa",
|
|
"metadata": {},
|
|
"source": [
|
|
"Now we need to transpile each of the circuits in the subexperiments. For that we first create a pass manager, and then use it to transpile each of the circuits."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "dcf80dc6-049a-4238-9665-eef90b48cefa",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"pm = generate_preset_pass_manager(optimization_level=2, backend=backend)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 19,
|
|
"id": "3d36b9ac-ba82-428d-9e99-ac8f5c55c9ae",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"isa_subexperiments = {\n",
|
|
" label: pm.run(partition_subexpts)\n",
|
|
" for label, partition_subexpts in subexperiments.items()\n",
|
|
"}"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 20,
|
|
"id": "962c4745-a235-4ef8-b56b-d3026be67fb6",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/962c4745-a235-4ef8-b56b-d3026be67fb6-0.avif\" alt=\"Output of the previous code cell\" />"
|
|
]
|
|
},
|
|
"execution_count": 20,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"isa_subexperiments[0][0].draw(\"mpl\", fold=-1, idle_wires=False)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "37491b7a-3cec-44d5-8248-35e5b487f248",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Step 3: Execute using Qiskit primitives"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "80063438-8c31-4741-b70d-292839d875d5",
|
|
"metadata": {},
|
|
"source": [
|
|
"Now we shall execute each circuit in subexperiment. `Qiskit-addon-cutting` uses `SamplerV2` to execute the subexperiments."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "1c2c1ad6-bfc8-49e3-b2e8-bab1d1f8d1c2",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"with Batch(backend=backend) as batch:\n",
|
|
" sampler = SamplerV2(mode=batch)\n",
|
|
" jobs = {\n",
|
|
" label: sampler.run(subsystem_subexpts, shots=2**12)\n",
|
|
" for label, subsystem_subexpts in isa_subexperiments.items()\n",
|
|
" }"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "fa4b7aa1",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Step 4: Post-process and return result in desired classical format"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "94ade1e8-b650-4d5b-8885-83951fae85c2",
|
|
"metadata": {},
|
|
"source": [
|
|
"Once the circuits have been executed, we now need to retrieve the results and reconstruct the expectation value for the uncut circuit and the original observable."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 22,
|
|
"id": "5de87b89-8e9f-4cf2-98cc-2f4ee76010b6",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Retrieve results\n",
|
|
"results = {label: job.result() for label, job in jobs.items()}"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "5a39d14d-deb3-4e1a-8822-28eb577d8309",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"0.9674376845359803"
|
|
]
|
|
},
|
|
"execution_count": 23,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"reconstructed_expval_terms = reconstruct_expectation_values(\n",
|
|
" results,\n",
|
|
" coefficients,\n",
|
|
" subobservables,\n",
|
|
")\n",
|
|
"reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real\n",
|
|
"reconstructed_expval"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "779217c9",
|
|
"metadata": {},
|
|
"source": [
|
|
"#### Cross verify\n",
|
|
"\n",
|
|
"Let us now execute the circuit without cutting and check the outcome there. Note that for execution of the uncut circuit we can directly use `EstimatorV2` for calculating the expectation values. But we shall use the same `Primitive` throughout. So we shall use `SamplerV2` to get the probability distribution and calculate the expectation value using the `sampled_expectation_value` function.\n",
|
|
"\n",
|
|
"First we need to transpile the uncut `mbl` circuit."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 24,
|
|
"id": "8aa1cfd0-aed6-4a96-a82e-d828e976aa3c",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"sampler = SamplerV2(mode=backend)\n",
|
|
"\n",
|
|
"if mbl.num_clbits == 0:\n",
|
|
" mbl.measure_all()\n",
|
|
"isa_mbl = pm.run(mbl)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "f7f45111-4836-45d4-b8f9-56742b0aa34d",
|
|
"metadata": {},
|
|
"source": [
|
|
"Next we construct the `pub` and run the uncut circuit."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "1441aa8c-0efd-41d3-8709-f0c2730f2b8e",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"pub = (isa_mbl, params)\n",
|
|
"uncut_job = sampler.run([pub])"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 25,
|
|
"id": "58439307-a52f-43db-9e57-001ba38beafd",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"uncut_counts = uncut_job.result()[0].data.meas.get_counts()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "f092266c-b557-4966-bc67-c48fece33ad6",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"0.9498046875000001"
|
|
]
|
|
},
|
|
"execution_count": 26,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"uncut_expval = sampled_expectation_value(uncut_counts, M_z)\n",
|
|
"uncut_expval"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "d92e410c-54ff-4208-be67-3afddf694b6c",
|
|
"metadata": {},
|
|
"source": [
|
|
"We note that the expectation value obtained via wire cutting is closer to the ideal value of $+1$ than the uncut one. Let us now scale up the size of the problem."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "359397f3-5a00-4e55-84b3-34b779680dc5",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Part II. Scale it up!\n",
|
|
"\n",
|
|
"Previously, we showed the results for a 10-qubit MBL circuit. Next, we show that the improvement in expectation value is also obtained for larger circuits. To show that, we repeat the process for a 60-qubit MBL circuit."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "a985f7b9",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Step 1: Map classical inputs to a quantum problem"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 29,
|
|
"id": "9f0fe181-1500-4e98-874e-0222e859d420",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"num_qubits = 60\n",
|
|
"depth = 2\n",
|
|
"mbl = MBLChainCircuit(num_qubits, depth)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "8649f4fe-3d51-4dc0-9fc9-814afa36aa76",
|
|
"metadata": {},
|
|
"source": [
|
|
"We create a random set of values for $\\vec{\\phi}$"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "10bdb44d-c1b3-4c97-9d36-5c8055cbbd38",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"phis = list(np.random.rand(mbl.num_parameters - 1))\n",
|
|
"theta = [0]\n",
|
|
"params = theta + phis"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "879126bf-184b-4f8f-8e95-4c1c6d747706",
|
|
"metadata": {},
|
|
"source": [
|
|
"Next we construct the cut circuit"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "060efb46-24e3-41d8-aabe-7ff9eb49f7c0",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)\n",
|
|
"mbl_cut.assign_parameters(params, inplace=True)\n",
|
|
"mbl_cut.draw(\"mpl\", fold=-1)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "cf55debd",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Step 2: Optimize problem for quantum hardware execution"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "ba70a8ab-bbcc-4bed-a5be-48a1e534902d",
|
|
"metadata": {},
|
|
"source": [
|
|
"As shown for the small scale example, we partition the circuit and the observable for the cutting experiments."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 32,
|
|
"id": "41691a7d-d2c9-40e5-a4fb-4c0cba64f355",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"mbl_move = cut_wires(mbl_cut)\n",
|
|
"\n",
|
|
"# Define observable\n",
|
|
"observable = PauliList(\n",
|
|
" [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)]\n",
|
|
")\n",
|
|
"new_obs = expand_observables(observable, mbl, mbl_move)\n",
|
|
"\n",
|
|
"# Partition the circuit into subcircuits\n",
|
|
"partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)\n",
|
|
"\n",
|
|
"# Get subcircuits\n",
|
|
"subcircuits = partitioned_problem.subcircuits"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "156baa5b-9aa6-4081-b295-541ca7ce8fee",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"subobservables = partitioned_problem.subobservables"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "71529591-2385-4dba-ac7f-bc3e56ca74dc",
|
|
"metadata": {},
|
|
"source": [
|
|
"We also create a `SparsePauliOp` object for the observable with proper co-efficients."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "6199cca9-887e-41cd-a89b-f5bda8026a69",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"M_z = SparsePauliOp(\n",
|
|
" [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)],\n",
|
|
" coeffs=[1 / num_qubits] * num_qubits,\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "83e1ab21-d755-4e24-9150-32c65db9671f",
|
|
"metadata": {},
|
|
"source": [
|
|
"Next we generate the subexperiments and transpile each circuit in the subexperiment."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 38,
|
|
"id": "34d13e5e-f5e2-4b9e-be65-7da075f3fb37",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"subexperiments, coefficients = generate_cutting_experiments(\n",
|
|
" circuits=subcircuits,\n",
|
|
" observables=subobservables,\n",
|
|
" num_samples=np.inf,\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 39,
|
|
"id": "91d7da19-ed57-4927-a28b-71415edf9131",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"isa_subexperiments = {\n",
|
|
" label: pm.run(partition_subexpts)\n",
|
|
" for label, partition_subexpts in subexperiments.items()\n",
|
|
"}"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "a27bba81",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Step 3: Execute using Qiskit primitives"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "4bcdee21-7efa-49e0-94a5-81131e2a32ed",
|
|
"metadata": {},
|
|
"source": [
|
|
"We use the `Batch` mode to execute all the circuits in the subexperiments."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 43,
|
|
"id": "73311dac-faef-48b4-b833-2bb8dbb35401",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"with Batch(backend=backend) as batch:\n",
|
|
" sampler = SamplerV2(mode=batch)\n",
|
|
" jobs = {\n",
|
|
" label: sampler.run(subsystem_subexpts, shots=2**12)\n",
|
|
" for label, subsystem_subexpts in isa_subexperiments.items()\n",
|
|
" }"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "daac83ec",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Step 4: Post-process and return result in desired classical format"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "e19e4a53-5951-4032-bc38-fe0e8219de53",
|
|
"metadata": {},
|
|
"source": [
|
|
"Let us now retrieve the results for each circuit in the subexperiment and reconstruct the expectation value corresponding to the uncut circuit and the original observable."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 44,
|
|
"id": "1ab6f47f-3f4e-462c-b516-fac0e6bf5259",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Retrieve results\n",
|
|
"results = {label: job.result() for label, job in jobs.items()}"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 45,
|
|
"id": "5a501461-6097-4391-851a-266149fd8d99",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"0.9631355921427409"
|
|
]
|
|
},
|
|
"execution_count": 45,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"reconstructed_expval_terms = reconstruct_expectation_values(\n",
|
|
" results,\n",
|
|
" coefficients,\n",
|
|
" subobservables,\n",
|
|
")\n",
|
|
"reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real\n",
|
|
"reconstructed_expval"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "5c3944af-557c-4e59-807f-99f94315962a",
|
|
"metadata": {},
|
|
"source": [
|
|
"#### Cross verify\n",
|
|
"\n",
|
|
"As in the small scale example, we shall once more obtain the expectation value by executing the uncut circuit, and compare the result with circuit cutting. We shall use the `SamplerV2` to maintain uniformity in the use of Primitives."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 48,
|
|
"id": "8c001186-79ad-47ae-8b35-54d91fd9ef05",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"sampler = SamplerV2(mode=backend)\n",
|
|
"\n",
|
|
"if mbl.num_clbits == 0:\n",
|
|
" mbl.measure_all()\n",
|
|
"isa_mbl = pm.run(mbl)\n",
|
|
"\n",
|
|
"pub = (isa_mbl, params)\n",
|
|
"uncut_job = sampler.run([pub])"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 49,
|
|
"id": "f061f692-2828-41cd-826c-3515ac9a4335",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"0.9426757812499998"
|
|
]
|
|
},
|
|
"execution_count": 49,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"uncut_counts = uncut_job.result()[0].data.meas.get_counts()\n",
|
|
"uncut_expval = sampled_expectation_value(uncut_counts, M_z)\n",
|
|
"uncut_expval"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "64ae72e7-7b4e-4a69-bab4-1ef37b0e4e60",
|
|
"metadata": {},
|
|
"source": [
|
|
"#### Visualize\n",
|
|
"\n",
|
|
"Let us visualize the improvement obtained in the expectation value by using wire cutting."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "19dad6bb-544e-45b9-90c4-3555681e4e5b",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/19dad6bb-544e-45b9-90c4-3555681e4e5b-0.avif\" alt=\"Output of the previous code cell\" />"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"ax = plt.gca()\n",
|
|
"methods = [\"cut\", \"uncut\"]\n",
|
|
"values = [reconstructed_expval, uncut_expval]\n",
|
|
"\n",
|
|
"plt.bar(methods, values, color=\"#a56eff\", width=0.4, edgecolor=\"#8a3ffc\")\n",
|
|
"plt.axhline(y=1, color=\"k\", linestyle=\"--\")\n",
|
|
"ax.set_ylim([0.85, 1.02])\n",
|
|
"plt.text(0.3, 0.99, \"Exact result\")\n",
|
|
"plt.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "545962c9-17c6-4ab9-8cdd-6e7a486df2c6",
|
|
"metadata": {},
|
|
"source": [
|
|
"#### Inference\n",
|
|
"\n",
|
|
"We observe that both in the small and large scale problems wire cutting leads to a better result than the uncut one. Note that no error mitigation techniques have been used for these experiments. Therefore, the improvement in result that has been obtained is only due to wire cutting. It may be possible to further improve the results using different mitigation methods together with circuit cutting.\n",
|
|
"\n",
|
|
"Moreover, in this notebook, we computed both the subcircuits on the same hardware. In [\\[5\\], \\[6\\]](#references), the authors shows a method to distribute the subcircuits on different hardware using noise information in order to maximize the noise suppression, and parallelize the process."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "487af7a3",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Appendix: resource scaling consideration"
|
|
]
|
|
},
|
|
{
|
|
"attachments": {},
|
|
"cell_type": "markdown",
|
|
"id": "53bccce5-2348-495b-9b42-0002e0052611",
|
|
"metadata": {},
|
|
"source": [
|
|
"The number of circuits to be executed increases with the number of cuts. Therefore, while many cuts can produce small subcircuits, thus further improving the performance, it also leads to a significantly high number of circuit executions, which may not be practical for most cases. Below, we show an example of the number of subcircuits corresponding to the number of cuts for a 50-qubit circuit.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"We note that even for 5 cuts the number of subexperiments is around 200k. Therefore, circuit cutting should be used only when the number of cuts is small."
|
|
]
|
|
},
|
|
{
|
|
"attachments": {},
|
|
"cell_type": "markdown",
|
|
"id": "a9b1019b-826b-4224-91f3-faf1c7a2fbd6",
|
|
"metadata": {},
|
|
"source": [
|
|
"### One example of cut-friendly and cut-unfriendly circuits each\n",
|
|
"\n",
|
|
"#### Cut-friendly circuit\n",
|
|
"\n",
|
|
"As noted earlier, a circuit is cut-friendly when the circuit can be partitioned into smaller disjoint subcircuits with a small number of cuts. Any hardware-efficient circuit, i.e., a circuit which requires little to no SWAP gates when mapped to the hardware coupling map, is, in general, cut-friendly. Below, we show an example of an excitation preserving ansatz, which is used in Quantum Chemistry. Note that such a circuit can be partitioned into two subcircuits with a single cut irrespective of the number of qubits.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"#### Cut-unfriendly circuit\n",
|
|
"\n",
|
|
"A circuit is cut-unfriendly if, in general, the number of cuts required to form disjoint partitions grow significantly with the depth of the number of qubits. Recall that with each cut an extra qubit is required. So with the number of cuts, the effective number of qubits also increase. Below we show an example of a 3-qubit Grover circuit with a possible cutting instance.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"We note that three cuts are required, and the cut is more vertical than horizontal. This means, that the number of cuts is expected to scale linearly with the number of qubits, which is not amenable for cutting."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "66bffbc1-8ab0-4890-b306-439699f511dd",
|
|
"metadata": {},
|
|
"source": [
|
|
"## References\n",
|
|
"\n",
|
|
"\n",
|
|
"[1] Peng, T., Harrow, A. W., Ozols, M., & Wu, X. (2020). Simulating large quantum circuits on a small quantum computer. Physical review letters, 125(15), 150504.\n",
|
|
"\n",
|
|
"[2] Tang, W., Tomesh, T., Suchara, M., Larson, J., & Martonosi, M. (2021, April). Cutqc: using small quantum computers for large quantum circuit evaluations. In Proceedings of the 26th ACM International conference on architectural support for programming languages and operating systems (pp. 473-486).\n",
|
|
"\n",
|
|
"[3] Perlin, M. A., Saleem, Z. H., Suchara, M., & Osborn, J. C. (2021). Quantum circuit cutting with maximum-likelihood tomography. npj Quantum Information, 7(1), 64.\n",
|
|
"\n",
|
|
"[4] Majumdar, R., & Wood, C. J. (2022). Error mitigated quantum circuit cutting. arXiv preprint arXiv:2211.13431.\n",
|
|
"\n",
|
|
"[5] Khare, T., Majumdar, R., Sangle, R., Ray, A., Seshadri, P. V., & Simmhan, Y. (2023). Parallelizing Quantum-Classical Workloads: Profiling the Impact of Splitting Techniques. In 2023 IEEE International Conference on Quantum Computing and Engineering (QCE) (Vol. 1, pp. 990-1000). IEEE.\n",
|
|
"\n",
|
|
"[6] Bhoumik, D., Majumdar, R., Saha, A., & Sur-Kolay, S. (2023). Distributed Scheduling of Quantum Circuits with Noise and Time Optimization. arXiv preprint arXiv:2309.06005."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "8e0e5ad4",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Tutorial survey\n",
|
|
"\n",
|
|
"Please take one minute to provide feedback on this tutorial. Your insights will help us improve our content offerings and user experience.\n",
|
|
"\n",
|
|
"[Link to survey](https://your.feedback.ibm.com/jfe/form/SV_3BLFkNVEuh0QBWm)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "2c38b07a",
|
|
"metadata": {},
|
|
"source": [
|
|
"© IBM Corp. 2024, 2025"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"description": "Use wire cutting to partition circuits into many smaller subcircuits.",
|
|
"kernelspec": {
|
|
"display_name": "Python 3",
|
|
"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"
|
|
},
|
|
"platform": "cloud",
|
|
"title": "Wire cutting for expectation values estimation"
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|