Quantenfehlerkorrektur (QEC) ist heute eines der aktivsten Forschungsgebiete im Quantencomputing. Ziel der QEC ist es, die in den Qubits gespeicherten, empfindlichen Quanteninformationen vor Rauschen und Dekohärenz zu schützen.
Die Zuverlässigkeit eines Qubits wird anhand der Fehlerwahrscheinlichkeit gemessen. Heutige physikalische Qubits erreichen eine Fehlerrate von etwa 1 Fehler pro 10.000 Gatteroperationen (\(10^{-4}\)). Für eine optimale Effizienz muss die Fehlerrate jedoch mindestens 1 zu 1 Milliarde (\(10^{-9}\)) oder sogar 1 zu 1 Billion (\(10^{-12}\)) betragen.

Dieses Dokument bietet eine kurze und prägnante Einführung in die Quantenfehlerkorrektur (QEC) aus Implementierungsperspektive. Der erste Abschnitt gibt einen Überblick über die verschiedenen Fehlerkorrekturverfahren zur Erzeugung logischer Qubits. Der zweite Abschnitt untersucht die mögliche Implementierung von Gatteroperationen auf logisch korrigierten Qubits. Der letzte Abschnitt stellt das Cuda-QEC-Framework und seinen programmatischen Ansatz zur Erstellung von Quantenfehlerkorrektursystemen vor.
Die Herausforderung für QEC ist auf den ersten Blick einfach: Man muss zunächst erkennen, wenn ein Fehler aufgetreten ist, und ihn dann korrigieren. Allerdings muss dies geschehen, ohne jemals die Quantenzustände der Qubits zu messen oder zu kollabieren. Hier kommt das Konzept der verschränkten Hilfsqubits ins Spiel:
Anstatt Informationen in einem einzelnen Qubit zu speichern, kodiert QEC ein logisches Qubit über mehrere physikalische Qubits, die in zwei Kategorien unterteilt sind: Daten-Qubits und Hilfs-Qubits (auch Mess-Qubits genannt). Die Gruppe von Qubits bildet einen verschränkten Zustand, sodass die Hilfs-Qubits ausgelesen werden können, ohne den Zustand des Daten-Qubits zu beeinträchtigen. In der Abbildung unten zeigt die rote Linie verschiedene mögliche Zuordnungen logischer Qubits zu physikalischen Qubits.

