Lewati ke konten utama

Kode repetisi

Estimasi penggunaan: kurang dari 10 detik pada prosesor Heron (CATATAN: Ini hanya estimasi. Waktu eksekusi aktualmu mungkin berbeda.)

Hasil pembelajaran

  • Cara mengimplementasikan kode koreksi error bit-flip menggunakan circuit dinamis

  • Cara mengukur stabilizer untuk mendeteksi error kuantum tanpa merusak informasi yang dienkode

  • Cara mengevaluasi performa koreksi error kuantum dengan membandingkan hasil yang terkoreksi dan tidak terkoreksi

Prasyarat

Latar belakang

Untuk mengaktifkan koreksi kesalahan kuantum (QEC) secara real-time, kamu perlu bisa mengontrol alur program kuantum secara dinamis selama eksekusi agar Gate kuantum bisa dikondisikan berdasarkan hasil pengukuran. Tutorial ini menjalankan kode bit-flip, yang merupakan bentuk QEC yang sangat sederhana. Tutorial ini mendemonstrasikan dynamic Circuit kuantum yang bisa melindungi qubit yang telah diencode dari satu kesalahan bit-flip, lalu mengevaluasi performa kode bit-flip tersebut.

Kamu bisa memanfaatkan qubit ancilla tambahan dan entanglement untuk mengukur stabilizer yang tidak mentransformasi informasi kuantum yang diencode, namun tetap memberimu informasi tentang beberapa kelas kesalahan yang mungkin telah terjadi. Kode stabilizer kuantum mengenkode kk qubit logis ke dalam nn qubit fisik. Kode stabilizer secara kritis berfokus pada pengoreksian sekumpulan kesalahan diskrit dengan dukungan dari grup Pauli Πn\Pi^n.

Dalam tutorial ini, kita mendemonstrasikan kode bit-flip menggunakan eksperimen memori kuantum yang sederhana. Kita akan menyiapkan qubit yang dienkode dalam keadaan logis 1ˉ111|\bar{1}\rangle \equiv |111\rangle dan kemudian menjalankan beberapa siklus waktu idle (untuk mengakumulasi kesalahan) yang diikuti dengan deteksi dan koreksi kesalahan. Kita kemudian mengukur probabilitas kesalahan logis sebagai fungsi dari jumlah siklus (waktu), di mana probabilitas kesalahan logis adalah probabilitas menemukan qubit dalam keadaan yang tidak pulih ke 1ˉ|\bar{1}\rangle setelah pengukuran akhir (secara konkret, keadaan yang berhubungan dengan kesalahan adalah 000|000\rangle, 001|001\rangle, 010|010\rangle, dan 100|100\rangle).

Kita akan membandingkan laju kesalahan versus waktu dengan laju kesalahan qubit individu yang tidak dienkode, dan juga dengan penggunaan kode repetisi di mana kita hanya mendeteksi dan mengoreksi kesalahan setelah pengukuran akhir, tetapi tidak secara dinamis selama sirkuit berjalan.

Catatan: kode repetisi hanya memungkinkan koreksi kesalahan bit-flip dan karena itu bukan kode koreksi kesalahan yang lengkap. Namun, karena kesederhanaannya, ini adalah titik awal yang bagus untuk mengilustrasikan bagaimana menerapkan koreksi kesalahan pada komputer kuantum. Eksperimen memori di bawah ini hanya menguji satu jenis kesalahan (peluruhan 1ˉ|\bar{1}\rangle menjadi 0ˉ|\bar{0}\rangle) dan secara teknis hanya mendemonstrasikan perlindungan informasi klasik.

Persyaratan

Sebelum memulai tutorial ini, pastikan kamu telah menginstal hal-hal berikut:

  • Qiskit SDK v2.0 atau lebih baru, dengan dukungan visualisasi

  • Qiskit Runtime v0.40 atau lebih baru (pip install qiskit-ibm-runtime)

Pengaturan

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-ibm-runtime scipy seaborn
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

sns.set()
plt.rc("xtick", labelsize=20)
plt.rc("ytick", labelsize=20)
plt.rc("lines", linewidth=3)
plt.rc("font", size=20)
plt.rc("legend", fontsize="large")
plt.rc("axes", labelsize=20)
plt.rcParams["figure.figsize"] = 15, 6
plt.rcParams["legend.title_fontsize"] = 25
# Qiskit imports
from qiskit import (
QuantumCircuit,
QuantumRegister,
ClassicalRegister,
)

# qiskit-ibm-runtime
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
from qiskit_ibm_runtime.circuit import MidCircuitMeasure

service = QiskitRuntimeService()

Contoh simulator skala kecil

Kita akan melewatkan langkah ini karena tujuan eksperimen ini adalah untuk mengukur masa pakai qubit logis di bawah noise perangkat keras nyata, seperti amplitude damping selama delay idle; simulator tanpa noise tidak akan menunjukkan kesalahan apa pun yang bisa dideteksi dan dikoreksi.

