Entanglement jarak jauh dengan dynamic circuits
Estimasi penggunaan: 4 menit pada prosesor Heron r2. (CATATAN: Ini hanya perkiraan. Waktu aktual bisa berbeda.)
Latar Belakang​
Entanglement jarak jauh antara qubit-qubit yang berjauhan memang menantang pada perangkat dengan konektivitas terbatas. Tutorial ini menunjukkan bagaimana dynamic circuits bisa menghasilkan entanglement seperti itu dengan mengimplementasikan gate controlled-X jarak jauh (LRCX) menggunakan protokol berbasis pengukuran.
Mengikuti pendekatan Elisa Bäumer et al. di 1, metode ini menggunakan pengukuran mid-circuit dan feedforward untuk mencapai gate kedalaman konstan terlepas dari jarak antar qubit. Caranya dengan membuat Bell pair perantara, mengukur satu qubit dari setiap pasangan, dan menerapkan gate yang dikondisikan secara klasik untuk menyebarkan entanglement ke seluruh perangkat. Ini menghindari rantai SWAP yang panjang, sehingga mengurangi kedalaman sirkuit dan paparan terhadap error gate dua qubit.
Dalam notebook ini, kita mengadaptasi protokol tersebut untuk hardware IBM Quantum® dan memperluasnya untuk menjalankan beberapa operasi LRCX secara paralel, sehingga kita bisa mengeksplorasi bagaimana performa berskala dengan jumlah operasi kondisional yang simultan.
Persyaratan​
Sebelum memulai tutorial ini, pastikan kamu sudah menginstal:
- Qiskit SDK v2.0 atau lebih baru, dengan dukungan visualisasi
- Qiskit Runtime (
pip install qiskit-ibm-runtime) v0.37 atau lebih baru
Setup​
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-ibm-runtime
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.classical import expr
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.visualization import plot_circuit_layout
from qiskit_ibm_runtime import (
QiskitRuntimeService,
Batch,
SamplerV2 as Sampler,
)
import matplotlib.pyplot as plt
import numpy as np
Langkah 1: Memetakan input klasik ke masalah kuantum​
Kita akan mengimplementasikan gate CNOT jarak jauh antara dua qubit yang berjauhan, mengikuti konstruksi dynamic-circuit yang ditunjukkan di bawah (diadaptasi dari Gbr. 1a di Ref. 1). Ide utamanya adalah menggunakan "bus" qubit ancilla, yang diinisialisasi ke , untuk memediasi teleportasi gate jarak jauh.

