2373 lines
98 KiB
Plaintext
2373 lines
98 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "ba4719fa",
|
|
"metadata": {
|
|
"heading_collapsed": true,
|
|
"slideshow": {
|
|
"slide_type": "slide"
|
|
}
|
|
},
|
|
"source": [
|
|
"{/* cspell:ignore XIXI, IZIZ, XZXZ, YZYI, ZZZI, YIYZ, ZIZZ, IXXX, XYIY, IYXY, ZYYX, YYZX, ZXYY, YXZY, quasidistillation */}\n",
|
|
"\n",
|
|
"# Long-range entanglement with dynamic circuits\n",
|
|
"*Usage estimate: 90 minutes on IBM Sherbrooke (NOTE: This is an estimate only. Your runtime may vary.)*"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "619fd9f9",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Background\n",
|
|
"Long-range entanglement between distant qubits on a quantum processor can be a challenging task for devices with limited qubit connectivity. This tutorial shows three different ways that can be used to generate long-range entanglement between qubits on a line, at varying distances between each other:\n",
|
|
"- a unitary-based implementation, which uses SWAP operations to reduce the distance between distant qubits and entangle them directly,\n",
|
|
"- a measurement-based implementation with post-processing, which discards some amount of information to generate the desired entangled state, and\n",
|
|
"- a measurement-based implementation with dynamic circuits, which uses measurement and feedforward of information during the quantum computation to entangle the qubits.\n",
|
|
"In particular, the results show the effectiveness of dynamic circuits in generating long-range entanglement between two unconnected qubits at utility scales.\n",
|
|
"The notebook uses the ideas and results from [1] by Elisa Bäumer et al."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "1f0b8be3",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Requirements\n",
|
|
"\n",
|
|
"Before starting this tutorial, ensure that you have the following installed:\n",
|
|
"\n",
|
|
"- Qiskit SDK 2.0 or later, with visualization support ( `pip install 'qiskit[visualization]'` )\n",
|
|
"- Qiskit Runtime ( `pip install qiskit-ibm-runtime` ) 0.37 or later"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "6f0c85a6-8b59-4543-8e9f-788eaeef886a",
|
|
"metadata": {
|
|
"hidden": true
|
|
},
|
|
"source": [
|
|
"## Setup"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"id": "9d02f8a1-3069-4507-91ed-73b7f6610f39",
|
|
"metadata": {
|
|
"hidden": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"import random\n",
|
|
"from typing import List, Dict, Union, Optional, Callable\n",
|
|
"\n",
|
|
"import matplotlib.pyplot as plt\n",
|
|
"import numpy as np\n",
|
|
"from IPython.display import clear_output\n",
|
|
"\n",
|
|
"from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister\n",
|
|
"from qiskit.circuit import Gate\n",
|
|
"from qiskit.circuit.classical import expr\n",
|
|
"from qiskit.circuit.library import XGate\n",
|
|
"from qiskit.primitives import BitArray\n",
|
|
"from qiskit.providers.backend import BackendV2 as Backend\n",
|
|
"from qiskit.quantum_info import Pauli, PauliList\n",
|
|
"from qiskit.result import marginal_counts\n",
|
|
"from qiskit.transpiler import CouplingMap, InstructionDurations\n",
|
|
"from qiskit.transpiler.passmanager import PassManager\n",
|
|
"from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
|
|
"from qiskit_ibm_runtime import Batch, SamplerV2 as Sampler\n",
|
|
"from qiskit_ibm_runtime import QiskitRuntimeService\n",
|
|
"from qiskit_ibm_runtime.transpiler.passes.scheduling import (\n",
|
|
" DynamicCircuitInstructionDurations,\n",
|
|
" ALAPScheduleAnalysis,\n",
|
|
" PadDynamicalDecoupling,\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "c75dbecb-c273-4955-991e-557f9b5a41df",
|
|
"metadata": {
|
|
"hidden": true
|
|
},
|
|
"source": [
|
|
"## Step 1: Map classical inputs to a quantum problem\n",
|
|
"\n",
|
|
"In this tutorial you will run a gate teleportation circuit in three different setups, where you always assume a line of n qubits (for varying n with n-2 empty ancillas in the middle and a CNOT to apply between the two ends):\n",
|
|
"\n",
|
|
"- Unitary-based implementation swapping the qubits to the middle\n",
|
|
"- Measurement-based implementation with post-processing\n",
|
|
"- Measurement-based implementation with dynamic circuits\n",
|
|
"\n",
|
|
"For each implementation you measure the average gate fidelity to compare among the different implementations. For details on how the average gate fidelity is calculated, see the [Appendix](#appendix-calculating-the-average-fidelity)."
|
|
]
|
|
},
|
|
{
|
|
"attachments": {},
|
|
"cell_type": "markdown",
|
|
"id": "28f3fa83-e6da-4a76-ab86-09142557bc1b",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Experimental setup\n",
|
|
"\n",
|
|
"The experiments in this notebook use a 1-D line of qubits with a coupling map that ensures that no shortcuts can be taken."
|
|
]
|
|
},
|
|
{
|
|
"attachments": {},
|
|
"cell_type": "markdown",
|
|
"id": "e0025777",
|
|
"metadata": {
|
|
"heading_collapsed": true
|
|
},
|
|
"source": [
|
|
"#### Define 1-D line\n",
|
|
"\n",
|
|
"First, set up a line of qubits through the machine that you intend to use such that you avoid broken qubits or areas with high readout errors. To do this, examine the calibration data (which can be found online or via the command `plot_error_map(backend)`). For example, suppose that you use `ibm_sherbrooke` and that you need to avoid, for example, qubits 20 and 71 as well as qubits 56 and 73. One such qubit line would be:\n",
|
|
"\n",
|
|
""
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "b00af850-c30b-44c8-b40c-e0c6642ceb24",
|
|
"metadata": {},
|
|
"source": [
|
|
"We will describe the line as a simple list of integer indices. In this tutorial, we will choose a random qubit line using the following function."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"id": "3e1790a5-eea3-4e8e-9fd9-a7cba6f1bedf",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def get_1d_qubit_line(\n",
|
|
" coupling_map: CouplingMap, start_qubit: int = 0\n",
|
|
") -> List[int]:\n",
|
|
" \"\"\"\n",
|
|
" Use random search to find a 1D line, repeating until we find a line of sufficient length.\n",
|
|
" This is very inefficient, but it's fine for this demo.\n",
|
|
" \"\"\"\n",
|
|
"\n",
|
|
" def get_random_line(\n",
|
|
" coupling_map: CouplingMap, start_qubit: int\n",
|
|
" ) -> List[int]:\n",
|
|
" \"\"\"\n",
|
|
" Do a random, self-avoiding walk on the coupling map to get a 1D line.\n",
|
|
" \"\"\"\n",
|
|
" edge_list = list(coupling_map.get_edges())\n",
|
|
" path = [start_qubit]\n",
|
|
" while True:\n",
|
|
" # Get edges connected to the current qubit that don't connect to a qubit we've already visited\n",
|
|
" current_qubit = path[-1]\n",
|
|
" previously_visited_qubits = path[:-1]\n",
|
|
" candidate_edges = [\n",
|
|
" edge\n",
|
|
" for edge in edge_list\n",
|
|
" if current_qubit in edge\n",
|
|
" and not any(\n",
|
|
" qubit in previously_visited_qubits for qubit in edge\n",
|
|
" )\n",
|
|
" ]\n",
|
|
" if candidate_edges == []:\n",
|
|
" return path\n",
|
|
" next_edge = random.choice(candidate_edges)\n",
|
|
" edge_list.remove(next_edge)\n",
|
|
" next_qubit = next(\n",
|
|
" qubit for qubit in next_edge if qubit != path[-1]\n",
|
|
" )\n",
|
|
" path.append(next_qubit)\n",
|
|
"\n",
|
|
" # Now repeat the random walk many times to find a long line\n",
|
|
" return max(\n",
|
|
" [get_random_line(coupling_map, start_qubit) for _ in range(1000)],\n",
|
|
" key=lambda x: len(x),\n",
|
|
" )"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "9ced27b1-088e-403b-ae8a-77b8be563321",
|
|
"metadata": {},
|
|
"source": [
|
|
"The following cell shows an example of such a line."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"id": "cbc72c85-a9ac-482e-8948-65d2a6efaf72",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Found line of length 76:\n",
|
|
"0 → 14 → 18 → 19 → 20 → 33 → 39 → 38 → 37 → 52 → 56 → 57 → 58 → 71 → 77 → 78 → 79 → 91 → 98 → 99 → 100 → 101 → 102 → 92 → 83 → 84 → 85 → 73 → 66 → 65 → 64 → 54 → 45 → 44 → 43 → 34 → 24 → 25 → 26 → 16 → 8 → 9 → 10 → 11 → 12 → 17 → 30 → 31 → 32 → 36 → 51 → 50 → 49 → 55 → 68 → 69 → 70 → 74 → 89 → 88 → 87 → 93 → 106 → 107 → 108 → 112 → 126 → 125 → 124 → 123 → 122 → 121 → 120 → 119 → 118 → 110\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"from qiskit_ibm_runtime.fake_provider import FakeSherbrooke\n",
|
|
"\n",
|
|
"qubit_line = get_1d_qubit_line(FakeSherbrooke().coupling_map)\n",
|
|
"print(f\"Found line of length {len(qubit_line)}:\")\n",
|
|
"print(\" → \".join(str(q) for q in qubit_line))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "11e35681-d7d0-4d5d-8764-5517181d91d0",
|
|
"metadata": {},
|
|
"source": [
|
|
"#### Set primary parameters\n",
|
|
"\n",
|
|
"In this section are definitions for some common parameters that you will use later. You'll need to specify these parameters for a particular backend. In order to do so, you will need an account on [IBM Quantum™ Platform](https://quantum-computing.ibm.com/). More details on how to initialize your account can be found in the [documentation](/docs/guides/setup-channel#set-up-to-use-ibm-quantum-platform)."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "4fc13eba-bc50-4607-88f9-8ada04b69794",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Backend is: ibm_brisbane\n",
|
|
"Found line of length 76.\n",
|
|
"Using coupling map:\n",
|
|
" [[14, 0], [14, 18], [18, 19], [20, 19], [21, 20], [21, 22], [15, 22], [4, 15], [4, 5], [6, 5], [6, 7], [7, 8], [16, 8], [16, 26], [27, 26], [28, 27], [28, 29], [30, 29], [30, 31], [31, 32], [32, 36], [36, 51], [50, 51], [50, 49], [48, 49], [48, 47], [46, 47], [46, 45], [44, 45], [43, 44], [42, 43], [42, 41], [41, 53], [53, 60], [60, 61], [62, 61], [62, 72], [81, 72], [81, 82], [82, 83], [84, 83], [85, 84], [85, 86], [86, 87], [93, 87], [93, 106], [105, 106], [105, 104], [111, 104], [122, 111], [122, 121], [121, 120], [120, 119], [118, 119], [117, 118], [116, 117], [116, 115], [114, 115], [114, 109], [109, 96], [97, 96], [97, 98], [98, 91], [91, 79], [79, 78], [77, 78], [77, 71], [58, 71], [57, 58], [56, 57], [52, 56], [52, 37], [37, 38], [39, 38], [40, 39]]\n",
|
|
"Maximum number of qubits between CNOT for ibm_brisbane is 74 with the given qubit line.\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"def coupling_map_from_qubit_line(\n",
|
|
" coupling_map: CouplingMap, qubit_line: List[List[int]]\n",
|
|
") -> CouplingMap:\n",
|
|
" \"\"\"\n",
|
|
" Modify the full coupling map to force linearity in the qubit layout\n",
|
|
" \"\"\"\n",
|
|
" new_coupling_map = []\n",
|
|
" # Iterate the line pair-wise and append edges that contain both qubits\n",
|
|
" for qubit, next_qubit in zip(qubit_line, qubit_line[1:]):\n",
|
|
" edge = next(\n",
|
|
" edge\n",
|
|
" for edge in coupling_map.get_edges()\n",
|
|
" if qubit in edge and next_qubit in edge\n",
|
|
" )\n",
|
|
" new_coupling_map.append(edge)\n",
|
|
" return CouplingMap(new_coupling_map)\n",
|
|
"\n",
|
|
"\n",
|
|
"# Set up access to IBM Quantum devices\n",
|
|
"service = QiskitRuntimeService()\n",
|
|
"backend = service.least_busy(\n",
|
|
" operational=True, simulator=False, min_num_qubits=127\n",
|
|
")\n",
|
|
"\n",
|
|
"# Set qubit line and coupling map\n",
|
|
"QUBIT_LINE = get_1d_qubit_line(backend.coupling_map)\n",
|
|
"COUPLING_MAP_1D = coupling_map_from_qubit_line(\n",
|
|
" backend.coupling_map, QUBIT_LINE\n",
|
|
")\n",
|
|
"MAX_POSSIBLE_QUBITS_BTW_CNOT = len(QUBIT_LINE) - 2\n",
|
|
"\n",
|
|
"# Use this duration class to get appropriate durations for dynamic\n",
|
|
"# circuit backend scheduling\n",
|
|
"DURATIONS = DynamicCircuitInstructionDurations.from_backend(backend)\n",
|
|
"\n",
|
|
"print(f\"Backend is: {backend.name}\")\n",
|
|
"print(f\"Found line of length {len(QUBIT_LINE)}.\")\n",
|
|
"print(\"Using coupling map:\\n\", COUPLING_MAP_1D)\n",
|
|
"print(\n",
|
|
" f\"Maximum number of qubits between CNOT for {backend.name} is {MAX_POSSIBLE_QUBITS_BTW_CNOT} with the given qubit line.\"\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "56d793dc-19d4-4197-9f34-118683d95486",
|
|
"metadata": {},
|
|
"source": [
|
|
"Next, set the global scope of the experiment. These variables can be used in each circuit type or can be set individually in each experiment that will override these globals."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"id": "c7718980-3efe-4810-afb0-29f8d252a2ed",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Set which Paulis to sample (default is all 16 combinations that have a non-zero expectation)\n",
|
|
"SAMPLES = list(range(16))\n",
|
|
"\n",
|
|
"# Level of optimizations the transpiler uses: There are 4 optimization levels ranging from 0 to 3,\n",
|
|
"# where higher optimization levels take more time and computational effort but may yield a more\n",
|
|
"# optimal circuit. level 0 does no explicit optimization, it will just try to make a circuit\n",
|
|
"# runnable by transforming it to match a topology and basis gate set, if necessary.\n",
|
|
"# Level 1, 2 and 3 do light, medium and heavy optimization, using a combination of passes, and by\n",
|
|
"# configuring the passes to search for better solutions.\n",
|
|
"OPTIMIZATION_LEVEL = 1\n",
|
|
"\n",
|
|
"# Set to True to use dynamical decoupling\n",
|
|
"USE_DYNAMIC_DECOUPLING = True\n",
|
|
"\n",
|
|
"# Default dynamic decoupling sequence if dynamic decoupling is used\n",
|
|
"DD_SEQUENCE = [XGate(), XGate()]\n",
|
|
"\n",
|
|
"# Set the number of repetitions of each circuit, for sampling.\n",
|
|
"# The number of qubits between the control and target are grouped into blocks\n",
|
|
"# of length 4. The provided min and max number of qubits will be modified to\n",
|
|
"# align with these block sizes.\n",
|
|
"SHOTS = 1000\n",
|
|
"\n",
|
|
"# The min number of qubits between the control and target qubits on line\n",
|
|
"MIN_NUMBER_QUBITS = 0\n",
|
|
"\n",
|
|
"# The max number of qubits between the control and target qubits on line\n",
|
|
"# The max for MIN_NUMBER_QUBITS is len(QUBIT_LINE) - 2\n",
|
|
"# max_number_qubits must satisfy MAX_NUMBER_QUBITS - MIN_NUMBER_QUBITS = 3 (mod 4)\n",
|
|
"# at this point. This is just to make things easier to break jobs up. Not a real limitation.\n",
|
|
"MAX_NUMBER_QUBITS = 63\n",
|
|
"assert (\n",
|
|
" (MAX_NUMBER_QUBITS - MIN_NUMBER_QUBITS) % 4 == 3\n",
|
|
"), \"MAX_NUMBER_QUBITS must satisfy MAX_NUMBER_QUBITS - MIN_NUMBER_QUBITS = 3 (mod 4)\"\n",
|
|
"assert (MAX_NUMBER_QUBITS + 2) <= len(\n",
|
|
" QUBIT_LINE\n",
|
|
"), \"MAX_NUMBER_QUBITS must satisfy MAX_NUMBER_QUBITS + 2 <= len(QUBIT_LINE)\""
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "222c5d97-8da0-4abe-b7ba-457701ad1fc7",
|
|
"metadata": {
|
|
"code_folding": [],
|
|
"hidden": true
|
|
},
|
|
"source": [
|
|
"#### Monte Carlo state certification\n",
|
|
"Next, you utilize the methods `prep_P_ij_conj` and `meas_P_kl` to prepare the circuits for Monte Carlo state certification. The method can be used to calculate the average state fidelity of the state prepared by a circuit without having to estimate the full quantum state (for example with state tomography or similar technqiques). For more details see the [Appendix](#appendix-calculating-the-average-fidelity). Monte Carlo state certification requires us to prepare the complex conjugate of random product of eigenstates of local Pauli operators, corresponding to the Paulis $P_i^*$ and $P_j^*$ - and then to measure the the final state in different Pauli bases, corresponding to the Pauli operators $P_k$ and $P_l$. You can do this with the following two methods: the `prep_P_ij_conj` method prepares a list of circuits so that the control and target qubits are in the eigenstates of $P_i^*$ and $P_j^*$, respectively; and the `meas_P_kl` method prepares a list of circuits so that the control and target qubits are measured in the $P_k$ and $P_l$ bases, respectively."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"id": "b524396c",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def prep_P_ij_conj(\n",
|
|
" circuits: List[QuantumCircuit], P_prep: PauliList\n",
|
|
") -> List[QuantumCircuit]:\n",
|
|
" \"\"\"Prepare circuits with the possible complex conjugates of the eigenstates of given Pauli operators\n",
|
|
"\n",
|
|
" The first and last qubits are prepared in one of the four possible eigenstates of P_i^* and P_j^*\n",
|
|
" respectively. The resulting collection of circuits covers all four possibilities.\n",
|
|
"\n",
|
|
" Assumes that circuits have qubits 0, ... ,n+1 where the long range NOT is between qubit 0 (control) to n+1 (target)\n",
|
|
"\n",
|
|
" Arg:\n",
|
|
" circuits (List[QuantumCircuit]: List of 4 Quantum Circuits with at least two data qubits\n",
|
|
" P_prep (PauliList): A pair of single qubit Paulis with the first Pauli referred to as P_i and the second as P_j\n",
|
|
"\n",
|
|
" Returns:\n",
|
|
" List[QuantumCircuits]\n",
|
|
" \"\"\"\n",
|
|
"\n",
|
|
" pauliX = Pauli(\"X\")\n",
|
|
" pauliY = Pauli(\"Y\")\n",
|
|
" for p in range(2):\n",
|
|
" for q in range(2):\n",
|
|
" qc = circuits[2 * p + q]\n",
|
|
" if p == 1:\n",
|
|
" qc.x(0)\n",
|
|
" if q == 1:\n",
|
|
" qc.x(-1)\n",
|
|
" for i in range(2):\n",
|
|
" if P_prep[i] == pauliX:\n",
|
|
" qc.h(-i)\n",
|
|
" if P_prep[i] == pauliY:\n",
|
|
" qc.h(-i) # Change basis to initialise in Y^* basis\n",
|
|
" qc.s(-i) # i.e. Apply SH = (HS^\\dagger)^*\n",
|
|
" circuits[2 * p + q] = qc\n",
|
|
" return circuits\n",
|
|
"\n",
|
|
"\n",
|
|
"def meas_P_kl(\n",
|
|
" circuits: List[QuantumCircuit], P_meas: PauliList\n",
|
|
") -> List[QuantumCircuit]:\n",
|
|
" \"\"\"Prepare circuits so that the final state can be measured in different Pauli bases with a Z measurement\n",
|
|
"\n",
|
|
" The first and last qubits are the qubits that the given operator P_meas will operate\n",
|
|
"\n",
|
|
" Arg:\n",
|
|
" circuits (List[QuantumCircuit]: List of 4 Quantum Circuits with at least two data qubits\n",
|
|
" P_meas (PauliList): A pair of single qubit Paulis with the first Pauli referred to as P_k and the second as P_l\n",
|
|
" \"\"\"\n",
|
|
" pauliX = Pauli(\"X\")\n",
|
|
" pauliY = Pauli(\"Y\")\n",
|
|
" for p in range(2):\n",
|
|
" for q in range(2):\n",
|
|
" qc = circuits[2 * p + q]\n",
|
|
" for i in range(2):\n",
|
|
" if P_meas[i] == pauliX:\n",
|
|
" qc.h(\n",
|
|
" -i\n",
|
|
" ) # Change of basis to measure X by measuring in Z basis: i.e., apply H\n",
|
|
" if P_meas[i] == pauliY:\n",
|
|
" qc.sdg(\n",
|
|
" -i\n",
|
|
" ) # Change of basis to measure Y by measuring in Z basis\n",
|
|
" qc.h(-i) # i.e. apply HS^dagger\n",
|
|
" qc.measure(0, 0)\n",
|
|
" qc.measure(-1, 1)\n",
|
|
" circuits[2 * p + q] = qc\n",
|
|
" return circuits"
|
|
]
|
|
},
|
|
{
|
|
"attachments": {},
|
|
"cell_type": "markdown",
|
|
"id": "d892de47-78f4-46b7-b425-da5ce4b9c6b8",
|
|
"metadata": {
|
|
"hidden": true,
|
|
"scrolled": true
|
|
},
|
|
"source": [
|
|
"#### Unitary-based implementation swapping the qubits to the middle\n",
|
|
"\n",
|
|
"First, examine the case where a long-range CNOT gate is implemented using nearest-neighbor connections and unitary gates. In the following figure, on the left is a circuit for a long-range CNOT gate spanning a 1D chain of n-qubits subject to nearest-neighbor connections only. On the right is an equivalent unitary decomposition implementable with local CNOT gates, circuit depth O(n).\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"The circuit on the right can be implemented as follows:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"id": "d54f7f67-ae85-45a6-86aa-9d0151d572bc",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def CNOT_unitary(\n",
|
|
" qc: QuantumCircuit, control_qubit: int, target_qubit: int\n",
|
|
") -> QuantumCircuit:\n",
|
|
" \"\"\"Generate a CNOT gate between data qubit control_qubit and data qubit target_qubit using local CNOTs\n",
|
|
"\n",
|
|
" Assumes that the long-range CNOT gate will be spanning a 1D chain of n-qubits subject to nearest-neighbor\n",
|
|
" connections only with the chain starting at the control qubit and finishing at the target qubit.\n",
|
|
"\n",
|
|
" Assumes that control_qubit < target_qubit (as integers) and that the provided circuit qc has |0> set\n",
|
|
" qubits control_qubit+1, ..., target_qubit-1\n",
|
|
"\n",
|
|
" Args:\n",
|
|
" qc (QuantumCicruit) : A Quantum Circuit to add the long range localized unitary CNOT\n",
|
|
" control_qubit (int) : The qubit used as the control.\n",
|
|
" target_qubi (int) : The qubit targeted by the gate.\n",
|
|
"\n",
|
|
" Example:\n",
|
|
"\n",
|
|
" qc = QuantumCircuit(8,2)\n",
|
|
" qc = CNOT_unitary(qc, 0, 7)\n",
|
|
"\n",
|
|
" Returns:\n",
|
|
" QuantumCircuit\n",
|
|
"\n",
|
|
" \"\"\"\n",
|
|
" assert target_qubit > control_qubit\n",
|
|
" n = target_qubit - control_qubit - 1\n",
|
|
" k = int(n / 2)\n",
|
|
" qc.barrier()\n",
|
|
" for i in range(control_qubit, control_qubit + k):\n",
|
|
" qc.cx(i, i + 1)\n",
|
|
" qc.cx(i + 1, i)\n",
|
|
" qc.cx(-i - 1, -i - 2)\n",
|
|
" qc.cx(-i - 2, -i - 1)\n",
|
|
" if n % 2 == 1:\n",
|
|
" qc.cx(k + 2, k + 1)\n",
|
|
" qc.cx(k + 1, k + 2)\n",
|
|
" qc.barrier()\n",
|
|
" qc.cx(k, k + 1)\n",
|
|
" for i in range(control_qubit, control_qubit + k):\n",
|
|
" qc.cx(k - i, k - 1 - i)\n",
|
|
" qc.cx(k - 1 - i, k - i)\n",
|
|
" qc.cx(k + i + 1, k + i + 2)\n",
|
|
" qc.cx(k + i + 2, k + i + 1)\n",
|
|
" if n % 2 == 1:\n",
|
|
" qc.cx(-2, -1)\n",
|
|
" qc.cx(-1, -2)\n",
|
|
" qc.barrier()\n",
|
|
" return qc"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "7c6b8e59-c90b-4276-873e-a94ac2d375d3",
|
|
"metadata": {
|
|
"code_folding": [],
|
|
"hidden": true
|
|
},
|
|
"source": [
|
|
"The `build_circuits_uni` method therefore builds a list of circuits to run with different Paulis $P_i, P_j, P_k$ and $P_l$ in order to estimate the state fidelity with the Monte Carlo state certification method."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"id": "8df7c4a6",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def build_circuits_uni(n: int, samples: List[int]) -> List[QuantumCircuit]:\n",
|
|
" \"\"\"Builds the unitary circuits needed to estimate the average gate fidelity\n",
|
|
"\n",
|
|
" Args:\n",
|
|
" samples (List[int]): Which of the 16 Paulis with non-zero expectation value to prepare and measure\n",
|
|
" n (int): Number of qubits between the control and target of the CNOT\n",
|
|
" \"\"\"\n",
|
|
" circuits_all = []\n",
|
|
" # 16 Paulis with non-zero expectation value to prepare and measure\n",
|
|
" P_lkji = PauliList(\n",
|
|
" [\n",
|
|
" \"IIII\",\n",
|
|
" \"XIXI\",\n",
|
|
" \"IZIZ\",\n",
|
|
" \"XZXZ\",\n",
|
|
" \"YZYI\",\n",
|
|
" \"ZZZI\",\n",
|
|
" \"YIYZ\",\n",
|
|
" \"ZIZZ\",\n",
|
|
" \"XXIX\",\n",
|
|
" \"IXXX\",\n",
|
|
" \"XYIY\",\n",
|
|
" \"IYXY\",\n",
|
|
" \"ZYYX\",\n",
|
|
" \"YYZX\",\n",
|
|
" \"ZXYY\",\n",
|
|
" \"YXZY\",\n",
|
|
" ]\n",
|
|
" )\n",
|
|
" for sample in samples:\n",
|
|
" P_prep = P_lkji[sample][0:2]\n",
|
|
" P_meas = P_lkji[sample][2:4]\n",
|
|
" circuits = [QuantumCircuit(n + 2, 2) for i in range(4)]\n",
|
|
" circuits = prep_P_ij_conj(\n",
|
|
" circuits, P_prep\n",
|
|
" ) # Prepare circuits in eigenstates P_i^* and P_j^*\n",
|
|
" circuits = [\n",
|
|
" CNOT_unitary(circuit, 0, n + 1) for circuit in circuits\n",
|
|
" ] # Add long range CNOT\n",
|
|
" circuits = meas_P_kl(\n",
|
|
" circuits, P_meas\n",
|
|
" ) # Prepare circuits to measure the final state in P_k and P_l bases\n",
|
|
" circuits_all += circuits\n",
|
|
" return circuits_all"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "c15fa5c8",
|
|
"metadata": {},
|
|
"source": [
|
|
"For example:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"id": "e048983d-0441-40a1-944f-6f6725f01096",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<Image src=\"/docs/images/tutorials/long-range-entanglement/extracted-outputs/e048983d-0441-40a1-944f-6f6725f01096-0.avif\" alt=\"Output of the previous code cell\" />"
|
|
]
|
|
},
|
|
"execution_count": 9,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"# Sample circuit\n",
|
|
"n = 6\n",
|
|
"sample = [11]\n",
|
|
"test_circuits = build_circuits_uni(n, sample)\n",
|
|
"test_circuits[3].draw(\"mpl\", fold=-1)"
|
|
]
|
|
},
|
|
{
|
|
"attachments": {},
|
|
"cell_type": "markdown",
|
|
"id": "e3e88181",
|
|
"metadata": {
|
|
"heading_collapsed": true
|
|
},
|
|
"source": [
|
|
"#### Measurement-based implementation with post-processing\n",
|
|
"\n",
|
|
"Next, examine the case where a long-range CNOT gate is implemented using nearest-neighbor connections of a measurement-based CNOT with post-processing. In the following figure, below on the left is a circuit for a long-range CNOT gate spanning a 1D chain of n-qubits subject to nearest-neighbor connections only. On the right is an equivalent implementable with local CNOT gates with measurements, and which requires post-processing.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"The circuit on the right can be implemented as follows:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"id": "8fb3b80e-240d-4795-9142-7b87bd24b5c3",
|
|
"metadata": {
|
|
"hidden": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def CNOT_postproc(\n",
|
|
" qc: QuantumCircuit,\n",
|
|
" control_qubit: int,\n",
|
|
" target_qubit: int,\n",
|
|
" c1: Optional[ClassicalRegister] = None,\n",
|
|
" c2: Optional[ClassicalRegister] = None,\n",
|
|
" add_barriers: Optional[bool] = True,\n",
|
|
") -> QuantumCircuit:\n",
|
|
" \"\"\"Generate a CNOT gate between data qubit control_qubit and data qubit target_qubit using Bell pairs.\n",
|
|
"\n",
|
|
" Post processing is used to enable the CNOT gate via the provided classical registers c1 and c2\n",
|
|
"\n",
|
|
" Assumes that the long-range CNOT gate will be spanning a 1D chain of n-qubits subject to nearest-neighbor\n",
|
|
" connections only with the chain starting at the control qubit and finishing at the target qubit.\n",
|
|
"\n",
|
|
" Assumes that control_qubit < target_qubit (as integers) and that the provided circuit qc has |0> set\n",
|
|
" qubits control_qubit+1, ..., target_qubit-1\n",
|
|
"\n",
|
|
" n = target_qubit - control_qubit - 1 : Number of qubits between the target and control qubits\n",
|
|
" k = int(n/2) : Number of Bell pairs created\n",
|
|
"\n",
|
|
" Args:\n",
|
|
" qc (QuantumCicruit) : A Quantum Circuit to add the long range localized unitary CNOT\n",
|
|
" control_qubit (int) : The qubit used as the control.\n",
|
|
" target_qubi (int) : The qubit targeted by the gate.\n",
|
|
"\n",
|
|
" Optional Args:\n",
|
|
" c1 (ClassicalRegister) : Default = None. Required if n > 1. Register requires k bits\n",
|
|
" c2 (ClassicalRegister) : Default = None. Required if n > 0. Register requires n - k bits\n",
|
|
" add_barriers (bool) : Default = True. Include barriers before and after long range CNOT\n",
|
|
"\n",
|
|
" Returns:\n",
|
|
" QuantumCircuit\n",
|
|
" \"\"\"\n",
|
|
" assert target_qubit > control_qubit\n",
|
|
" n = target_qubit - control_qubit - 1\n",
|
|
" k = int(n / 2)\n",
|
|
"\n",
|
|
" # Determine where to start the bell pairs and\n",
|
|
" # add an extra CNOT when n is odd\n",
|
|
" if n % 2 == 0:\n",
|
|
" x0 = 1\n",
|
|
" else:\n",
|
|
" x0 = 2\n",
|
|
" qc.cx(0, 1)\n",
|
|
"\n",
|
|
" # Create k Bell pairs\n",
|
|
" for i in range(k):\n",
|
|
" qc.h(x0 + 2 * i)\n",
|
|
" qc.cx(x0 + 2 * i, x0 + 2 * i + 1)\n",
|
|
"\n",
|
|
" # Entangle Bell pairs and data qubits and measure\n",
|
|
" for i in range(k + 1):\n",
|
|
" qc.cx(x0 - 1 + 2 * i, x0 + 2 * i)\n",
|
|
"\n",
|
|
" for i in range(1, k + x0):\n",
|
|
" qc.h(2 * i + 1 - x0)\n",
|
|
" qc.measure(2 * i + 1 - x0, c2[i - 1])\n",
|
|
"\n",
|
|
" for i in range(k):\n",
|
|
" qc.measure(2 * i + x0, c1[i])\n",
|
|
"\n",
|
|
" if add_barriers is True:\n",
|
|
" qc.barrier()\n",
|
|
" return qc"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "75c0cde5-1300-40ec-9fea-e3018b762ee2",
|
|
"metadata": {
|
|
"hidden": true
|
|
},
|
|
"source": [
|
|
"Again, utilize the methods `prep_P_ij_conj` and `meas_P_kl` to prepare the circuits for Monte Carlo state certification."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 11,
|
|
"id": "df8920d5-68ea-4c6c-8c7c-787d23e4ed2c",
|
|
"metadata": {
|
|
"hidden": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def build_circuits_postproc(\n",
|
|
" n: int, samples: List[int]\n",
|
|
") -> List[QuantumCircuit]:\n",
|
|
" \"\"\"\n",
|
|
" Args:\n",
|
|
" n (int): Number of qubits between the control and target qubits\n",
|
|
" \"\"\"\n",
|
|
" assert n >= 0, \"Error: n needs to be a non-negative integer\"\n",
|
|
" circuits_all = []\n",
|
|
"\n",
|
|
" qr = QuantumRegister(\n",
|
|
" n + 2, name=\"q\"\n",
|
|
" ) # Circuit with n qubits between control and target\n",
|
|
" cr = ClassicalRegister(\n",
|
|
" 2, name=\"cr\"\n",
|
|
" ) # Classical register for measuring long range CNOT\n",
|
|
"\n",
|
|
" k = int(n / 2) # Number of Bell States to be used\n",
|
|
" c1 = ClassicalRegister(\n",
|
|
" k, name=\"c1\"\n",
|
|
" ) # Classical register needed for post processing\n",
|
|
" c2 = ClassicalRegister(\n",
|
|
" n - k, name=\"c2\"\n",
|
|
" ) # Classical register needed for post processing\n",
|
|
"\n",
|
|
" # 16 Paulis with non-zero expectation value to prepare and measure\n",
|
|
" P_lkji = PauliList(\n",
|
|
" [\n",
|
|
" \"IIII\",\n",
|
|
" \"XIXI\",\n",
|
|
" \"IZIZ\",\n",
|
|
" \"XZXZ\",\n",
|
|
" \"YZYI\",\n",
|
|
" \"ZZZI\",\n",
|
|
" \"YIYZ\",\n",
|
|
" \"ZIZZ\",\n",
|
|
" \"XXIX\",\n",
|
|
" \"IXXX\",\n",
|
|
" \"XYIY\",\n",
|
|
" \"IYXY\",\n",
|
|
" \"ZYYX\",\n",
|
|
" \"YYZX\",\n",
|
|
" \"ZXYY\",\n",
|
|
" \"YXZY\",\n",
|
|
" ]\n",
|
|
" )\n",
|
|
"\n",
|
|
" for sample in samples:\n",
|
|
" P_prep = P_lkji[sample][0:2]\n",
|
|
" P_meas = P_lkji[sample][2:4]\n",
|
|
" if n > 1:\n",
|
|
" circuits = [\n",
|
|
" QuantumCircuit(qr, cr, c1, c2, name=\"CNOT\") for i in range(4)\n",
|
|
" ]\n",
|
|
" elif n == 1:\n",
|
|
" circuits = [\n",
|
|
" QuantumCircuit(qr, cr, c2, name=\"CNOT\") for i in range(4)\n",
|
|
" ]\n",
|
|
" elif n == 0:\n",
|
|
" circuits = [QuantumCircuit(qr, cr, name=\"CNOT\") for i in range(4)]\n",
|
|
" circuits = prep_P_ij_conj(\n",
|
|
" circuits, P_prep\n",
|
|
" ) # Prepare control and target qubits\n",
|
|
" # in eigenstates of P_i^* and P_j^* respectively\n",
|
|
" circuits = [\n",
|
|
" CNOT_postproc(\n",
|
|
" qc=circuit, control_qubit=0, target_qubit=n + 1, c1=c1, c2=c2\n",
|
|
" )\n",
|
|
" for circuit in circuits\n",
|
|
" ] # Add long range CNOT\n",
|
|
" circuits = meas_P_kl(\n",
|
|
" circuits, P_meas\n",
|
|
" ) # Prepare circuits to measure the control and target\n",
|
|
" # qubits in P_k and P_l bases respectively\n",
|
|
" circuits_all += circuits\n",
|
|
" return circuits_all"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "3cab24fd",
|
|
"metadata": {},
|
|
"source": [
|
|
"This is an example of a circuit (including the state certification step):"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 12,
|
|
"id": "db53b428-14e0-485a-9e9e-b17d59936c02",
|
|
"metadata": {
|
|
"hidden": true
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<Image src=\"/docs/images/tutorials/long-range-entanglement/extracted-outputs/db53b428-14e0-485a-9e9e-b17d59936c02-0.avif\" alt=\"Output of the previous code cell\" />"
|
|
]
|
|
},
|
|
"execution_count": 12,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"# Example Circuit\n",
|
|
"n = 6\n",
|
|
"sample = [15]\n",
|
|
"test_post_proc_circuits = build_circuits_postproc(n, sample)\n",
|
|
"test_post_proc_circuits[1].draw(\"mpl\")"
|
|
]
|
|
},
|
|
{
|
|
"attachments": {},
|
|
"cell_type": "markdown",
|
|
"id": "1439e5e6-af49-4b7a-95ba-ac7cc75a31e7",
|
|
"metadata": {
|
|
"heading_collapsed": true
|
|
},
|
|
"source": [
|
|
"#### Long-range measurement-based CNOT with feedforward\n",
|
|
"\n",
|
|
"Finally, examine the case where a long-range CNOT gate is implemented using measurement-based CNOT with feedforward (dynamic circuits). In the following figure, on the left is a circuit for a long-range CNOT gate spanning a 1D chain of n-qubits subject to nearest-neighbor connections only. On the right is an equivalent implementable with local CNOT gates, measurement-based CNOT with feedforward (dynamic circuits).\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"The circuit on the right can be implemented as follows:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 13,
|
|
"id": "5c864fcf-0b77-44df-9750-0cd510640305",
|
|
"metadata": {
|
|
"hidden": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def CNOT_dyn(\n",
|
|
" qc: QuantumCircuit,\n",
|
|
" control_qubit: int,\n",
|
|
" target_qubit: int,\n",
|
|
" c1: Optional[ClassicalRegister] = None,\n",
|
|
" c2: Optional[ClassicalRegister] = None,\n",
|
|
" add_barriers: Optional[bool] = True,\n",
|
|
") -> QuantumCircuit:\n",
|
|
" \"\"\"Generate a CNOT gate between data qubit control_qubit and data qubit target_qubit using Bell Pairs.\n",
|
|
"\n",
|
|
" Post processing is used to enable the CNOT gate via the provided classical registers c1 and c2\n",
|
|
"\n",
|
|
" Assumes that the long-range CNOT gate will be spanning a 1D chain of n-qubits subject to nearest-neighbor\n",
|
|
" connections only with the chain starting at the control qubit and finishing at the target qubit.\n",
|
|
"\n",
|
|
" Assumes that control_qubit < target_qubit (as integers) and that the provided circuit qc has |0> set\n",
|
|
" qubits control_qubit+1, ..., target_qubit-1\n",
|
|
"\n",
|
|
" n = target_qubit - control_qubit - 1 : Number of qubits between the target and control qubits\n",
|
|
" k = int(n/2) : Number of Bell pairs created\n",
|
|
"\n",
|
|
" Args:\n",
|
|
" qc (QuantumCicruit) : A Quantum Circuit to add the long range localized unitary CNOT\n",
|
|
" control_qubit (int) : The qubit used as the control.\n",
|
|
" target_qubi (int) : The qubit targeted by the gate.\n",
|
|
"\n",
|
|
" Optional Args:\n",
|
|
" c1 (ClassicalRegister) : Default = None. Required if n > 1. Register requires k bits\n",
|
|
" c2 (ClassicalRegister) : Default = None. Required if n > 0. Register requires n - k bits\n",
|
|
" add_barriers (bool) : Default = True. Include barriers before and after long range CNOT\n",
|
|
"\n",
|
|
" Note: This approach uses two if_test statements. A better (more performant) approach is\n",
|
|
" to have the parity values combined into a single classical register and then use a switch\n",
|
|
" statement. This was done in the associated paper my modifying the qasm file directly. The ability\n",
|
|
" to use a switch statement via Qiskit in this way is a future release capability.\n",
|
|
"\n",
|
|
" Returns:\n",
|
|
" QuantumCircuit\n",
|
|
" \"\"\"\n",
|
|
" assert target_qubit > control_qubit\n",
|
|
" n = target_qubit - control_qubit - 1\n",
|
|
" t = int(n / 2)\n",
|
|
"\n",
|
|
" if add_barriers is True:\n",
|
|
" qc.barrier()\n",
|
|
"\n",
|
|
" # Determine where to start the bell pairs and\n",
|
|
" # add an extra CNOT when n is odd\n",
|
|
" if n % 2 == 0:\n",
|
|
" x0 = 1\n",
|
|
" else:\n",
|
|
" x0 = 2\n",
|
|
" qc.cx(0, 1)\n",
|
|
"\n",
|
|
" # Create t Bell pairs\n",
|
|
" for i in range(t):\n",
|
|
" qc.h(x0 + 2 * i)\n",
|
|
" qc.cx(x0 + 2 * i, x0 + 2 * i + 1)\n",
|
|
"\n",
|
|
" # Entangle Bell pairs and data qubits and measure\n",
|
|
" for i in range(t + 1):\n",
|
|
" qc.cx(x0 - 1 + 2 * i, x0 + 2 * i)\n",
|
|
"\n",
|
|
" for i in range(1, t + x0):\n",
|
|
" if i == 1:\n",
|
|
" qc.h(2 * i + 1 - x0)\n",
|
|
" qc.measure(2 * i + 1 - x0, c2[i - 1])\n",
|
|
" parity_control = expr.lift(c2[i - 1])\n",
|
|
" else:\n",
|
|
" qc.h(2 * i + 1 - x0)\n",
|
|
" qc.measure(2 * i + 1 - x0, c2[i - 1])\n",
|
|
" parity_control = expr.bit_xor(c2[i - 1], parity_control)\n",
|
|
"\n",
|
|
" for i in range(t):\n",
|
|
" if i == 0:\n",
|
|
" qc.measure(2 * i + x0, c1[i])\n",
|
|
" parity_target = expr.lift(c1[i])\n",
|
|
" else:\n",
|
|
" qc.measure(2 * i + x0, c1[i])\n",
|
|
" parity_target = expr.bit_xor(c1[i], parity_target)\n",
|
|
"\n",
|
|
" if n > 0:\n",
|
|
" with qc.if_test(parity_control):\n",
|
|
" qc.z(0)\n",
|
|
"\n",
|
|
" if n > 1:\n",
|
|
" with qc.if_test(parity_target):\n",
|
|
" qc.x(-1)\n",
|
|
"\n",
|
|
" if add_barriers is True:\n",
|
|
" qc.barrier()\n",
|
|
" return qc"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "d278d6c6-b46b-4be7-8419-942f92f9a63d",
|
|
"metadata": {
|
|
"hidden": true
|
|
},
|
|
"source": [
|
|
"Put it together with the Monte Carlo state certification methods `prep_P_ij_conj` and `meas_P_kl`:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 14,
|
|
"id": "4486c01c-c3ca-47e2-a11f-58134d86806b",
|
|
"metadata": {
|
|
"hidden": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def build_circuits_dyn(n: int, samples: List[int]) -> List[QuantumCircuit]:\n",
|
|
" \"\"\" \"\"\"\n",
|
|
" assert n >= 0, \"Error: n needs to be a non-negative integer\"\n",
|
|
" circuits_all = []\n",
|
|
"\n",
|
|
" qr = QuantumRegister(\n",
|
|
" n + 2, name=\"q\"\n",
|
|
" ) # Circuit with n qubits between control and target\n",
|
|
" cr = ClassicalRegister(\n",
|
|
" 2, name=\"cr\"\n",
|
|
" ) # Classical register for measuring long range CNOT\n",
|
|
"\n",
|
|
" k = int(n / 2) # Number of Bell States to be used\n",
|
|
" c1 = ClassicalRegister(\n",
|
|
" k, name=\"c1\"\n",
|
|
" ) # Classical register needed for post processing\n",
|
|
" c2 = ClassicalRegister(\n",
|
|
" n - k, name=\"c2\"\n",
|
|
" ) # Classical register needed for post processing\n",
|
|
"\n",
|
|
" # 16 Paulis with non-zero expectation value to prepare and measure\n",
|
|
" P_lkji = PauliList(\n",
|
|
" [\n",
|
|
" \"IIII\",\n",
|
|
" \"XIXI\",\n",
|
|
" \"IZIZ\",\n",
|
|
" \"XZXZ\",\n",
|
|
" \"YZYI\",\n",
|
|
" \"ZZZI\",\n",
|
|
" \"YIYZ\",\n",
|
|
" \"ZIZZ\",\n",
|
|
" \"XXIX\",\n",
|
|
" \"IXXX\",\n",
|
|
" \"XYIY\",\n",
|
|
" \"IYXY\",\n",
|
|
" \"ZYYX\",\n",
|
|
" \"YYZX\",\n",
|
|
" \"ZXYY\",\n",
|
|
" \"YXZY\",\n",
|
|
" ]\n",
|
|
" )\n",
|
|
"\n",
|
|
" for sample in samples:\n",
|
|
" P_prep = P_lkji[sample][0:2]\n",
|
|
" P_meas = P_lkji[sample][2:4]\n",
|
|
" if n > 1:\n",
|
|
" circuits = [\n",
|
|
" QuantumCircuit(qr, cr, c1, c2, name=\"CNOT\") for i in range(4)\n",
|
|
" ]\n",
|
|
" elif n == 1:\n",
|
|
" circuits = [\n",
|
|
" QuantumCircuit(qr, cr, c2, name=\"CNOT\") for i in range(4)\n",
|
|
" ]\n",
|
|
" elif n == 0:\n",
|
|
" circuits = [QuantumCircuit(qr, cr, name=\"CNOT\") for i in range(4)]\n",
|
|
" circuits = prep_P_ij_conj(\n",
|
|
" circuits, P_prep\n",
|
|
" ) # Prepare control and target qubits\n",
|
|
" # in eigenstates of P_i^* and P_j^* respectively\n",
|
|
" circuits = [\n",
|
|
" CNOT_dyn(\n",
|
|
" qc=circuit, control_qubit=0, target_qubit=n + 1, c1=c1, c2=c2\n",
|
|
" )\n",
|
|
" for circuit in circuits\n",
|
|
" ] # Add long range CNOT\n",
|
|
" circuits = meas_P_kl(\n",
|
|
" circuits, P_meas\n",
|
|
" ) # Prepare circuits to measure the control and target\n",
|
|
" # qubits in P_k and P_l bases respectively\n",
|
|
" circuits_all += circuits\n",
|
|
" return circuits_all"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "ae97fd46",
|
|
"metadata": {},
|
|
"source": [
|
|
"This results in the following example:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 15,
|
|
"id": "f9277469-cc4e-464c-b803-e11f6ad3603a",
|
|
"metadata": {
|
|
"hidden": true
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<Image src=\"/docs/images/tutorials/long-range-entanglement/extracted-outputs/f9277469-cc4e-464c-b803-e11f6ad3603a-0.avif\" alt=\"Output of the previous code cell\" />"
|
|
]
|
|
},
|
|
"execution_count": 15,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"# View an example circuit with Monte Carlo Prep\n",
|
|
"\n",
|
|
"n_qubits = 4\n",
|
|
"sample = range(16)\n",
|
|
"example_post_proc_circuits = build_circuits_dyn(n_qubits, sample)\n",
|
|
"example_post_proc_circuits[16].draw(\"mpl\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "66325a1a-5b3b-41fd-b4d0-e1e1702d89a0",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Step 2: Optimize problem for quantum hardware execution"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "aa55c1a8-3f16-4483-a90c-1eae1a40e90e",
|
|
"metadata": {},
|
|
"source": [
|
|
"Because you have already specified the physical qubit layout and built the circuits with a line topology in mind, there is no need to further optimize the circuits."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "210a251f-a4b0-43f1-b081-67da215719bb",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Step 3: Execute using Qiskit primitives\n",
|
|
"In this step you execute the experiment on the specified backend. A lightweight transpilation is done before submission to ensure that the circuits have all the physical parameters needed for execution on the device. This is done in the `submit_circuits` function, which also splits the circuits into smaller batches to be submitted to the device."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 16,
|
|
"id": "9b960de5",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def submit_circuits(\n",
|
|
" min_qubits: int,\n",
|
|
" max_qubits: int,\n",
|
|
" num_circuits_per_job: int,\n",
|
|
" qubit_line: List[int],\n",
|
|
" coupling_map: Union[CouplingMap, List],\n",
|
|
" samples: List[int],\n",
|
|
" optimization_level: int,\n",
|
|
" backend: Backend,\n",
|
|
" shots: int,\n",
|
|
" build_circuits: Callable,\n",
|
|
" transpile_dynamic: Optional[bool] = True,\n",
|
|
" use_dynamic_decoupling: Optional[bool] = True,\n",
|
|
" dd_sequence: Optional[List[Gate]] = [XGate(), XGate()],\n",
|
|
" durations: Optional[InstructionDurations] = None,\n",
|
|
") -> List[str]:\n",
|
|
" \"\"\"\n",
|
|
" Submit circuits in appropriate batches\n",
|
|
" \"\"\"\n",
|
|
"\n",
|
|
" # Calculated constants and storage variables\n",
|
|
" line_length = len(qubit_line)\n",
|
|
" num_samples = len(samples)\n",
|
|
" num_circuits = (max_qubits - min_qubits + 1) * 4 * num_samples\n",
|
|
" nr_jobs = int(num_circuits / num_circuits_per_job)\n",
|
|
"\n",
|
|
" # Run some parameter checks\n",
|
|
" # Min number of qubits between control and target must be a non-negative integer\n",
|
|
" assert min_qubits >= 0, \"Error: min_qubits must be >= 0\"\n",
|
|
"\n",
|
|
" # Max number of qubits between control and target musts be <= line_length - 2\n",
|
|
" assert (\n",
|
|
" max_qubits + 2\n",
|
|
" ) <= line_length, \"Error: max_qubits must be <= len(qubit_line) - 2\"\n",
|
|
"\n",
|
|
" # (max_qubits - min_qubits) must equal to 3(mod 4)\n",
|
|
" rem = (max_qubits - min_qubits) % 4\n",
|
|
" assert rem == 3, \"Fail: (max_qubits - min_qubits) must equal to 3(mod 4)\"\n",
|
|
"\n",
|
|
" # First transpile all the circuits\n",
|
|
" print(\"Transpiling circuits...\")\n",
|
|
"\n",
|
|
" all_transpiled_circs = []\n",
|
|
"\n",
|
|
" for n in range(min_qubits, max_qubits + 1):\n",
|
|
" layout = qubit_line[: n + 2]\n",
|
|
" circuits = build_circuits(n, samples)\n",
|
|
"\n",
|
|
" clear_output(wait=True)\n",
|
|
" percentage_completed = (n - min_qubits + 1) / (\n",
|
|
" max_qubits - min_qubits + 1\n",
|
|
" )\n",
|
|
"\n",
|
|
" print(\n",
|
|
" f\"[{percentage_completed:.0%} completed] Transpiling circuits \"\n",
|
|
" + f\"with {n} qubits between CNOT\"\n",
|
|
" )\n",
|
|
"\n",
|
|
" # Generate the main Qiskit transpile passes.\n",
|
|
" pm = generate_preset_pass_manager(\n",
|
|
" coupling_map=coupling_map,\n",
|
|
" initial_layout=layout,\n",
|
|
" optimization_level=optimization_level,\n",
|
|
" backend=backend,\n",
|
|
" )\n",
|
|
"\n",
|
|
" if use_dynamic_decoupling is True:\n",
|
|
" # Configure the as-late-as-possible scheduling pass and DD insertion pass\n",
|
|
" pm.scheduling = PassManager(\n",
|
|
" [\n",
|
|
" ALAPScheduleAnalysis(durations),\n",
|
|
" PadDynamicalDecoupling(durations, dd_sequence),\n",
|
|
" ]\n",
|
|
" )\n",
|
|
"\n",
|
|
" transpiled_circuits = pm.run(circuits)\n",
|
|
" all_transpiled_circs.extend(transpiled_circuits)\n",
|
|
"\n",
|
|
" clear_output(wait=True)\n",
|
|
" print(\"Sumbitting jobs ...\")\n",
|
|
"\n",
|
|
" job_ids = []\n",
|
|
"\n",
|
|
" with Batch(backend=backend):\n",
|
|
" sampler = Sampler()\n",
|
|
" for job_num in range(nr_jobs):\n",
|
|
" transpiled_circs = all_transpiled_circs[\n",
|
|
" num_circuits_per_job * job_num : num_circuits_per_job\n",
|
|
" * (job_num + 1)\n",
|
|
" ]\n",
|
|
"\n",
|
|
" # Submit circuits\n",
|
|
" print(\"Submitting circuits:\")\n",
|
|
"\n",
|
|
" percentage_completed = job_num / nr_jobs\n",
|
|
" print(f\"[{percentage_completed:.0%} completed]\")\n",
|
|
"\n",
|
|
" job = sampler.run(transpiled_circs, shots=shots)\n",
|
|
" job_ids.append(job.job_id())\n",
|
|
" print(\n",
|
|
" \"Job id for circuits \"\n",
|
|
" + f\"[{num_circuits_per_job*nr_jobs}, {num_circuits_per_job*(nr_jobs + 1) -1 }] : {job.job_id()}\"\n",
|
|
" )\n",
|
|
"\n",
|
|
" clear_output(wait=True)\n",
|
|
"\n",
|
|
" clear_output(wait=True)\n",
|
|
" print(\"All jobs submitted.\\n\")\n",
|
|
"\n",
|
|
" # Display qubit ranges and job ids\n",
|
|
" for job_num in range(nr_jobs):\n",
|
|
" print(\n",
|
|
" f\"[{num_circuits_per_job*job_num}, {num_circuits_per_job*(job_num + 1)}]: \"\n",
|
|
" f\"Id = {job_ids[job_num]}\"\n",
|
|
" )\n",
|
|
"\n",
|
|
" return job_ids"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "efdbec84",
|
|
"metadata": {},
|
|
"source": [
|
|
"First, set the parameters for the unitary approach and submit circuits."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 17,
|
|
"id": "f561ce90-32bb-47cc-b0bd-2e910c9e62e3",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"All jobs submitted.\n",
|
|
"\n",
|
|
"[0, 256]: Id = d03qhcxnhqag008vm5m0\n",
|
|
"[256, 512]: Id = d03qhdxrxz8g008g1bng\n",
|
|
"[512, 768]: Id = d03qhexqnmvg0082cnz0\n",
|
|
"[768, 1024]: Id = d03qhg6rxz8g008g1bp0\n",
|
|
"[1024, 1280]: Id = d03qhhed8drg008jr4bg\n",
|
|
"[1280, 1536]: Id = d03qhk6qnmvg0082cnzg\n",
|
|
"[1536, 1792]: Id = d03qhmyd8drg008jr4dg\n",
|
|
"[1792, 2048]: Id = d03qhqynhqag008vm5n0\n",
|
|
"[2048, 2304]: Id = d03qhszkzhn0008e4njg\n",
|
|
"[2304, 2560]: Id = d03qhvqkzhn0008e4nk0\n",
|
|
"[2560, 2816]: Id = d03qhyf6rr3g008s8s00\n",
|
|
"[2816, 3072]: Id = d03qj10d8drg008jr4fg\n",
|
|
"[3072, 3328]: Id = d03qj3rrxz8g008g1br0\n",
|
|
"[3328, 3584]: Id = d03qj6g6rr3g008s8s1g\n",
|
|
"[3584, 3840]: Id = d03qj9hd8drg008jr4h0\n",
|
|
"[3840, 4096]: Id = d03qjcsnhqag008vm5pg\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# Set local parameters\n",
|
|
"SAMPLES_UNI = SAMPLES\n",
|
|
"OPTIMIZATION_LEVEL_UNI = OPTIMIZATION_LEVEL\n",
|
|
"SHOTS_UNI = SHOTS\n",
|
|
"MIN_NUMBER_QUBITS_UNI = MIN_NUMBER_QUBITS\n",
|
|
"MAX_NUMBER_QUBITS_UNI = MAX_NUMBER_QUBITS\n",
|
|
"NUM_CIRCUITS_PER_JOB_UNI = 256\n",
|
|
"USE_DYNAMIC_DECOUPLING_UNI = False\n",
|
|
"\n",
|
|
"# Submit jobs for using unitary circuit approach\n",
|
|
"job_ids_uni = submit_circuits(\n",
|
|
" MIN_NUMBER_QUBITS_UNI,\n",
|
|
" MAX_NUMBER_QUBITS_UNI,\n",
|
|
" NUM_CIRCUITS_PER_JOB_UNI,\n",
|
|
" QUBIT_LINE,\n",
|
|
" COUPLING_MAP_1D,\n",
|
|
" SAMPLES_UNI,\n",
|
|
" OPTIMIZATION_LEVEL_UNI,\n",
|
|
" backend,\n",
|
|
" SHOTS_UNI,\n",
|
|
" build_circuits_uni,\n",
|
|
" use_dynamic_decoupling=USE_DYNAMIC_DECOUPLING_UNI,\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "0ca51a9d",
|
|
"metadata": {},
|
|
"source": [
|
|
"Then, do the same for the measurement-based post-selection approach."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 18,
|
|
"id": "5c644035-2457-4a11-8790-662d790893eb",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"All jobs submitted.\n",
|
|
"\n",
|
|
"[0, 128]: Id = d03qm4gnhqag008vm5wg\n",
|
|
"[128, 256]: Id = d03qm58nhqag008vm5xg\n",
|
|
"[256, 384]: Id = d03qm5r6rr3g008s8sa0\n",
|
|
"[384, 512]: Id = d03qm6gqnmvg0082cp8g\n",
|
|
"[512, 640]: Id = d03qm78d8drg008jr4p0\n",
|
|
"[640, 768]: Id = d03qm81qnmvg0082cp90\n",
|
|
"[768, 896]: Id = d03qm8hd8drg008jr4q0\n",
|
|
"[896, 1024]: Id = d03qm99kzhn0008e4ns0\n",
|
|
"[1024, 1152]: Id = d03qma1qnmvg0082cp9g\n",
|
|
"[1152, 1280]: Id = d03qmasrxz8g008g1c0g\n",
|
|
"[1280, 1408]: Id = d03qmbh6rr3g008s8sag\n",
|
|
"[1408, 1536]: Id = d03qmd9rxz8g008g1c10\n",
|
|
"[1536, 1664]: Id = d03qme9rxz8g008g1c20\n",
|
|
"[1664, 1792]: Id = d03qmf1kzhn0008e4ntg\n",
|
|
"[1792, 1920]: Id = d03qmgaqnmvg0082cpag\n",
|
|
"[1920, 2048]: Id = d03qmh2qnmvg0082cpb0\n",
|
|
"[2048, 2176]: Id = d03qmj2qnmvg0082cpbg\n",
|
|
"[2176, 2304]: Id = d03qmjtqnmvg0082cpc0\n",
|
|
"[2304, 2432]: Id = d03qmkjkzhn0008e4nvg\n",
|
|
"[2432, 2560]: Id = d03qmmt6rr3g008s8sc0\n",
|
|
"[2560, 2688]: Id = d03qmntnhqag008vm5zg\n",
|
|
"[2688, 2816]: Id = d03qmptnhqag008vm600\n",
|
|
"[2816, 2944]: Id = d03qmqtkzhn0008e4nwg\n",
|
|
"[2944, 3072]: Id = d03qmrvnhqag008vm610\n",
|
|
"[3072, 3200]: Id = d03qmsvkzhn0008e4nx0\n",
|
|
"[3200, 3328]: Id = d03qmtvd8drg008jr4sg\n",
|
|
"[3328, 3456]: Id = d03qmyvkzhn0008e4ny0\n",
|
|
"[3456, 3584]: Id = d03qmzvkzhn0008e4nyg\n",
|
|
"[3584, 3712]: Id = d03qn14rxz8g008g1c80\n",
|
|
"[3712, 3840]: Id = d03qn24qnmvg0082cpd0\n",
|
|
"[3840, 3968]: Id = d03qn3md8drg008jr4tg\n",
|
|
"[3968, 4096]: Id = d03qn4wkzhn0008e4nzg\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# Set local parameters\n",
|
|
"SAMPLES_POSTPROC = SAMPLES\n",
|
|
"OPTIMIZATION_LEVEL_POSTPROC = OPTIMIZATION_LEVEL\n",
|
|
"SHOTS_POSTPROC = SHOTS\n",
|
|
"MIN_NUMBER_QUBITS_POSTPROC = MIN_NUMBER_QUBITS\n",
|
|
"MAX_NUMBER_QUBITS_POSTPROC = MAX_NUMBER_QUBITS\n",
|
|
"NUM_CIRCUITS_PER_JOB_POSTPROC = 128\n",
|
|
"USE_DYNAMIC_DECOUPLING_POSTPROC = USE_DYNAMIC_DECOUPLING\n",
|
|
"DURATIONS_POSTPROC = DURATIONS\n",
|
|
"\n",
|
|
"# Submit jobs for the measurement based post selection approach\n",
|
|
"job_ids_postproc = submit_circuits(\n",
|
|
" MIN_NUMBER_QUBITS_POSTPROC,\n",
|
|
" MAX_NUMBER_QUBITS_POSTPROC,\n",
|
|
" NUM_CIRCUITS_PER_JOB_POSTPROC,\n",
|
|
" QUBIT_LINE,\n",
|
|
" COUPLING_MAP_1D,\n",
|
|
" SAMPLES_POSTPROC,\n",
|
|
" OPTIMIZATION_LEVEL_POSTPROC,\n",
|
|
" backend,\n",
|
|
" SHOTS_POSTPROC,\n",
|
|
" build_circuits_postproc,\n",
|
|
" use_dynamic_decoupling=USE_DYNAMIC_DECOUPLING_POSTPROC,\n",
|
|
" durations=DURATIONS_POSTPROC,\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "d539cc4c",
|
|
"metadata": {},
|
|
"source": [
|
|
"Finally, for the measurement-based dynamic circuit approach:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 19,
|
|
"id": "5b85cae8-a2e3-40cd-a1ea-65a59e7e810e",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"All jobs submitted.\n",
|
|
"\n",
|
|
"[0, 16]: Id = d03qqrqkzhn0008e4p7g\n",
|
|
"[16, 32]: Id = d03qqs7d8drg008jr540\n",
|
|
"[32, 48]: Id = d03qqsqqnmvg0082cpng\n",
|
|
"[48, 64]: Id = d03qqt7qnmvg0082cpp0\n",
|
|
"[64, 80]: Id = d03qqtqd8drg008jr54g\n",
|
|
"[80, 96]: Id = d03qqtzd8drg008jr550\n",
|
|
"[96, 112]: Id = d03qqvfrxz8g008g1chg\n",
|
|
"[112, 128]: Id = d03qqvzqnmvg0082cppg\n",
|
|
"[128, 144]: Id = d03qqwf6rr3g008s8sp0\n",
|
|
"[144, 160]: Id = d03qqwznhqag008vm6a0\n",
|
|
"[160, 176]: Id = d03qqx7kzhn0008e4p8g\n",
|
|
"[176, 192]: Id = d03qqxqqnmvg0082cpq0\n",
|
|
"[192, 208]: Id = d03qqy7d8drg008jr55g\n",
|
|
"[208, 224]: Id = d03qqyqnhqag008vm6ag\n",
|
|
"[224, 240]: Id = d03qqyznhqag008vm6b0\n",
|
|
"[240, 256]: Id = d03qqzfnhqag008vm6bg\n",
|
|
"[256, 272]: Id = d03qqzqkzhn0008e4p90\n",
|
|
"[272, 288]: Id = d03qr00d8drg008jr560\n",
|
|
"[288, 304]: Id = d03qr0grxz8g008g1cj0\n",
|
|
"[304, 320]: Id = d03qr10nhqag008vm6c0\n",
|
|
"[320, 336]: Id = d03qr1grxz8g008g1cjg\n",
|
|
"[336, 352]: Id = d03qr1rd8drg008jr570\n",
|
|
"[352, 368]: Id = d03qr28qnmvg0082cprg\n",
|
|
"[368, 384]: Id = d03qr2rrxz8g008g1ckg\n",
|
|
"[384, 400]: Id = d03qr30kzhn0008e4pa0\n",
|
|
"[400, 416]: Id = d03qr3g6rr3g008s8sq0\n",
|
|
"[416, 432]: Id = d03qr40qnmvg0082cps0\n",
|
|
"[432, 448]: Id = d03qr48d8drg008jr57g\n",
|
|
"[448, 464]: Id = d03qr4rqnmvg0082cpsg\n",
|
|
"[464, 480]: Id = d03qr58d8drg008jr580\n",
|
|
"[480, 496]: Id = d03qr5grxz8g008g1cm0\n",
|
|
"[496, 512]: Id = d03qr60kzhn0008e4pag\n",
|
|
"[512, 528]: Id = d03qr6g6rr3g008s8sr0\n",
|
|
"[528, 544]: Id = d03qr70d8drg008jr58g\n",
|
|
"[544, 560]: Id = d03qr786rr3g008s8srg\n",
|
|
"[560, 576]: Id = d03qr7rnhqag008vm6dg\n",
|
|
"[576, 592]: Id = d03qr89nhqag008vm6e0\n",
|
|
"[592, 608]: Id = d03qr8sqnmvg0082cptg\n",
|
|
"[608, 624]: Id = d03qr91qnmvg0082cpv0\n",
|
|
"[624, 640]: Id = d03qr9hnhqag008vm6eg\n",
|
|
"[640, 656]: Id = d03qra1nhqag008vm6f0\n",
|
|
"[656, 672]: Id = d03qrahqnmvg0082cpvg\n",
|
|
"[672, 688]: Id = d03qraskzhn0008e4pb0\n",
|
|
"[688, 704]: Id = d03qrb9qnmvg0082cpw0\n",
|
|
"[704, 720]: Id = d03qrbsd8drg008jr5a0\n",
|
|
"[720, 736]: Id = d03qrc96rr3g008s8ss0\n",
|
|
"[736, 752]: Id = d03qrcsqnmvg0082cpwg\n",
|
|
"[752, 768]: Id = d03qrd9d8drg008jr5ag\n",
|
|
"[768, 784]: Id = d03qrdskzhn0008e4pbg\n",
|
|
"[784, 800]: Id = d03qre1d8drg008jr5b0\n",
|
|
"[800, 816]: Id = d03qrehqnmvg0082cpx0\n",
|
|
"[816, 832]: Id = d03qrf1kzhn0008e4pc0\n",
|
|
"[832, 848]: Id = d03qrf9rxz8g008g1cn0\n",
|
|
"[848, 864]: Id = d03qrfs6rr3g008s8st0\n",
|
|
"[864, 880]: Id = d03qrgad8drg008jr5bg\n",
|
|
"[880, 896]: Id = d03qrgtqnmvg0082cpxg\n",
|
|
"[896, 912]: Id = d03qrh2d8drg008jr5c0\n",
|
|
"[912, 928]: Id = d03qrhjqnmvg0082cpy0\n",
|
|
"[928, 944]: Id = d03qrj2d8drg008jr5cg\n",
|
|
"[944, 960]: Id = d03qrjjd8drg008jr5d0\n",
|
|
"[960, 976]: Id = d03qrjtkzhn0008e4pcg\n",
|
|
"[976, 992]: Id = d03qrkakzhn0008e4pd0\n",
|
|
"[992, 1008]: Id = d03qrktd8drg008jr5dg\n",
|
|
"[1008, 1024]: Id = d03qrm2d8drg008jr5e0\n",
|
|
"[1024, 1040]: Id = d03qrmjnhqag008vm6h0\n",
|
|
"[1040, 1056]: Id = d03qrn2qnmvg0082cpzg\n",
|
|
"[1056, 1072]: Id = d03qrnj6rr3g008s8svg\n",
|
|
"[1072, 1088]: Id = d03qrntkzhn0008e4pdg\n",
|
|
"[1088, 1104]: Id = d03qrpanhqag008vm6hg\n",
|
|
"[1104, 1120]: Id = d03qrpt6rr3g008s8swg\n",
|
|
"[1120, 1136]: Id = d03qrqanhqag008vm6j0\n",
|
|
"[1136, 1152]: Id = d03qrqtqnmvg0082cq00\n",
|
|
"[1152, 1168]: Id = d03qrrbrxz8g008g1cpg\n",
|
|
"[1168, 1184]: Id = d03qrrvrxz8g008g1cq0\n",
|
|
"[1184, 1200]: Id = d03qrs3kzhn0008e4pe0\n",
|
|
"[1200, 1216]: Id = d03qrskqnmvg0082cq0g\n",
|
|
"[1216, 1232]: Id = d03qrt3qnmvg0082cq10\n",
|
|
"[1232, 1248]: Id = d03qrtknhqag008vm6jg\n",
|
|
"[1248, 1264]: Id = d03qrv3rxz8g008g1cqg\n",
|
|
"[1264, 1280]: Id = d03qrvb6rr3g008s8sx0\n",
|
|
"[1280, 1296]: Id = d03qrvv6rr3g008s8sxg\n",
|
|
"[1296, 1312]: Id = d03qrwbkzhn0008e4peg\n",
|
|
"[1312, 1328]: Id = d03qrwvqnmvg0082cq20\n",
|
|
"[1328, 1344]: Id = d03qrxkrxz8g008g1cs0\n",
|
|
"[1344, 1360]: Id = d03qrxvqnmvg0082cq2g\n",
|
|
"[1360, 1376]: Id = d03qrybqnmvg0082cq30\n",
|
|
"[1376, 1392]: Id = d03qryvkzhn0008e4pgg\n",
|
|
"[1392, 1408]: Id = d03qrzbd8drg008jr5fg\n",
|
|
"[1408, 1424]: Id = d03qrzvqnmvg0082cq3g\n",
|
|
"[1424, 1440]: Id = d03qs0ckzhn0008e4ph0\n",
|
|
"[1440, 1456]: Id = d03qs0w6rr3g008s8sz0\n",
|
|
"[1456, 1472]: Id = d03qs1cnhqag008vm6m0\n",
|
|
"[1472, 1488]: Id = d03qs1mkzhn0008e4phg\n",
|
|
"[1488, 1504]: Id = d03qs24kzhn0008e4pj0\n",
|
|
"[1504, 1520]: Id = d03qs2mnhqag008vm6mg\n",
|
|
"[1520, 1536]: Id = d03qs2wrxz8g008g1csg\n",
|
|
"[1536, 1552]: Id = d03qs3crxz8g008g1ct0\n",
|
|
"[1552, 1568]: Id = d03qs44nhqag008vm6n0\n",
|
|
"[1568, 1584]: Id = d03qs4md8drg008jr5g0\n",
|
|
"[1584, 1600]: Id = d03qs4wqnmvg0082cq40\n",
|
|
"[1600, 1616]: Id = d03qs5mqnmvg0082cq4g\n",
|
|
"[1616, 1632]: Id = d03qs64rxz8g008g1cv0\n",
|
|
"[1632, 1648]: Id = d03qs6cd8drg008jr5gg\n",
|
|
"[1648, 1664]: Id = d03qs74nhqag008vm6p0\n",
|
|
"[1664, 1680]: Id = d03qs7md8drg008jr5hg\n",
|
|
"[1680, 1696]: Id = d03qs85d8drg008jr5j0\n",
|
|
"[1696, 1712]: Id = d03qs8dd8drg008jr5jg\n",
|
|
"[1712, 1728]: Id = d03qs8xqnmvg0082cq5g\n",
|
|
"[1728, 1744]: Id = d03qs9drxz8g008g1cvg\n",
|
|
"[1744, 1760]: Id = d03qs9xrxz8g008g1cw0\n",
|
|
"[1760, 1776]: Id = d03qsa5nhqag008vm6qg\n",
|
|
"[1776, 1792]: Id = d03qsan6rr3g008s8t00\n",
|
|
"[1792, 1808]: Id = d03qsb56rr3g008s8t0g\n",
|
|
"[1808, 1824]: Id = d03qsbn6rr3g008s8t10\n",
|
|
"[1824, 1840]: Id = d03qsc5rxz8g008g1cwg\n",
|
|
"[1840, 1856]: Id = d03qscnd8drg008jr5k0\n",
|
|
"[1856, 1872]: Id = d03qsd56rr3g008s8t1g\n",
|
|
"[1872, 1888]: Id = d03qsdnd8drg008jr5kg\n",
|
|
"[1888, 1904]: Id = d03qse5nhqag008vm6rg\n",
|
|
"[1904, 1920]: Id = d03qsed6rr3g008s8t20\n",
|
|
"[1920, 1936]: Id = d03qsexd8drg008jr5m0\n",
|
|
"[1936, 1952]: Id = d03qsfdd8drg008jr5mg\n",
|
|
"[1952, 1968]: Id = d03qsfxnhqag008vm6s0\n",
|
|
"[1968, 1984]: Id = d03qsg6kzhn0008e4pm0\n",
|
|
"[1984, 2000]: Id = d03qsgp6rr3g008s8t2g\n",
|
|
"[2000, 2016]: Id = d03qsh6qnmvg0082cq80\n",
|
|
"[2016, 2032]: Id = d03qshpqnmvg0082cq8g\n",
|
|
"[2032, 2048]: Id = d03qsj6qnmvg0082cq90\n",
|
|
"[2048, 2064]: Id = d03qsjpd8drg008jr5ng\n",
|
|
"[2064, 2080]: Id = d03qsk6rxz8g008g1cxg\n",
|
|
"[2080, 2096]: Id = d03qskekzhn0008e4pmg\n",
|
|
"[2096, 2112]: Id = d03qsky6rr3g008s8t3g\n",
|
|
"[2112, 2128]: Id = d03qsmerxz8g008g1cy0\n",
|
|
"[2128, 2144]: Id = d03qsmyd8drg008jr5p0\n",
|
|
"[2144, 2160]: Id = d03qsnenhqag008vm6t0\n",
|
|
"[2160, 2176]: Id = d03qsnyrxz8g008g1cyg\n",
|
|
"[2176, 2192]: Id = d03qsped8drg008jr5pg\n",
|
|
"[2192, 2208]: Id = d03qspyd8drg008jr5q0\n",
|
|
"[2208, 2224]: Id = d03qsqerxz8g008g1cz0\n",
|
|
"[2224, 2240]: Id = d03qsqy6rr3g008s8t4g\n",
|
|
"[2240, 2256]: Id = d03qsrf6rr3g008s8t50\n",
|
|
"[2256, 2272]: Id = d03qsrzqnmvg0082cq9g\n",
|
|
"[2272, 2288]: Id = d03qss7nhqag008vm6tg\n",
|
|
"[2288, 2304]: Id = d03qssq6rr3g008s8t5g\n",
|
|
"[2304, 2320]: Id = d03qst76rr3g008s8t60\n",
|
|
"[2320, 2336]: Id = d03qstqd8drg008jr5qg\n",
|
|
"[2336, 2352]: Id = d03qsv7d8drg008jr5r0\n",
|
|
"[2352, 2368]: Id = d03qsvqrxz8g008g1d00\n",
|
|
"[2368, 2384]: Id = d03qsw7d8drg008jr5rg\n",
|
|
"[2384, 2400]: Id = d03qswfnhqag008vm6v0\n",
|
|
"[2400, 2416]: Id = d03qswz6rr3g008s8t7g\n",
|
|
"[2416, 2432]: Id = d03qsxf6rr3g008s8t80\n",
|
|
"[2432, 2448]: Id = d03qsxzkzhn0008e4png\n",
|
|
"[2448, 2464]: Id = d03qsyfqnmvg0082cqb0\n",
|
|
"[2464, 2480]: Id = d03qsyzqnmvg0082cqbg\n",
|
|
"[2480, 2496]: Id = d03qszfd8drg008jr5s0\n",
|
|
"[2496, 2512]: Id = d03qszzrxz8g008g1d0g\n",
|
|
"[2512, 2528]: Id = d03qt086rr3g008s8t9g\n",
|
|
"[2528, 2544]: Id = d03qt0r6rr3g008s8ta0\n",
|
|
"[2544, 2560]: Id = d03qt10qnmvg0082cqc0\n",
|
|
"[2560, 2576]: Id = d03qt1g6rr3g008s8tb0\n",
|
|
"[2576, 2592]: Id = d03qt28rxz8g008g1d10\n",
|
|
"[2592, 2608]: Id = d03qt2rkzhn0008e4pq0\n",
|
|
"[2608, 2624]: Id = d03qt306rr3g008s8tbg\n",
|
|
"[2624, 2640]: Id = d03qt3gnhqag008vm6wg\n",
|
|
"[2640, 2656]: Id = d03qt40d8drg008jr5tg\n",
|
|
"[2656, 2672]: Id = d03qt4gnhqag008vm6x0\n",
|
|
"[2672, 2688]: Id = d03qt50nhqag008vm6xg\n",
|
|
"[2688, 2704]: Id = d03qt5gd8drg008jr5v0\n",
|
|
"[2704, 2720]: Id = d03qt60rxz8g008g1d2g\n",
|
|
"[2720, 2736]: Id = d03qt6grxz8g008g1d30\n",
|
|
"[2736, 2752]: Id = d03qt70kzhn0008e4pr0\n",
|
|
"[2752, 2768]: Id = d03qt7rrxz8g008g1d3g\n",
|
|
"[2768, 2784]: Id = d03qt89rxz8g008g1d40\n",
|
|
"[2784, 2800]: Id = d03qt8skzhn0008e4prg\n",
|
|
"[2800, 2816]: Id = d03qt91kzhn0008e4ps0\n",
|
|
"[2816, 2832]: Id = d03qt9hkzhn0008e4pt0\n",
|
|
"[2832, 2848]: Id = d03qta1kzhn0008e4ptg\n",
|
|
"[2848, 2864]: Id = d03qta9d8drg008jr5vg\n",
|
|
"[2864, 2880]: Id = d03qtas6rr3g008s8tc0\n",
|
|
"[2880, 2896]: Id = d03qtb9nhqag008vm6yg\n",
|
|
"[2896, 2912]: Id = d03qtbsqnmvg0082cqd0\n",
|
|
"[2912, 2928]: Id = d03qtch6rr3g008s8tcg\n",
|
|
"[2928, 2944]: Id = d03qtd1nhqag008vm6z0\n",
|
|
"[2944, 2960]: Id = d03qtdhqnmvg0082cqdg\n",
|
|
"[2960, 2976]: Id = d03qte1kzhn0008e4pv0\n",
|
|
"[2976, 2992]: Id = d03qte9rxz8g008g1d50\n",
|
|
"[2992, 3008]: Id = d03qtesqnmvg0082cqe0\n",
|
|
"[3008, 3024]: Id = d03qtf9kzhn0008e4pvg\n",
|
|
"[3024, 3040]: Id = d03qtfsnhqag008vm6zg\n",
|
|
"[3040, 3056]: Id = d03qtgaqnmvg0082cqeg\n",
|
|
"[3056, 3072]: Id = d03qtgt6rr3g008s8td0\n",
|
|
"[3072, 3088]: Id = d03qthaqnmvg0082cqfg\n",
|
|
"[3088, 3104]: Id = d03qthtkzhn0008e4pw0\n",
|
|
"[3104, 3120]: Id = d03qtja6rr3g008s8tdg\n",
|
|
"[3120, 3136]: Id = d03qtjtqnmvg0082cqg0\n",
|
|
"[3136, 3152]: Id = d03qtkarxz8g008g1d70\n",
|
|
"[3152, 3168]: Id = d03qtkt6rr3g008s8teg\n",
|
|
"[3168, 3184]: Id = d03qtma6rr3g008s8tf0\n",
|
|
"[3184, 3200]: Id = d03qtmtd8drg008jr5w0\n",
|
|
"[3200, 3216]: Id = d03qtna6rr3g008s8tfg\n",
|
|
"[3216, 3232]: Id = d03qtntkzhn0008e4px0\n",
|
|
"[3232, 3248]: Id = d03qtpanhqag008vm710\n",
|
|
"[3248, 3264]: Id = d03qtptd8drg008jr5wg\n",
|
|
"[3264, 3280]: Id = d03qtqad8drg008jr5x0\n",
|
|
"[3280, 3296]: Id = d03qtqtrxz8g008g1d7g\n",
|
|
"[3296, 3312]: Id = d03qtrkd8drg008jr5xg\n",
|
|
"[3312, 3328]: Id = d03qtrvrxz8g008g1d80\n",
|
|
"[3328, 3344]: Id = d03qtsbkzhn0008e4pxg\n",
|
|
"[3344, 3360]: Id = d03qtsv6rr3g008s8tg0\n",
|
|
"[3360, 3376]: Id = d03qttvqnmvg0082cqh0\n",
|
|
"[3376, 3392]: Id = d03qtvbnhqag008vm71g\n",
|
|
"[3392, 3408]: Id = d03qtvvrxz8g008g1d8g\n",
|
|
"[3408, 3424]: Id = d03qtwb6rr3g008s8tgg\n",
|
|
"[3424, 3440]: Id = d03qtwvkzhn0008e4pz0\n",
|
|
"[3440, 3456]: Id = d03qtxbd8drg008jr5yg\n",
|
|
"[3456, 3472]: Id = d03qtxvkzhn0008e4pzg\n",
|
|
"[3472, 3488]: Id = d03qtybrxz8g008g1d90\n",
|
|
"[3488, 3504]: Id = d03qtyvnhqag008vm720\n",
|
|
"[3504, 3520]: Id = d03qtzb6rr3g008s8th0\n",
|
|
"[3520, 3536]: Id = d03qv04nhqag008vm72g\n",
|
|
"[3536, 3552]: Id = d03qv0mnhqag008vm730\n",
|
|
"[3552, 3568]: Id = d03qv14qnmvg0082cqj0\n",
|
|
"[3568, 3584]: Id = d03qv1mkzhn0008e4q0g\n",
|
|
"[3584, 3600]: Id = d03qv24nhqag008vm740\n",
|
|
"[3600, 3616]: Id = d03qv2mnhqag008vm74g\n",
|
|
"[3616, 3632]: Id = d03qv34nhqag008vm750\n",
|
|
"[3632, 3648]: Id = d03qv3mkzhn0008e4q1g\n",
|
|
"[3648, 3664]: Id = d03qv44nhqag008vm75g\n",
|
|
"[3664, 3680]: Id = d03qv4md8drg008jr5zg\n",
|
|
"[3680, 3696]: Id = d03qv54rxz8g008g1db0\n",
|
|
"[3696, 3712]: Id = d03qv5wqnmvg0082cqjg\n",
|
|
"[3712, 3728]: Id = d03qv6c6rr3g008s8tkg\n",
|
|
"[3728, 3744]: Id = d03qv6wkzhn0008e4q3g\n",
|
|
"[3744, 3760]: Id = d03qv7crxz8g008g1dc0\n",
|
|
"[3760, 3776]: Id = d03qv7wqnmvg0082cqk0\n",
|
|
"[3776, 3792]: Id = d03qv8nqnmvg0082cqm0\n",
|
|
"[3792, 3808]: Id = d03qv8xrxz8g008g1dcg\n",
|
|
"[3808, 3824]: Id = d03qv9nrxz8g008g1dd0\n",
|
|
"[3824, 3840]: Id = d03qva56rr3g008s8tm0\n",
|
|
"[3840, 3856]: Id = d03qvanrxz8g008g1ddg\n",
|
|
"[3856, 3872]: Id = d03qvb5qnmvg0082cqmg\n",
|
|
"[3872, 3888]: Id = d03qvbxqnmvg0082cqn0\n",
|
|
"[3888, 3904]: Id = d03qvcdrxz8g008g1de0\n",
|
|
"[3904, 3920]: Id = d03qvcxd8drg008jr60g\n",
|
|
"[3920, 3936]: Id = d03qvdnqnmvg0082cqng\n",
|
|
"[3936, 3952]: Id = d03qve5nhqag008vm77g\n",
|
|
"[3952, 3968]: Id = d03qvend8drg008jr610\n",
|
|
"[3968, 3984]: Id = d03qvfdrxz8g008g1deg\n",
|
|
"[3984, 4000]: Id = d03qvfxkzhn0008e4q50\n",
|
|
"[4000, 4016]: Id = d03qvge6rr3g008s8tn0\n",
|
|
"[4016, 4032]: Id = d03qvgyqnmvg0082cqp0\n",
|
|
"[4032, 4048]: Id = d03qvhe6rr3g008s8tng\n",
|
|
"[4048, 4064]: Id = d03qvhy6rr3g008s8tp0\n",
|
|
"[4064, 4080]: Id = d03qvjyrxz8g008g1dfg\n",
|
|
"[4080, 4096]: Id = d03qvkeqnmvg0082cqq0\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# Set local parameters\n",
|
|
"SAMPLES_DYN = SAMPLES\n",
|
|
"OPTIMIZATION_LEVEL_DYN = OPTIMIZATION_LEVEL\n",
|
|
"SHOTS_DYN = SHOTS\n",
|
|
"MIN_NUMBER_QUBITS_DYN = MIN_NUMBER_QUBITS\n",
|
|
"MAX_NUMBER_QUBITS_DYN = MAX_NUMBER_QUBITS\n",
|
|
"DURATIONS_DYN = DURATIONS\n",
|
|
"DD_SEQUENCE_DYN = DD_SEQUENCE\n",
|
|
"NUM_CIRCUITS_PER_JOB_DYN = 16\n",
|
|
"USE_DYNAMIC_DECOUPLING_DYN = USE_DYNAMIC_DECOUPLING\n",
|
|
"\n",
|
|
"# Submit jobs for the measurement based dynamic circuit approach\n",
|
|
"job_ids_dyn = submit_circuits(\n",
|
|
" MIN_NUMBER_QUBITS_DYN,\n",
|
|
" MAX_NUMBER_QUBITS_DYN,\n",
|
|
" NUM_CIRCUITS_PER_JOB_DYN,\n",
|
|
" QUBIT_LINE,\n",
|
|
" COUPLING_MAP_1D,\n",
|
|
" SAMPLES_DYN,\n",
|
|
" OPTIMIZATION_LEVEL_DYN,\n",
|
|
" backend,\n",
|
|
" SHOTS_DYN,\n",
|
|
" build_circuits_dyn,\n",
|
|
" durations=DURATIONS_DYN,\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "db31a1df",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Step 4: Post-process and return result in desired classical format\n",
|
|
"\n",
|
|
"After the experiments have successfully executed, proceed to post-process the resulting counts to gain insight on the final results. You can take advantage of resampling techniques (also known as [bootstrapping](https://en.wikipedia.org/wiki/Bootstrapping_(statistics))) to calculate average fidelities and deviations from the experimental counts."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 31,
|
|
"id": "0670acf4",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def resample_single_dictionary(d):\n",
|
|
" \"\"\"Resample a single dictionary based on its weights.\"\"\"\n",
|
|
" keys = list(d.keys())\n",
|
|
" weights = list(d.values())\n",
|
|
" sum_weights = sum(weights)\n",
|
|
"\n",
|
|
" if sum_weights == 0:\n",
|
|
" return d\n",
|
|
"\n",
|
|
" resampled_keys = random.choices(keys, weights=weights, k=sum_weights)\n",
|
|
"\n",
|
|
" # Count the occurrences of each key in the resampled keys\n",
|
|
" resampled_counts = {}\n",
|
|
" for key in resampled_keys:\n",
|
|
" resampled_counts[key] = resampled_counts.get(key, 0) + 1\n",
|
|
"\n",
|
|
" return resampled_counts\n",
|
|
"\n",
|
|
"\n",
|
|
"def resample_dict_list(dict_list, n_samples):\n",
|
|
" \"\"\"Resample the entire list of dictionaries n_samples times.\"\"\"\n",
|
|
" resampled_lists = []\n",
|
|
"\n",
|
|
" for _ in range(n_samples):\n",
|
|
" new_version = [resample_single_dictionary(d) for d in dict_list]\n",
|
|
" resampled_lists.append(new_version)\n",
|
|
"\n",
|
|
" return resampled_lists"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "4d939f70",
|
|
"metadata": {},
|
|
"source": [
|
|
"In addition, to post-process the results, you need to extract the information from the Monte Carlo state certification protocol - thus, depending on the preparation/measurement basis, you will group the results differently. The utility functions below are meant to carry out this procedure:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 32,
|
|
"id": "e10d498d-443f-4875-99bf-bb0b3f6a8e3a",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def parity(string: str) -> int:\n",
|
|
" return string.count(\"1\") % 2\n",
|
|
"\n",
|
|
"\n",
|
|
"def parities(string: str) -> str:\n",
|
|
" strings = string.split()\n",
|
|
" parities = [parity(val) for val in strings]\n",
|
|
" return parities\n",
|
|
"\n",
|
|
"\n",
|
|
"def postproc_counts(counts, i, samples):\n",
|
|
" P_lkji = PauliList(\n",
|
|
" [\n",
|
|
" \"IIII\",\n",
|
|
" \"XIXI\",\n",
|
|
" \"IZIZ\",\n",
|
|
" \"XZXZ\",\n",
|
|
" \"YZYI\",\n",
|
|
" \"ZZZI\",\n",
|
|
" \"YIYZ\",\n",
|
|
" \"ZIZZ\",\n",
|
|
" \"XXIX\",\n",
|
|
" \"IXXX\",\n",
|
|
" \"XYIY\",\n",
|
|
" \"IYXY\",\n",
|
|
" \"ZYYX\",\n",
|
|
" \"YYZX\",\n",
|
|
" \"ZXYY\",\n",
|
|
" \"YXZY\",\n",
|
|
" ]\n",
|
|
" )\n",
|
|
"\n",
|
|
" PauliI = Pauli(\"I\")\n",
|
|
" PauliX = Pauli(\"X\")\n",
|
|
" PauliZ = Pauli(\"Z\")\n",
|
|
"\n",
|
|
" P_k = P_lkji[samples[i]][2]\n",
|
|
" P_l = P_lkji[samples[i]][3]\n",
|
|
"\n",
|
|
" # determine parities\n",
|
|
" counts_post = {\"00\": 0, \"01\": 0, \"10\": 0, \"11\": 0}\n",
|
|
"\n",
|
|
" for key in counts:\n",
|
|
" parities_list = parities(key)\n",
|
|
" w = len(parities_list)\n",
|
|
" if w == 3:\n",
|
|
" parity_of_c2, parity_of_c1, _ = parities_list\n",
|
|
" elif w == 2:\n",
|
|
" parity_of_c1 = 0\n",
|
|
" parity_of_c2, _ = parities_list\n",
|
|
" else:\n",
|
|
" parity_of_c1 = 0\n",
|
|
" parity_of_c2 = 0\n",
|
|
"\n",
|
|
" # add parity_of_c2 to q0 (key[-1]) only if P_k is 'X' or 'Y'\n",
|
|
" if P_k == PauliI or P_k == PauliZ:\n",
|
|
" parity_of_c2 = 0\n",
|
|
"\n",
|
|
" # add parity_c1 to q1 (key[-2]) only if P_l is 'I' or 'Z' or 'Y'\n",
|
|
" if P_l == PauliX:\n",
|
|
" parity_of_c1 = 0\n",
|
|
"\n",
|
|
" control_qubit_value = int(key[-1]) # Control qubit q0\n",
|
|
" target_qubit_value = int(key[-2]) # Target qubit q1\n",
|
|
"\n",
|
|
" new_control_qubit_value = (control_qubit_value + parity_of_c2) % 2\n",
|
|
" new_target_qubit_value = (target_qubit_value + parity_of_c1) % 2\n",
|
|
"\n",
|
|
" new_key = str(new_target_qubit_value) + str(new_control_qubit_value)\n",
|
|
"\n",
|
|
" counts_post[new_key] += counts[key]\n",
|
|
"\n",
|
|
" return counts_post\n",
|
|
"\n",
|
|
"\n",
|
|
"def post_process_postproc(count, i, p, q, samples):\n",
|
|
" return postproc_counts(count, i, samples)\n",
|
|
"\n",
|
|
"\n",
|
|
"def post_process_dyn(count, i, p, q, samples):\n",
|
|
" return marginal_counts(count, indices=range(2))\n",
|
|
"\n",
|
|
"\n",
|
|
"def process_fidelities(\n",
|
|
" counts: Union[dict[str, int], List[dict[str, int]]],\n",
|
|
" samples: List[int],\n",
|
|
" shots: int,\n",
|
|
" post_process: Optional[Callable] = None,\n",
|
|
") -> List[float]:\n",
|
|
" \"\"\"Calculate the estimated process fidelities from experiment counts data\n",
|
|
"\n",
|
|
" Args:\n",
|
|
" counts (dict[str:int] or List[dict[str:int]]): counts data from an experiment\n",
|
|
" samples (List[int]): which of the 16 Paulis with non-zero expectation value to prepare and measure\n",
|
|
" shots (int): Number of shots used in experiment\n",
|
|
" post_process (Callable): Post process the counts with post_proc if given. Default = None\n",
|
|
" \"\"\"\n",
|
|
" exp_all = []\n",
|
|
" # 16 Paulis with non-zero expectation value to prepare and measure\n",
|
|
" P_lkji = PauliList(\n",
|
|
" [\n",
|
|
" \"IIII\",\n",
|
|
" \"XIXI\",\n",
|
|
" \"IZIZ\",\n",
|
|
" \"XZXZ\",\n",
|
|
" \"YZYI\",\n",
|
|
" \"ZZZI\",\n",
|
|
" \"YIYZ\",\n",
|
|
" \"ZIZZ\",\n",
|
|
" \"XXIX\",\n",
|
|
" \"IXXX\",\n",
|
|
" \"XYIY\",\n",
|
|
" \"IYXY\",\n",
|
|
" \"ZYYX\",\n",
|
|
" \"YYZX\",\n",
|
|
" \"ZXYY\",\n",
|
|
" \"YXZY\",\n",
|
|
" ]\n",
|
|
" )\n",
|
|
" sign_rho_lkji = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1]\n",
|
|
"\n",
|
|
" PauliI = Pauli(\"I\")\n",
|
|
"\n",
|
|
" for i in range(len(samples)):\n",
|
|
" P_i = P_lkji[samples[i]][0]\n",
|
|
" P_j = P_lkji[samples[i]][1]\n",
|
|
" P_k = P_lkji[samples[i]][2]\n",
|
|
" P_l = P_lkji[samples[i]][3]\n",
|
|
"\n",
|
|
" exp = 0\n",
|
|
" # initial state p with eig value p_eig prepared\n",
|
|
" for p in range(2):\n",
|
|
" if P_i == PauliI:\n",
|
|
" p_eig = 1\n",
|
|
" else:\n",
|
|
" p_eig = (-1) ** p\n",
|
|
"\n",
|
|
" # initial state q with eig value q_eig prepared\n",
|
|
" for q in range(2):\n",
|
|
" if P_j == PauliI:\n",
|
|
" q_eig = 1\n",
|
|
" else:\n",
|
|
" q_eig = (-1) ** q\n",
|
|
"\n",
|
|
" # post process count if provided\n",
|
|
" if post_process is not None:\n",
|
|
" if len(counts) > 0:\n",
|
|
" counts_post = post_process(\n",
|
|
" counts[i * 4 + 2 * p + q], i, p, q, samples\n",
|
|
" )\n",
|
|
" else:\n",
|
|
" if len(counts) > 0:\n",
|
|
" counts_post = counts[i * 4 + 2 * p + q]\n",
|
|
"\n",
|
|
" # measurement projecting to states r with eig value r_eig\n",
|
|
" for r in range(2):\n",
|
|
" if P_k == PauliI:\n",
|
|
" r_eig = 1\n",
|
|
" else:\n",
|
|
" r_eig = (-1) ** r\n",
|
|
" for s in range(2):\n",
|
|
" if P_l == PauliI:\n",
|
|
" s_eig = 1\n",
|
|
" else:\n",
|
|
" s_eig = (-1) ** s\n",
|
|
"\n",
|
|
" str_r = str(r)\n",
|
|
" str_s = str(s)\n",
|
|
"\n",
|
|
" try:\n",
|
|
" exp += (\n",
|
|
" p_eig\n",
|
|
" * q_eig\n",
|
|
" * s_eig\n",
|
|
" * r_eig\n",
|
|
" * counts_post[str_s + str_r]\n",
|
|
" / shots\n",
|
|
" / 4\n",
|
|
" / sign_rho_lkji[samples[i]]\n",
|
|
" )\n",
|
|
" except KeyError:\n",
|
|
" pass\n",
|
|
"\n",
|
|
" exp_all.append(exp)\n",
|
|
" return exp_all\n",
|
|
"\n",
|
|
"\n",
|
|
"def get_counts_from_bitarray(instance):\n",
|
|
" \"\"\"\n",
|
|
" Extract counts from result data\n",
|
|
" \"\"\"\n",
|
|
" for field, value in instance.__dict__.items():\n",
|
|
" if isinstance(value, BitArray):\n",
|
|
" return value.get_counts()\n",
|
|
" return None\n",
|
|
"\n",
|
|
"\n",
|
|
"def cal_average_fidelities(\n",
|
|
" job_ids: List[str],\n",
|
|
" min_qubits: int,\n",
|
|
" max_qubits: int,\n",
|
|
" samples: List[int],\n",
|
|
" shots: int,\n",
|
|
" num_circuits_per_job: int,\n",
|
|
" post_process: Optional[Callable] = None,\n",
|
|
" all_counts: Optional[List[Dict]] = None,\n",
|
|
" display: Optional[bool] = True,\n",
|
|
" debug: Optional[bool] = False,\n",
|
|
" n_bootstrap_sample: Optional[int] = 4,\n",
|
|
") -> (List[float], List[float]):\n",
|
|
" \"\"\"\n",
|
|
" Calculate the average gate fidelities\n",
|
|
" \"\"\"\n",
|
|
"\n",
|
|
" proc_fidelities = []\n",
|
|
" proc_std = []\n",
|
|
" nr_jobs = len(job_ids)\n",
|
|
" num_samples = len(samples)\n",
|
|
" empty_counts = {\"00\": 0, \"01\": 0, \"10\": 0, \"11\": 0}\n",
|
|
" if all_counts is None:\n",
|
|
" counts_flag = False\n",
|
|
" all_counts = []\n",
|
|
" else:\n",
|
|
" counts_flag = True\n",
|
|
"\n",
|
|
" if debug is True:\n",
|
|
" print(f\"{nr_jobs} to process\")\n",
|
|
" if len(all_counts) == 0:\n",
|
|
" for j in range(nr_jobs):\n",
|
|
" job = service.job(job_ids[j])\n",
|
|
"\n",
|
|
" if job.status() == \"RUNNING\":\n",
|
|
" raise ValueError(\n",
|
|
" f\"Job id : {job_ids[j]} returned status 'RUNNING', wait for all jobs to complete then try again.\"\n",
|
|
" )\n",
|
|
" if job.status() != \"DONE\":\n",
|
|
" print(\n",
|
|
" f\"Job id : {job_ids[j]} returned status '{job.status()}'; adding empty dictionaries.\"\n",
|
|
" )\n",
|
|
" all_counts += [empty_counts] * num_circuits_per_job\n",
|
|
" continue\n",
|
|
" if display is True:\n",
|
|
" print(\n",
|
|
" f\"Retrieving job data: {job_ids[j]}: {j} of {nr_jobs-1}\"\n",
|
|
" )\n",
|
|
" result = job.result()\n",
|
|
" for i in range(len(result)):\n",
|
|
" counts = get_counts_from_bitarray(result[i].data)\n",
|
|
" all_counts.append(counts)\n",
|
|
"\n",
|
|
" if debug is False:\n",
|
|
" clear_output(wait=True)\n",
|
|
" else:\n",
|
|
" print(\"Using provided all_counts data instead of loading from server\")\n",
|
|
" print(all_counts)\n",
|
|
"\n",
|
|
" for n in range(min_qubits, max_qubits + 1):\n",
|
|
" if display is True:\n",
|
|
" print(\n",
|
|
" f\"Resampling counts for n = {n}: {max_qubits + 1 - n} remaining\"\n",
|
|
" )\n",
|
|
" counts = all_counts[\n",
|
|
" (n - min_qubits) * 4 * num_samples : (n - min_qubits + 1)\n",
|
|
" * 4\n",
|
|
" * num_samples\n",
|
|
" ]\n",
|
|
" proc_fid_temp = []\n",
|
|
"\n",
|
|
" for _ in range(n_bootstrap_sample):\n",
|
|
" resample_counts = resample_dict_list(counts, 1)[0]\n",
|
|
" sample_fidelities = process_fidelities(\n",
|
|
" resample_counts, samples, shots, post_process\n",
|
|
" )\n",
|
|
" proc_fid_temp.append(np.mean(sample_fidelities))\n",
|
|
"\n",
|
|
" mean, std = (\n",
|
|
" np.mean(np.array(proc_fid_temp)),\n",
|
|
" np.std(np.array(proc_fid_temp)),\n",
|
|
" )\n",
|
|
" proc_fidelities.append(mean)\n",
|
|
" proc_std.append(std)\n",
|
|
" if debug is False:\n",
|
|
" clear_output(wait=True)\n",
|
|
"\n",
|
|
" if display is True:\n",
|
|
" print(\"Process fidelities:\")\n",
|
|
" print([\"{0:0.3f}\".format(i) for i in proc_fidelities])\n",
|
|
" print(\"Process fidelities std:\")\n",
|
|
" print([\"{0:0.3f}\".format(i) for i in proc_std])\n",
|
|
"\n",
|
|
" # Calculate average gate fidelity from the process fidelity\n",
|
|
"\n",
|
|
" avg_gate_fidelities = []\n",
|
|
"\n",
|
|
" for i in range(len(proc_fidelities)):\n",
|
|
" # Use result of Horodecki et al. to calculate the average gate fidelity\n",
|
|
" avg_gate_fidelity = (proc_fidelities[i] * 4 + 1) / 5\n",
|
|
" avg_gate_fidelities.append(avg_gate_fidelity)\n",
|
|
"\n",
|
|
" if display is True:\n",
|
|
" print(\"Average Gate Fidelites\")\n",
|
|
" print([\"{0:0.3f}\".format(i) for i in avg_gate_fidelities])\n",
|
|
"\n",
|
|
" # Calculate average gate fidelity std from the process fidelity std\n",
|
|
"\n",
|
|
" avg_gate_stds = []\n",
|
|
"\n",
|
|
" for i in range(len(proc_std)):\n",
|
|
" # We scale the std as in the average gate fidelity\n",
|
|
" avg_gate_std = (proc_std[i] * 4) / 5\n",
|
|
" avg_gate_stds.append(avg_gate_std)\n",
|
|
"\n",
|
|
" if display is True:\n",
|
|
" print(\"Average Gate Std\")\n",
|
|
" print([\"{0:0.3f}\".format(i) for i in avg_gate_stds])\n",
|
|
"\n",
|
|
" if counts_flag is True:\n",
|
|
" return (avg_gate_fidelities, avg_gate_stds, all_counts)\n",
|
|
" else:\n",
|
|
" return (avg_gate_fidelities, avg_gate_stds)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 33,
|
|
"id": "12c9ef74-1c91-4e69-a22d-326d8db1a192",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Process fidelities:\n",
|
|
"['0.867', '0.614', '0.542', '0.120', '0.590', '0.467', '0.495', '0.505', '0.529', '0.499', '0.499', '0.464', '0.444', '0.402', '0.424', '0.330', '0.371', '0.395', '0.383', '0.385', '0.372', '0.318', '0.333', '0.339', '0.347', '0.335', '0.352', '0.216', '0.310', '0.300', '0.233', '0.283', '0.280', '0.208', '0.256', '0.270', '0.171', '0.202', '0.123', '0.209', '0.199', '0.186', '0.148', '0.164', '0.156', '0.156', '0.174', '0.123', '0.190', '0.176', '0.134', '0.147', '0.074', '0.156', '0.159', '0.170', '0.142', '0.150', '0.141', '0.124', '0.157', '0.151', '0.069', '0.116']\n",
|
|
"Process fidelities std:\n",
|
|
"['0.002', '0.003', '0.002', '0.003', '0.003', '0.003', '0.004', '0.003', '0.001', '0.002', '0.004', '0.002', '0.002', '0.002', '0.004', '0.005', '0.004', '0.002', '0.003', '0.002', '0.002', '0.005', '0.003', '0.004', '0.004', '0.005', '0.003', '0.003', '0.003', '0.002', '0.003', '0.004', '0.002', '0.003', '0.007', '0.003', '0.005', '0.001', '0.003', '0.000', '0.001', '0.002', '0.002', '0.003', '0.005', '0.002', '0.003', '0.003', '0.002', '0.003', '0.003', '0.002', '0.002', '0.002', '0.001', '0.002', '0.003', '0.002', '0.002', '0.004', '0.004', '0.005', '0.003', '0.002']\n",
|
|
"Average Gate Fidelites\n",
|
|
"['0.894', '0.691', '0.633', '0.296', '0.672', '0.574', '0.596', '0.604', '0.624', '0.600', '0.600', '0.571', '0.555', '0.521', '0.539', '0.464', '0.497', '0.516', '0.507', '0.508', '0.497', '0.454', '0.467', '0.471', '0.478', '0.468', '0.481', '0.373', '0.448', '0.440', '0.386', '0.426', '0.424', '0.366', '0.405', '0.416', '0.337', '0.362', '0.299', '0.367', '0.359', '0.349', '0.319', '0.331', '0.325', '0.324', '0.339', '0.298', '0.352', '0.341', '0.307', '0.317', '0.259', '0.325', '0.327', '0.336', '0.314', '0.320', '0.313', '0.299', '0.326', '0.321', '0.255', '0.293']\n",
|
|
"Average Gate Std\n",
|
|
"['0.002', '0.002', '0.002', '0.003', '0.002', '0.003', '0.003', '0.003', '0.001', '0.001', '0.003', '0.002', '0.001', '0.002', '0.003', '0.004', '0.003', '0.001', '0.002', '0.002', '0.002', '0.004', '0.003', '0.003', '0.003', '0.004', '0.003', '0.002', '0.002', '0.001', '0.002', '0.004', '0.001', '0.002', '0.005', '0.002', '0.004', '0.001', '0.003', '0.000', '0.001', '0.001', '0.002', '0.002', '0.004', '0.002', '0.002', '0.002', '0.001', '0.002', '0.003', '0.001', '0.002', '0.002', '0.001', '0.002', '0.003', '0.002', '0.002', '0.003', '0.003', '0.004', '0.002', '0.002']\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# No post processing of the counts is required in the Unitary circuits. The average gate fidelities can now be calculated:\n",
|
|
"avg_gate_fidelities_uni, avg_gate_stds_uni = cal_average_fidelities(\n",
|
|
" job_ids_uni,\n",
|
|
" MIN_NUMBER_QUBITS_UNI,\n",
|
|
" MAX_NUMBER_QUBITS_UNI,\n",
|
|
" SAMPLES_UNI,\n",
|
|
" SHOTS_UNI,\n",
|
|
" NUM_CIRCUITS_PER_JOB_UNI,\n",
|
|
")\n",
|
|
"\n",
|
|
"\n",
|
|
"avg_gate_fidelities_postproc, avg_gate_stds_postproc = cal_average_fidelities(\n",
|
|
" job_ids_postproc,\n",
|
|
" MIN_NUMBER_QUBITS_POSTPROC,\n",
|
|
" MAX_NUMBER_QUBITS_POSTPROC,\n",
|
|
" SAMPLES_POSTPROC,\n",
|
|
" SHOTS_POSTPROC,\n",
|
|
" NUM_CIRCUITS_PER_JOB_POSTPROC,\n",
|
|
" post_process=post_process_postproc,\n",
|
|
")\n",
|
|
"\n",
|
|
"\n",
|
|
"avg_gate_fidelities_dyn, avg_gate_stds_dyn = cal_average_fidelities(\n",
|
|
" job_ids_dyn,\n",
|
|
" MIN_NUMBER_QUBITS_DYN,\n",
|
|
" MAX_NUMBER_QUBITS_DYN,\n",
|
|
" SAMPLES_DYN,\n",
|
|
" SHOTS_DYN,\n",
|
|
" NUM_CIRCUITS_PER_JOB_DYN,\n",
|
|
" post_process=post_process_dyn,\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "982f8e1b-5cd8-440f-af89-8b81eed1a15d",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Plot the results\n",
|
|
"To appreciate the results visually, the cell below plots the estimated gate fidelities measured at varying distance between entangled qubits for the three different methods. In general, the fidelity will decrease with increasing distance. The results show that although the unitary method (using SWAPs to implement a long-range entangling interaction) performs better at short distances, there is a cross-over to a regime where dynamic circuits become a better option. This is true for both the measurement-and-feedforward technique as well as the post-processing one."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 34,
|
|
"id": "0400e350",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<Image src=\"/docs/images/tutorials/long-range-entanglement/extracted-outputs/0400e350-0.avif\" alt=\"Output of the previous code cell\" />"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"fig, ax = plt.subplots()\n",
|
|
"\n",
|
|
"ax.errorbar(\n",
|
|
" range(MIN_NUMBER_QUBITS_UNI, MAX_NUMBER_QUBITS_UNI + 1),\n",
|
|
" avg_gate_fidelities_uni,\n",
|
|
" avg_gate_stds_uni,\n",
|
|
" fmt=\"o-.\",\n",
|
|
" color=\"c\",\n",
|
|
" label=\"Unitary\",\n",
|
|
")\n",
|
|
"ax.errorbar(\n",
|
|
" range(MIN_NUMBER_QUBITS_UNI, MAX_NUMBER_QUBITS_UNI + 1),\n",
|
|
" avg_gate_fidelities_postproc,\n",
|
|
" avg_gate_stds_postproc,\n",
|
|
" fmt=\"o-.\",\n",
|
|
" color=\"y\",\n",
|
|
" label=\"Post-processing\",\n",
|
|
")\n",
|
|
"ax.errorbar(\n",
|
|
" range(MIN_NUMBER_QUBITS_UNI, MAX_NUMBER_QUBITS_UNI + 1),\n",
|
|
" avg_gate_fidelities_dyn,\n",
|
|
" avg_gate_stds_dyn,\n",
|
|
" fmt=\"o-.\",\n",
|
|
" color=\"m\",\n",
|
|
" label=\"Dynamic\",\n",
|
|
")\n",
|
|
"ax.axhline(y=1 / 4, color=\"g\", linestyle=\"--\", label=\"Random gate\")\n",
|
|
"legend = ax.legend(frameon=True)\n",
|
|
"for text in legend.get_texts():\n",
|
|
" text.set_color(\"black\") # Set the legend text color to black\n",
|
|
"legend.get_frame().set_facecolor(\n",
|
|
" \"white\"\n",
|
|
") # Set the legend background color to white\n",
|
|
"legend.get_frame().set_edgecolor(\n",
|
|
" \"black\"\n",
|
|
") # Optional: set the legend border color to black\n",
|
|
"ax.set_xlabel(\"Number of qubits between control and target\", color=\"black\")\n",
|
|
"ax.set_ylabel(\"Teleported gate fidelity\", color=\"black\")\n",
|
|
"ax.grid(linestyle=\":\", linewidth=0.6, alpha=0.4, color=\"gray\")\n",
|
|
"ax.set_ylim((0.2, 1))\n",
|
|
"ax.set_facecolor(\"white\") # Set the background color of the axes\n",
|
|
"fig.patch.set_facecolor(\"white\") # Set the background color of the figure\n",
|
|
"\n",
|
|
"# Ensure the axis lines and ticks are visible\n",
|
|
"for spine in ax.spines.values():\n",
|
|
" spine.set_visible(True)\n",
|
|
" spine.set_color(\"black\") # Set the color of the axis lines to black\n",
|
|
"ax.tick_params(axis=\"x\", colors=\"black\")\n",
|
|
"ax.tick_params(axis=\"y\", colors=\"black\")\n",
|
|
"\n",
|
|
"plt.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "0c1ffc24",
|
|
"metadata": {
|
|
"heading_collapsed": true
|
|
},
|
|
"source": [
|
|
"#### Data from the paper\n",
|
|
"\n",
|
|
"The results from this tutorial are likely to vary from the results of the paper due to different calibrations and machines used. The code presented above also uses a slightly different method to calculate parities than was used in the paper, as well as some other differences to make the notebook cleaner and more accessible to a wider audience."
|
|
]
|
|
},
|
|
{
|
|
"attachments": {},
|
|
"cell_type": "markdown",
|
|
"id": "8a3adfa1-7fa1-4632-be7f-a316c26c844d",
|
|
"metadata": {
|
|
"hidden": true
|
|
},
|
|
"source": [
|
|
"#### Plot from Paper\n",
|
|
"\n",
|
|
""
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "804dcf16-7a26-477d-b9eb-34e383fc983d",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Appendix: Calculating the average fidelity"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "e9e8a1f4-64f7-431a-a188-789590ab2357",
|
|
"metadata": {},
|
|
"source": [
|
|
"The *fidelity* [2] of two states $\\rho$ and $\\sigma$ is defined by\n",
|
|
"\n",
|
|
"$$\\mathcal{F}(\\rho,\\sigma) = \\mathrm{Tr}\\left(\\sqrt{\\sqrt{\\rho}\\sigma\\sqrt{\\rho}} \\right)^2$$\n",
|
|
"\n",
|
|
"If one of $\\rho$ or $\\sigma$ is a pure state then this reduces to $\\mathcal{F}(\\rho,\\sigma)=\\mathrm{Tr}(\\rho\\sigma)$.\n",
|
|
"*Gate fidelity* is a tool for comparing how well the implemented quantum channel $\\xi$ approximates the desired unitary channel $\\mathcal{U}(\\rho) = U\\rho{U^\\dagger}$. Gate fidelity is a function defined on pure states as follows:\n",
|
|
"\n",
|
|
"$$\n",
|
|
"\\mathcal{F}_{\\xi,\\mathcal{U}}(|\\phi\\rangle) := \\mathcal{F}\\bigl(\\xi(|\\phi\\rangle\\langle\\phi|), \\mathcal{U}(|\\phi\\rangle\\langle\\phi|)\\bigl)\n",
|
|
"= \\langle\\phi|(\\mathcal{U}^\\dagger\\circ\\xi)(|\\phi\\rangle\\langle\\phi|)|\\phi\\rangle\n",
|
|
":= \\mathcal{F}_{\\mathcal{U}^\\dagger\\circ\\xi}(|\\phi\\rangle).\n",
|
|
"$$\n",
|
|
"\n",
|
|
"Here $\\mathcal{F}_{\\mathcal{U}^\\dagger\\circ\\xi}$ can be thought of as measuring how noisy the channel $\\mathcal{U}^\\dagger\\circ\\xi$ is. The average gate fidelity of a channel $\\mathcal{U}^\\dagger\\circ\\xi$ is defined by averaging the gate fidelity via the induced haar measure (the Fubini-Stufy meaure):\n",
|
|
"\n",
|
|
"$$\\mathcal{F}_{avg}(\\mathcal{U},\\xi):=\\mathcal{F}_{avg}(\\mathcal{U}^\\dagger\\circ\\xi) := \\int\\langle\\phi|(\\mathcal{U}^\\dagger\\circ\\xi)(|\\phi\\rangle\\langle\\phi|)|\\phi\\rangle d\\phi$$\n",
|
|
"\n",
|
|
"To calculate the average gate fidelity of the channel $\\mathcal{U}^\\dagger\\circ\\xi$ we use a result of Horodecki et al. [3] which relates the average gate fidelity to the entanglement fedilty of a channel. The entanglement fidelity of the channel $\\mathcal{U}^\\dagger\\circ\\xi$ is defined as\n",
|
|
"\n",
|
|
"$$\n",
|
|
"\\mathcal{F}_{ent}(\\mathcal{U}^\\dagger\\circ\\xi) := \\mathcal{F}_{ent}(\\rho_{\\mathcal{U}^\\dagger\\circ\\xi}) :=\\langle\\psi_+|\\rho_{\\mathcal{U}^\\dagger\\circ\\xi}|\\psi_+\\rangle = \\mathrm{Tr}(\\mathcal{U}^\\dagger\\circ\\xi)/d^2.\n",
|
|
"$$\n",
|
|
"\n",
|
|
"where $\\rho_{\\mathcal{U}^\\dagger\\circ\\xi}$ is the density operator obtained from the channel $\\mathcal{U}^\\dagger\\circ\\xi$ via the Choi-Jamoiłkawski isomorphism\n",
|
|
"\n",
|
|
"$$\n",
|
|
"\\rho_{\\mathcal{U}^\\dagger\\circ\\xi} = \\bigl(I\\otimes(\\mathcal{U}^\\dagger\\circ\\xi)\\bigr)(|\\psi_+\\rangle\\langle\\psi_+|)\n",
|
|
"$$\n",
|
|
"\n",
|
|
"and where $|\\psi_+\\rangle$ is the maximally entangle state\n",
|
|
"\n",
|
|
"$$\n",
|
|
"|\\psi_+\\rangle = \\frac{1}{\\sqrt{d}}\\sum_{i=0}^{d-1}|i\\rangle \\otimes |i\\rangle.\n",
|
|
"$$\n",
|
|
"In our specific situation, where $\\mathcal{U}$ is a unitary channel, the entanglement fidelity of $\\mathcal{U}^\\dagger\\circ\\xi$ can be written in terms of the *process fidelity* of the two Choi states $\\rho_\\mathcal{U}$ and $\\rho_{\\xi}$ as follows:\n",
|
|
"$$\n",
|
|
"\\mathcal{F}_{ent}(\\mathcal{U}^\\dagger\\circ\\xi) = \\mathcal{F}_{proc}(\\rho_\\mathcal{U}, \\rho_{\\xi}) := \\mathcal{F}(\\rho_\\mathcal{U}, \\rho_{\\xi})\n",
|
|
"$$\n",
|
|
"and so we see via Proposition 1 of Horodecki et al. [3] that\n",
|
|
"$$\n",
|
|
"\\mathcal{F}_{avg}(\\mathcal{U},\\xi) = \\mathcal{F}_{avg}(\\mathcal{U}^\\dagger\\circ\\xi) = \\frac{d\\mathcal{F}_{ent}(\\mathcal{U}^\\dagger\\circ\\xi) + 1}{d+1} = \\frac{d\\mathcal{F}(\\rho_\\mathcal{U}, \\rho_{\\xi}) +1}{d+1}\n",
|
|
"$$\n",
|
|
"Calculating the process fidelity between two states can now be achieved via Monte Carlo state certification.\n",
|
|
"\n",
|
|
"As per [4] a direct implementation of the quantum Monte Carlo state certification would\n",
|
|
"prepare a maximally entangled state $|\\psi_+\\rangle$, apply $\\xi$ to half of\n",
|
|
"the system, and then measure random Pauli operators on all\n",
|
|
"qubits. A more practical approach consists of preparing the\n",
|
|
"complex conjugate of random product of eigenstates of local\n",
|
|
"Pauli operators (corresponding to the resulting state after half\n",
|
|
"of the entangled state is measured destructively), applying the\n",
|
|
"transformation $\\xi$ to the system, and finally measuring a random Pauli operator on each qubit. This can be seen from the following equality:\n",
|
|
"\n",
|
|
"$$\n",
|
|
"\\mathrm{Tr}\\bigl[(P_i\\otimes P_j\\otimes P_k\\otimes P_l)(I\\otimes\\xi)(|\\psi_+\\rangle\\langle\\psi_+|)\\bigr]\n",
|
|
"= \\frac{1}{d}\\mathrm{Tr}\\bigl[(P_k\\otimes P_l)\\cdot \\xi(P_i^*\\otimes P_j^*)\\bigl]\n",
|
|
"$$\n",
|
|
"\n",
|
|
"The following three experiments use the modified and simplified version of Monte Carlo state certification combined with the relations derived above to calculate the average gate fidelity of the channel $\\xi$. For more details see [1] and associated references."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "f3809ec3-742c-4388-b0c1-7fec552c248b",
|
|
"metadata": {},
|
|
"source": [
|
|
"## References\n",
|
|
"\n",
|
|
"[1] Efficient Long-Range Entanglement using Dynamic Circuits, by\n",
|
|
"*Elisa Bäumer, Vinay Tripathi, Derek S. Wang, Patrick Rall, Edward H. Chen, Swarnadeep Majumder, Alireza Seif, Zlatko K. Minev*. IBM Quantum, (2023).\n",
|
|
"https://arxiv.org/abs/2308.13065\n",
|
|
"\n",
|
|
"[2] Quantum Computation and Quantum Information, by *Nielsen and Chuang*, Section 9.2.2, (2010)\n",
|
|
"\n",
|
|
"[3] General teleportation channel, singlet fraction, and quasidistillation, by *M. Horodecki, P. Horodecki, and R. Horodecki*, Phys. Rev. A 60, 1888 (1999).\n",
|
|
"\n",
|
|
"[4] Practical characterization of quantum devices without tomography, by *M. P. da Silva, O. Landon-Cardinal, and D. Poulin*, Phys. Rev. Lett. 107, 210404 (2011)."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "bf40bed3",
|
|
"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_5nJZASV7wzDVLF4)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "ae395952",
|
|
"metadata": {},
|
|
"source": [
|
|
"© IBM Corp. 2024"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"description": "This tutorial shows three different ways that can be used to generate long-range entanglement between qubits on a line, at varying distances between each other.",
|
|
"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": "Long-range entanglement with dynamic circuits"
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|