Skip to content

Commit

Permalink
Phrase tutorials as Qiskit patterns (#682)
Browse files Browse the repository at this point in the history
* Add section headers to denote Qiskit pattern steps

* Introduce text about Qiskit patterns

Co-authored-by: Ibrahim Shehzad <[email protected]>

* Rename `final_expval` to `reconstructed_expval`

---------

Co-authored-by: Ibrahim Shehzad <[email protected]>
  • Loading branch information
garrison and ibrahim-shehzad authored Sep 16, 2024
1 parent 587b084 commit 1ed9d8a
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 46 deletions.
10 changes: 5 additions & 5 deletions docs/how-tos/how_to_specify_cut_wires.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -350,12 +350,12 @@
"metadata": {},
"outputs": [],
"source": [
"reconstructed_expvals = reconstruct_expectation_values(\n",
"reconstructed_expval_terms = reconstruct_expectation_values(\n",
" results,\n",
" coefficients,\n",
" subobservables,\n",
")\n",
"final_expval = np.dot(reconstructed_expvals, observable.coeffs)"
"reconstructed_expval = np.dot(reconstructed_expval_terms, observable.coeffs)"
]
},
{
Expand All @@ -380,11 +380,11 @@
"exact_expval = (\n",
" estimator.run([(qc_0.decompose(\"cut_wire\"), observable)]).result()[0].data.evs\n",
")\n",
"print(f\"Reconstructed expectation value: {np.real(np.round(final_expval, 8))}\")\n",
"print(f\"Reconstructed expectation value: {np.real(np.round(reconstructed_expval, 8))}\")\n",
"print(f\"Exact expectation value: {np.round(exact_expval, 8)}\")\n",
"print(f\"Error in estimation: {np.real(np.round(final_expval-exact_expval, 8))}\")\n",
"print(f\"Error in estimation: {np.real(np.round(reconstructed_expval-exact_expval, 8))}\")\n",
"print(\n",
" f\"Relative error in estimation: {np.real(np.round((final_expval-exact_expval) / exact_expval, 8))}\"\n",
" f\"Relative error in estimation: {np.real(np.round((reconstructed_expval-exact_expval) / exact_expval, 8))}\"\n",
")"
]
}
Expand Down
38 changes: 26 additions & 12 deletions docs/tutorials/01_gate_cutting_to_reduce_circuit_width.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,28 @@
"source": [
"## Gate Cutting to Reduce Circuit Width\n",
"\n",
"In this tutorial we will simulate the expectation value of a four-qubit circuit using only two-qubit experiments by cutting gates in the circuit.\n",
"In this notebook, we will work through the steps of a [Qiskit pattern](https://docs.quantum.ibm.com/guides/intro-to-patterns) while using **circuit cutting** to reduce the number of qubits in a circuit. We will cut gates to enable us to reconstruct the expectation value of a four-qubit circuit using only two-qubit experiments.\n",
"\n",
"Like any circuit knitting technique, gate cutting can be described as three consecutive steps:\n",
"These are the steps that we will take:\n",
"\n",
"- **cut** some non-local gates in the circuit and possibly separate the circuit into subcircuits\n",
"- **execute** many sampled subexperiments using the Qiskit Sampler primitive\n",
"- **reconstruct** the expectation value of the full-sized circuit"
"- **Step 1: Map problem to quantum circuits and operators:**\n",
" - Map the hamiltonian onto a quantum circuit.\n",
"- **Step 2: Optimize for target hardware** [_Uses the cutting addon_]:\n",
" - <font color='#0F62FE'>Cut the circuit and observable.</font>\n",
" - Transpile the subexperiments for hardware.\n",
"- **Step 3: Execute on target hardware:**\n",
" - Run the subexperiments obtained in Step 2 using a `Sampler` primitive.\n",
"- **Step 4: Post-process results** [_Uses the cutting addon_]:\n",
" - <font color='#0F62FE'>Combine the results of Step 3 to reconstruct the expectation value of the observable in question.</font>"
]
},
{
"cell_type": "markdown",
"id": "510910a6",
"metadata": {},
"source": [
"## Step 1: Map\n",
"\n",
"### Create a circuit to cut"
]
},
Expand Down Expand Up @@ -76,6 +84,8 @@
"id": "162a5629",
"metadata": {},
"source": [
"## Step 2: Optimize\n",
"\n",
"### Separate the circuit and observable according to a specified qubit partitioning\n",
"\n",
"Each label in `partition_labels` corresponds to the `circuit` qubit in the same index. Qubits sharing a common partition label will be grouped together, and non-local gates spanning more than one partition will be cut.\n",
Expand Down Expand Up @@ -293,6 +303,8 @@
"id": "d8870454-2173-4454-90b4-a034779510e0",
"metadata": {},
"source": [
"## Step 3: Execute\n",
"\n",
"### Run the subexperiments using the Qiskit Runtime Sampler primitive"
]
},
Expand Down Expand Up @@ -340,7 +352,9 @@
"id": "f0032570",
"metadata": {},
"source": [
"### Reconstruct the expectation values\n",
"## Step 4: Post-process\n",
"\n",
"### Reconstruct the expectation value\n",
"\n",
"Reconstruct expectation values for each observable term and combine them to reconstruct the expectation value for the original observable."
]
Expand All @@ -355,22 +369,22 @@
"from qiskit_addon_cutting import reconstruct_expectation_values\n",
"\n",
"# Get expectation values for each observable term\n",
"reconstructed_expvals = reconstruct_expectation_values(\n",
"reconstructed_expval_terms = reconstruct_expectation_values(\n",
" results,\n",
" coefficients,\n",
" subobservables,\n",
")\n",
"\n",
"# Reconstruct final expectation value\n",
"final_expval = np.dot(reconstructed_expvals, observable.coeffs)"
"reconstructed_expval = np.dot(reconstructed_expval_terms, observable.coeffs)"
]
},
{
"cell_type": "markdown",
"id": "53beaca3",
"metadata": {},
"source": [
"### Compare the reconstructed expectation values with the exact expectation value from the original circuit and observable"
"### Compare the reconstructed expectation value with the exact expectation value from the original circuit and observable"
]
},
{
Expand All @@ -395,11 +409,11 @@
"\n",
"estimator = EstimatorV2()\n",
"exact_expval = estimator.run([(qc, observable)]).result()[0].data.evs\n",
"print(f\"Reconstructed expectation value: {np.real(np.round(final_expval, 8))}\")\n",
"print(f\"Reconstructed expectation value: {np.real(np.round(reconstructed_expval, 8))}\")\n",
"print(f\"Exact expectation value: {np.round(exact_expval, 8)}\")\n",
"print(f\"Error in estimation: {np.real(np.round(final_expval-exact_expval, 8))}\")\n",
"print(f\"Error in estimation: {np.real(np.round(reconstructed_expval-exact_expval, 8))}\")\n",
"print(\n",
" f\"Relative error in estimation: {np.real(np.round((final_expval-exact_expval) / exact_expval, 8))}\"\n",
" f\"Relative error in estimation: {np.real(np.round((reconstructed_expval-exact_expval) / exact_expval, 8))}\"\n",
")"
]
}
Expand Down
64 changes: 47 additions & 17 deletions docs/tutorials/02_gate_cutting_to_reduce_circuit_depth.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,28 @@
"source": [
"# Gate Cutting to Reduce Circuit Depth\n",
"\n",
"In this tutorial, we will simulate some expectation values on a circuit of a certain depth by cutting gates resulting in swap gates and executing subexperiments on shallower circuits.\n",
"In this tutorial, we will reduce a circuit's depth by cutting distant gates, avoiding the swap gates that would otherwise be introduced by routing.\n",
"\n",
"Like any circuit knitting technique, gate cutting can be described as three consecutive steps:\n",
"These are the steps that we will take in this [Qiskit pattern](https://docs.quantum.ibm.com/guides/intro-to-patterns):\n",
"\n",
"- **decompose** some non-local gates in the circuit and possibly separate the circuit into subcircuits\n",
"- **execute** many sampled subexperiments using the Qiskit Sampler primitive\n",
"- **reconstruct** the expectation value of the full-sized circuit"
"- **Step 1: Map problem to quantum circuits and operators:**\n",
" - Map the hamiltonian onto a quantum circuit.\n",
"- **Step 2: Optimize for target hardware** [_Uses the cutting addon_]:\n",
" - <font color='#0F62FE'>Cut the circuit and observable.</font>\n",
" - Transpile the subexperiments for hardware.\n",
"- **Step 3: Execute on target hardware:**\n",
" - Run the subexperiments obtained in Step 2 using a `Sampler` primitive.\n",
"- **Step 4: Post-process results** [_Uses the cutting addon_]:\n",
" - <font color='#0F62FE'>Combine the results of Step 3 to reconstruct the expectation value of the observable in question.</font>"
]
},
{
"cell_type": "markdown",
"id": "07f7f75d",
"metadata": {},
"source": [
"## Step 1: Map\n",
"\n",
"### Create a circuit to run on the backend"
]
},
Expand Down Expand Up @@ -75,6 +83,8 @@
"id": "699105a3-903e-49d8-826b-cc8b9b85c9df",
"metadata": {},
"source": [
"## Step 2: Optimize\n",
"\n",
"### Specify a backend\n",
"\n",
"You can provide either a fake backend or a hardware backend from Qiskit Runtime."
Expand Down Expand Up @@ -307,20 +317,38 @@
"id": "fd9a126c",
"metadata": {},
"source": [
"### Prepare subexperiments for the backend and run them using the Qiskit Runtime Sampler primitive"
"### Prepare subexperiments for the backend"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "16968c09-13f0-499b-91d0-91b58f1ce6eb",
"id": "736a7d14-71b0-47a0-847d-4972ab886918",
"metadata": {},
"outputs": [],
"source": [
"from qiskit_ibm_runtime import SamplerV2\n",
"\n",
"# Transpile the subeperiments to the backend's instruction set architecture (ISA)\n",
"isa_subexperiments = pass_manager.run(subexperiments)\n",
"isa_subexperiments = pass_manager.run(subexperiments)"
]
},
{
"cell_type": "markdown",
"id": "241fc5e5-d367-4a31-aa1f-424acdbbd8dd",
"metadata": {},
"source": [
"## Step 3: Execute\n",
"\n",
"### Run the subexperiments using the Qiskit Runtime Sampler primitive"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e4dcc97b-8336-4eb9-9cc0-0d9b87a287e3",
"metadata": {},
"outputs": [],
"source": [
"from qiskit_ibm_runtime import SamplerV2\n",
"\n",
"# Set up the Qiskit Runtime Sampler primitive. For a fake backend, this will use a local simulator.\n",
"sampler = SamplerV2(backend)\n",
Expand All @@ -345,7 +373,9 @@
"id": "f04d9134-651b-446e-93f4-aa0281786200",
"metadata": {},
"source": [
"### Reconstruct the expectation values\n",
"## Step 4: Post-process\n",
"\n",
"### Reconstruct the expectation value\n",
"\n",
"Reconstruct expectation values for each observable term and combine them to reconstruct the expectation value for the original observable."
]
Expand All @@ -359,21 +389,21 @@
"source": [
"from qiskit_addon_cutting import reconstruct_expectation_values\n",
"\n",
"reconstructed_expvals = reconstruct_expectation_values(\n",
"reconstructed_expval_terms = reconstruct_expectation_values(\n",
" results,\n",
" coefficients,\n",
" observable.paulis,\n",
")\n",
"# Reconstruct final expectation value\n",
"final_expval = np.dot(reconstructed_expvals, observable.coeffs)"
"reconstructed_expval = np.dot(reconstructed_expval_terms, observable.coeffs)"
]
},
{
"cell_type": "markdown",
"id": "3fc2327f",
"metadata": {},
"source": [
"### Compare the reconstructed expectation values with the exact expectation value from the original circuit and observable"
"### Compare the reconstructed expectation value with the exact expectation value from the original circuit and observable"
]
},
{
Expand All @@ -398,11 +428,11 @@
"\n",
"estimator = EstimatorV2()\n",
"exact_expval = estimator.run([(circuit, observable)]).result()[0].data.evs\n",
"print(f\"Reconstructed expectation value: {np.real(np.round(final_expval, 8))}\")\n",
"print(f\"Reconstructed expectation value: {np.real(np.round(reconstructed_expval, 8))}\")\n",
"print(f\"Exact expectation value: {np.round(exact_expval, 8)}\")\n",
"print(f\"Error in estimation: {np.real(np.round(final_expval-exact_expval, 8))}\")\n",
"print(f\"Error in estimation: {np.real(np.round(reconstructed_expval-exact_expval, 8))}\")\n",
"print(\n",
" f\"Relative error in estimation: {np.real(np.round((final_expval-exact_expval) / exact_expval, 8))}\"\n",
" f\"Relative error in estimation: {np.real(np.round((reconstructed_expval-exact_expval) / exact_expval, 8))}\"\n",
")"
]
}
Expand Down
36 changes: 25 additions & 11 deletions docs/tutorials/03_wire_cutting_via_move_instruction.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,26 @@
"\n",
"In this tutorial, we will reconstruct expectation values of a seven-qubit circuit by splitting it into two four-qubit circuits using wire cutting.\n",
"\n",
"Like any circuit knitting technique, wire cutting can be described as three consecutive steps:\n",
"These are the steps that we will take in this [Qiskit pattern](https://docs.quantum.ibm.com/guides/intro-to-patterns):\n",
"\n",
"- **cut** some wires in the circuit and possibly separate the circuit into subcircuits\n",
"- **execute** many sampled subexperiments using the Qiskit Sampler primitive\n",
"- **reconstruct** the expectation value of the full-sized circuit"
"- **Step 1: Map problem to quantum circuits and operators:**\n",
" - Map the hamiltonian onto a quantum circuit.\n",
"- **Step 2: Optimize for target hardware** [_Uses the cutting addon_]:\n",
" - <font color='#0F62FE'>Cut the circuit and observable.</font>\n",
" - Transpile the subexperiments for hardware.\n",
"- **Step 3: Execute on target hardware:**\n",
" - Run the subexperiments obtained in Step 2 using a `Sampler` primitive.\n",
"- **Step 4: Post-process results** [_Uses the cutting addon_]:\n",
" - <font color='#0F62FE'>Combine the results of Step 3 to reconstruct the expectation value of the observable in question.</font>"
]
},
{
"cell_type": "markdown",
"id": "ae63d837-a7f5-40a5-8186-f98076bb4cd9",
"metadata": {},
"source": [
"## Step 1: Map\n",
"\n",
"### Create a circuit to cut\n",
"\n",
"First, we begin with a circuit inspired by Fig. 1(a) of [arXiv:2302.03366v1](https://arxiv.org/abs/2302.03366v1)."
Expand Down Expand Up @@ -108,6 +116,8 @@
"id": "9f56b094-0c6f-456f-9641-1424395fc6bd",
"metadata": {},
"source": [
"## Step 2: Optimize\n",
"\n",
"### Create a new circuit where `Move` instructions have been placed at the desired cut locations\n",
"\n",
"Given the above circuit, we would like to place two wire cuts on the middle qubit line, so that the circuit can separate into two circuits of four qubits each. One way to do this is to manually place two-qubit `Move` instructions that move the state from one qubit wire to another. A `Move` instruction is conceptually equivalent to a reset operation on the second qubit, followed by a SWAP gate. The effect of this instruction is to transfer the state of the first (source) qubit to the second (detination) qubit, while discarding the incoming state of the second qubit. For this to work as intended, it is important that the second (destination) qubit share no entanglement with the remainder of the system; otherwise, the reset operation will cause the state of the remainder of the system to be partially collapsed.\n",
Expand Down Expand Up @@ -387,6 +397,8 @@
"id": "9578c64a-8087-4696-bb2b-ca53d45442ba",
"metadata": {},
"source": [
"## Step 3: Execute\n",
"\n",
"### Run the subexperiments using the Qiskit Runtime Sampler primitive"
]
},
Expand Down Expand Up @@ -434,7 +446,9 @@
"id": "3f6e4eed-eff7-4e16-ad8d-8070011a555b",
"metadata": {},
"source": [
"### Reconstruct the expectation values\n",
"## Step 4: Post-process\n",
"\n",
"### Reconstruct the expectation value\n",
"\n",
"Reconstruct expectation values for each observable term and combine them to reconstruct the expectation value for the original observable."
]
Expand All @@ -448,20 +462,20 @@
"source": [
"from qiskit_addon_cutting import reconstruct_expectation_values\n",
"\n",
"reconstructed_expvals = reconstruct_expectation_values(\n",
"reconstructed_expval_terms = reconstruct_expectation_values(\n",
" results,\n",
" coefficients,\n",
" subobservables,\n",
")\n",
"final_expval = np.dot(reconstructed_expvals, observable.coeffs)"
"reconstructed_expval = np.dot(reconstructed_expval_terms, observable.coeffs)"
]
},
{
"cell_type": "markdown",
"id": "cdc793f2-3e2b-417b-b863-7b9d57b86a8f",
"metadata": {},
"source": [
"### Compare the reconstructed expectation values with the exact expectation value from the original circuit and observable"
"### Compare the reconstructed expectation value with the exact expectation value from the original circuit and observable"
]
},
{
Expand All @@ -486,11 +500,11 @@
"\n",
"estimator = EstimatorV2()\n",
"exact_expval = estimator.run([(qc_0, observable)]).result()[0].data.evs\n",
"print(f\"Reconstructed expectation value: {np.real(np.round(final_expval, 8))}\")\n",
"print(f\"Reconstructed expectation value: {np.real(np.round(reconstructed_expval, 8))}\")\n",
"print(f\"Exact expectation value: {np.round(exact_expval, 8)}\")\n",
"print(f\"Error in estimation: {np.real(np.round(final_expval-exact_expval, 8))}\")\n",
"print(f\"Error in estimation: {np.real(np.round(reconstructed_expval-exact_expval, 8))}\")\n",
"print(\n",
" f\"Relative error in estimation: {np.real(np.round((final_expval-exact_expval) / exact_expval, 8))}\"\n",
" f\"Relative error in estimation: {np.real(np.round((reconstructed_expval-exact_expval) / exact_expval, 8))}\"\n",
")"
]
}
Expand Down
Loading

0 comments on commit 1ed9d8a

Please sign in to comment.