Seperti yang diilustrasikan dalam gambar, prosesnya bekerja sebagai berikut:
- Siapkan rantai Bell pair yang menghubungkan qubit kontrol dan target melalui ancilla perantara.
- Lakukan pengukuran Bell antara qubit tetangga yang tidak saling terjerat, menukar entanglement langkah demi langkah hingga kontrol dan target berbagi sebuah Bell pair.
- Gunakan Bell pair ini untuk teleportasi gate, mengubah CNOT lokal menjadi CNOT jarak jauh yang deterministik dengan kedalaman konstan.
Pendekatan ini menggantikan rantai SWAP yang panjang dengan protokol kedalaman konstan, mengurangi paparan terhadap error gate dua qubit dan membuat operasi ini berskala dengan ukuran perangkat.
Selanjutnya, kita akan menelusuri implementasi dynamic-circuit dari sirkuit LRCX. Di bagian akhir, kita juga akan menyediakan implementasi berbasis unitary untuk perbandingan, guna menyoroti keunggulan dynamic circuits dalam konteks ini.
(i) Inisialisasi sirkuit​
Kita mulai dengan masalah kuantum sederhana yang akan menjadi dasar perbandingan. Secara spesifik, kita menginisialisasi sirkuit dengan qubit kontrol di indeks 0 dan menerapkan gate Hadamard padanya. Ini menghasilkan keadaan superposisi yang, jika diikuti oleh operasi controlled-X, menghasilkan Bell state antara qubit kontrol dan target.
Pada tahap ini, kita belum membangun controlled-X jarak jauh (LRCX) itu sendiri. Sebaliknya, tujuan kita adalah mendefinisikan sirkuit awal yang jelas dan minimal yang menyoroti peran LRCX. Pada Langkah 2, kita akan menunjukkan bagaimana LRCX dapat diimplementasikan sebagai optimasi menggunakan dynamic circuits, dan membandingkan performanya dengan ekuivalen unitarynya. Yang penting, protokol LRCX dapat diterapkan pada sirkuit awal apa pun. Di sini kita menggunakan setup Hadamard sederhana ini untuk kejelasan demonstrasi.
distance = 6 # The distance of the CNOT gate, with the convention that a distance of zero is a nearest-neighbor CNOT.
def initialize_circuit(distance):
assert distance >= 0
control = 0 # control qubit
n = distance # number of qubits between target and control
qr = QuantumRegister(
n + 2, name="q"
) # Circuit with n qubits between control and target
cr = ClassicalRegister(
2, name="cr"
) # Classical register for measuring control and target qubits
k = int(n / 2) # Number of Bell States to be used
allcr = [cr]
if (
distance > 1
): # This classical register will be used to store ZZ measurements. It is only used for long-range CX gates with distance > 1
c1 = ClassicalRegister(
k, name="c1"
) # Classical register needed for post processing
allcr.append(c1)
if (
distance > 0
): # This classical register will be used to store XX measurements. It is only used if distance > 0
c2 = ClassicalRegister(
n - k, name="c2"
) # Classical register needed for post processing
allcr.append(c2)
qc = QuantumCircuit(qr, *allcr, name="CNOT")
# Apply a Hadamard gate to the control qubit such that the long-range CNOT gate will prepare a Bell state (|00> + |11>)/sqrt(2)
qc.h(control)
return qc
qc = initialize_circuit(distance)
qc.draw(fold=-1, output="mpl", scale=0.5)
Langkah 2: Mengoptimalkan masalah untuk eksekusi hardware kuantum​
Pada langkah ini, kita menunjukkan cara membangun sirkuit LRCX menggunakan dynamic circuits. Tujuannya adalah mengoptimalkan sirkuit untuk eksekusi pada hardware dengan mengurangi kedalaman dibandingkan implementasi unitary murni. Untuk mengilustrasikan manfaatnya, kita akan menampilkan baik konstruksi LRCX dinamis maupun ekuivalen unitarynya, lalu membandingkan performanya setelah transpilasi. Yang penting, meskipun di sini kita menerapkan LRCX pada masalah yang diinisialisasi Hadamard sederhana, protokol ini bisa diterapkan pada sirkuit mana pun yang membutuhkan CNOT jarak jauh.
(ii) Menyiapkan Bell pair​
Kita mulai dengan membuat rantai Bell pair di sepanjang jalur antara qubit kontrol dan target. Jika jaraknya ganjil, kita pertama-tama menerapkan CNOT dari kontrol ke tetangganya, yang merupakan CNOT yang akan diteleportasikan. Untuk jarak genap, CNOT ini akan diterapkan setelah langkah persiapan Bell pair. Rantai Bell pair kemudian menjerat pasangan qubit yang berurutan, membangun sumber daya yang diperlukan untuk membawa informasi kontrol di seluruh perangkat.
# Determine where to start the Bell pair chain and add an extra CNOT when n is odd
def check_even(n: int) -> int:
"""Return 1 if n is even, else 2."""
return 1 if n % 2 == 0 else 2
def prepare_bell_pairs(qc, add_barriers=True):
n = qc.num_qubits - 2 # number of qubits between target and control
k = int(n / 2)
if add_barriers:
qc.barrier()
x0 = check_even(n)
if n % 2 != 0:
qc.cx(0, 1)
# Create k Bell pairs
for i in range(k):
qc.h(x0 + 2 * i)
qc.cx(x0 + 2 * i, x0 + 2 * i + 1)
return qc
qc = prepare_bell_pairs(qc)
qc.draw(output="mpl", fold=-1, scale=0.5)
(iii) Mengukur pasangan qubit tetangga dalam basis Bell​
Selanjutnya, kita mengukur qubit tetangga yang tidak saling terjerat dalam basis Bell (pengukuran dua qubit dari dan ). Ini menciptakan Bell pair jarak jauh antara qubit target dan qubit yang berdekatan dengan kontrol (dengan koreksi Pauli, yang akan diimplementasikan melalui feedforward pada langkah berikutnya). Secara paralel, kita mengimplementasikan pengukuran entangling yang menteleportasikan gate CNOT untuk bekerja pada qubit target yang dimaksud.
def measure_bell_basis(qc, add_barriers=True):
n = qc.num_qubits - 2 # number of qubits between target and control
k = int(n / 2)
if n > 1:
_, c1, c2 = qc.cregs
elif n > 0:
_, c2 = qc.cregs
# Determine where to start the Bell pair chain and add an extra CNOT when n is odd
x0 = 1 if n % 2 == 0 else 2
# Entangling layer that implements the Bell measurement (and additionally adds the CNOT to be teleported, if n is even)
for i in range(k + 1):
qc.cx(x0 - 1 + 2 * i, x0 + 2 * i)
for i in range(1, k + x0):
if i == 1:
qc.h(2 * i + 1 - x0)
else:
qc.h(2 * i + 1 - x0)
if add_barriers:
qc.barrier()
# Map the ZZ measurements onto classical register c1
for i in range(k):
if i == 0:
qc.measure(2 * i + x0, c1[i])
else:
qc.measure(2 * i + x0, c1[i])
# Map the XX measurements onto classical register c2
for i in range(1, k + x0):
if i == 1:
qc.measure(2 * i + 1 - x0, c2[i - 1])
else:
qc.measure(2 * i + 1 - x0, c2[i - 1])
return qc
qc = measure_bell_basis(qc)
qc.draw(output="mpl", fold=-1, scale=0.5)
(iv) Selanjutnya, terapkan koreksi feedforward untuk memperbaiki operator byproduct Pauli​
Pengukuran basis Bell memperkenalkan byproduct Pauli yang harus dikoreksi menggunakan hasil pengukuran yang tercatat. Ini dilakukan dalam dua langkah. Pertama, kita perlu menghitung paritas dari semua pengukuran , yang kemudian digunakan untuk menerapkan gate secara kondisional pada qubit target. Demikian pula, paritas pengukuran dihitung dan digunakan untuk menerapkan gate secara kondisional pada qubit kontrol.
Dengan framework ekspresi klasik baru di Qiskit, paritas ini dapat dihitung langsung di lapisan pemrosesan klasik sirkuit. Alih-alih menerapkan serangkaian gate kondisional individual untuk setiap bit pengukuran, kita dapat membangun ekspresi klasik tunggal yang merepresentasikan XOR (paritas) dari semua hasil pengukuran yang relevan. Ekspresi ini kemudian digunakan sebagai kondisi dalam satu blok if_test, memungkinkan gate koreksi diterapkan dengan kedalaman konstan. Pendekatan ini sekaligus menyederhanakan sirkuit dan memastikan bahwa koreksi feedforward tidak menambah latensi yang tidak perlu.
def apply_ffwd_corrections(qc):
control = 0 # control qubit
target = qc.num_qubits - 1 # target qubit
n = qc.num_qubits - 2 # number of qubits between target and control
k = int(n / 2)
x0 = check_even(n)
if n > 1:
_, c1, c2 = qc.cregs
elif n > 0:
_, c2 = qc.cregs
# First, let's compute the parity of all ZZ measurements
for i in range(k):
if i == 0:
parity_ZZ = expr.lift(
c1[i]
) # Store the value of the first ZZ measurement in parity_ZZ
else:
parity_ZZ = expr.bit_xor(
c1[i], parity_ZZ
) # Successively compute the parity via XOR operations
for i in range(1, k + x0):
if i == 1:
parity_XX = expr.lift(
c2[i - 1]
) # Store the value of the first XX measurement in parity_XX
else:
parity_XX = expr.bit_xor(
c2[i - 1], parity_XX
) # Successively compute the parity via XOR operations
if n > 0:
with qc.if_test(parity_XX):
qc.z(control)
if n > 1:
with qc.if_test(parity_ZZ):
qc.x(target)
return qc
qc = apply_ffwd_corrections(qc)
qc.draw(output="mpl", fold=-1, scale=0.5)
(v) Terakhir, ukur qubit kontrol dan target​
Kita mendefinisikan fungsi pembantu yang memungkinkan pengukuran qubit kontrol dan target dalam basis , , atau . Untuk memverifikasi Bell state , nilai ekspektasi dan keduanya harus , karena keduanya adalah stabilizer dari keadaan tersebut. Pengukuran juga didukung di sini dan akan digunakan di bawah saat menghitung fidelitas.
def measure_in_basis(qc, basis="XX", add_barrier=True):
control = 0 # control qubit
target = qc.num_qubits - 1 # target qubit
assert basis in ["XX", "YY", "ZZ"]
qc = (
qc.copy()
) # We copy the circuit because we want to measure in different bases
cr = qc.cregs[0]
if add_barrier:
qc.barrier()
if basis == "XX":
qc.h(control)
qc.h(target)
elif basis == "YY":
qc.sdg(control)
qc.sdg(target)
qc.h(control)
qc.h(target)
qc.measure(control, cr[0])
qc.measure(target, cr[1])
return qc
qc_YY = measure_in_basis(qc.copy(), basis="YY")
display(
qc_YY.draw(output="mpl", fold=-1, scale=0.5)
) # Circuit for measuring in the YY basis
Satukan semuanya​
Kita menggabungkan berbagai langkah yang didefinisikan di atas untuk membuat gate CX jarak jauh di dua ujung sebuah garis 1D. Langkah-langkahnya meliputi:
- Menginisialisasi qubit kontrol dalam
- Menyiapkan Bell pair
- Mengukur pasangan qubit tetangga
- Menerapkan koreksi feedforward yang bergantung pada MCM
def lrcx(distance, prep_barrier=True, pre_measure_barrier=True):
qc = initialize_circuit(distance)
qc = prepare_bell_pairs(qc, prep_barrier)
qc = measure_bell_basis(qc, pre_measure_barrier)
qc = apply_ffwd_corrections(qc)
return qc
qc = lrcx(distance)
# Apply the measurement in the XX, YY, and ZZ bases
qc_XX, qc_YY, qc_ZZ = [
measure_in_basis(qc, basis=basis) for basis in ["XX", "YY", "ZZ"]
]
display(
qc_YY.draw(output="mpl", fold=-1, scale=0.5)
) # Circuit for measuring in the YY basis
Membuat sirkuit untuk berbagai jarak​
Sekarang kita membuat sirkuit CX jarak jauh untuk berbagai pemisahan qubit. Untuk setiap jarak, kita membangun sirkuit yang mengukur dalam basis , , dan , yang nantinya akan digunakan untuk menghitung fidelitas.
Daftar jarak mencakup pemisahan pendek dan panjang, dengan distance = 0 yang sesuai dengan CX tetangga terdekat. Jarak yang sama ini juga akan digunakan untuk menghasilkan sirkuit unitary yang sesuai nanti untuk perbandingan.
distances = [
0,
1,
2,
3,
6,
11,
16,
21,
28,
35,
44,
55,
60,
] # Distances for long range CX. distance of 0 is a nearest-neighbor CX
distances.sort()
assert (
min(distances) >= 0
) # Only works for distance larger than 2 because classical register cannot be empty
basis_list = ["XX", "YY", "ZZ"]
circuits_dyn = []
for distance in distances:
for basis in basis_list:
circuits_dyn.append(
measure_in_basis(lrcx(distance, prep_barrier=False), basis=basis)
)
print(f"Number of circuits: {len(circuits_dyn)}")
circuits_dyn[14].draw(fold=-1, output="mpl", idle_wires=False)
Number of circuits: 39
Implementasi berbasis unitary dengan menukar qubit ke tengah​
Sebagai perbandingan, kita pertama-tama memeriksa kasus di mana gate CNOT jarak jauh diimplementasikan menggunakan koneksi tetangga terdekat dan gate unitary. Pada gambar berikut, di sebelah kiri adalah sirkuit untuk gate CNOT jarak jauh yang mencakup rantai 1D n-qubit dengan koneksi tetangga terdekat saja. Di tengah adalah dekomposisi unitary yang setara yang dapat diimplementasikan dengan gate CNOT lokal, kedalaman sirkuit .