Nehmen wir an, wir möchten ein logisches Qubit korrigieren, das in mehrere physikalische Qubits kodiert ist. Tritt ein Fehler auf, kann eines der physikalischen Qubits umkippen, entweder in Form eines Bit-Flips (X), eines Phasen-Flips (Z) oder beides (Y).
Um einen Flip zu überprüfen, führt QEC periodische Syndrommessungen durch. Dabei werden spezifische Hilfsqubits gemessen, die mit den Datenqubits interagieren. Das Hilfsqubit wird zunächst mit dem Datenqubit verschränkt, anschließend erfolgt das Auslesen. Die Verschränkung wird nur kurzzeitig aufrechterhalten, gerade so lange, wie für das Auslesen erforderlich ist. Bei Google wird diese Verschränkung mithilfe von CZ-Gattern (Control-Z-Gattern) erreicht, die ihrerseits ebenfalls Verschränkung nutzen.
(Bildquelle: Google Quantum AI - Implementing surface code logical qubits)
Sobald die Syndrome erfasst sind, kann die Korrektur durchgeführt werden. Hierzu ermittelt ein klassischer Algorithmus den wahrscheinlich aufgetretenen Fehler. Anschließend wendet das System die entsprechende Korrektur (X-, Y- oder Z-Gate) an. Im Fall von Google wird zusätzlich eine dynamische Entkopplung (DD) eingesetzt, um Dephasierungsfehler (T2?) zu minimieren.
Die Daten- und Messqubits sind nichtlokal verteilt, sodass ein Fehler an einem oder zwei Qubits die Gruppenkonsistenz nicht beeinträchtigt. Diese Verteilung wird als Quantencode bezeichnet. Reale Systeme verwenden größere, komplexere Codes.
Shor-Code (9 Qubits): Korrigiert sowohl Bit- als auch Phasen-Flip-Fehler.
Steane-Code (7 Qubits): erster vollständig quantenmechanischer Fehlerkorrekturcode.
Oberflächencodes (\(2n^2-1\) Qubits)— Dabei werden Qubits auf einem 2D-Gitter angeordnet. Der Abstand \(n\) des Oberflächencodes gibt an, wie viele Fehler gleichzeitig korrigiert werden können.
In der Praxis ist jedes Messqubit für die projektive Messung eines Stabilisatoroperators zuständig, der für die Entscheidungslogik des klassischen Algorithmus verwendet wird. Ein Stabilisatorcode ist die Quantenverallgemeinerung linearer Codes in der klassischen Fehlerkorrektur, die Paritätsprüfungen nutzen, um Fehler auf Rauschbits zu erkennen.
Wie kann man einen Quantenalgorithmus auf logischen Qubits ausführen?
Die Ausführung eines Quantenalgorithmus auf physikalischen Qubits bedeutet, Gatter auf physikalischen Qubits anzuwenden. Doch wie verhält es sich bei der Ansprache logischer Qubits? Dies mag wie eine einfache Frage erscheinen, doch die Antwort ist nicht so eindeutig, und es gibt mehrere Ansätze für logische Qubit-Gatter. Beginnen wir mit Einzel-Qubit-Gattern und widmen wir uns später der Herausforderung der Mehr-Qubit-Gatter.
Anwendung von Einzel-Qubit-Gattern auf logische Qubits
Link zu Überschrift
Arten von Implementierungen logischer Gatter:
- Transversalgatter: Ein Transversalgatter wirkt unabhängig auf jedes physikalische Qubit.
Ein logisches Qubit-Gatter entspricht der Anwendung desselben Gatters auf jedes der n physikalischen Qubits. Beispielsweise wird beim 7-Qubit-Steane-Code ein logisches Hadamard-Gatter implementiert, indem ein physikalisches H-Gatter auf jedes der 7 physikalischen Qubits angewendet wird.
Bei Oberflächencodes werden logische Qubits als große Bereiche physikalischer Qubits kodiert. Logische Operationen werden durch das Zusammenführen und Aufteilen von Bereichen oder durch das Verflechten von Defekten implementiert, wodurch sich die Codetopologie ändert.
Die Abbildung rechts aus arxiv.2505.15907 zeigt eine Gitterchirurgie-Operation, bei der zwei Codeabschnitte entlang der Kante verbunden werden. Diese Operation benötigt O(d) Runden, um Fehlertoleranz zu erreichen.
Anwendung von Mehr-Qubit-Gattern auf logische Qubits
Link zu Überschrift
- Transversale Multi-Qubit-Gatter : Viele der Quantencodes (z. B. Steane, Bacon-Shor), logische CNOT-Gatter können transversal realisiert werden: \( CNOT_L=\sum_{i=1}^n CNOT(a_i,b_i) \)
Das heißt, für jedes physikalische Qubit \(a_i\) im Kontrollblock wird ein physikalisches CNOT auf das entsprechende Qubit \( b_i \) im Zielblock angewendet.
- Ancilla-basierte Multi-Qubit-Logikgatter : Wenn eine transversale Implementierung nicht möglich ist, können Multi-Qubit-Logikgatter über Ancilla-Qubits und Teleportationsschaltungen realisiert werden.
Das CCNOT-Gatter (Toffoli-Gatter) lässt sich beispielsweise in den meisten Codes nicht transversal implementieren (*). Stattdessen wird es durch die Erzeugung von magischen Zuständen realisiert. Diese Zustände werden bedingt „teleportiert“, beispielsweise mit einem CNOT-Gatter. Beachten Sie, dass das teleportierende Gatter in diesem Fall eine logische Gatteroperation ist.
Zum Beispiel CNOT mittels Gitterchirurgie und CZ mittels Flechtung.
Die Anwendung von Mehrqubit-Gattern auf logische Qubits erfolgt durch Operationen auf ihren kodierten physikalischen Qubits gemäß der Struktur des Codes:
Transversal: Paarweise Gatter zwischen physikalischen Qubits logischer Blöcke.
Ancilla-basiert: Teleportation oder Methoden des magischen Zustands verwenden.
Topologisch: Geometrische Operationen wie Flechten oder Gitterchirurgie durchführen.
Ich möchte mich bei allen Lesern entschuldigen, die bis hierher gelesen haben: Dieser Abschnitt ist etwas oberflächlich und geht nur an der Oberfläche, anstatt in die Tiefe zu gehen. Wichtig ist vorerst nur, dass es keine allgemeingültige Lösung für das Einzel-/Mehr-Qubit-Gatter eines logischen Qubits gibt, sondern eine Reihe von Vorgehensweisen, die für jedes Gatter und jeden Quantencode-Typ angewendet werden können.
Programmatischer Ansatz: Das Cuda-QX QEC-Framework
Link zu Überschrift

