Penanda aras litar dinamik dengan pasangan Bell yang dipotong
Anggaran penggunaan: 22 saat pada pemproses Heron r2 (NOTA: Ini adalah anggaran sahaja. Masa jalan sebenar anda mungkin berbeza.)
Latar belakang​
Perkakasan kuantum biasanya terhad kepada interaksi tempatan, tapi banyak algoritma memerlukan pembelitan Qubit yang jauh atau bahkan Qubit pada pemproses berasingan. Litar dinamik — iaitu litar dengan pengukuran pertengahan litar dan suap balik ke depan — menyediakan cara untuk mengatasi batasan ini dengan menggunakan komunikasi klasik masa nyata untuk melaksanakan operasi kuantum bukan setempat secara berkesan. Dalam pendekatan ini, hasil pengukuran dari satu bahagian litar (atau satu QPU) boleh mencetuskan Gate secara bersyarat pada bahagian lain, membolehkan kita mengangkut pembelitan merentasi jarak jauh. Ini membentuk asas skim operasi setempat dan komunikasi klasik (LOCC), di mana kita menggunakan keadaan sumber yang terikat (pasangan Bell) dan berkomunikasi hasil pengukuran secara klasik untuk menghubungkan Qubit yang jauh.
Salah satu kegunaan LOCC yang menjanjikan ialah untuk merealisasikan Gate CNOT jarak jauh maya melalui teleportasi, seperti yang ditunjukkan dalam tutorial pembelitan jarak jauh. Daripada CNOT jarak jauh terus (yang mungkin tidak dibenarkan oleh sambungan perkakasan), kita cipta pasangan Bell dan lakukan pelaksanaan Gate berasaskan teleportasi. Walau bagaimanapun, ketepatan operasi sedemikian bergantung pada ciri-ciri perkakasan. Penyahaskoheranan Qubit semasa kelewatan yang diperlukan (semasa menunggu hasil pengukuran) dan kependaman komunikasi klasik boleh merendahkan keadaan yang terikat. Selain itu, ralat pada pengukuran pertengahan litar lebih sukar untuk diperbetulkan berbanding ralat pada pengukuran akhir kerana ia merambat ke seluruh litar melalui Gate bersyarat.
Dalam eksperimen rujukan, para pengarang memperkenalkan penanda aras ketepatan pasangan Bell untuk mengenal pasti bahagian mana dalam peranti yang paling sesuai untuk pembelitan berasaskan LOCC. Ideanya ialah untuk menjalankan litar dinamik kecil pada setiap kumpulan empat Qubit yang bersambung dalam pemproses. Litar empat-Qubit ini pertama sekali mencipta pasangan Bell pada dua Qubit tengah, kemudian menggunakannya sebagai sumber untuk membelit dua Qubit tepi menggunakan LOCC. Secara konkrit, Qubit 1 dan 2 disediakan ke dalam pasangan Bell tidak dipotong secara setempat (menggunakan Hadamard dan CNOT), dan kemudian rutin teleportasi menggunakan pasangan Bell itu untuk membelit Qubit 0 dan 3. Qubit 1 dan 2 diukur semasa pelaksanaan litar, dan berdasarkan hasil tersebut, pembetulan Pauli (X pada Qubit 3 dan Z pada Qubit 0) dikenakan. Qubit 0 dan 3 kemudiannya ditinggalkan dalam keadaan Bell di penghujung litar.
Untuk mengukur kualiti pasangan terikat akhir ini, kita ukur penstabilnya: khususnya, pariti dalam asas () dan dalam asas (). Untuk pasangan Bell yang sempurna, kedua-dua jangkaan ini bersamaan +1. Dalam amalan, hingar perkakasan akan mengurangkan nilai-nilai ini. Oleh itu, kita ulang litar dua kali untuk setiap pasangan Qubit: satu litar mengukur Qubit 0 dan 3 dalam asas , dan satu lagi mengukurnya dalam asas . Daripada hasilnya, kita mendapat anggaran dan untuk pasangan Qubit tersebut. Kita gunakan min kuasa dua ralat (MSE) penstabil ini berbanding nilai ideal (1) sebagai metrik mudah untuk ketepatan pembelitan. MSE yang lebih rendah bermakna dua Qubit mencapai keadaan Bell yang lebih hampir kepada ideal (ketepatan lebih tinggi), manakala MSE yang lebih tinggi menunjukkan lebih banyak ralat. Dengan mengimbas eksperimen ini merentasi peranti, kita boleh menanda aras keupayaan pengukuran-dan-suap-balik kumpulan Qubit yang berbeza dan mengenal pasti pasangan Qubit terbaik untuk operasi LOCC.
Tutorial ini menunjukkan eksperimen pada peranti IBM Quantum® untuk menggambarkan cara litar dinamik boleh digunakan untuk menjana dan menilai pembelitan antara Qubit yang jauh. Kita akan memetakan semua rantai empat-Qubit linear pada peranti, menjalankan litar teleportasi pada setiap satunya, kemudian menggambarkan taburan nilai MSE. Prosedur hujung-ke-hujung ini menunjukkan cara memanfaatkan Qiskit Runtime dan ciri litar dinamik untuk memaklumkan pilihan yang sedar perkakasan bagi memotong litar atau mengedarkan algoritma kuantum merentasi sistem modular.
Keperluan​
Sebelum memulakan tutorial ini, pastikan anda telah memasang yang berikut:
- Qiskit SDK v2.0 atau lebih baharu, dengan sokongan visualisasi
- Qiskit Runtime v0.40 atau lebih baharu (
pip install qiskit-ibm-runtime)
Persediaan​
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-ibm-runtime
from qiskit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
from qiskit.transpiler import generate_preset_pass_manager
import numpy as np
import matplotlib.pyplot as plt
def create_bell_stab(initial_layouts):
"""
Create a circuit for a 1D chain of qubits (number of qubits must be a multiple of 4),
where a middle Bell pair is consumed to create a Bell at the edge.
Takes as input a list of lists, where each element of the list is a
1D chain of physical qubits that is used as the initial_layout for the transpiled circuit.
Returns a list of length-2 tuples, each tuple contains a circuit to measure the ZZ stabilizer and
a circuit to measure the XX stabilizer of the edge Bell state.
"""
bell_circuits = []
for (
initial_layout
) in initial_layouts: # Iterate over chains of physical qubits
assert (
len(initial_layout) % 4 == 0
), f"The length of the chain must be a multiple of 4, len(inital_layout)={len(initial_layout)}"
num_pairs = len(initial_layout) // 4
bell_parallel = QuantumCircuit(4 * num_pairs, 4 * num_pairs)
for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
(c0, c1) = pair_idx * 4, pair_idx * 4 + 3 # edge qubits
(ca0, ca1) = pair_idx * 4 + 1, pair_idx * 4 + 2 # middle qubits
bell_parallel.h(q0)
bell_parallel.h(q1)
bell_parallel.cx(q1, q2)
bell_parallel.cx(q0, q1)
bell_parallel.cx(q2, q3)
bell_parallel.h(q2)
# add barrier BEFORE measurements and add id in conditional
bell_parallel.barrier()
for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
(ca0, ca1) = pair_idx * 4 + 1, pair_idx * 4 + 2 # middle qubits
bell_parallel.measure(q1, ca0)
bell_parallel.measure(q2, ca1)
# bell_parallel.barrier() #remove barrier after measurement
for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
(ca0, ca1) = pair_idx * 4 + 1, pair_idx * 4 + 2 # middle qubits
with bell_parallel.if_test((ca0, 1)):
bell_parallel.x(q3)
with bell_parallel.if_test((ca1, 1)):
bell_parallel.z(q0)
bell_parallel.id(q0) # add id here for correct alignment
bell_zz = bell_parallel.copy()
bell_zz.barrier()
bell_xx = bell_parallel.copy()
bell_xx.barrier()
for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
bell_xx.h(q0)
bell_xx.h(q3)
bell_xx.barrier()
for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
(c0, c1) = pair_idx * 4, pair_idx * 4 + 3 # edge qubits
bell_zz.measure(q0, c0)
bell_zz.measure(q3, c1)
bell_xx.measure(q0, c0)
bell_xx.measure(q3, c1)
bell_circuits.append(bell_zz)
bell_circuits.append(bell_xx)
return bell_circuits
def get_mse(result, initial_layouts):
"""
given a result object and the initial layouts, returns a dict of layouts and their mse
"""
layout_mse = {}
for layout_idx, initial_layout in enumerate(initial_layouts):
layout_mse[tuple(initial_layout)] = {}
num_pairs = len(initial_layout) // 4
counts_zz = result[2 * layout_idx].data.c.get_counts()
total_shots = sum(counts_zz.values())
# Get ZZ expectation value
exp_zz_list = []
for pair_idx in range(num_pairs):
exp_zz = 0
for bitstr, shots in counts_zz.items():
bitstr = bitstr[::-1] # reverse order to big endian
b1, b0 = (
bitstr[pair_idx * 4],
bitstr[pair_idx * 4 + 3],
) # parse bitstring to get edge measurements for each 4-q chain
z_val0 = 1 if b0 == "0" else -1
z_val1 = 1 if b1 == "0" else -1
exp_zz += z_val0 * z_val1 * shots
exp_zz /= total_shots
exp_zz_list.append(exp_zz)
counts_xx = result[2 * layout_idx + 1].data.c.get_counts()
total_shots = sum(counts_xx.values())
# Get XX expectation value
exp_xx_list = []
for pair_idx in range(num_pairs):
exp_xx = 0
for bitstr, shots in counts_xx.items():
bitstr = bitstr[::-1] # reverse order to big endian
b1, b0 = (
bitstr[pair_idx * 4],
bitstr[pair_idx * 4 + 3],
) # parse bitstring to get edge measurements for each 4-q chain
x_val0 = 1 if b0 == "0" else -1
x_val1 = 1 if b1 == "0" else -1
exp_xx += x_val0 * x_val1 * shots
exp_xx /= total_shots
exp_xx_list.append(exp_xx)
mse_list = [
((exp_zz - 1) ** 2 + (exp_xx - 1) ** 2) / 2
for exp_zz, exp_xx in zip(exp_zz_list, exp_xx_list)
]
print(f"layout {initial_layout}")
for idx in range(num_pairs):
layout_mse[tuple(initial_layout)][
tuple(initial_layout[4 * idx : 4 * idx + 4])
] = mse_list[idx]
print(
f"qubits: {initial_layout[4*idx:4*idx+4]}, mse:, {round(mse_list[idx],4)}"
)
# print(f'exp_zz: {round(exp_zz_list[idx],4)}, exp_xx: {round(exp_xx_list[idx],4)}')
print(" ")
return layout_mse
def plot_mse_ecdfs(layouts_mse, combine_layouts=False):
"""
Plot CDF of MSE data for multiple layouts. Optionally combine all data in a single CDF
"""
if not combine_layouts:
for initial_layout, layouts in layouts_mse.items():
sorted_layouts = dict(
sorted(layouts.items(), key=lambda item: item[1])
) # sort layouts by mse
# get layouts and mses
layout_list = list(sorted_layouts.keys())
mse_list = np.asarray(list(sorted_layouts.values()))
# convert to numpy
x = np.array(mse_list)
y = np.arange(1, len(x) + 1) / len(x)
# Prepend (x[0], 0) to start CDF at zero
x = np.insert(x, 0, x[0])
y = np.insert(y, 0, 0)
# Create the plot
plt.plot(
x,
y,
marker="x",
linestyle="-",
label=f"qubits: {initial_layout}",
)
# add qubits labels for the edge pairs
for xi, yi, q in zip(x[1:], y[1:], layout_list):
plt.annotate(
[q[0], q[3]],
(xi, yi),
textcoords="offset points",
xytext=(5, -10),
ha="left",
fontsize=8,
)
elif combine_layouts:
all_layouts = {}
all_initial_layout = []
for (
initial_layout,
layouts,
) in layouts_mse.items(): # puts together all layout information
all_layouts.update(layouts)
all_initial_layout += initial_layout
sorted_layouts = dict(
sorted(all_layouts.items(), key=lambda item: item[1])
) # sort layouts by mse
# get layouts and mses
layout_list = list(sorted_layouts.keys())
mse_list = np.asarray(list(sorted_layouts.values()))
# convert to numpy
x = np.array(mse_list)
y = np.arange(1, len(x) + 1) / len(x)
# Prepend (x[0], 0) to start CDF at zero
x = np.insert(x, 0, x[0])
y = np.insert(y, 0, 0)
# Create the plot
plt.plot(
x,
y,
marker="x",
linestyle="-",
label=f"qubits: {sorted(list(set(all_initial_layout)))}",
)
# add qubit labels for the edge pairs
for xi, yi, q in zip(x[1:], y[1:], layout_list):
plt.annotate(
[q[0], q[3]],
(xi, yi),
textcoords="offset points",
xytext=(5, -10),
ha="left",
fontsize=8,
)
plt.xscale("log")
plt.xlabel("Mean squared error of ⟨ZZ⟩ and ⟨XX⟩")
plt.ylabel("Cumulative distribution function")
plt.title("CDF for different initial layouts")
plt.grid(alpha=0.3)
plt.show()
Langkah 1: Petakan input klasik kepada masalah kuantum​
Langkah pertama adalah mencipta satu set Circuit kuantum untuk menilai semua calon pautan Bell-pair yang disesuaikan dengan topologi peranti. Kita mencari secara aturcara peta gandingan peranti untuk semua rantai empat Qubit yang bersambung secara linear. Setiap rantai sedemikian (dilabel dengan indeks Qubit ) berfungsi sebagai kes ujian untuk Circuit pertukaran keterikatan. Dengan mengenal pasti semua laluan panjang-4 yang mungkin, kita memastikan liputan maksimum untuk pengelompokan Qubit yang boleh merealisasikan protokol ini.
service = QiskitRuntimeService()
backend = service.least_busy(operational=True)
Kita menjana rantai-rantai ini dengan menggunakan fungsi pembantu yang melakukan carian tamak pada graf peranti. Ia mengembalikan "jalur" empat rantai empat-Qubit yang dibundel kepada kumpulan 16-Qubit (Circuit dinamik pada masa ini mengehadkan saiz daftar pengukuran kepada 16 Qubit). Pembundelan membolehkan kita menjalankan pelbagai eksperimen empat-Qubit secara selari pada bahagian cip yang berbeza, dan menggunakan keseluruhan peranti dengan cekap. Setiap jalur 16-Qubit mengandungi empat rantai yang tidak bertindih, bermakna tiada Qubit yang digunakan semula dalam kumpulan tersebut. Sebagai contoh, satu jalur mungkin terdiri daripada rantai , , , dan semuanya dibungkus bersama. Sebarang Qubit yang tidak disertakan dalam jalur dikembalikan dalam pembolehubah leftover.
from itertools import chain
from collections import defaultdict
def stripes16_from_backend(backend):
"""
Creates stripes of 16 qubits, four non-overlapping four-qubit chains, that cover as much of
the coupling map as possible. Returns any unused qubits as leftovers.
"""
# get the undirected adjacency list
edges = backend.coupling_map.get_edges()
graph = defaultdict(set)
for u, v in edges:
graph[u].add(v)
graph[v].add(u)
qubits = sorted(graph) # all qubit indices that appear
# greedy search for 4-long linear chains (blocks) ────────────
used = set() # qubits already placed in a block
blocks = [] # each block is a four-qubit list
for q in qubits: # deterministic order for reproducibility
if q in used:
continue # already consumed by earlier block
# depth-first "straight" walk of length 3 without revisiting nodes
def extend(path):
if len(path) == 4:
return path
tip = path[-1]
for nbr in sorted(graph[tip]): # deterministic
if nbr not in path and nbr not in used:
maybe = extend(path + [nbr])
if maybe:
return maybe
return None
block = extend([q])
if block: # found a 4-node path
blocks.append(block)
used.update(block)
# bundle four four-qubit blocks into one 16-qubit stripe (max number of measurement compatible with if-else)
stripes = [
list(chain.from_iterable(blocks[i : i + 4]))
for i in range(0, len(blocks) // 4 * 4, 4) # full groups of four
]
leftovers = set(qubits) - set(chain.from_iterable(stripes))
return stripes, leftovers
initial_layouts, leftover = stripes16_from_backend(backend)
Seterusnya, kita membina Circuit untuk setiap jalur 16-Qubit. Rutin ini melakukan perkara berikut untuk setiap rantai:
- Sediakan pasangan Bell tengah: Gunakan Hadamard pada Qubit 1 dan CNOT dari Qubit 1 ke Qubit 2. Ini melilit Qubit 1 dan 2 (mewujudkan keadaan Bell