Contoh perangkat keras skala besar

Langkah 1. Petakan input klasik ke masalah kuantum

Pilih backend

Untuk mendeteksi kesalahan selama sirkuit berjalan, kita perlu memilih backend yang memiliki akses ke metode MidCircuitMeasure (lihat dokumentasi).

# You can see all backends that support mid-circuit measurements by running the following code.
service.backends(filters=lambda b: "measure_2" in b.supported_instructions)
[<IBMBackend('ibm_pittsburgh')>,
<IBMBackend('ibm_boston')>,
<IBMBackend('ibm_kingston')>]
# Choose the least busy backend that supports mid-circuit measurements (`measure_2`).

backend = service.least_busy(
filters=lambda b: "measure_2" in b.supported_instructions,
operational=True,
simulator=False,
dynamic_circuits=True,
)
# backend = service.backend(backend_name) # alternatively, you could choose a specific backend
print(backend.name)
ibm_boston

Bangun urutan sirkuit stabilizer bit-flip yang mengimplementasikan beberapa putaran deteksi dan koreksi error

Kode bit-flip adalah salah satu contoh kode stabilizer yang paling sederhana. Kode ini melindungi state dari satu kesalahan bit-flip (X) pada salah satu qubit encoding. Pertimbangkan aksi kesalahan bit-flip XX, yang memetakan 01|0\rangle \rightarrow |1\rangle dan 10|1\rangle \rightarrow |0\rangle pada salah satu qubit kita, maka kita punya ϵ={E0,E1,E2}={IIX,IXI,XII}\epsilon = \{E_0, E_1, E_2 \} = \{IIX, IXI, XII\}. Kode ini memerlukan lima qubit: tiga digunakan untuk mengenkode state yang dilindungi ("data qubits"), dan dua sisanya digunakan sebagai ancilla pengukuran stabilizer.

Di bawah ini kamu akan membangun sirkuit yang (1) menyiapkan qubit data dalam keadaan logis 1ˉ|\bar{1} \rangle, kemudian (2) menjalankan beberapa siklus delay 5μs5 \mu s diikuti dengan koreksi kesalahan (termasuk reset qubit sindrom), dan (3) membaca keadaan qubit data.

Kita juga akan menguji masa pakai keadaan 1|1\rangle tanpa menggunakan koreksi kesalahan dengan menyertakan tiga qubit referensi yang kita siapkan dalam keadaan 1|1\rangle, biarkan idle, lalu dibaca.

def build_qc(
qreg_data,
qreg_syndrome,
creg_data,
creg_syndrome,
qreg_ref=None,
creg_ref=None,
):
"""Build a typical error correction circuit"""
if qreg_ref:
return QuantumCircuit(
qreg_data,
qreg_syndrome,
creg_data,
creg_syndrome,
qreg_ref,
creg_ref,
)
else:
return QuantumCircuit(
qreg_data, qreg_syndrome, creg_data, creg_syndrome
)

def encode_bit_flip(circuit, qreg_data, qreg_ref=None) -> QuantumCircuit:
"""Encode bit-flip. This is done by simply adding a cx"""

for q in qreg_data:
circuit.x(q)

if qreg_ref:
for q in qreg_ref:
circuit.x(q)

circuit.barrier()

return circuit

def measure_syndrome_bit(
circuit, qreg_data, qreg_syndrome, creg_syndrome, qreg_ref=None
):
"""
Measure the syndrome by measuring the parity.
We reset our ancilla qubits after measuring the stabilizer
so we can reuse them for repeated stabilizer measurements.
Because we have already observed the state of the qubit,
we can write the conditional reset protocol directly to
avoid another round of qubit measurement if we used
the `reset` instruction.
"""
circuit.cx(qreg_data[0], qreg_syndrome[0])
circuit.cx(qreg_data[1], qreg_syndrome[0])
circuit.cx(qreg_data[0], qreg_syndrome[1])
circuit.cx(qreg_data[2], qreg_syndrome[1])
circuit.barrier()

for q_measure, c_measure in zip(qreg_syndrome, creg_syndrome):
circuit.append(MidCircuitMeasure(), [q_measure], [c_measure])

with circuit.if_test((creg_syndrome[0], 1)):
circuit.x(qreg_syndrome[0])
with circuit.if_test((creg_syndrome[1], 1)):
circuit.x(qreg_syndrome[1])

circuit.barrier()
return circuit

def apply_correction_bit(circuit, qreg_data, creg_syndrome):
"""We can detect where an error occurred and correct our state"""
with circuit.if_test((creg_syndrome, 3)):
circuit.x(qreg_data[0])
with circuit.if_test((creg_syndrome, 1)):
circuit.x(qreg_data[1])
with circuit.if_test((creg_syndrome, 2)):
circuit.x(qreg_data[2])
circuit.barrier()
return circuit

