Teleportación cuántica y codificación superdensa
Kifumi Numata (26 abr 2024)
Descargar el PDF de la clase original. Ten en cuenta que algunos fragmentos de código pueden estar desactualizados, ya que son imágenes estáticas.
El tiempo aproximado de QPU para este experimento es de 10 segundos.
1. Introducción
Para resolver un problema cuántico a escala de utilidad, necesitamos ser capaces de mover información en un computador cuántico de un qubit a otro. Existen protocolos conocidos para ello, y algunos de los más fundamentales se formularon en el contexto de la transferencia de información entre partes distantes. En esta lección, ocasionalmente usamos un lenguaje que se ajusta a este contexto, como "amigos distantes que intercambian información". Sin embargo, ten en cuenta que estos protocolos tienen una importancia más amplia para la computación cuántica. En esta lección consideramos los siguientes protocolos de comunicación cuántica:
- Teleportación cuántica Uso de un estado entrelazado compartido (a veces denominado E-bit) para enviar un estado cuántico desconocido a un amigo distante, complementado con comunicación clásica.
- Codificación superdensa cuántica Cómo transmitir dos bits de información enviando un solo qubit a un amigo distante (también utilizando qubits entrelazados previamente compartidos).
Para información de fondo adicional sobre estos temas, recomendamos la lección 4 en "Fundamentos de la información cuántica" sobre Entrelazamiento en acción.
En la descripción anterior, un "estado cuántico desconocido" es simplemente un estado de la forma descrita en la lección anterior:
donde y son números complejos tales que . Con esto podemos escribir el estado cuántico como
Dado que queremos ser capaces de transferir la información de un estado cuántico arbitrario, comenzamos esta lección creando dicho estado.
2. Matrices de densidad
El estado cuántico también puede escribirse como una matriz de densidad. Esta representación es útil para expresar mezclas probabilísticas de estados cuánticos puros. En el caso de un solo qubit, se puede escribir:
La matriz de densidad es una combinación lineal de las matrices de Pauli:
O en general:
donde .
El vector de Bloch es .
Ahora creamos un estado cuántico arbitrario usando números aleatorios.
# 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)
Podemos representar este vector de Bloch en la esfera de Bloch.
from qiskit.visualization import plot_bloch_vector
r = [rx, ry, rz]
plot_bloch_vector(r)
3. Tomografía de estado cuántico
Si medimos el estado cuántico solo en la base computacional ( y ), se pierde la información de fase (la información sobre números complejos). Sin embargo, si generamos muchas copias de repitiendo el proceso de preparación (los estados no pueden clonarse, pero los procesos de preparación pueden repetirse), podemos estimar los valores de mediante tomografía de estado cuántico de la matriz de densidad . Partiendo de la forma:
se cumple:
En el caso de :
La última transformación se cumple para . Obtenemos como la probabilidad de menos la probabilidad de .
Estimación del valor
Para estimar , generamos un estado cuántico y lo medimos. Repetimos este proceso muchas veces. Luego usamos la estadística de medición para estimar las probabilidades anteriores y así determinar .
Para generar el estado cuántico aleatorio, usamos la puerta unitaria general con los parámetros . (Más información en 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")
Usando el AerSimulator, medimos en la base computacional para estimar .
# 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
Mediante la tomografía de estado cuántico, hemos estimado el valor . Como en este caso nosotros mismos elegimos el parámetro para el estado "aleatorio", conocemos el valor verdadero de y podemos verificar nuestro resultado. Sin embargo, en el trabajo real a escala de utilidad, dicha verificación no siempre es tan sencilla. Cómo verificar resultados cuánticos lo discutiremos más adelante en este curso. Por ahora, simplemente señalemos que nuestra estimación fue suficientemente precisa.
Ejercicio 1: Estimación del valor
Los computadores cuánticos de IBM® miden a lo largo del eje (a veces también se dice "en la base " o "en la base computacional"). Sin embargo, mediante rotaciones antes de la medición, también podemos medir la proyección del estado cuántico en el eje x. M ás concretamente: si rotamos nuestro sistema de modo que lo que antes apuntaba a lo largo de ahora apunte a lo largo de , podemos usar el mismo hardware de medición a lo largo de y aún así obtener información sobre el estado que antes estaba a lo largo de . Así es como la mayoría de los computadores cuánticos (y todos los computadores cuánticos de IBM) realizan mediciones a lo largo de múltiples ejes.
Con esta comprensión, intenta escribir código que estime el valor de usando tomografía de estado cuántico.
Solución:
# 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
Ejercicio 2: Estimación del valor
Con la misma lógica que antes, podemos rotar el sistema antes de la medición para determinar .
Intenta escribir código que estime el valor de usando tomografía de estado cuántico. Puedes partir del ejemplo anterior y realizar otras rotaciones. (Más información sobre las puertas utilizadas, incluyendo sdg, en la referencia de la API.)
Solución:
# 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
Con esto hemos estimado todos los componentes de y podemos indicar el vector completo.
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).
Con este método de tomografía de estado cuántico, has estimado el vector aleatorio original con bastante precisión.
4. Teleportación cuántica
Imaginemos que una persona llamada Alice quiere enviar un estado cuántico desconocido a su amigo distante Bob. Supongamos que solo pueden comunicarse clásicamente (por ejemplo, por correo electrónico o teléfono). Alice no puede copiar el estado cuántico (debido al teorema de no clonación). Si repitiera el mismo proceso de preparación muchas veces, podría construir estadísticas, como acabamos de hacer. Pero, ¿qué pasa si solo hay un único estado desconocido? Este podría provenir de un proceso físico que deseas investigar, o ser parte de un cálculo cuántico más grande. ¿Cómo podría Alice transferir el estado a Bob? Esto es posible si ella y Bob comparten un recurso cuántico valioso: un estado entrelazado compartido, como el estado de Bell de la lección anterior: A veces también se denomina "par EPR" o "E-bit" (una unidad fundamental de entrelazamiento). Si Alice comparte un estado entrelazado de este tipo con Bob, puede teleportar el estado cuántico desconocido a Bob mediante una serie de operaciones cuánticas y transmitiéndole dos bits de información clásica.
4.1 El protocolo de teleportación cuántica
Requisito previo: Alice tiene un estado cuántico desconocido que debe enviarse a Bob. Alice y Bob comparten un estado entrelazado de 2 qubits (E-bit), donde cada uno tiene uno de los qubits en su ubicación física.
Aquí describimos el procedimiento sin explicación detallada. Los detalles se implementan más abajo.
- Alice entrelaza con su parte del E-bit mediante la puerta CNOT.
- Alice aplica una puerta Hadamard a y mide ambos qubits en la base computacional.
- Alice envía a Bob sus resultados de medición ("00", "01", "10" o "11").
- Bob realiza una corrección en su parte del par E-bit basándose en los dos bits de información de Alice.
- Para "00", Bob no hace nada.
- Para "01", Bob aplica la puerta X.
- Para "10", Bob aplica la puerta Z.
- Para "11", Bob aplica iY = ZX.
- La parte de Bob del E-bit se convierte en .
Esto también se cubre con más detalle en Fundamentos de la información cuántica. El procedimiento se hará más claro al implementarlo en Qiskit.
4.2 Circuito cuántico para simular la teleportación cuántica
Como es habitual, aplicamos el framework Qiskit Patterns. Esta sección se centra exclusivamente en el mapeo.
Paso 1: Mapear el problema a circuitos cuánticos y operadores
Para describir el escenario anterior, necesitamos un circuito con tres qubits: dos para el par entrelazado compartido por Alice y Bob y uno para el estado cuántico desconocido .
from qiskit import QuantumCircuit
import numpy as np
# create 3-qubits circuit
qc = QuantumCircuit(3, 3)
qc.draw(output="mpl")
Al principio, Alice tiene un estado cuántico desconocido . Lo creamos usando la puerta .
# 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")
Podemos visualizar el estado creado, pero solo porque sabemos qué parámetros se usaron en la puerta . Si este estado proviniera de un proceso cuántico complejo, no sería reconstruible sin muchas repeticiones del proceso y la recopilación de estadísticas (como en la tomografía).
# 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)

