Repetition Codes
Zeitschätzung: weniger als 1 Minute auf an Heron-Prozessor (HINWEIS: Des ist nur a Schätzung. Deine Laufzeit kann abweichen.)
Hintergrund
Damit a echte Quantenfehlerkorrektur (QEC) in Echtzeit funktioniert, brauchst du die Möglichkeit, den Programmablauf während der Ausführung dynamisch zu steuern — also Quantengates abhängig von Messergebnissen zu schalten. Des Tutorial führt den Bit-Flip-Code aus, der eine sehr einfache Form von QEC ist. Es demonstriert einen dynamischen Quantenschaltkreis, der an kodierten Qubit vor einem einzelnen Bit-Flip-Fehler schützt, und bewertet dann die Leistung vom Bit-Flip-Code.
Du kannst zusätzliche Ancilla-Qubits und Verschränkung nutzen, um Stabilisatoren zu messen, die die kodierte Quanteninformation net verändern, dir aber trotzdem über bestimmte Fehlerklassen Auskunft geben, die aufgetreten sein könnten. Ein Quantenstabilisator-Code kodiert logische Qubits in physikalische Qubits. Stabilisator-Codes konzentrieren sich besonders aufs Korrigieren eines diskreten Fehlersatzes, der durch die Pauli-Gruppe unterstützt wird.
Mehr Infos zu QEC findest du in Quantum Error Correction for Beginners.
Voraussetzungen
Bevor du mit dem Tutorial anfängst, stell sicher, dass du folgendes installiert hast:
- Qiskit SDK v2.0 oder neuer, mit Visualisierungs-Unterstützung
- Qiskit Runtime v0.40 oder neuer (
pip install qiskit-ibm-runtime)
Setup
# Qiskit imports
from qiskit import (
QuantumCircuit,
QuantumRegister,
ClassicalRegister,
)
# Qiskit Runtime
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
from qiskit_ibm_runtime.circuit import MidCircuitMeasure
service = QiskitRuntimeService()
Schritt 1. Klassische Eingaben auf's Quantenproblem abbilden
Einen Bit-Flip-Stabilisator-Schaltkreis bauen
Der Bit-Flip-Code ist eines der einfachsten Beispiele für an Stabilisator-Code. Er schützt den Zustand gegen einen einzelnen Bit-Flip-(X)-Fehler auf jedem der Kodierungs-Qubits. Schau dir die Wirkung vom Bit-Flip-Fehler an, der und auf jedem unserer Qubits abbildet — dann haben wir . Der Code braucht fünf Qubits: drei werden zum Kodieren des geschützten Zustands verwendet, und die restlichen zwei dienen als Stabilisator-Mess-Ancillas.
# Choose the least busy backend that supports `measure_2`.
backend = service.least_busy(
filters=lambda b: "measure_2" in b.supported_instructions,
operational=True,
simulator=False,
dynamic_circuits=True,
)
qreg_data = QuantumRegister(3)
qreg_measure = QuantumRegister(2)
creg_data = ClassicalRegister(3, name="data")
creg_syndrome = ClassicalRegister(2, name="syndrome")
state_data = qreg_data[0]
ancillas_data = qreg_data[1:]
def build_qc():
"""Build a typical error correction circuit"""
return QuantumCircuit(qreg_data, qreg_measure, creg_data, creg_syndrome)
def initialize_qubits(circuit: QuantumCircuit):
"""Initialize qubit to |1>"""
circuit.x(qreg_data[0])
circuit.barrier(qreg_data)
return circuit
def encode_bit_flip(circuit, state, ancillas) -> QuantumCircuit:
"""Encode bit-flip. This is done by simply adding a cx"""
for ancilla in ancillas:
circuit.cx(state, ancilla)
circuit.barrier(state, *ancillas)
return circuit
def measure_syndrome_bit(circuit, qreg_data, qreg_measure, creg_measure):
"""
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_measure[0])
circuit.cx(qreg_data[1], qreg_measure[0])
circuit.cx(qreg_data[0], qreg_measure[1])
circuit.cx(qreg_data[2], qreg_measure[1])
circuit.barrier(*qreg_data, *qreg_measure)
circuit.append(MidCircuitMeasure(), [qreg_measure[0]], [creg_measure[0]])
circuit.append(MidCircuitMeasure(), [qreg_measure[1]], [creg_measure[1]])
with circuit.if_test((creg_measure[0], 1)):
circuit.x(qreg_measure[0])
with circuit.if_test((creg_measure[1], 1)):
circuit.x(qreg_measure[1])
circuit.barrier(*qreg_data, *qreg_measure)
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(qreg_data)
return circuit
def apply_final_readout(circuit, qreg_data, creg_data):
"""Read out the final measurements"""
circuit.barrier(qreg_data)
circuit.measure(qreg_data, creg_data)
return circuit
def build_error_correction_sequence(apply_correction: bool) -> QuantumCircuit:
circuit = build_qc()
circuit = initialize_qubits(circuit)
circuit = encode_bit_flip(circuit, state_data, ancillas_data)
circuit = measure_syndrome_bit(
circuit, qreg_data, qreg_measure, creg_syndrome
)
if apply_correction:
circuit = apply_correction_bit(circuit, qreg_data, creg_syndrome)
circuit = apply_final_readout(circuit, qreg_data, creg_data)
return circuit
circuit = build_error_correction_sequence(apply_correction=True)
circuit.draw(output="mpl", style="iqp", cregbundle=False)
Schritt 2. Das Problem für die Quantenausführung optimieren
Um die Gesamtausführungszeit von Jobs zu verkürzen, akzeptieren die Qiskit-Primitives nur Schaltkreise und Observablen, die den Anweisungen und der Konnektivität vom Zielsystem entsprechen (als Instruction Set Architecture (ISA)-Schaltkreise und Observablen bezeichnet). Mehr über Transpilation erfahren.
ISA-Schaltkreise generieren
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
isa_circuit.draw("mpl", style="iqp", idle_wires=False)


no_correction_circuit = build_error_correction_sequence(
apply_correction=False
)
isa_no_correction_circuit = pm.run(no_correction_circuit)
Schritt 3. Mit Qiskit-Primitives ausführen
Führ die Version mit Korrektur und eine ohne Korrektur aus.
sampler_no_correction = Sampler(backend)
job_no_correction = sampler_no_correction.run(
[isa_no_correction_circuit], shots=1000
)
result_no_correction = job_no_correction.result()[0]
sampler_with_correction = Sampler(backend)
job_with_correction = sampler_with_correction.run([isa_circuit], shots=1000)
result_with_correction = job_with_correction.result()[0]
print(f"Data (no correction):\n{result_no_correction.data.data.get_counts()}")
print(
f"Syndrome (no correction):\n{result_no_correction.data.syndrome.get_counts()}"
)
Data (no correction):
{'111': 878, '011': 42, '110': 35, '101': 40, '100': 1, '001': 2, '000': 2}
Syndrome (no correction):
{'00': 942, '10': 33, '01': 22, '11': 3}
print(f"Data (corrected):\n{result_with_correction.data.data.get_counts()}")
print(
f"Syndrome (corrected):\n{result_with_correction.data.syndrome.get_counts()}"
)
Data (corrected):
{'111': 889, '110': 25, '000': 11, '011': 45, '101': 17, '010': 10, '001': 2, '100': 1}
Syndrome (corrected):
{'00': 929, '01': 39, '10': 20, '11': 12}
Schritt 4. Nachbearbeitung und Ergebnis im klassischen Format zurückgeben
Du siehst, dass der Bit-Flip-Code viele Fehler erkannt und korrigiert hat, was insgesamt zu weniger Fehlern geführt hat.
def decode_result(data_counts, syndrome_counts):
shots = sum(data_counts.values())
success_trials = data_counts.get("000", 0) + data_counts.get("111", 0)
failed_trials = shots - success_trials
error_correction_events = shots - syndrome_counts.get("00", 0)
print(
f"Bit flip errors were detected/corrected on {error_correction_events}/{shots} trials."
)
print(
f"A final parity error was detected on {failed_trials}/{shots} trials."
)
# non-corrected marginalized results
data_result = result_no_correction.data.data.get_counts()
marginalized_syndrome_result = result_no_correction.data.syndrome.get_counts()
print(
f"Completed bit code experiment data measurement counts (no correction): {data_result}"
)
print(
f"Completed bit code experiment syndrome measurement counts (no correction): {marginalized_syndrome_result}"
)
decode_result(data_result, marginalized_syndrome_result)
Completed bit code experiment data measurement counts (no correction): {'111': 878, '011': 42, '110': 35, '101': 40, '100': 1, '001': 2, '000': 2}
Completed bit code experiment syndrome measurement counts (no correction): {'00': 942, '10': 33, '01': 22, '11': 3}
Bit flip errors were detected/corrected on 58/1000 trials.
A final parity error was detected on 120/1000 trials.
# corrected marginalized results
corrected_data_result = result_with_correction.data.data.get_counts()
corrected_syndrome_result = result_with_correction.data.syndrome.get_counts()
print(
f"Completed bit code experiment data measurement counts (corrected): {corrected_data_result}"
)
print(
f"Completed bit code experiment syndrome measurement counts (corrected): {corrected_syndrome_result}"
)
decode_result(corrected_data_result, corrected_syndrome_result)
Completed bit code experiment data measurement counts (corrected): {'111': 889, '110': 25, '000': 11, '011': 45, '101': 17, '010': 10, '001': 2, '100': 1}
Completed bit code experiment syndrome measurement counts (corrected): {'00': 929, '01': 39, '10': 20, '11': 12}
Bit flip errors were detected/corrected on 71/1000 trials.
A final parity error was detected on 100/1000 trials.
Tutorial-Umfrage
Bitte nimm dir kurz Zeit für diese Umfrage, um Feedback zu dem Tutorial zu geben. Deine Rückmeldungen helfen uns, unsere Inhalte und die Nutzererfahrung zu verbessern.