In dem Artikel Letztes Mal habe ich die Theorie des Stabilisatorcodes verstanden, daher werde ich dieses Mal ein konkretes Beispiel für Stabilisatorcode aufgreifen und den Quantenberechnungssimulator [qlazy] verwenden ](Https://github.com/samn33/qlazy), um den Betrieb zu überprüfen. Am Ende von Letztes Mal habe ich die Quellen von "Shor-Code", "Steane-Code" und "5-Quantenbit-Code" eingeführt. Von diesen wurden "Shor-Code" und "Steane-Code" bereits in einem anderen Artikel [^ 1] auf ihre Funktion überprüft. Daher werden wir hier "5-Quantenbit-Code" implementieren und die Operation überprüfen.
Zuerst werde ich die vier Ursprünge und den logischen Z-Operator noch einmal auflisten.
Quelle | Operator |
---|---|
Obwohl in der Tabelle nicht gezeigt, ist der logische X-Operator $ X \ otimes X \ otimes X \ otimes X \ otimes X $.
Ich möchte darauf basierend eine Quantenschaltung entwerfen, die jedoch einige Vorbereitungen erfordert.
Um einen logischen Basiszustand $ \ ket {0_L} $ aus $ \ ket {00000} $ zu erstellen, ändern Sie den Operator $ g_ {i} ^ {\ prime} $ entsprechend der Quelle $ g_i $ in $ g_ {i Ich musste mich dafür entscheiden, mit $ und allen anderen $ g_j (j \ ne i) $ nicht austauschbar zu sein. Ich dachte, ich sollte eine Prüfmatrix [^ 2] für den obigen Generator erstellen und eine Prüfmatrix für $ g_ {i} ^ {\ prime} $ erstellen, und als Ergebnis von Versuch und Irrtum fand ich Folgendes. Ist fertig [^ 3].
[^ 2]: Siehe Vorheriger Artikel. [^ 3]: Vielleicht gibt es ein universelles und intelligentes Verfahren, aber ich bin damit zufrieden.
Operator | |
---|---|
Bereiten Sie als Rauschmenge alles vor, was die Pauli-Gruppenoperatoren $ X, Z, XZ $ für jedes Quantenbit betreibt, wie unten gezeigt.
Lärm | Operator |
---|---|
Wir benötigen auch eine Liste der gemessenen Werte $ \ {\ beta_ {l} ^ {(i)} \} $, wenn die Quelle mit dem hinzugefügten Rauschen $ E_i $ gemessen wird.
g_l E_i = \beta_{l}^{(i)} E_i g_l \tag{1}
Es kann mit berechnet werden. Das heißt, wenn $ g_l $ und $ E_i $ konvertierbar sind, dann ist $ \ beta_ {l} ^ {(i)} = + 1 $ und wenn sie nicht konvertierbar sind, dann ist $ \ beta_ {l} ^ {(i)} = - Es ist 1 $. Unten finden Sie eine Liste der Messungen für jedes Geräusch. In der zweiten Spalte wird der gemessene Wert $ + 1 $ als $ + $ und der gemessene Wert $ -1 $ als $ - $ aufgeführt. Außerdem bedeutet der gemessene Wert von $ + 1 $ $ \ ket {0} $, und der gemessene Wert von $ -1 $ bedeutet $ \ ket {1}
Lärm | Anzeige des Messwertes | |
---|---|---|
Die Vorbereitungen sind jetzt abgeschlossen. Nun ist die Quantenschaltung unten gezeigt.
Zunächst zeigen wir eine Schaltung, die den logischen Basiszustand $ \ ket {0_L} $ aus dem physischen Basiszustand $ \ ket {00000} $ erhält.
|0> --H---*---H---M
|0> --H---|-------|-------*---H---M
|0> --H---|-------|-------|-------|-------*---H---M
|0> --H---|-------|-------|-------|-------|-------|-------*---H---M
|0> --H---|-------|-------|-------|-------|-------|-------|-------|-------*---H---M
| | | | | | | | | |
|0> ----| |---| |---| |---| |---| |---| |---| |---| |---| |---| |---
|0> ----| |---| |---| |---| |---| |---| |---| |---| |---| |---| |---
|0> ----|g1 |---|g1'|---|g2 |---|g2'|---|g3 |---|g3'|---|g4 |---|g4'|---|g5 |---|g5'|--- |0L>
|0> ----| |---| |---| |---| |---| |---| |---| |---| |---| |---| |---
|0> ----| |---| |---| |---| |---| |---| |---| |---| |---| |---| |---
Als nächstes wird der unbekannte 1-Quantenzustand $ \ ket {\ psi} $ unter Verwendung der Technik der Quantenteleportation codiert.
|psi> ----------*---H-----------M
| |
|0> --H---*---X---------M |
| | |
------X-------------X-----Z---
------X-------------X-----Z---
|0L> ------X-------------X-----Z--- |psi_L>
------X-------------X-----Z---
------X-------------X-----Z---
Nachdem wir den codierten Status $ \ ket {\ psi_L} $ haben, werden wir etwas Rauschen hinzufügen. Hier repräsentiert Ei eine Art Geräusch.
---| |---
---| |---
|psi_L> ---|Ei|--- |psi_L'>
---| |---
---| |---
Wenn Sie schließlich das Syndrom zur Fehlererkennung messen und die inverse Berechnung des Rauschens zur Wiederherstellung durchführen, wird der Zustand wiederhergestellt.
|0> --H---*--------------------------H-----M
|0> --H---|-------*------------------H-----M
|0> --H---|-------|-------*----------H-----M
|0> --H---|-------|-------|------*---H-----M
| | | | |
----| |---| |---| |--| |-----| |----
----| |---| |---| |--| |-----| |----
|psi_L'> ----|g1 |---|g2 |---|g3 |--|g4 |-----|E+ |---- |psi>
----| |---| |---| |--| |-----| |----
----| |---| |---| |--| |-----| |----
Das ist alles für die Quantenschaltung.
Lassen Sie uns ein Implementierungsbeispiel von qlazy zeigen.
import numpy as np
from qlazypy import QState
def logic_x(self, qid):
[self.x(q) for q in qid]
return self
def logic_z(self, qid):
[self.z(q) for q in qid]
return self
def ctr_logic_x(self, q, qid):
[self.cx(q, qtar) for qtar in qid]
return self
def ctr_logic_z(self, q, qid):
[self.cz(q, qtar) for qtar in qid]
return self
def ctr_g1(self, q, qid):
self.cx(q, qid[0]).cz(q, qid[1]).cz(q, qid[2]).cx(q, qid[3])
return self
def ctr_g2(self, q, qid):
self.cx(q, qid[1]).cz(q, qid[2]).cz(q, qid[3]).cx(q, qid[4])
return self
def ctr_g3(self, q, qid):
self.cx(q, qid[0]).cx(q, qid[2]).cz(q, qid[3]).cz(q, qid[4])
return self
def ctr_g4(self, q, qid):
self.cz(q, qid[0]).cx(q, qid[1]).cx(q, qid[3]).cz(q, qid[4])
return self
def encode(self, phase, qid_anc, qid_cod):
# make logical zero state: |0>_L
# g1
self.h(qid_anc[0]).ctr_g1(qid_anc[0], qid_cod).h(qid_anc[0])
self.m(qid=[qid_anc[0]])
mval = self.m_value(binary=True)
if mval == '1': self.z(qid_cod[0]).z(qid_cod[2])
self.reset(qid=[qid_anc[0]])
# g2
self.h(qid_anc[1]).ctr_g2(qid_anc[1], qid_cod).h(qid_anc[1])
self.m(qid=[qid_anc[1]])
mval = self.m_value(binary=True)
if mval == '1': self.z(qid_cod[0]).z(qid_cod[1]).z(qid_cod[2]).z(qid_cod[3])
self.reset(qid=[qid_anc[1]])
# g3
self.h(qid_anc[2]).ctr_g3(qid_anc[2], qid_cod).h(qid_anc[2])
self.m(qid=[qid_anc[2]])
mval = self.m_value(binary=True)
if mval == '1': self.z(qid_cod[0]).z(qid_cod[1]).z(qid_cod[3]).z(qid_cod[4])
self.reset(qid=[qid_anc[2]])
# g4
self.h(qid_anc[3]).ctr_g4(qid_anc[3], qid_cod).h(qid_anc[3])
self.m(qid=[qid_anc[3]])
mval = self.m_value(binary=True)
if mval == '1': self.z(qid_cod[0]).z(qid_cod[2]).z(qid_cod[3])
self.reset(qid=[qid_anc[3]])
# logical z
self.h(qid_anc[4]).ctr_logic_z(qid_anc[4], qid_cod).h(qid_anc[4])
self.m(qid=[qid_anc[4]])
mval = self.m_value(binary=True)
if mval == '1':
self.x(qid_cod[0]).x(qid_cod[1]).x(qid_cod[2]).x(qid_cod[3]).x(qid_cod[4])
self.reset(qid=[qid_anc[4]])
# make random quantum state and encode (with quantum teleportation)
self.reset(qid=qid_anc)
self.u3(qid_anc[0], alpha=phase[0], beta=phase[1], gamma=phase[2]) # input quantum state
print("* input quantum state")
self.show(qid=[qid_anc[0]])
self.h(qid_anc[1])
self.ctr_logic_x(qid_anc[1], qid_cod).cx(qid_anc[0], qid_anc[1])
self.h(qid_anc[0])
self.m(qid=qid_anc[0:2])
mval = self.m_value(binary=True)
if mval == '00': pass
elif mval == '01': self.logic_x(qid_cod)
elif mval == '10': self.logic_z(qid_cod)
elif mval == '11': self.logic_x(qid_cod).logic_z(qid_cod)
self.reset(qid=qid_anc)
return self
def add_noise(self, q, qid, kind):
if kind == 'X': self.x(qid[q])
elif kind == 'Z': self.z(qid[q])
elif kind == 'XZ': self.z(qid[q]).x(qid[q])
return self
def correct_err(self, qid_anc, qid_cod):
self.reset(qid=qid_anc)
# syndrome
self.h(qid_anc[0]).ctr_g1(qid_anc[0], qid_cod).h(qid_anc[0])
self.h(qid_anc[1]).ctr_g2(qid_anc[1], qid_cod).h(qid_anc[1])
self.h(qid_anc[2]).ctr_g3(qid_anc[2], qid_cod).h(qid_anc[2])
self.h(qid_anc[3]).ctr_g4(qid_anc[3], qid_cod).h(qid_anc[3])
self.m(qid=qid_anc[0:4])
mval = self.m_value(binary=True)
print("* syndrome =", mval)
# recovery
if mval == '0000': pass
elif mval == '0001': self.x(qid_cod[0])
elif mval == '1010': self.z(qid_cod[0])
elif mval == '1011': self.z(qid_cod[0]).x(qid_cod[0])
elif mval == '1000': self.x(qid_cod[1])
elif mval == '0101': self.z(qid_cod[1])
elif mval == '1101': self.z(qid_cod[1]).x(qid_cod[1])
elif mval == '1100': self.x(qid_cod[2])
elif mval == '0010': self.z(qid_cod[2])
elif mval == '1110': self.z(qid_cod[2]).x(qid_cod[2])
elif mval == '0110': self.x(qid_cod[3])
elif mval == '1001': self.z(qid_cod[3])
elif mval == '1111': self.z(qid_cod[3]).x(qid_cod[3])
elif mval == '0011': self.x(qid_cod[4])
elif mval == '0100': self.z(qid_cod[4])
elif mval == '0111': self.z(qid_cod[4]).x(qid_cod[4])
return self
if __name__ == '__main__':
QState.add_methods(logic_x, logic_z, ctr_logic_x, ctr_logic_z,
ctr_g1, ctr_g2, ctr_g3, ctr_g4,
encode, add_noise, correct_err)
# create registers
qid_anc = QState.create_register(5)
qid_cod = QState.create_register(5)
qnum = QState.init_register(qid_anc, qid_cod)
# parameters for input quantum state (U3 gate params)
phase = [np.random.rand(), np.random.rand(), np.random.rand()]
# encode quantum state
qs_ini = QState(qnum)
qs_ini.encode(phase, qid_anc, qid_cod)
qs_fin = qs_ini.clone()
# noise
q = np.random.randint(0, len(qid_cod))
kind = np.random.choice(['X','Z','XZ'])
print("* noise '{:}' to #{:} qubit".format(kind, q))
qs_fin.add_noise(q, qid_cod, kind)
# error correction
qs_fin.correct_err(qid_anc, qid_cod)
# result
fid = qs_ini.fidelity(qs_fin, qid=qid_cod)
print("* fidelity = {:.6f}".format(fid))
QState.free_all(qs_ini, qs_fin)
Ich werde erklären, was Sie in der richtigen Reihenfolge tun. Schauen Sie sich den Hauptverarbeitungsabschnitt an.
QState.add_methods(logic_x, logic_z, ctr_logic_x, ctr_logic_z,
ctr_g1, ctr_g2, ctr_g3, ctr_g4,
encode, add_noise, correct_err)
Registrieren Sie nun die oben gezeigte benutzerdefinierte Methode als Methode der QState-Klasse, die den Quantenzustand darstellt.
# create registers
qid_anc = QState.create_register(5)
qid_cod = QState.create_register(5)
qnum = QState.init_register(qid_anc, qid_cod)
Generieren Sie dann das Register, das von nun an verwendet werden soll. Dies setzt die Variablenwerte wie qid_anc = [0,1,2,3,4], qid_cod = [5,6,7,8,9], qnum = 10 [^ 4]. qid_anc ist 5 Hilfsquantenbits. In der oben gezeigten Quantenschaltung sind 5 Hilfsquantenbits erforderlich, um den logischen Basiszustand zu erzeugen, 2, um den Codezustand bei der Quantenteleportation zu erzeugen, und 4, um den Fehler zu korrigieren. Fünf Hilfsquantenbits sind ausreichend, da der Zustand unmittelbar vor jedem Schritt zurückgesetzt werden kann. qid_cod ist ein Quantenbit zur Beschreibung des Vorzeichenzustands. Da es sich um einen 5-Quantenbit-Code handelt, ist es ausreichend, 5 Quantenbits vorzubereiten.
[^ 4]: Natürlich können Sie den Variablenwert direkt festlegen, ohne eine solche Klassenmethode zu verwenden. Wenn es um komplizierte Quantenschaltungen geht, denke ich, dass dies einfacher zu implementieren ist. Das aktuelle Beispiel ist nicht so kompliziert, es spielt also keine Rolle, welches.
# parameters for input quantum state (U3 gate params)
phase = [np.random.rand(), np.random.rand(), np.random.rand()]
Es werden also 3 zufällige Parameterphasen erzeugt, um den Codezustand entsprechend zu erzeugen (3 Parameter für den U3-Gate-Betrieb).
# encode quantum state
qs_ini = QState(qnum)
qs_ini.encode(phase, qid_anc, qid_cod)
qs_fin = qs_ini.clone()
Erstellen Sie also zuerst den Anfangszustand einschließlich des Hilfsquantenbits und konvertieren Sie ihn dann mit der Codierungsmethode in den codierten Zustand. Ich kopiere qs_ini nach qs_fin, um letztendlich zu bewerten, ob der ursprüngliche Zustand und der fehlerkorrigierte Zustand übereinstimmen.
Schauen wir uns nun den Inhalt der Codierungsmethode an.
# g1
self.h(qid_anc[0]).ctr_g1(qid_anc[0], qid_cod).h(qid_anc[0])
self.m(qid=[qid_anc[0]])
mval = self.m_value(binary=True)
if mval == '1': self.z(qid_cod[0]).z(qid_cod[2])
self.reset(qid=[qid_anc[0]])
...
Erstellt den logischen Basiszustand $ \ ket {0_L} $. Führen Sie die in der obigen Quantenschaltung gezeigten Operationen der Reihe nach aus. Der gemessene Wert (Index) der indirekten Messung liegt in der Variablen mval. Wenn dies "0" ist, tun Sie nichts, wenn es "1" ist, berechnen Sie $ g_ {1} ^ {\ prime} = ZIZII $. Wenn Sie fertig sind, setzen Sie das 0. verwendete Hilfsquantenbit zurück. Im Folgenden wird dasselbe für alle anderen Generatoren und den logischen Z-Operator durchgeführt.
self.u3(qid_anc[0], alpha=phase[0], beta=phase[1], gamma=phase[2]) # input quantum state
print("* input quantum state")
self.show(qid=[qid_anc[0]])
Dann wird ein zufälliger 1-Quantenbitzustand erzeugt, indem das U3-Gatter mit der Phase als Argument für das 0. Hilfsquantenbit berechnet wird. Dann wird der Status angezeigt.
self.h(qid_anc[1])
self.ctr_logic_x(qid_anc[1], qid_cod).cx(qid_anc[0], qid_anc[1])
self.h(qid_anc[0])
self.m(qid=qid_anc[0:2])
mval = self.m_value(binary=True)
if mval == '00': pass
elif mval == '01': self.logic_x(qid_cod)
elif mval == '10': self.logic_z(qid_cod)
elif mval == '11': self.logic_x(qid_cod).logic_z(qid_cod)
self.reset(qid=qid_anc)
Führen Sie nun die Quantenteleportationsschaltung aus. Das Ergebnis der Ausführung von zwei Messungen wird in mval eingegeben. Wenn Sie den Operator "Nichts tun" / "Logisches X" / "Logisches Z" / "Logisches XZ" gemäß den vier Mustern ausführen, 5 aus dem zuvor zufällig erstellten 1-Quantenbit-Zustand Sie können den codierten Zustand des Quantenbits erhalten.
Kehren Sie zum Hauptverarbeitungsabschnitt zurück.
# noise
q = np.random.randint(0, len(qid_cod))
kind = np.random.choice(['X','Z','XZ'])
print("* noise '{:}' to #{:} qubit".format(kind, q))
qs_fin.add_noise(q, qid_cod, kind)
Dann wird dem codierten Zustand von 5 Quantenbits zufällig Rauschen hinzugefügt. Wählen Sie zufällig das Quantenbit und zufällig den Rauschtyp aus X / Z / XZ. Die eigentliche Verarbeitung erfolgt mit der Methode add_noise. Einzelheiten finden Sie in der Funktionsdefinition.
# error correction
qs_fin.correct_err(qid_anc, qid_cod)
Führen Sie dann eine Fehlerkorrektur durch.
Werfen wir einen Blick auf den Inhalt der Methoderect_err.
self.reset(qid=qid_anc)
Setzen Sie also zuerst alle Hilfsquantenbits zurück.
# syndrome
self.h(qid_anc[0]).ctr_g1(qid_anc[0], qid_cod).h(qid_anc[0])
self.h(qid_anc[1]).ctr_g2(qid_anc[1], qid_cod).h(qid_anc[1])
self.h(qid_anc[2]).ctr_g3(qid_anc[2], qid_cod).h(qid_anc[2])
self.h(qid_anc[3]).ctr_g4(qid_anc[3], qid_cod).h(qid_anc[3])
self.m(qid=qid_anc[0:4])
mval = self.m_value(binary=True)
print("* syndrome =", mval)
Dann wird eine Fehlererkennungsmessung (Symdrom-Messung) durchgeführt. Der gemessene Wert (4-stellige binäre Zeichenfolge) wird in die Variable mval eingegeben. Abhängig von dieser Binärzeichenfolge,
# recovery
if mval == '0000': pass
elif mval == '0001': self.x(qid_cod[0])
elif mval == '1010': self.z(qid_cod[0])
elif mval == '1011': self.z(qid_cod[0]).x(qid_cod[0])
elif mval == '1000': self.x(qid_cod[1])
elif mval == '0101': self.z(qid_cod[1])
elif mval == '1101': self.z(qid_cod[1]).x(qid_cod[1])
elif mval == '1100': self.x(qid_cod[2])
elif mval == '0010': self.z(qid_cod[2])
elif mval == '1110': self.z(qid_cod[2]).x(qid_cod[2])
elif mval == '0110': self.x(qid_cod[3])
elif mval == '1001': self.z(qid_cod[3])
elif mval == '1111': self.z(qid_cod[3]).x(qid_cod[3])
elif mval == '0011': self.x(qid_cod[4])
elif mval == '0100': self.z(qid_cod[4])
elif mval == '0111': self.z(qid_cod[4]).x(qid_cod[4])
Um die inverse Berechnung des Rauschens durchzuführen. Dies sollte den Vorzeichenstatus wiederherstellen.
Kehren Sie zum Hauptverarbeitungsabschnitt zurück.
# result
fid = qs_ini.fidelity(qs_fin, qid=qid_cod)
print("* fidelity = {:.6f}".format(fid))
Berechnen Sie dann die Wiedergabetreue und zeigen Sie sie an, um den Unterschied zwischen dem ursprünglichen Quantenzustand und dem Quantenzustand nach der Fehlerkorrektur zu sehen [^ 5]. Wenn die Fehlerkorrektur erfolgreich ist, sollte der FID 1.0 sein.
[^ 5]: Wenn das Argument qid für die Wiedergabetreue-Methode angegeben wird, wird die Wiedergabetreue für das Subsystem berechnet, das durch eine bestimmte Liste von Quantenbitnummern dargestellt wird.
Lassen Sie uns nun das obige Programm ausführen.
* input quantum state
c[0] = +0.3382-0.0000*i : 0.1143 |++
c[1] = -0.6657+0.6652*i : 0.8857 |++++++++++
* noise 'Z' to #4 qubit
* syndrome = 0100
* fidelity = 1.000000
Ein richtig eingestellter 1-Quantenzustand wurde codiert und Rauschen 'Z' wurde zum 4. Quantenbit hinzugefügt, aber das Fehlermuster war '0100' ($ E_ {14} = IIIIZ $) durch Syndrom-Messung. Infolge der Durchführung der inversen Berechnung wurde die Wiedergabetreue zu 1,000000. Die Fehlerkorrektur war also erfolgreich.
Wenn Sie es erneut ausführen,
* input quantum state
c[0] = +0.7701-0.0000*i : 0.5931 |+++++++
c[1] = -0.1945+0.6075*i : 0.4069 |+++++
* noise 'X' to #1 qubit
* syndrome = 1000
* fidelity = 1.000000
Dann wurde dem Zustand, der sich vom vorherigen unterscheidet, ein anderes Rauschmuster hinzugefügt, aber die Fehlerkorrektur war auch in diesem Fall erfolgreich. Ich habe es oft versucht und es ist nicht gescheitert. Also, Glückwunsch, Glückwunsch.
Damit ist die viermal fortgesetzte Serie "Stabilizer Code" abgeschlossen. Es gibt einige interessante ungelöste Probleme, wie die Standardform von Stabilisatorcodes und das Erstellen eines logischen Basiszustands nur mit einheitlichen Operationen, aber ich möchte fortfahren. Nächstes Mal planen wir "Fehlertolerante Quantenberechnung".
das ist alles
Recommended Posts