def apply_final_readout(
circuit, qreg_data, creg_data, qreg_ref=None, creg_ref=None
):
"""Read out the final measurements"""

circuit.barrier()
if qreg_ref:
circuit.measure(qreg_ref, creg_ref)
circuit.measure(qreg_data, creg_data)

return circuit
def build_error_correction_sequence(
num_cycles, cycles_per_circuit, nq_ref=3, delay=None
) -> QuantumCircuit:
circuits = []
reps = []

qreg_data = QuantumRegister(3, name="qdata")
qreg_syndrome = QuantumRegister(2, name="qsyndrome")
creg_data = ClassicalRegister(3, name="cdata")
creg_syndrome = ClassicalRegister(2, name="csyndrome")
qreg_ref = QuantumRegister(nq_ref, name="qreference")
creg_ref = ClassicalRegister(nq_ref, name="creference")

circuit = build_qc(
qreg_data,
qreg_syndrome,
creg_data,
creg_syndrome,
qreg_ref=qreg_ref,
creg_ref=creg_ref,
)
circuit = encode_bit_flip(circuit, qreg_data, qreg_ref=qreg_ref)

circuit_n = circuit.copy()
circuit_n = apply_final_readout(
circuit_n, qreg_data, creg_data, qreg_ref=qreg_ref, creg_ref=creg_ref
)
circuits.append(circuit_n)
reps.append(0)

for i in range(1, num_cycles + 1):
if delay:
circuit.delay(delay, unit="us")
circuit.barrier()
circuit = measure_syndrome_bit(
circuit,
qreg_data,
qreg_syndrome,
creg_syndrome,
qreg_ref=qreg_ref,
)
circuit = apply_correction_bit(circuit, qreg_data, creg_syndrome)
circuit_n = circuit.copy()
if i % cycles_per_circuit == 0:
circuit_n = apply_final_readout(
circuit_n,
qreg_data,
creg_data,
qreg_ref=qreg_ref,
creg_ref=creg_ref,
)
circuits.append(circuit_n)
reps.append(i)

return circuits, np.array(reps)

num_cycles = 40
cycles_per_circuit = 4
nq_ref = 3
circuits, rep_array = build_error_correction_sequence(
num_cycles, cycles_per_circuit, nq_ref=3, delay=5
)
circuits[1].draw(output="mpl", cregbundle=False, fold=50)

Output of the previous code cell

Langkah 2: Optimalkan masalah untuk eksekusi kuantum pada perangkat keras

Untuk mengurangi total waktu eksekusi job, primitif Qiskit hanya menerima Circuit dan observable yang sesuai dengan instruksi dan konektivitas yang didukung oleh sistem target (disebut sebagai ISA circuit dan observable). Pelajari lebih lanjut tentang transpilasi.

Buat ISA Circuit

Kita akan mulai dengan mencari layout awal (yaitu, pemilihan qubit fisik untuk memetakan sirkuit kita) dengan mentranspilasi sirkuit terpanjang kita menggunakan preset pass manager dengan optimization level 3.

from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
isa_circuit_ref = pm.run(circuits[-1])
init_layout = isa_circuit_ref.layout.initial_index_layout(
filter_ancillas=True
)
print(init_layout)
[56, 44, 62, 43, 63, 22, 48, 67]

Untuk qubit referensi yang akan kita bandingkan dengan memori kuantum logis kita, kita akan memilihnya sebagai qubit terbaik yang tersedia dari segi waktu koherensi amplitude damping (T1T_1).

# get all qubits ordered by T1
t1_data = []
for i in range(backend.num_qubits):
try:
t1_us = backend.properties().t1(i) * 1e6
except Exception:
t1_us = 0.0
t1_data.append((i, t1_us))

t1_data_sorted = sorted(t1_data, key=lambda x: x[1], reverse=True)

# exclude the qubits we have already mapped the error correcting code to
t1_data_sorted = [
t1_data for t1_data in t1_data_sorted if t1_data[0] not in init_layout[:5]
]

# use the best qubits in terms of T1 for the reference qubits
init_layout = init_layout[:5] + [t1_data[0] for t1_data in t1_data_sorted[:3]]
print(init_layout)
[56, 44, 62, 43, 63, 143, 131, 31]
# These are the resulting T1 times
properties = backend.properties()
print("Amplitude damping decoherence times for code data qubits:")
for q in init_layout[:3]:
t1 = properties.t1(q)
print(f"qubit {q}: T1 = {t1 * 1e6:.0f} mus")

