Quantenteleportation und Superdense Coding
Kifumi Numata (26. Apr. 2024)
PDF des Originalvortrags herunterladen. Beachte, dass einige Code-Ausschnitte veraltet sein können, da es sich um statische Bilder handelt.
Die ungefähre QPU-Zeit für dieses Experiment beträgt 10 Sekunden.
1. Einführung
Um ein quantentechnisches Problem im Utility-Maßstab zu lösen, müssen wir in der Lage sein, Information auf einem Quantencomputer von einem Qubit zu einem anderen zu bewegen. Es gibt bekannte Protokolle dafür, wobei einige der grundlegendsten im Kontext der Übertragung von Information zwischen weit entfernten Parteien formuliert wurden. In dieser Lektion verwenden wir gelegentlich Sprechweisen, die zu diesem Kontext passen, etwa „entfernte Freunde, die Information austauschen". Behalte jedoch im Hinterkopf, dass diese Protokolle eine weiterreichende Bedeutung für das Quantencomputing haben. In dieser Lektion betrachten wir die folgenden Quantenkommunikationsprotokolle:
- Quantenteleportation Verwendung eines gemeinsam genutzten verschränkten Zustands (manchmal als E-Bit bezeichnet), um einen unbekannten Quantenzustand an einen entfernten Freund zu senden – ergänzt durch klassische Kommunikation.
- Quantensuperdense Coding Wie man zwei Bits an Information übermittelt, indem man ein einzelnes Qubit an einen entfernten Freund sendet (ebenfalls unter Verwendung zuvor gemeinsam genutzter verschränkter Qubits).
Für weiterführende Hintergrundinformationen zu diesen Themen empfehlen wir Lektion 4 in „Grundlagen der Quanteninformation" über Verschränkung in der Praxis.
In der obigen Beschreibung bezeichnet ein „unbekannter Quantenzustand" einfach einen Zustand der Form, wie er in der vorherigen Lektion beschrieben wurde:
wobei und komplexe Zahlen sind, sodass gilt. Damit können wir den Quantenzustand schreiben als
Da wir in der Lage sein wollen, die Information eines beliebigen Quantenzustands zu übertragen, beginnen wir diese Lektion damit, einen solchen Zustand zu erzeugen.
2. Dichtematrizen
Den Quantenzustand können wir auch als Dichtematrix schreiben. Diese Darstellung ist nützlich, um probabilistische Mischungen reiner Quantenzustände auszudrücken. Im Fall eines einzelnen Qubits lässt sich schreiben:
Die Dichtematrix ist eine Linearkombination der Pauli-Matrizen:
Oder allgemein:
wobei gilt.
Der Bloch-Vektor lautet .
Nun erzeugen wir mithilfe von Zufallszahlen einen beliebigen Quantenzustand.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-aer qiskit-ibm-runtime
import numpy as np
# create a random 1-qubit state from a random (theta, varphi) to define r vector
np.random.seed(1) # fixing seed for repeatibility
theta = np.random.uniform(0.0, 1.0) * np.pi # from 0 to pi
varphi = np.random.uniform(0.0, 2.0) * np.pi # from 0 to 2*pi
def get_r_vec(theta, varphi):
rx = np.sin(theta) * np.cos(varphi)
ry = np.sin(theta) * np.sin(varphi)
rz = np.cos(theta)
return (rx, ry, rz)
# get r vector
rx, ry, rz = get_r_vec(theta, varphi)
print("theta=" + str(theta), ",varphi=" + str(varphi))
print("(rx, ry, rz) = (" + str(rx) + ", " + str(ry) + ", " + str(rz) + ")")
theta=1.3101132663588946 ,varphi=4.525932273597346
(rx, ry, rz) = (-0.1791150283307452, -0.9494670044331133, 0.2577405946274022)
Wir können diesen Bloch-Vektor auf der Bloch-Kugel darstellen.
from qiskit.visualization import plot_bloch_vector
r = [rx, ry, rz]
plot_bloch_vector(r)
3. Quantenzustandstomographie
Misst man den Quantenzustand nur in der Rechenbasis ( und ), geht die Phaseninformation (die Information über komplexe Zahlen) verloren. Wenn wir jedoch viele Kopien von erzeugen, indem wir den Präparationsprozess wiederholen (Zustände können nicht geklont werden, aber Präparationsprozesse können wiederholt werden), können wir die Werte von durch Quantenzustandstomographie der Dichtematrix schätzen. Ausgehend von der Form:
gilt:
Im Fall von :
Die letzte Umformung gilt für . Wir erhalten also als Wahrscheinlichkeit von minus Wahrscheinlichkeit von .
Schätzung des -Werts
Um zu schätzen, erzeugen wir einen Quantenzustand und messen ihn. Diesen Vorgang wiederholen wir viele Male. Anschließend nutzen wir die Messstatistik, um die obigen Wahrscheinlichkeiten zu schätzen und damit zu bestimmen.
Zur Erzeugung des zufälligen Quantenzustands verwenden wir das allgemeine unitäre Gate mit den Parametern . (Weitere Informationen findest du unter U-Gate.)
from qiskit import QuantumCircuit
# create a 1-qubit quantum state psi from theta, varphi parameters
qc = QuantumCircuit(1, 1)
qc.u(theta, varphi, 0.0, 0)
# measure in computational basis
qc.measure(0, 0)
qc.draw(output="mpl")
Mithilfe des AerSimulator messen wir in der Rechenbasis, um zu schätzen.
# see if the expected value of measuring in the computational basis
# approaches the limit of rz
from qiskit_aer import AerSimulator
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import Sampler
from qiskit.visualization import plot_histogram
# Define backend
backend = AerSimulator()
nshots = 1000 # or 10000
# nshots = 10000
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=nshots)
result = job.result()
# Extract counts data
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
{'1': 375, '0': 625}
rz_approx = (counts["0"] - counts["1"]) / nshots
print("rz = ", rz, " and approx of rz = ", rz_approx)
rz = 0.2577405946274022 and approx of rz = 0.25
Mithilfe der Quantenzustandstomographie haben wir den Wert geschätzt. Da wir in diesem Fall den Parameter für den „zufälligen" Zustand selbst gewählt haben, kennen wir den wahren Wert von und können unser Ergebnis überprüfen. Bei echter Arbeit im Utility-Maßstab ist eine solche Überprüfung jedoch nicht immer so einfach möglich. Wie wir Quantenergebnisse überprüfen können, besprechen wir später in diesem Kurs. Für jetzt sei einfach festgehalten, dass unsere Schätzung hinreichend genau war.
Übung 1: Schätzung des -Werts
IBM® Quantencomputer messen entlang der -Achse (manchmal auch als „in der -Basis" oder „in der Rechenbasis" bezeichnet). Durch Rotationen vor der Messung können wir jedoch auch die Projektion des Quantenzustands auf die x-Achse messen. Genauer gesagt: Wenn wir unser System so drehen, dass das, was zuvor entlang von zeigte, nun entlang von zeigt, können wir dieselbe Messhardware entlang von verwenden und trotzdem Information über den Zustand gewinnen, der eben noch entlang von war. So führen die meisten Quantencomputer (und alle IBM Quantencomputer) Messungen entlang mehrerer Achsen durch.
Mit diesem Verständnis versuch selbst, Code zu schreiben, der den Wert von mithilfe der Quantenzustandstomographie schätzt.
Lösung:
# create a 1-qubit quantum state psi from theta, varphi parameters
qc = QuantumCircuit(1, 1)
qc.u(theta, varphi, 0.0, 0)
qc.h(0)
qc.measure(0, 0)
qc.draw(output="mpl")
# Define backend
backend = AerSimulator()
nshots = 10000
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=nshots)
result = job.result()
# Extract counts data
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
{'1': 5925, '0': 4075}
rx_approx = (counts["0"] - counts["1"]) / nshots
print("rx = ", rx, " and approx of rx = ", rx_approx)
rx = -0.1791150283307452 and approx of rx = -0.185
Übung 2: Schätzung des -Werts
Mit denselben logischen Überlegungen wie zuvor können wir das System vor der Messung drehen, um zu bestimmen.
Versuch selbst, Code zu schreiben, der den Wert von mithilfe der Quantenzustandstomographie schätzt. Du kannst beim vorherigen Beispiel ansetzen und andere Rotationen vornehmen. (Weitere Informationen zu den verwendeten Gates, einschließlich sdg, findest du in der API-Referenz.)
Lösung:
# create a 1-qubit quantum state psi from theta, varphi parameters
qc = QuantumCircuit(1, 1)
qc.u(theta, varphi, 0.0, 0)
qc.sdg(0)
qc.h(0)
qc.measure(0, 0)
qc.draw(output="mpl")
# Define backend
backend = AerSimulator()
nshots = 10000
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=nshots)
result = job.result()
# Extract counts data
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
{'1': 9759, '0': 241}
ry_approx = (counts["0"] - counts["1"]) / nshots
print("ry = ", ry, " and approx of ry = ", ry_approx)
ry = -0.9494670044331133 and approx of ry = -0.9518
Damit haben wir alle Komponenten von geschätzt und können den vollständigen Vektor angeben.
print("Estimated vector is (", rx_approx, ",", ry_approx, ",", rz_approx, ").")
print("Original random vector was (" + str(rx) + ", " + str(ry) + ", " + str(rz) + ").")
Estimated vector is ( -0.185 , -0.9518 , 0.25 ).
Original random vector was (-0.1791150283307452, -0.9494670044331133, 0.2577405946274022).
Mit dieser Methode der Quantenzustandstomographie hast du den ursprünglichen Zufallsvektor recht genau geschätzt.
4. Quantenteleportation
Stellen wir uns vor, eine Person namens Alice möchte einen unbekannten Quantenzustand an ihren weit entfernten Freund Bob senden. Angenommen, sie können nur klassisch kommunizieren (z. B. per E-Mail oder Telefon). Alice kann den Quantenzustand nicht kopieren (wegen des No-Cloning-Theorems). Wenn sie denselben Präparationsprozess viele Male wiederholen würde, könnte sie Statistiken aufbauen, wie wir es gerade getan haben. Aber was, wenn es nur einen einzigen unbekannten Zustand gibt? Dieser könnte aus einem physikalischen Prozess stammen, den du untersuchen möchtest, oder Teil einer größeren Quantenberechnung sein. Wie könnte Alice den Zustand dann zu Bob übertragen? Das ist möglich, wenn sie und Bob eine wertvolle Quantenressource teilen: einen gemeinsam genutzten verschränkten Zustand, wie den Bell-Zustand aus der vorherigen Lektion: Manchmal wird dieser auch als „EPR-Paar" oder „E-Bit" (eine fundamentale Einheit der Verschränkung) bezeichnet. Wenn Alice einen solchen verschränkten Zustand mit Bob teilt, kann sie den unbekannten Quantenzustand durch eine Reihe von Quantenoperationen zu Bob teleportieren und ihm dabei zwei Bits klassischer Information übermitteln.
4.1 Das Protokoll der Quantenteleportation
Voraussetzung: Alice hat einen unbekannten Quantenzustand , der an Bob gesendet werden soll. Alice und Bob teilen einen verschränkten 2-Qubit-Zustand (E-Bit), wobei jeder eines der Qubits an seinem physischen Standort hat.
Hier skizzieren wir das Verfahren ohne ausführliche Erklärung. Die Details werden weiter unten implementiert.
- Alice verschränkt mit ihrem Teil des E-Bits über das CNOT-Gate.
- Alice wendet ein Hadamard-Gate auf an und misst beide ihrer Qubits in der Rechenbasis.
- Alice sendet Bob ihre Messergebnisse (entweder „00", „01", „10" oder „11").
- Bob führt basierend auf Alices zwei Bits Information eine Korrektur an seinem Teil des E-Bit-Paares durch.
- Bei „00" tut Bob nichts.
- Bei „01" wendet Bob das X-Gate an.
- Bei „10" wendet Bob das Z-Gate an.
- Bei „11" wendet Bob iY = ZX an.
- Bobs Teil des E-Bits wird zu .
Dies wird auch in Grundlagen der Quanteninformation ausführlicher behandelt. Das Vorgehen wird beim Umsetzen in Qiskit noch klarer.
4.2 Quantenschaltkreis zur Simulation der Quantenteleportation
Wie gewohnt wenden wir das Qiskit-Patterns-Framework an. Dieser Abschnitt konzentriert sich ausschließlich auf das Mapping.
Schritt 1: Problem auf Quantenschaltkreise und Operatoren abbilden
Um das obige Szenario zu beschreiben, benötigen wir einen Circuit mit drei Qubits: zwei für das von Alice und Bob gemeinsam genutzte verschränkte Paar und eines für den unbekannten Quantenzustand .
from qiskit import QuantumCircuit
import numpy as np
# create 3-qubits circuit
qc = QuantumCircuit(3, 3)
qc.draw(output="mpl")
Zu Beginn hat Alice einen unbekannten Quantenzustand . Wir erzeugen diesen mithilfe des -Gates.
# Create the unknown quantum state using the u-gate. Alice has this.
qc.u(theta, varphi, 0.0, 0)
qc.barrier() # for visual separation
qc.draw(output="mpl")
Wir können den erzeugten Zustand visualisieren – aber nur, weil wir wissen, welche Parameter im -Gate verwendet wurden. Wäre dieser Zustand aus einem komplexen Quantenprozess entstanden, wäre er ohne viele Wiederholungen des Prozesses und das Sammeln von Statistiken (wie bei der Tomographie) nicht rekonstruierbar.
# show the quantum state on bloch sphere
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_bloch_multivector
out_vector = Statevector(qc)
plot_bloch_multivector(out_vector)

