Kod ulangan
Anggaran penggunaan: kurang dari 10 saat pada pemproses Heron (NOTA: Ini adalah anggaran sahaja. Masa jalan sebenar mungkin berbeza.)
Hasil pembelajaran
-
Cara melaksanakan kod pembetulan ralat bit-flip menggunakan litar dinamik
-
Cara mengukur stabiliser untuk mengesan ralat kuantum tanpa memusnahkan maklumat yang dikodkan
-
Cara menilai prestasi pembetulan ralat kuantum dengan membandingkan keputusan yang dibetulkan dan tidak dibetulkan
Prasyarat
Latar Belakang
Untuk membolehkan pembetulan ralat kuantum (QEC) masa nyata, anda perlu dapat mengawal aliran program kuantum secara dinamik semasa pelaksanaan supaya Gate kuantum boleh dikondisikan pada keputusan pengukuran. Tutorial ini menjalankan kod bit-flip, yang merupakan bentuk QEC yang sangat mudah. Ia menunjukkan litar kuantum dinamik yang boleh melindungi qubit yang dikodkan daripada ralat bit-flip tunggal, kemudian menilai prestasi kod bit-flip tersebut.
Anda boleh memanfaatkan qubit ancilla tambahan dan penjalingan untuk mengukur penstabil yang tidak mengubah maklumat kuantum yang dikodkan, sambil tetap memaklumkan anda tentang beberapa kelas ralat yang mungkin telah berlaku. Kod penstabil kuantum mengekodkan qubit logik ke dalam qubit fizikal. Kod penstabil memberi tumpuan secara kritikal pada pembetulan set ralat diskret dengan sokongan daripada kumpulan Pauli .
Dalam tutorial ini, kami menunjukkan kod bit-flip menggunakan eksperimen memori kuantum yang mudah. Kami akan menyediakan qubit yang dikodkan dalam keadaan logik dan kemudian melaksanakan berbilang kitaran masa terbiar (untuk mengumpul ralat) diikuti oleh pengesanan dan pembetulan ralat. Kami kemudian mengukur kebarangkalian ralat logik sebagai fungsi bilangan kitaran (masa), di mana kebarangkalian ralat logik ialah kebarangkalian mendapati qubit dalam keadaan yang tidak memulihkan selepas pengukuran akhir (secara konkrit, keadaan yang sepadan dengan ralat ialah , , , dan ).
Kami akan membandingkan kadar ralat berbanding masa dengan kadar ralat qubit individu yang tidak dikodkan, dan juga penggunaan kod pengulangan di mana kami hanya mengesan dan membetulkan ralat selepas pengukuran akhir, tetapi bukan secara dinamik semasa litar.
Nota: kod pengulangan hanya membenarkan pembetulan ralat bit-flip dan oleh itu bukanlah kod pembetulan ralat yang lengkap. Walau bagaimanapun, kerana kesederhanaannya, ia adalah titik permulaan yang baik untuk menggambarkan cara melaksanakan pembetulan ralat pada komputer kuantum. Eksperimen memori di bawah hanya menguji satu jenis ralat ( yang mereput kepada ) dan secara teknikal hanya menunjukkan perlindungan maklumat klasik.
Keperluan
Sebelum memulakan tutorial ini, pastikan anda telah memasang yang berikut:
-
Qiskit SDK v2.0 atau lebih baru, dengan sokongan visualisasi
-
Qiskit Runtime v0.40 atau lebih baru (
pip install qiskit-ibm-runtime)
Persediaan
# 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 berskala kecil
Kami akan mengetepikan langkah ini kerana matlamat eksperimen ini adalah untuk mengukur jangka hayat qubit logik di bawah hingar perkakasan sebenar, seperti redaman amplitud semasa kelewatan terbiar; simulator tanpa hingar tidak akan menunjukkan sebarang ralat untuk dikesan dan dibetulkan.
Contoh perkakasan berskala besar
Langkah 1. Petakan input klasik kepada masalah kuantum
Pilih backend
Untuk mengesan ralat semasa litar, kita perlu memilih backend yang mempunyai akses kepada kaedah 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
Bina satu siri litar stabiliser bit-flip yang melaksanakan berbilang pusingan pengesanan dan pembetulan ralat
Kod bit-flip adalah antara contoh kod penstabil yang paling mudah. Ia melindungi keadaan daripada ralat bit-flip (X) tunggal pada mana-mana qubit pengekodan. Pertimbangkan tindakan ralat bit-flip , yang memetakan dan pada mana-mana qubit kita, maka kita ada . Kod ini memerlukan lima qubit: tiga digunakan untuk mengekodkan keadaan yang dilindungi ("qubit data"), dan dua yang lain digunakan sebagai ancilla pengukuran penstabil.
Di bawah anda akan membina litar yang (1) menyediakan qubit data dalam keadaan logik , kemudian (2) menjalankan berbilang kitaran kelewatan diikuti oleh pembetulan ralat (termasuk set semula qubit sindrom), dan (3) membaca keadaan qubit data.
Kami juga akan menguji jangka hayat keadaan tanpa menggunakan pembetulan ralat dengan menyertakan tiga qubit rujukan yang kami sediakan dalam keadaan , biarkan terbiar, dan kemudian 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)

Langkah 2: Optimumkan masalah untuk pelaksanaan hardware kuantum
Untuk mengurangkan jumlah masa pelaksanaan kerja, primitif Qiskit hanya menerima litar dan observable yang mematuhi arahan dan sambungan yang disokong oleh sistem sasaran (dirujuk sebagai litar dan observable ISA, iaitu instruction set architecture). Ketahui lebih lanjut tentang transpilasi.
Jana litar ISA
Kami akan mulakan dengan mencari susun atur awal (iaitu, pemilihan qubit fizikal untuk memetakan litar kami) dengan mentranspil litar terpanjang kami menggunakan pengurus laluan pratetap dengan tahap pengoptimuman 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]
Bagi qubit rujukan yang akan kami bandingkan dengan memori kuantum logik kami, kami akan memilih qubit yang terbaik yang ada dari segi masa keselarasan redaman amplitud ().
# 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)

Langkah 3. Jalankan 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: Memproses pasca dan mengembalikan keputusan dalam format klasik yang dikehendaki
Kami kini akan membandingkan kadar ralat lawan masa antara memori logik menggunakan kod pengulangan 3-qubit di satu pihak, dan qubit rujukan individu yang tidak dikodkan di pihak yang 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

Kami dengan jelas melihat jangka hayat yang lebih baik bagi keadaan , walaupun berbanding dengan qubit fizikal (tidak dikodkan) yang terbaik. Walau bagaimanapun, ingat bahawa eksperimen ini hanya membetulkan satu jenis ralat, iaitu bit-flip. Bolehkah anda meningkatkan jangka hayat qubit logik? Anda mungkin ingin meneroka pengoptimuman masa kelewatan, meningkatkan skala saiz kod pengulangan melebihi , dan sebagainya.
Langkah seterusnya
Jika anda mendapati kerja ini menarik, anda mungkin berminat dengan bahan berikut:
-
Kursus asas pembetulan ralat kuantum - ketahui lebih lanjut tentang pembetulan ralat kuantum
-
Tutorial pengesanan ralat overhed rendah dengan kod ruang-masa - ketahui cara menggunakan semakan Pauli untuk mengesan ralat dan sampel pasca-pilih