Sirkuit di tengah dapat diimplementasikan sebagai berikut:
def cnot_unitary(distance):
"""Generate a long range CNOT gate using local CNOTs on a 1D chain of qubits subject to n
nearest-neighbor connections only.
Args:
distance (int) : The distance of the CNOT gate, with the convention that a distance of 0 is a nearest-neighbor CNOT.
Returns:
QuantumCircuit: A Quantum Circuit implementing a long-range CNOT gate between qubit 0 and qubit distance+1
"""
assert distance >= 0
n = distance # number of qubits between target and control
qr = QuantumRegister(
n + 2, name="q"
) # Circuit with n qubits between control and target
cr = ClassicalRegister(
2, name="cr"
) # Classical register for measuring control and target qubits
qc = QuantumCircuit(qr, cr, name="CNOT_unitary")
control_qubit = 0
qc.h(control_qubit) # Prepare the control qubit in the |+> state
k = int(n / 2)
qc.barrier()
for i in range(control_qubit, control_qubit + k):
qc.cx(i, i + 1)
qc.cx(i + 1, i)
qc.cx(-i - 1, -i - 2)
qc.cx(-i - 2, -i - 1)
if n % 2 == 1:
qc.cx(k + 2, k + 1)
qc.cx(k + 1, k + 2)
qc.barrier()
qc.cx(k, k + 1)
for i in range(control_qubit, control_qubit + k):
qc.cx(k - i, k - 1 - i)
qc.cx(k - 1 - i, k - i)
qc.cx(k + i + 1, k + i + 2)
qc.cx(k + i + 2, k + i + 1)
if n % 2 == 1:
qc.cx(-2, -1)
qc.cx(-1, -2)
return qc
Sekarang buat semua sirkuit unitary, dan bangun sirkuit yang mengukur dalam basis , , dan , sama seperti yang kita lakukan untuk sirkuit dinamis di atas.
circuits_uni = []
for distance in distances:
for basis in basis_list:
circuits_uni.append(
measure_in_basis(cnot_unitary(distance), basis=basis)
)
print(f"Number of circuits: {len(circuits_uni)}")
circuits_uni[14].draw(fold=-1, output="mpl", idle_wires=False)
Number of circuits: 39
Setelah kita memiliki sirkuit dinamis dan unitary untuk berbagai jarak, kita siap untuk transpilasi. Pertama, kita perlu memilih perangkat Backend.
# Set up access to IBM Quantum devices
from qiskit.circuit import IfElseOp
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=156
)
Langkah berikut memastikan bahwa Backend mendukung instruksi if_else, yang diperlukan untuk versi terbaru dynamic circuits. Karena fitur ini masih dalam akses awal, kita secara eksplisit menambahkan IfElseOp ke target Backend jika belum tersedia.
if "if_else" not in backend.target.operation_names:
backend.target.add_instruction(IfElseOp, name="if_else")
Gunakan string Layer Fidelity untuk memilih rantai 1D​
Karena kita ingin membandingkan performa sirkuit dinamis dan unitary pada rantai 1D, kita menggunakan string Layer Fidelity untuk memilih topologi linear dari rantai qubit terbaik dari perangkat. Ini memastikan bahwa kedua jenis sirkuit ditranspilasi dengan batasan konektivitas yang sama, memungkinkan perbandingan performa yang adil.
# This selects best qubits for longest distance and uses the same control for all lengths
lf_qubits = backend.properties().to_dict()[
"general_qlists"
] # best linear chain qubits
chosen_layouts = {
distance: [
val["qubits"]
for val in lf_qubits
if val["name"] == f"lf_{distances[-1] + 2}"
][0][: distance + 2]
for distance in distances
}
print(chosen_layouts[max(distances)]) # best qubits at each distance
[10, 11, 12, 13, 14, 15, 19, 35, 34, 33, 39, 53, 54, 55, 59, 75, 74, 73, 72, 71, 58, 51, 50, 49, 48, 47, 46, 45, 44, 43, 56, 63, 62, 61, 76, 81, 82, 83, 84, 85, 77, 65, 66, 67, 68, 69, 78, 89, 90, 91, 98, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101]
isa_circuits_dyn = []
isa_circuits_uni = []
# Using the same initial layouts for both circuits for better apples to apples comparison
for qc in circuits_dyn:
pm = generate_preset_pass_manager(
optimization_level=1,
backend=backend,
initial_layout=chosen_layouts[qc.num_qubits - 2],
)
isa_circuits_dyn.append(pm.run(qc))
for qc in circuits_uni:
pm = generate_preset_pass_manager(
optimization_level=1,
backend=backend,
initial_layout=chosen_layouts[qc.num_qubits - 2],
)
isa_circuits_uni.append(pm.run(qc))
print(
f"2Q depth: {isa_circuits_dyn[14].depth(lambda x: x.operation.num_qubits == 2)}"
)
isa_circuits_dyn[14].draw("mpl", fold=-1, idle_wires=0)
2Q depth: 2