Antes de que este protocolo comience, presuponemos que Alice y Bob ya tienen un par entrelazado compartido. Si Alice y Bob realmente se encuentran en diferentes ubicaciones, pueden haber establecido el estado compartido antes de que el estado desconocido fuera creado. Como estas operaciones ocurren en qubits diferentes, su orden no importa aquí, y este orden es práctico para la visualización.
# 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")
A continuación, Alice entrelaza con su parte del E-bit compartido, usando la puerta y la puerta , y mide ambos en la base computacional.
# 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 envía a Bob sus resultados de medición ("00", "01", "10" o "11"), y Bob realiza una corrección en su parte del E-bit compartido basándose en los dos bits de información de Alice. Después, el qubit de Bob se convierte en .
# 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")
Has completado un circuito de teleportación cuántica. Veamos el estado de salida de este circuito usando el simulador de vector de estado.
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)

Puedes ver que el estado cuántico creado por la puerta del qubit 0 (el qubit que originalmente contenía el estado secreto) fue transferido al qubit 2 (el qubit de Bob).
Puedes ejecutar la celda anterior varias veces para convencerte. Quizás notes que los qubits 0 y 1 cambian, pero el qubit 2 siempre permanece en el estado .
4.3 Ejecutar y confirmar el resultado aplicando U inversa
Arriba verificamos visualmente que el estado teleportado se ve correcto. Otra forma de confirmar la teleportación correcta es aplicar la puerta inversa al qubit de Bob, de modo que podamos medir "0". Como da la identidad: si el qubit de Bob está en el estado , aplicar la inversa debería dar .
# 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")
Ejecutamos el circuito primero con el AerSimulator antes de pasar a un computador cuántico real.
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}
En la notación Little-Endian, el qubit 2 es el más a la izquierda (o el inferior en las etiquetas de columnas). Observa que el qubit más a la izquierda en las etiquetas de columnas es un 0 en todos los resultados posibles. Esto nos indica que la probabilidad de medir en el estado es del 100%. Este es el resultado esperado y confirma que el protocolo de teleportación ha funcionado correctamente.
4.4 Teleportación en un computador cuántico real
A continuación, ejecutamos la teleportación en un computador cuántico real. Usando la función de circuitos dinámicos, podemos reaccionar a los resultados de medición durante el circuito y ejecutar las operaciones condicionales del circuito de teleportación en tiempo real. Para resolver problemas con computadores cuánticos reales, seguimos los cuatro pasos de Qiskit Patterns.
- Mapear el problema a circuitos cuánticos y operadores
- Optimizar para el hardware objetivo
- Ejecutar en el hardware objetivo
- Postprocesar los resultados
Ejercicio 3: Construir el circuito de teleportación
Intenta construir todo el circuito de teleportación desde cero para poner a prueba tu comprensión. Desplázate hacia arriba si necesitas una referencia.
Solución:
# 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")
Como recordatorio: aplicar la puerta inversa solo sirve para verificar el comportamiento esperado. No es parte de la transferencia del estado a Bob, y no usaríamos esta puerta inversa si el único objetivo fuera la transferencia de información cuántica.
Paso 2: Optimizar para el hardware objetivo
Para ejecutar en hardware, importa QiskitRuntimeService y carga tus credenciales guardadas. Selecciona el backend con la cola más corta.
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')
Veamos el mapa de acoplamiento del dispositivo seleccionado.
from qiskit.visualization import plot_gate_map
plot_gate_map(backend)