Bevor dieses Protokoll überhaupt beginnt, setzen wir voraus, dass Alice und Bob bereits ein gemeinsames verschränktes Paar haben. Wenn Alice und Bob sich wirklich an verschiedenen Orten befinden, haben sie den gemeinsamen Zustand möglicherweise aufgebaut, bevor der unbekannte Zustand überhaupt erzeugt wurde. Da diese Vorgänge auf verschiedenen Qubits stattfinden, spielt ihre Reihenfolge hier keine Rolle – und diese Reihenfolge ist für die Visualisierung praktisch.
# Alice and Bob are together in the same place and set up an entangled pair.
qc.h(1)
qc.cx(1, 2)
qc.barrier() # for visual separation.
# We can consider that Alice and Bob might move their qubits to different physical locations, now.
qc.draw(output="mpl")
Als Nächstes verschränkt Alice mit ihrem Teil des gemeinsamen E-Bits, indem sie das -Gate und das -Gate verwendet, und misst beide in der Rechenbasis.
# Alice entangles the unknown state with her part of the e-bit, using the CNOT gate and H gate.
qc.cx(0, 1)
qc.h(0)
qc.barrier()
# Alice measures the two qubits.
qc.measure(0, 0)
qc.measure(1, 1)
qc.draw(output="mpl")
Alice sendet Bob ihre Messergebnisse (entweder „00", „01", „10" oder „11"), und Bob führt basierend auf Alices zwei Bits Information eine Korrektur an seinem Teil des gemeinsamen E-Bits durch. Anschließend wird Bobs Qubit zu .
# Alice sent the results to Bob. Bob applies correction
with qc.if_test((0, 1)):
qc.z(2)
with qc.if_test((1, 1)):
qc.x(2)
qc.barrier()
qc.draw(output="mpl")
Du hast einen Quantenteleportations-Circuit fertiggestellt! Schauen wir uns den Ausgabezustand dieses Circuits mithilfe des Statevector-Simulators an.
from qiskit_aer import StatevectorSimulator
backend = StatevectorSimulator()
out_vector = backend.run(qc, shots=1).result().get_statevector() # set shots = 1
plot_bloch_multivector(out_vector)