print(
f"2Q depth: {isa_circuits_uni[14].depth(lambda x: x.operation.num_qubits == 2)}"
)
isa_circuits_uni[14].draw("mpl", fold=-1, idle_wires=False)
2Q depth: 13

Visualisasikan qubit yang digunakan untuk sirkuit LRCX​
Di bagian ini, kita memeriksa bagaimana sirkuit LRCX dipetakan ke hardware. Kita mulai dengan memvisualisasikan qubit fisik yang digunakan dalam sirkuit, kemudian mempelajari bagaimana jarak kontrol-target dalam layout memengaruhi jumlah operasi.
# Note: the qubit coordinates must be hard-coded.
# The backend API does not currently provide this information directly.
# If using a different backend, you will need to adjust the coordinates accordingly,
# or set the qubit_coordinates = None to use the default layout coordinates.
def _heron_coords_r2():
"""Generate coordinates for the Heron layout in R2. Note"""
cord_map = np.array(
[
[
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
3,
7,
11,
15,
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
1,
5,
9,
13,
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
3,
7,
11,
15,
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
1,
5,
9,
13,
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
3,
7,
11,
15,
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
1,
5,
9,
13,
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
3,
7,
11,
15,
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
],
-1
* np.array([j for i in range(15) for j in [i] * [16, 4][i % 2]]),
],
dtype=int,
)
hcords = []
ycords = cord_map[0]
xcords = cord_map[1]
for i in range(156):
hcords.append([xcords[i] + 1, np.abs(ycords[i]) + 1])
return hcords
# Visualize the active qubits in the circuit layout
plot_circuit_layout(
circuit=isa_circuits_uni[-1],
backend=backend,
view="physical",
qubit_coordinates=_heron_coords_r2(),
)