Diferentes dispositivos pueden tener diferentes mapas de acoplamiento, y cada dispositivo tiene qubits y acopladores que son más eficientes que otros. Además, diferentes computadores cuánticos pueden tener diferentes puertas nativas (puertas que el hardware puede ejecutar directamente). Durante la transpilación, el circuito cuántico abstracto se reescribe en puertas que el computador cuántico objetivo puede ejecutar, y se elige el mapeo óptimo a qubits físicos (entre otras cosas). La transpilación es un tema amplio y multifacético. Puedes encontrar más información en la referencia de la API.
# 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)
Paso 3: Ejecutar el circuito
Usando el primitivo Runtime Sampler, ejecutamos el circuito objetivo.
# 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'
También puedes verificar el estado del trabajo a través de tu panel de IBM Quantum®.
# 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'
Cuando se muestre 'DONE', puedes obtener el resultado con la siguiente celda.
# 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}
Paso 4: Postprocesar los resultados
# Step 4: Post-process the results
from qiskit.visualization import plot_histogram
plot_histogram(result_real[0].data.c.get_counts())
Puedes interpretar los resultados anteriores directamente. Alternativamente, puedes usar marginal_count para filtrar los resultados de Bob en el qubit 2.
# 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)
Como vemos aquí, hay algunos resultados donde se midió . Estos se deben al ruido y los errores. En particular, los circuitos dinámicos tienen una tasa de error más alta porque la medición en medio del circuito consume tiempo.
4.5 Conclusiones clave sobre la teleportación cuántica
Usando un par de qubits entrelazados compartidos (E-bit), podemos transferir un estado cuántico a un amigo distante.
-
¿Puede la teleportación cuántica enviar el estado cuántico más rápido que la luz? No, porque Alice debe comunicar los resultados de medición a Bob por vía clásica.
-
¿Violaría la teleportación cuántica el "teorema de no clonación", que prohíbe copiar un estado cuántico? No, porque el estado cuántico original que Alice tenía en uno de sus qubits se perdió durante la medición: colapsó a o .
5. Codificación superdensa
Prácticamente la misma configuración se puede utilizar para otro propósito. Supongamos que Alice quiere enviar a Bob dos bits de información clásica, pero no tiene medios de comunicación clásicos. Sin embargo, comparte un par entrelazado con Bob y puede enviar su qubit a la ubicación de Bob. Observa la diferencia con el protocolo de teleportación cuántica: en la teleportación, la comunicación clásica estaba disponible y el objetivo era enviar un estado cuántico. Aquí, la comunicación clásica no es posible, y utilizan la transferencia de un qubit para intercambiar dos bits de información clásica.
5.1 El protocolo de codificación superdensa
Requisito previo: Alice tiene dos bits de información, por ejemplo . Alice y Bob comparten un par entrelazado (E-bit), pero no pueden comunicarse clásicamente.
- Alice realiza una de las siguientes operaciones en su parte del E-bit.
- Si , no hace nada.
- Si , aplica la puerta Z.
- Si , aplica la puerta X.
- Si , aplica la puerta Z y la puerta X.
- Alice envía su parte del E-bit a la ubicación de Bob.
- Bob aplica una puerta CNOT (el qubit de Alice como control, su qubit como objetivo), luego una puerta H al qubit de Alice, y mide ambos qubits. Los posibles estados de salida y resultados de las operaciones de Bob son:
El signo negativo en es una fase global y por lo tanto no es medible.
5.2 Circuito cuántico para simular la codificación superdensa
Basándote en el protocolo de codificación superdensa, puedes construir el circuito de codificación superdensa de la siguiente manera. Intenta cambiar el mensaje msg que Alice quiere transmitir a Bob.
from qiskit import QuantumCircuit
Los pasos de Qiskit Patterns se indican en los comentarios del código.
# 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)
Puedes ver que Bob ha recibido el mensaje que Alice quería enviarle.
A continuación, probamos con un computador cuántico real.
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())
El resultado coincide con nuestras expectativas. Es notable que la codificación superdensa en un computador cuántico real presenta menos errores que la teleportación cuántica. Una razón podría ser que la teleportación cuántica utiliza circuitos dinámicos, mientras que la codificación superdensa no. Aprenderemos más sobre errores en circuitos cuánticos en lecciones posteriores.
6. Resumen
En esta lección implementamos dos protocolos cuánticos. Aunque los escenarios con amigos distantes están algo alejados de la computación cuántica en una sola QPU, tienen aplicaciones en la computación cuántica y nos ayudan a comprender mejor la transferencia de información cuántica.
- Teleportación cuántica: Aunque no podemos copiar estados cuánticos, podemos teleportar estados cuánticos desconocidos usando entrelazamiento compartido.
- Codificación superdensa cuántica: Un par entrelazado compartido y la transferencia de un qubit permiten la comunicación de dos bits de información clásica.
# See the version of Qiskit
import qiskit
qiskit.__version__
'2.0.2'