Du kannst sehen, dass der durch das -Gate von Qubit 0 erzeugte Quantenzustand (das Qubit, das ursprünglich den geheimen Zustand hält) auf Qubit 2 (Bobs Qubit) übertragen wurde.
Du kannst die obige Zelle mehrmals ausführen, um dich zu überzeugen. Vielleicht fällt dir auf, dass sich die Qubits 0 und 1 verändern, Qubit 2 aber stets im Zustand bleibt.
4.3 Ausführen und Überprüfen durch Anwenden von U invers
Oben haben wir visuell überprüft, dass der teleportierte Zustand korrekt aussieht. Eine weitere Möglichkeit, die korrekte Teleportation zu bestätigen, besteht darin, das inverse -Gate auf Bobs Qubit anzuwenden, sodass wir „0" messen können. Da die Identität ergibt: Wenn Bobs Qubit im Zustand ist, sollte das Anwenden der Inversen ergeben.
# Apply the inverse of u-gate to measure |0>
qc.u(theta, varphi, 0.0, 2).inverse() # inverse of u(theta,varphi,0.0)
qc.measure(2, 2) # add measurement gate
qc.draw(output="mpl")
Wir führen den Circuit zunächst mit dem AerSimulator aus, bevor wir zu einem echten Quantencomputer übergehen.
from qiskit_aer import AerSimulator
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import Sampler
from qiskit.visualization import plot_histogram
# Define backend
backend = AerSimulator()
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=nshots)
result = job.result()
# Extract counts data
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
{'011': 2510, '010': 2417, '000': 2635, '001': 2438}
In der Little-Endian-Notation ist Qubit 2 das ganz linke (bzw. unterste in den Spaltenbeschriftungen) Qubit. Beachte, dass das am weitesten links bzw. unten stehende Qubit in den Spaltenbeschriftungen bei allen möglichen Ergebnissen eine 0 ist. Das zeigt uns, dass die Wahrscheinlichkeit, im Zustand zu messen, 100 % beträgt. Dies ist das erwartete Ergebnis und bestätigt, dass das Teleportationsprotokoll korrekt funktioniert hat.
4.4 Teleportation auf einem echten Quantencomputer
Als Nächstes führen wir die Teleportation auf einem echten Quantencomputer durch. Mithilfe der Dynamic-Circuit-Funktion können wir während des Circuits auf Messergebnisse reagieren und die bedingten Operationen im Teleportations-Circuit in Echtzeit ausführen. Zur Lösung von Problemen mit echten Quantencomputern folgen wir den vier Schritten der Qiskit Patterns.
- Problem auf Quantenschaltkreise und Operatoren abbilden
- Für die Zielhardware optimieren
- Auf der Zielhardware ausführen
- Ergebnisse nachverarbeiten
Übung 3: Teleportations-Circuit aufbauen
Versuch, den gesamten Teleportations-Circuit von Grund auf neu aufzubauen, um dein Verständnis zu testen. Scroll nach oben, falls du eine Erinnerungsstütze brauchst.
Lösung:
# Step 1: Map problem to quantum circuits and operators
# Create the circuit with 3-qubits and 1-bit
qc = QuantumCircuit(3, 3)
# Alice creates an unknown quantum state using the u-gate.
qc.u(theta, varphi, 0.0, 0)
qc.barrier() # for visual separation
# Eve creates EPR pair and sends q1 to Alice and q2 to Bob
##your code goes here##
qc.h(1)
qc.cx(1, 2)
qc.barrier()
# Alice entangles the unknown state with her EPR part, using the CNOT gate and H gate.
##your code goes here##
qc.cx(0, 1)
qc.h(0)
qc.barrier()
# Alice measures the two qubits.
##your code goes here##
qc.measure(0, 0)
qc.measure(1, 1)
# Alice sent the results to Bob. Now, Bob applies correction
##your code goes here##
with qc.if_test((0, 1)):
qc.z(2)
with qc.if_test((1, 1)):
qc.x(2)
qc.barrier()
# Apply the inverse of u-gate to measure |0>
qc.u(theta, varphi, 0.0, 2).inverse()
qc.measure(2, 2)
qc.draw(output="mpl")
Zur Erinnerung: Das Anwenden des inversen -Gates dient nur der Überprüfung des erwarteten Verhaltens. Es ist kein Bestandteil der Übertragung des Zustands zu Bob, und wir würden dieses inverse -Gate nicht verwenden, wenn das einzige Ziel die Übertragung von Quanteninformation wäre.
Schritt 2: Für die Zielhardware optimieren
Um auf Hardware auszuführen, importiere QiskitRuntimeService und lade deine gespeicherten Zugangsdaten. Wähle den Backend mit der kürzesten Warteschlange.
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
service.backends()
[<IBMBackend('ibm_brisbane')>,
<IBMBackend('ibm_torino')>]
# You can also identify the least busy device
backend = service.least_busy(operational=True)
print("The least busy device is ", backend)
The least busy device is <IBMBackend('ibm_brisbane')>
# You can specify the device
# backend = service.backend('ibm_brisbane')
Schauen wir uns die Coupling Map des ausgewählten Geräts an.
from qiskit.visualization import plot_gate_map
plot_gate_map(backend)