Langkah 3: Eksekusi menggunakan Qiskit primitives​
Pada langkah ini, kita menjalankan eksperimen di backend yang sudah ditentukan. Kita juga memanfaatkan batching untuk menjalankan eksperimen secara efisien di berbagai percobaan. Menjalankan percobaan berulang memungkinkan kita menghitung rata-rata untuk perbandingan yang lebih akurat antara metode unitary dan dinamis, serta mengukur variasinya dengan membandingkan deviasi antar percobaan.
print(backend.name)
ibm_kingston
Pilih jumlah percobaan dan lakukan eksekusi secara batch.
num_trials = 10
jobs_uni = []
jobs_dyn = []
with Batch(backend=backend) as batch:
sampler = Sampler(mode=batch)
for _ in range(num_trials):
jobs_uni.append(sampler.run(isa_circuits_uni, shots=1024))
jobs_dyn.append(sampler.run(isa_circuits_dyn, shots=1024))
Langkah 4: Post-process dan kembalikan hasil dalam format klasik yang diinginkan​
Setelah eksperimen berhasil dijalankan, kita melakukan post-processing pada hasil pengukuran untuk mengekstrak metrik yang bermakna. Pada langkah ini, kita:
- Mendefinisikan metrik kualitas untuk mengevaluasi performa LRCX jarak jauh.
- Menghitung nilai ekspektasi operator Pauli dari hasil pengukuran mentah.
- Menggunakan nilai tersebut untuk menghitung fidelity dari Bell state yang dihasilkan.
Analisis ini memberikan gambaran yang jelas tentang seberapa baik Circuit dinamis bekerja dibandingkan implementasi baseline unitary.
Metrik kualitas​
Untuk mengevaluasi keberhasilan protokol LRCX jarak jauh, kita mengukur seberapa dekat state keluaran dengan Bell state ideal. Cara yang mudah untuk mengkuantifikasi ini adalah dengan menghitung fidelity state menggunakan nilai ekspektasi operator Pauli. Fidelity untuk Bell state pada state kontrol dan target dapat dihitung setelah mengetahui , , dan . Secara khusus,
Untuk menghitung nilai ekspektasi ini dari data pengukuran mentah, kita mendefinisikan sejumlah fungsi pembantu:
compute_ZZ_expectation: Diberikan hasil pengukuran, menghitung nilai ekspektasi operator Pauli dua-Qubit dalam basis .compute_fidelity: Menggabungkan nilai ekspektasi , , dan ke dalam ekspresi fidelity di atas.get_counts_from_bitarray: Utilitas untuk mengekstrak hitungan dari objek hasil backend.
def compute_ZZ_expectation(counts):
total = sum(counts.values())
expectation = 0
for bitstring, count in counts.items():
# Ensure bitstring is 2 bits
z1 = (-1) ** (int(bitstring[-1]))
z2 = (-1) ** (int(bitstring[-2]))
expectation += z1 * z2 * count
return expectation / total
def compute_fidelity(counts_xx, counts_yy, counts_zz):
xx, yy, zz = [
compute_ZZ_expectation(c) for c in [counts_xx, counts_yy, counts_zz]
]
return 1 / 4 * (1 + xx - yy + zz)
Kita menghitung fidelity untuk Circuit LRCX dinamis. Untuk setiap jarak, kita mengekstrak hasil pengukuran dalam basis , , dan . Hasil-hasil ini digabungkan menggunakan fungsi pembantu yang telah didefinisikan sebelumnya untuk menghitung fidelity sesuai dengan . Ini memberikan fidelity yang teramati dari protokol yang dieksekusi secara dinamis pada setiap jarak.
fidelities_dyn = []
# loop over trials
for job in jobs_dyn:
result_dyn = job.result()
trial_fidelities = []
# loop over all distances
for ind, dist in enumerate(distances):
counts_xx = result_dyn[ind * 3].data.cr.get_counts()
counts_yy = result_dyn[ind * 3 + 1].data.cr.get_counts()
counts_zz = result_dyn[ind * 3 + 2].data.cr.get_counts()
trial_fidelities.append(
compute_fidelity(counts_xx, counts_yy, counts_zz)
)
fidelities_dyn.append(trial_fidelities)
# average over trials for each distance
avg_fidelities_dyn = np.mean(fidelities_dyn, axis=0)
std_fidelities_dyn = np.std(fidelities_dyn, axis=0)
Sekarang kita menghitung fidelity untuk Circuit LRCX unitary, dengan cara yang sama seperti yang kita lakukan untuk Circuit dinamis di atas.
fidelities_uni = []
# loop over trials
for job in jobs_uni:
result_uni = job.result()
trial_fidelities = []
# loop over all distances
for ind, dist in enumerate(distances):
counts_xx = result_uni[ind * 3].data.cr.get_counts()
counts_yy = result_uni[ind * 3 + 1].data.cr.get_counts()
counts_zz = result_uni[ind * 3 + 2].data.cr.get_counts()
trial_fidelities.append(
compute_fidelity(counts_xx, counts_yy, counts_zz)
)
fidelities_uni.append(trial_fidelities)
# average over trials for each distance
avg_fidelities_uni = np.mean(fidelities_uni, axis=0)
std_fidelities_uni = np.std(fidelities_uni, axis=0)
Plot hasil​
Agar bisa mengapresiasi hasilnya secara visual, sel di bawah ini memplot estimasi fidelity Gate yang diukur pada berbagai jarak antara Qubit yang terjerat untuk setiap metode.
fig, ax = plt.subplots()
# Unitary with error bars
ax.errorbar(
distances,
avg_fidelities_uni,
yerr=std_fidelities_uni,
fmt="o-.",
color="c",
ecolor="c",
elinewidth=1,
capsize=4,
label="Unitary",
)
# Dynamic with error bars
ax.errorbar(
distances,
avg_fidelities_dyn,
yerr=std_fidelities_dyn,
fmt="o-.",
color="m",
ecolor="m",
elinewidth=1,
capsize=4,
label="Dynamic",
)
# Random gate baseline
ax.axhline(y=1 / 4, linestyle="--", color="gray", label="Random gate")
legend = ax.legend(frameon=True)
for text in legend.get_texts():
text.set_color("black")
legend.get_frame().set_facecolor("white")
legend.get_frame().set_edgecolor("black")
ax.set_title(
"Bell State Fidelity vs Control–Target Separation", color="black"
)
ax.set_xlabel("Distance", color="black")
ax.set_ylabel("Bell state fidelity", color="black")
ax.grid(linestyle=":", linewidth=0.6, alpha=0.4, color="gray")
ax.set_ylim((0.2, 1))
ax.set_facecolor("white")
fig.patch.set_facecolor("white")
for spine in ax.spines.values():
spine.set_visible(True)
spine.set_color("black")
ax.tick_params(axis="x", colors="black")
ax.tick_params(axis="y", colors="black")
plt.show()