print("\nAmplitude damping decoherence times for reference qubits:")
for q in init_layout[-3:]:
t1 = properties.t1(q)
print(f"qubit {q}: T1 = {t1 * 1e6:.0f} mus")
Amplitude damping decoherence times code data qubits:
qubit 56: T1 = 322 mus
qubit 44: T1 = 263 mus
qubit 62: T1 = 290 mus

Amplitude damping decoherence times reference qubits:
qubit 143: T1 = 442 mus
qubit 131: T1 = 410 mus
qubit 31: T1 = 401 mus
# now we transpile all circuits to this initial layout; this way each circuit is run on the same qubits and we can make a fair comparison
pm = generate_preset_pass_manager(
backend=backend,
optimization_level=3,
initial_layout=init_layout,
)
isa_circuits = pm.run(circuits)
isa_circuits[1].draw("mpl", cregbundle=False, fold=50)

Output of the previous code cell

Langkah 3. Eksekusi menggunakan primitif Qiskit

sampler = Sampler(mode=backend)

sampler.options.environment.job_tags = ["TUT-REPCODE"]
sampler.options.max_execution_time = 600 # this workload is expected to be under 10s, but it is generally a good habit to set a max execution time (here 600s = 10m)
job = sampler.run(isa_circuits, shots=1000)
print(job.job_id())
job.status()
'DONE'

Langkah 4: Pascapemrosesan dan kembalikan hasil dalam format klasik yang diinginkan

Sekarang kita akan membandingkan laju kesalahan vs. waktu antara memori logis yang menggunakan kode repetisi 3-qubit di satu sisi, dan qubit referensi individu yang tidak dienkode di sisi lain.

results = job.result()
def correct_counts(counts_dict):
"""
Corrects the measured logical qubit encoded in the repetition code using majority vote
"""

result = {"000": 0, "111": 0}
for bitstring, count in counts_dict.items():
key = "111" if bitstring.count("1") > 1 else "000"
result[key] += count
return result

accuracy = [] # logical qubit
accuracies_ref = np.zeros(
(len(results), nq_ref)
) # accuracies on individual reference qubits

for n, pub_result in enumerate(results):
# logical accuracy (one minus error probability) for active error correction with repetition code
counts = pub_result.data.cdata.get_counts()
shots = sum(counts.values())
counts_corrected = correct_counts(counts)
accuracy.append(counts_corrected.get("111", 0) / shots)

# accuracy for individual physical reference qubits without any error correction
for i in range(nq_ref):
counts = pub_result.data.creference.slice_bits(i).get_counts()
accuracies_ref[n, i] = counts.get("1", 0) / shots

accuracy = np.array(accuracy)
def error_proba_t1(N, t1):
"""
Exponential fitting function for amplitude damping vs. number of cycles
"""

t_cycle = 7.3e-6 # approximate time per cycle = 5 mus delay + 2.3 mus for error correction
return 1 - np.exp(-t_cycle * N / t1)

fig, ax = plt.subplots(1, 1, figsize=(15, 5))
ax.plot(
rep_array, (1.0 - accuracy) * 100.0, "ko-", linewidth=3, label="rep code"
)

for i in range(nq_ref):
accuracy_1q = accuracies_ref[:, i]
if i == 0:
ax.plot(
rep_array,
(1.0 - accuracy_1q) * 100.0,
"go-",
linewidth=1,
label="1q reference",
)
else:
ax.plot(rep_array, (1.0 - accuracy_1q) * 100.0, "go-", linewidth=1)

params_bf, pcov = curve_fit(
error_proba_t1, rep_array, 1.0 - accuracy, bounds=([0, 5e-3])
)
t1_bf = params_bf[0]
print(f"Best-fit effective T1 = {t1_bf * 1e6:.0f} us")
error_prob_bf = error_proba_t1(
rep_array, t1_bf
) # np.array([1 - np.exp(-t_cycle * nt/t1) for nt in rep_array])
ax.plot(rep_array, error_prob_bf * 100.0, "k--", linewidth=1)

ax.set_xlabel("error correction rounds")
ax.set_ylabel("error [%]")
ax.set_ylim(bottom=0)
ax.legend(fontsize=15);
Best-fit effective T1 = 1397 us

Output of the previous code cell

Kita dengan jelas melihat masa pakai keadaan 1ˉ|\bar{1}\rangle yang lebih baik, bahkan dibandingkan dengan qubit fisik terbaik (tidak dienkode). Namun, perlu diingat bahwa eksperimen ini hanya mengoreksi satu jenis kesalahan, yaitu bit-flip. Bisakah kamu meningkatkan masa pakai qubit logis? Kamu mungkin ingin mengeksplorasi pengoptimalan waktu delay, memperbesar ukuran kode repetisi melebihi n=3n=3, dan sebagainya.

Langkah selanjutnya

Jika kamu merasa pekerjaan ini menarik, kamu mungkin tertarik dengan materi berikut:

Rekomendasi