Verschiedene Geräte können unterschiedliche Coupling Maps haben, und jedes Gerät verfügt über Qubits und Koppler, die leistungsfähiger sind als andere. Zudem können verschiedene Quantencomputer unterschiedliche native Gates haben (Gates, die die Hardware direkt ausführen kann). Beim Transpilieren wird der abstrakte Quantenschaltkreis in Gates umgeschrieben, die der Zielquantencomputer ausführen kann, und es wird das optimale Mapping auf physische Qubits gewählt (unter anderem). Transpilation ist ein umfangreiches und vielschichtiges Thema. Weitere Informationen dazu findest du in der API-Referenz.
# Step 2: Optimize for target hardware
# Transpile the circuit into basis gates executable on the hardware
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
pm = generate_preset_pass_manager(backend=backend, optimization_level=2)
qc_compiled = pm.run(qc)
qc_compiled.draw("mpl", idle_wires=False, fold=-1)
Schritt 3: Circuit ausführen
Mithilfe des Sampler Runtime-Primitives führen wir den Ziel-Circuit aus.
# Step 3: Execute the target circuit
sampler = Sampler(backend)
job = sampler.run([qc_compiled])
job_id = job.job_id()
print("job id:", job_id)
job id: d13nkhpn2txg008jt0d0
# Check the job status
job.status()
'DONE'
Du kannst den Job-Status auch über dein IBM Quantum® Dashboard überprüfen.
# If the Notebook session got disconnected you can also check your job status by running the following code
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
job_real = service.job(job.job_id()) # Input your job-id between the quotations
job_real.status()
'DONE'
Wenn 'DONE' angezeigt wird, kannst du das Ergebnis mit der folgenden Zelle abrufen.
# Execute after 'DONE' is displayed
result_real = job_real.result()
print(result_real[0].data.c.get_counts())
{'001': 992, '110': 430, '011': 579, '010': 605, '111': 402, '000': 925, '100': 57, '101': 106}
Schritt 4: Ergebnisse nachverarbeiten
# Step 4: Post-process the results
from qiskit.visualization import plot_histogram
plot_histogram(result_real[0].data.c.get_counts())
Du kannst die obigen Ergebnisse direkt interpretieren. Alternativ kannst du mit marginal_count Bobs Ergebnisse auf Qubit 2 herausfiltern.
# trace out Bob's results on qubit 2
from qiskit.result import marginal_counts
bobs_qubit = 2
real_counts = result_real[0].data.c.get_counts()
bobs_counts = marginal_counts(real_counts, [bobs_qubit])
plot_histogram(bobs_counts)
Wie wir hier sehen, gibt es einige Ergebnisse, bei denen gemessen wurde. Diese sind auf Rauschen und Fehler zurückzuführen. Insbesondere haben dynamische Circuits eine höhere Fehlerrate, weil die Messung mitten im Circuit zeitaufwendig ist.
4.5 Wichtigste Erkenntnisse zur Quantenteleportation
Mithilfe eines gemeinsam genutzten verschränkten Qubit-Paares (E-Bit) können wir einen Quantenzustand zu einem entfernten Freund übertragen.
-
Kann Quantenteleportation den Quantenzustand schneller als Licht senden? Nein, denn Alice muss Bob die Messergebnisse auf klassischem Weg mitteilen.
-
Würde Quantenteleportation das „No-Cloning-Theorem" verletzen, das das Kopieren eines Quantenzustands verbietet? Nein, denn der ursprüngliche Quantenzustand, den Alice auf einem ihrer Qubits hatte, ging bei der Messung verloren – er kollabierte zu oder .
5. Superdense Coding
Nahezu dieselbe Konfiguration lässt sich für einen anderen Zweck nutzen. Angenommen, Alice möchte Bob zwei Bits klassischer Information senden, hat aber keine klassischen Kommunikationsmittel. Sie teilt jedoch ein verschränktes Paar mit Bob und darf ihr Qubit zu Bobs Standort schicken. Beachte den Unterschied zum Quantenteleportationsprotokoll: Bei der Teleportation war klassische Kommunikation verfügbar, und das Ziel war, einen Quantenzustand zu senden. Hier ist klassische Kommunikation nicht möglich, und sie nutzen die Übertragung eines Qubits, um zwei Bits klassischer Information auszutauschen.
5.1 Das Protokoll des Superdense Codings
Voraussetzung: Alice hat zwei Bits Information, etwa . Alice und Bob teilen ein verschränktes Paar (E-Bit), können aber nicht klassisch kommunizieren.
- Alice führt eine der folgenden Operationen an ihrem Teil des E-Bits durch.
- Wenn , tut sie nichts.
- Wenn , wendet sie das Z-Gate an.
- Wenn , wendet sie das X-Gate an.
- Wenn , wendet sie das Z-Gate und das X-Gate an.
- Alice schickt ihren Teil des E-Bits zu Bobs Standort.
- Bob wendet ein CNOT-Gate an (Alices Qubit als Kontrolle, sein Qubit als Ziel), dann ein H-Gate auf Alices Qubit, und misst beide Qubits. Die möglichen Ausgangszustände und Ergebnisse von Bobs Operationen sind:
Das negative Vorzeichen bei ist eine globale Phase und damit nicht messbar.
5.2 Quantenschaltkreis zur Simulation des Superdense Codings
Auf Basis des Superdense-Coding-Protokolls kannst du den Superdense-Coding-Circuit wie folgt aufbauen. Versuche, die Nachricht msg, die Alice an Bob übermitteln möchte, zu ändern.
from qiskit import QuantumCircuit
Die Schritte des Qiskit-Patterns sind in den Code-Kommentaren angegeben.
# Step 1: Map problem to quantum circuits and operators
# Create 2-qubits circuit
qc = QuantumCircuit(2, 2)
# Eve creates EPR pair and send q0 to Alice and q1 to Bob
qc.h(0)
qc.cx(0, 1)
qc.barrier()
# set message which Alice wants to transform to Bob
msg = "11" # You can change the message
if msg == "00":
pass
elif msg == "10":
qc.x(0)
elif msg == "01":
qc.z(0)
elif msg == "11":
qc.z(0)
qc.x(0)
qc.barrier()
# Bob receives EPR qubit from Alice and performs unitary operations
qc.cx(0, 1)
qc.h(0)
qc.barrier()
# Bob measures q0 and q1
qc.measure(0, 0)
qc.measure(1, 1)
qc.draw(output="mpl")
# We will execute on a simulator first
from qiskit_aer import AerSimulator
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import Sampler
# Define backend
backend = AerSimulator()
shots = 1000
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job_sim = sampler.run([isa_qc], shots=shots)
result_sim = job_sim.result()
# Extract counts data
counts = result_sim[0].data.c.get_counts()
print(counts)
{'11': 1000}
# Visualize the results
from qiskit.visualization import plot_histogram
plot_histogram(counts)
Du siehst, dass Bob die Nachricht erhalten hat, die Alice ihm senden wollte.
Als Nächstes probieren wir es mit einem echten Quantencomputer.
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
backend = service.least_busy(operational=True)
print("The least busy device is ", backend)
The least busy device is <IBMBackend('ibm_brisbane')>
# Step 1 was already completed before the simulator job above.
# Step 2: Optimize for target hardware
# Transpile the circuit into basis gates executable on the hardware
pm = generate_preset_pass_manager(backend=backend, optimization_level=2)
qc_compiled = pm.run(qc)
qc_compiled.draw("mpl", idle_wires=False)
# Step 3:Execute the target circuit
sampler = Sampler(backend)
job = sampler.run([qc_compiled])
job_id = job.job_id()
print("job id:", job_id)
job id: d13nnyq3grvg008j0zag
# Check the job status
job.status()
'DONE'
# If the Notebook session got disconnected you can also check your job status by running the following code
# from qiskit_ibm_runtime import QiskitRuntimeService
# service = QiskitRuntimeService()
job = service.job(job_id) # Input your job-id between the quotations
job.status()
'DONE'
# Execute after job has successfully run
real_result = job.result()
print(real_result[0].data.c.get_counts())
{'11': 3942, '01': 107, '10': 41, '00': 6}
# Step 4: post-process the results
from qiskit.visualization import plot_histogram
plot_histogram(real_result[0].data.c.get_counts())
Das Ergebnis entspricht unseren Erwartungen. Bemerkenswert ist, dass Superdense Coding auf einem echten Quantencomputer weniger Fehler aufweist als Quantenteleportation. Ein Grund dafür könnte sein, dass Quantenteleportation dynamische Circuits verwendet, Superdense Coding jedoch nicht. Mehr über Fehler in Quantenschaltkreisen lernen wir in späteren Lektionen.
6. Zusammenfassung
In dieser Lektion haben wir zwei Quantenprotokolle implementiert. Obwohl die Szenarien mit entfernten Freunden etwas vom Quantencomputing auf einer einzelnen QPU entfernt sind, haben sie Anwendungen im Quantencomputing und helfen uns, die Übertragung von Quanteninformation besser zu verstehen.
- Quantenteleportation: Obwohl wir Quantenzustände nicht kopieren können, können wir unbekannte Quantenzustände mithilfe gemeinsamer Verschränkung teleportieren.
- Quantensuperdense Coding: Ein gemeinsames verschränktes Paar und die Übertragung eines Qubits ermöglichen die Kommunikation von zwei Bits klassischer Information.
# See the version of Qiskit
import qiskit
qiskit.__version__
'2.0.2'