Dari plot fidelity di atas, LRCX tidak secara konsisten mengungguli implementasi unitary langsung. Bahkan, untuk pemisahan kontrol–target yang pendek, Circuit unitary mencapai fidelity yang lebih tinggi. Namun, pada pemisahan yang lebih besar, Circuit dinamis mulai mencapai fidelity yang lebih baik daripada implementasi unitary. Perilaku ini tidak mengejutkan pada hardware saat ini: meski Circuit dinamis mengurangi kedalaman Circuit dengan menghindari rantai SWAP yang panjang, mereka memperkenalkan waktu Circuit tambahan dari pengukuran mid-circuit, classical feedforward, dan penundaan jalur kontrol. Latensi tambahan ini meningkatkan dekoherensi dan kesalahan readout, yang dapat mengalahkan penghematan kedalaman pada jarak pendek.
Meski begitu, kita mengamati titik silang di mana pendekatan dinamis melampaui pendekatan unitary. Ini adalah hasil langsung dari penskalaan yang berbeda: kedalaman Circuit unitary tumbuh secara linier dengan jarak antara Qubit, sementara kedalaman Circuit dinamis tetap konstan.
Poin-poin penting:
- Manfaat langsung Circuit dinamis: Motivasi utama saat ini adalah berkurangnya kedalaman dua-Qubit, tidak harus peningkatan fidelity.
- Mengapa fidelity bisa lebih buruk hari ini: Waktu Circuit yang meningkat dari pengukuran dan operasi klasik sering mendominasi, terutama ketika pemisahan kontrol–target kecil.
- Ke depannya: Seiring hardware membaik, khususnya readout yang lebih cepat, latensi kontrol klasik yang lebih pendek, dan overhead mid-circuit yang berkurang, kita seharusnya mengharapkan pengurangan kedalaman dan durasi ini diterjemahkan menjadi keuntungan fidelity yang terukur.
# Compute metrics for each distance, skipping the basis circuits since they are identical for each distance
depths_2q_dyn = [
c.depth(lambda x: x.operation.num_qubits == 2)
for c in isa_circuits_dyn[::3]
]
meas_dyn = [
sum(1 for instr in c.data if instr.operation.name == "measure")
for c in isa_circuits_dyn[::3]
]
depths_2q_uni = [
c.depth(lambda x: x.operation.num_qubits == 2)
for c in isa_circuits_uni[::3]
]
meas_uni = [
sum(1 for instr in c.data if instr.operation.name == "measure")
for c in isa_circuits_uni[::3]
]
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
axes[0].plot(
distances, depths_2q_uni, "o-.", color="c", label="Unitary (2Q depth)"
)
axes[0].plot(
distances, depths_2q_dyn, "o-.", color="m", label="Dynamic (2Q depth)"
)
axes[0].set_xlabel("Number of qubits between control and target")
axes[0].set_ylabel("Two-qubit depth")
axes[0].grid(True, linestyle=":", linewidth=0.6, alpha=0.4)
axes[0].legend()
axes[1].plot(
distances, meas_uni, "o-.", color="c", label="Unitary (# measurements)"
)
axes[1].plot(
distances, meas_dyn, "o-.", color="m", label="Dynamic (# measurements)"
)
axes[1].set_xlabel("Number of qubits between control and target")
axes[1].set_ylabel("Number of measurements")
axes[1].grid(True, linestyle=":", linewidth=0.6, alpha=0.4)
axes[1].legend()
fig.suptitle("Scaling of Unitary vs Dynamic LRCX with Distance", fontsize=12)
plt.tight_layout()
plt.show()