Bereits 2024 stellte das Quantum-Team von Nvidia die CUDA-QX als Erweiterung der Open-Source-Plattform CUDA-Q vor. Diese Bibliothek bietet optimierte Bibliotheken für Quantenentwickler. Zwei der in CUDA-QX enthaltenen Bibliotheken sind CUDA-Q QEC (Primitive und Decoder zur Quantenfehlerkorrektur) und CUDA-Q Solvers (Hilfsfunktionen für VQE/ADAPT-VQE und QAOA).
Werfen wir einen kurzen Blick auf ihren Ansatz zur Bereitstellung einer programmatischen API für die benutzerdefinierte Implementierung der klassischen Logik des Stabilisatoroperators. Nachfolgend ein Minimalbeispiel, das den einfachsten Decoder implementiert, der zur Erkennung von Bit-Flip-Fehlern in einzelnen Qubits dient. Der Decoder prüft, ob das Syndrom mit einer einzelnen Spalte der Paritätsmatrix Hz übereinstimmt. In diesem Fall wird das Qubit als gekippt erkannt.
import cudaq_qec as qec
def run_simple_code_capacity( nShots=1000, errorProbability=0.1 ):
steane = qec.get_code("steane") # get built-in Steane code from CUDA-Q QEC
parity_check_matrix = steane.get_parity_z() # parity-check matrix for Z (bit-flip) errors
observable = steane.get_observables_z() # Get the Pauli Z observables of the code
decoder = MyCustomDecoder(Hz) # instantiate custom decoder
nLogicalErrors = 0
for _ in range(nShots):
# Generate noisy data
data = qec.generate_random_bit_flips(Hz.shape[1], errorProbability)
# Calculate which syndromes are flagged.
syndrome = (parity_check_matrix @ data) % 2
# Decode the syndrome to predict what happened to the data
result = decoder.decode(syndrome)
data_prediction = np.array(result.result, dtype=np.uint8)
# See if this prediction flipped the observable
predicted_observable = (observable @ data_prediction) % 2
# See if the observable was actually flipped
actual_observable = observable @ data % 2
if (predicted_observable != actual_observable):
nLogicalErrors += 1
return nLogicalErrors
Der wichtige Aspekt des obigen Algorithmus besteht darin, dass die Paritätsmatrix und die vorhergesagten Observablen aus dem vordefinierten Stean-Quantencode gewonnen werden. Ein einfacher Decoder kann dann folgendermaßen implementiert werden:
class SingleColumnMatchDecoder:
def __init__(self, Hz): # Hz is a parity-check matrix shape (nChecks, nDataQubits)
self.Hz = np.array(Hz) % 2
self.n_qubits = self.Hz.shape[1]
class SimpleDecoderResult:
def __init__(self):
self.result = np.zeros(self.n_qubits, dtype=np.uint8)
def flipped(self, q):
self.result[q]=1
return self
def decode(self, syndrome):
# ensure syndrome is 1D numpy array of length nChecks
s = np.asarray(syndrome).flatten() % 2
# Initialize the flip prediction list
prediction = SimpleDecoderResult()
# Compare to each column of Hz: if any column equals the syndrome, pick that qubit
for q in range(self.n_qubits):
col = self.Hz[:, q] % 2
if np.array_equal(col, s):
return prediction.flipped(q)
# no single-column match -> predict no flips (all zeros)
return prediction
Nachdem der Decoder installiert ist, kann der Algorithmus nun ausgeführt werden.
nShots = 1000
nLogicalErrors = run_simple_code_capacity(nShots)
print(f"Shots = {nShots}, Logical error rate ≈ {nLogicalErrors/nShots:.4f}")
Wie man sieht, basiert der Cuda QEC im Wesentlichen auf der Bereitstellung effizienter Werkzeuge und Bibliotheken zur Implementierung der klassischen Berechnungslogik des QEC. Die offene Frage ist, wie dieser Cuda QEC mit einem realen Quantenstack verbunden werden kann, insbesondere wie die Syndrome vom Kontrollstack an den QEC-Decoder übermittelt werden und wie die Decoder im Gegenzug den Kontrollstack über die anzuwendenden Korrekturgatter informieren. Hier kommt das faszinierende Framework Cuda-Q ins Spiel, aber diese Untersuchung hebe ich mir für ein späteres Memo auf!
Voilà, es ist klar, dass die intensive Beschäftigung mit Quantenfehlerkorrektur eine Vollzeitbeschäftigung ist. Daher muss ich auf einem relativ hohen Niveau bleiben, um die Entwicklung der Quantencomputerarchitektur voranzutreiben. Ich frage mich derzeit, ob ich von einem „logisch korrekten“ Quantencomputer oder einfach von einem „zuverlässigen“ Quantencomputer sprechen sollte. Im ersten Fall liegt der Schwerpunkt auf dem Modell, das die Korrektheit des Computers gewährleistet, im zweiten Fall auf seinem Verhalten.
Mir ist, wie bereits in einem früheren Beitrag erwähnt, unklar, ob die QEC als zusätzliche Funktion des Quantenalgorithmus verwendet werden sollte. Das würde bedeuten, dass der Compiler, der die Quantengattersequenz in vom Kontrollstapel ausführbaren Code übersetzt, auch für das „Inlining“ der Gatteroperationen verantwortlich ist, um den Oberflächencode auszulesen. Oder ob dies als Hintergrundprozess der zuverlässigen Quantencomputermaschine erfolgen sollte?
- Wenn Sie sich fragen, ob man Ancillary oder Auxiliary verwenden sollte, dann ist das richtige Wort Ancillary (lateinisch ancilla: Dienerin), dessen Bedeutung darin besteht, „notwendige Unterstützung für eine primäre Funktion zu leisten“, während Auxiliary (lateinisch auxilium: Hilfe) bedeutet, „zusätzliche Hilfe zu leisten oder Unterstützung in Reserve zu halten“.
Dieser Hinweis wurde nachträglich hinzugefügt, nachdem das ursprüngliche Memo verfasst worden war:
- Es gibt ein interessantes Tutorial zum Thema „Enge Integration von GPUs und QPUs für Quantenfehlerkorrektur und optimale Steuerung“ von der IEEE QCE24, dessen Videoaufzeichnung hier verfügbar ist: [https://video.computer.org/qce24-zoom/qce-5-room-518abc-20240917-190103.mp4.html]. Dieses Tutorial scheint viele Antworten und Erkenntnisse zu den oben genannten offenen Fragen zu liefern. Ich werde dieses Tutorial in einem späteren Memo genauer erläutern. Die Grundidee ist, dass eine Schaltung so umgeschrieben werden kann, dass sie zu einem gegebenen Quantencode passt, auf eine Oberflächentopologie reduziert und anschließend für geeignete Gatter (Clifford-Gatter) nachbearbeitet wird.