Plot kedalaman dua-Qubit ini menyoroti keunggulan utama LRCX yang diimplementasikan dengan Circuit dinamis: performanya tetap pada dasarnya konstan seiring pemisahan antara Qubit kontrol dan target meningkat. Sebaliknya, implementasi unitary tumbuh secara linier dengan jarak karena diperlukan rantai SWAP. Kedalaman menangkap penskalaan logis dari operasi dua-Qubit, sementara jumlah pengukuran mencerminkan overhead tambahan untuk Circuit dinamis. Pengukuran-pengukuran ini efisien karena dilakukan secara paralel, tetapi tetap memperkenalkan biaya tetap pada hardware saat ini.
Mengapa fidelity bisa lebih buruk hari ini: Waktu Circuit yang meningkat dari pengukuran dan operasi klasik sering mendominasi, terutama ketika pemisahan kontrol-target kecil. Misalnya, rata-rata panjang readout pada prosesor Heron r2 adalah 2.280 ns, sedangkan panjang Gate 2Q-nya hanya 68 ns.
Seiring pengukuran dan latensi klasik membaik, kita mengharapkan penskalaan kedalaman dan pengukuran yang konstan dari Circuit dinamis menghasilkan keuntungan fidelity dan runtime yang jelas pada Circuit yang lebih besar.
Referensi​
[1] Efficient Long-Range Entanglement using Dynamic Circuits, oleh Elisa Bäumer, Vinay Tripathi, Derek S. Wang, Patrick Rall, Edward H. Chen, Swarnadeep Majumder, Alireza Seif, Zlatko K. Minev. IBM Quantum, (2023). https://arxiv.org/abs/2308.13065