Pular para o conteúdo principal

Teleportação quântica e codificação superdensa

nota

Kifumi Numata (26 abr. 2024)

Baixe o PDF da aula original. Observe que alguns trechos de código podem estar desatualizados, pois são imagens estáticas.

O tempo estimado de QPU para executar este experimento é de 10 segundos.

1. Introdução

Para resolver qualquer problema quântico em escala utilitária, precisamos mover informações dentro de um computador quântico de um qubit para outro. Existem protocolos bem conhecidos para fazer isso, mas alguns dos mais fundamentais foram concebidos no contexto do envio de informações entre partes distantes. Ao longo desta lição, às vezes usaremos linguagem compatível com esse contexto, como "amigos distantes trocando informações". Mas tenha em mente que esses protocolos têm uma importância mais ampla na computação quântica. Nesta lição, consideramos os seguintes protocolos de comunicação quântica:

  • Teleportação quântica Usar um estado entrelaçado compartilhado (às vezes chamado de e-bit) para enviar um estado quântico desconhecido a um amigo distante, exigindo comunicação clássica adicional.
  • Codificação superdensa quântica Como enviar dois bits de informação transmitindo um único qubit a um amigo distante (também usando qubits entrelaçados compartilhados previamente).

Para mais contexto sobre esses tópicos, recomendamos a lição 4 de Fundamentos de Informação Quântica sobre Entrelaçamento em ação.

Na descrição acima, um "estado quântico desconhecido" se refere simplesmente a um estado da forma descrita na lição anterior:

ψ=α0+β1|\psi\rangle =\alpha|0\rangle+\beta|1\rangle

onde α\alpha e β\beta são números complexos tais que α2+β2=1|\alpha|^2+|\beta|^2 = 1. Isso nos permite escrever o estado quântico como

ψ=cosθ20+eiφsinθ21=(cosθ2eiφsinθ2)|\psi\rangle =\cos\frac{\theta}{2}|0\rangle+e^{i\varphi}\sin\frac{\theta}{2}|1\rangle= \left( \begin{matrix} \cos\frac{\theta}{2}\\ e^{i\varphi}\sin\frac{\theta}{2} \end{matrix} \right)

Como queremos ser capazes de transferir a informação em qualquer estado quântico aleatório, gerar tal estado é por onde começaremos esta lição.

2. Matrizes de densidade

Também podemos escrever o estado quântico ψ|\psi \rangle como sua matriz de densidade. Essa forma é útil para representar misturas probabilísticas de estados quânticos puros. No caso de um único qubit, podemos escrever

ψψρ=((cosθ2eiφsinθ2))((cosθ2eiφsinθ2))=12((1+cosθeiφsinθeiφsinθ1cosθ))|\psi \rangle \langle \psi| \equiv \rho = \left( \begin{pmatrix} \cos\frac{\theta}{2}\\ e^{i\varphi}\sin\frac{\theta}{2} \end{pmatrix} \right) \left( \begin{pmatrix} \cos\frac{\theta}{2} & e^{-i\varphi}\sin\frac{\theta}{2} \end{pmatrix} \right) =\frac{1}{2}\left(\begin{pmatrix} 1+\cos\theta & e^{-i\varphi}\sin\theta\\ e^{-i\varphi}\sin\theta & 1-\cos\theta \end{pmatrix}\right)

Note que a matriz de densidade ρ\rho é uma soma linear de matrizes de Pauli, como abaixo:

ρ=12(I+(sinθcosφ)X+(sinθsinφ)Y+(cosθ)Z)\rho = \frac{1}{2}\bigl( \textbf{I} + (\sin{\theta}\cos{\varphi})\textbf{X}+ (\sin{\theta}\sin{\varphi})\textbf{Y} + (\cos{\theta})\textbf{Z} \bigr)

Ou, de forma geral,

ρ=12(I+rxX+ryY+rzZ)\rho = \frac{1}{2}(\textbf{I} + r_{x}\textbf{X}+ r_{y}\textbf{Y} + r_{z}\textbf{Z})

onde rx2+ry2+rz2=1r_{x}^2+r_{y}^2+r_{z}^2=1.

E o vetor de Bloch é r=(rx,ry,rz)\textbf{r} = (r_{x}, r_{y}, r_{z}).

Agora, vamos criar um estado quântico arbitrário usando números aleatórios.

# 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 exibir esse vetor de Bloch na esfera de Bloch.

from qiskit.visualization import plot_bloch_vector

r = [rx, ry, rz]
plot_bloch_vector(r)

Saída da célula de código anterior

3. Tomografia de estado quântico

Se você medir o estado quântico apenas na base computacional (0|0 \rangle e 1|1 \rangle), a informação de fase (a informação de número complexo) será perdida. Mas se tivermos muitas cópias de ψ|\psi \rangle repetindo o processo de preparação (não podemos clonar estados, mas podemos repetir processos de preparação), podemos estimar os valores de rx,ry,rzr_{x}, r_{y}, r_{z} realizando a tomografia de estado quântico para a matriz de densidade ρ\rho. Dado a forma:

ρ=12(I+rxX+ryY+rzZ)\rho = \frac{1}{2}(\textbf{I} + r_{x}\textbf{X}+ r_{y}\textbf{Y} + r_{z}\textbf{Z})

vale que

Tr(Xρ)=rx,Tr(Yρ)=ry,Tr(Zρ)=rzTr(\textbf{X} \rho) = r_{x}, \quad Tr(\textbf{Y} \rho) = r_{y}, \quad Tr(\textbf{Z} \rho) = r_{z}

No caso de rzr_{z},

Tr(Zρ)=0Zρ0+1Zρ1Tr(\textbf{Z} \rho) = \langle 0|\textbf{Z} \rho|0 \rangle + \langle 1|\textbf{Z} \rho|1 \rangle =0(0011)ρ0+1(0011)ρ1= \langle 0|(|0 \rangle\langle 0|-|1 \rangle\langle 1|) \rho|0 \rangle +\langle 1|(|0 \rangle\langle 0|-|1 \rangle\langle 1|) \rho|1 \rangle =0ρ01ρ1=\langle 0|\rho|0 \rangle- \langle 1| \rho|1 \rangle =0ψψ01ψψ1=\langle 0|\psi\rangle\langle \psi|0 \rangle - \langle 1| \psi\rangle\langle \psi|1 \rangle =α2β2=|\alpha|^2-|\beta|^2

A última transformação da equação é para ψ=α0+β1|\psi \rangle =\alpha|0\rangle+\beta|1\rangle. Portanto, podemos obter rzr_{z} pela probabilidade de 0|0 \rangle menos a probabilidade de 1|1 \rangle.

Estimar o valor de rzr_z

Para estimar rzr_z, criamos um estado quântico e o medimos. Em seguida, repetimos a preparação e a medição muitas vezes. Por fim, usamos as estatísticas das medições para estimar as probabilidades acima e, assim, estimar rzr_z.

Para criar o estado quântico aleatório, usaremos a porta unitária geral UU com os parâmetros θ,φ\theta, \varphi. (Consulte a porta U para mais informações.)

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")

Saída da célula de código anterior

Usando o AerSimulator, vamos medir na base computacional para estimar rzr_z.

# 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}

Saída da célula de código anterior

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

Usando o método de tomografia de estado quântico, estimamos o valor de rzr_z. Neste caso, como escolhemos um parâmetro para o estado "aleatório", sabemos o valor de rzr_z e podemos verificar nosso trabalho. Mas, por sua própria natureza, o trabalho em escala utilitária nem sempre é tão simples de verificar. Vamos discutir mais sobre como checar resultados quânticos mais adiante neste curso. Por enquanto, observe apenas que nossa estimativa foi razoavelmente precisa.

Exercício 1: Estimar o valor de rxr_x

Lembre-se de que os computadores quânticos IBM® medem ao longo do eixo zz (às vezes dito "na base zz" ou "na base computacional"). No entanto, usando rotações antes da medição, também podemos medir a projeção do estado quântico no eixo x. Para ser mais preciso, se rotacionarmos nosso sistema de modo que o que antes apontava ao longo de xx agora aponte ao longo de zz, podemos manter o mesmo hardware de medição ao longo de zz, mas aprender sobre o estado que estava ao longo de xx um momento atrás. É assim que a maioria dos computadores quânticos (e todos os computadores quânticos IBM) realiza medições ao longo de múltiplos eixos.

Com esse entendimento, tente escrever um código para estimar o valor de rxr_x, usando a tomografia de estado quântico.

Solução:

# 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")

Saída da célula de código anterior

# 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}

Saída da célula de código anterior

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

Exercício 2: Estimar o valor de ryr_y

Usando os mesmos argumentos lógicos de antes, podemos rotacionar o sistema antes da medição para aprender sobre ryr_y. Tente escrever o código por conta própria para estimar o valor de ryr_y usando a tomografia de estado quântico. Você pode começar com o exemplo anterior, mas usando rotações diferentes. (Para mais informações sobre as diversas portas utilizadas, incluindo sdg, consulte a referência da API.)

Solução:

# 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")

Saída da célula de código anterior

# 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}

Saída da célula de código anterior

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

Agora estimamos todos os componentes de r\vec{r} e podemos escrever o vetor 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).

Você obteve a estimativa do vetor aleatório original com bastante precisão usando esse método de tomografia de estado quântico.

4. Teletransporte quântico

Vamos considerar a situação em que uma personagem chamada Alice quer enviar um estado quântico desconhecido ψ|\psi \rangle para o seu amigo Bob, que está longe. Suponha que eles só podem se comunicar por meios clássicos (como e-mail ou telefone). Alice não pode copiar o estado quântico (por causa do teorema da não-clonagem). Se ela repetisse o mesmo processo de preparação muitas vezes, poderia acumular estatísticas, como acabamos de fazer. Mas e se houver apenas um único estado desconhecido? Esse estado pode ter surgido de um processo físico que você quer estudar. Ou pode ser parte de um cálculo quântico maior. Nesse caso, como Alice poderia enviar o estado para Bob? Ela pode, se ela e Bob compartilharem um recurso quântico valioso: um estado entrelaçado compartilhado, como o estado de Bell introduzido na lição anterior: 00+112.\frac {|00\rangle + |11\rangle}{\sqrt 2}. Às vezes você também pode ver isso referenciado como um "par EPR" ou um "e-bit" (uma unidade fundamental de entrelaçamento). Se Alice compartilha esse estado entrelaçado com Bob, ela pode teletransportar o estado quântico desconhecido para Bob realizando uma série de operações quânticas e enviando a ele dois bits de informação clássica.

4.1 O protocolo de teletransporte quântico

Suposição: Alice tem um estado quântico desconhecido ψ|\psi \rangle que deve ser enviado para Bob. Alice e Bob compartilham um estado entrelaçado de 2 qubits, ou e-bit, cada um tendo fisicamente um dos qubits em sua localização.

Aqui descrevemos o procedimento sem explicação detalhada. Eles serão implementados em detalhes abaixo.

  1. Alice entrelaça ψ|\psi \rangle com a sua parte do e-bit usando a porta CNOT.
  2. Alice aplica uma porta Hadamard a ψ|\psi \rangle e mede os dois qubits dela na base computacional.
  3. Alice envia para Bob os resultados das suas medições (seja "00", "01", "10" ou "11").
  4. Bob realiza um operador de correção com base nos dois bits de informação de Alice sobre a parte dele do par e-bit.
    • Se "00", Bob não faz nada.
    • Se "01", Bob aplica a porta X.
    • Se "10", Bob aplica a porta Z.
    • Se "11", Bob aplica iY = ZX.
  5. A parte de Bob do e-bit se torna ψ|\psi \rangle.

Isso também é trabalhado com mais detalhes em Basics of Quantum Information. Mas a situação ficará mais clara quando implementarmos isso no Qiskit.

4.2 Circuito quântico simulando o teletransporte quântico

Como sempre, vamos aplicar o framework de padrões do Qiskit. Esta subseção se concentrará apenas no mapeamento.

Passo 1: Mapear o problema para circuitos e operadores quânticos

Para descrever o cenário acima, precisamos de um circuito com três qubits: dois para o par entrelaçado compartilhado por Alice e Bob, e um para o estado quântico desconhecido ψ|\psi\rangle.

from qiskit import QuantumCircuit
import numpy as np
# create 3-qubits circuit
qc = QuantumCircuit(3, 3)

qc.draw(output="mpl")

Output of the previous code cell

No início, Alice tem um estado quântico desconhecido ψ.|\psi \rangle. Vamos criá-lo usando a porta UU.

# 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")

Output of the previous code cell

Podemos visualizar o estado que criamos, mas só porque sabemos quais parâmetros foram usados na porta UU. Se esse estado tivesse surgido de um processo quântico complicado, o estado não seria identificável sem executar o processo para criar o estado muitas vezes e coletar estatísticas como na tomografia.

# 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)

Output of the previous code cell

Antes mesmo de este protocolo começar, assumimos que Alice e Bob têm um par entrelaçado compartilhado. Se Alice e Bob estiverem realmente em locais diferentes, eles podem ter configurado o estado compartilhado antes de o estado desconhecido ψ|\psi\rangle ter sido criado. Como essas coisas estão acontecendo em qubits diferentes, a ordem aqui não importa, e esta ordem é conveniente para visualização.

# 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")

Output of the previous code cell

Em seguida, Alice entrelaça ψ|\psi \rangle com a sua parte do e-bit compartilhado, usando a porta CXCX e a porta HH, e os mede na 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")

Output of the previous code cell

Alice envia para Bob os resultados das suas medições (seja "00", "01", "10" ou "11"), e Bob realiza um operador de correção com base nos dois bits de informação de Alice sobre a parte dele do e-bit compartilhado. Então, o qubit de Bob se torna ψ|\psi \rangle.

# 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")

Output of the previous code cell

Você completou um circuito de teletransporte quântico! Vamos ver o estado de saída deste circuito usando o simulador de vetor 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)

Output of the previous code cell

Você pode ver que o estado quântico criado pela porta UU do qubit 0 (o qubit que originalmente continha o estado secreto) foi transferido para o qubit 2 (o qubit de Bob).

Você pode executar a célula acima algumas vezes para confirmar. Pode notar que os qubits 0 e 1 mudam de estado, mas o qubit 2 está sempre no estado ψ|\psi\rangle.

4.3 Executar e confirmar o resultado aplicando o inverso de U

Acima, verificamos visualmente que o estado teletransportado parecia correto. Outra forma de verificar se o estado quântico foi teletransportado corretamente é aplicar o inverso da porta UU no qubit de Bob, para que possamos medir '0'. Ou seja, como U1UU^{-1}U é a identidade, se o qubit de Bob estiver no estado criado a partir de U0,U|0\rangle, então aplicar o inverso deve produzir U1U0=0.U^{-1}U|0\rangle=|0\rangle.

# 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")

Output of the previous code cell

Vamos executar o circuito primeiro usando o AerSimulator, antes de passar para um computador quâ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}

Output of the previous code cell

Lembre-se de que na notação little endian, o qubit 2 é o qubit mais à esquerda (ou mais acima, nos rótulos de coluna). Note que o qubit mais à esquerda e mais acima nos rótulos de coluna é 0 para todos os resultados possíveis. Isso mostra que temos 100% de chance de medir q2q_2 no estado 0|0\rangle. Este é o resultado esperado e indica que o protocolo de teletransporte funcionou corretamente.

4.4 Teletransporte em um computador quântico real

A seguir, vamos realizar o teletransporte em um computador quântico real. Usando a função de circuito dinâmico, podemos operar no meio do circuito usando os resultados das medições, implementando em tempo real as operações condicionais do circuito de teletransporte. Para resolver problemas com computadores quânticos reais, seguiremos os quatro passos dos padrões do Qiskit.

  1. Mapear o problema para circuitos e operadores quânticos
  2. Otimizar para o hardware alvo
  3. Executar no hardware alvo
  4. Pós-processar os resultados

Exercício 3: Construir o circuito de teletransporte

Tente construir o circuito de teletransporte completo do zero para testar seu entendimento. Role para cima se precisar de um lembrete.

Solução:

# 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")

Output of the previous code cell

Como lembrete, aplicar o inverso da porta UU serve apenas para verificar o comportamento esperado. Não faz parte do envio do estado para Bob, e não usaríamos essa porta UU inversa se o único objetivo fosse transferir informação quântica.

Passo 2: Otimizar para o hardware alvo

Para executar no hardware, importe QiskitRuntimeService e carregue suas credenciais salvas. Selecione o backend com o menor número de jobs na fila.

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')

Vamos ver o mapa de acoplamento do dispositivo que você selecionou.

from qiskit.visualization import plot_gate_map

plot_gate_map(backend)

Output of the previous code cell

Dispositivos diferentes podem ter mapas de acoplamento diferentes, e cada dispositivo tem alguns qubits e acopladores com desempenho melhor do que outros. Além disso, computadores quânticos diferentes podem ter portas nativas diferentes (portas que o hardware consegue executar). Transpilar o circuito o reescreve usando portas que o computador quântico alvo pode executar e seleciona o mapeamento ideal para qubits físicos (entre outras coisas). A transpilação é um tema rico e complexo. Para mais informações sobre transpilação, consulte a referência da 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)

Output of the previous code cell

Passo 3: Executar o circuito

Usando o primitivo Runtime Sampler, vamos executar o circuito alvo.

# 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'

Você também pode verificar o status do job no seu painel do 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'

Se você ver 'DONE' exibido, pode obter o resultado executando a célula abaixo.

# 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}

Passo 4: Pós-processar os resultados

# Step 4: Post-process the results
from qiskit.visualization import plot_histogram

plot_histogram(result_real[0].data.c.get_counts())

Output of the previous code cell

Você pode interpretar os resultados acima diretamente. Ou, usando marginal_count, pode rastrear os resultados de Bob no 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)

Output of the previous code cell

Como vemos aqui, há alguns resultados em que medimos 1|1 \rangle. Esses são devidos a ruído e erros. Em particular, circuitos dinâmicos tendem a ter uma taxa de erro maior por causa da medição demorada no meio do circuito.

4.5 Principais conclusões sobre o teletransporte quântico

Podemos transportar um estado quântico para um amigo distante compartilhando um par de qubits entrelaçados (um e-bit).

  1. O teletransporte quântico pode enviar o estado quântico mais rápido do que a luz? Não, porque Alice precisa comunicar os resultados das medições para Bob de forma clássica.

  2. O teletransporte quântico quebraria o "teorema da não-clonagem", que proíbe a cópia de um estado quântico? Não, porque o estado quântico original dado à Alice em um dos seus qubits foi destruído na medição. Ele colapsou para 0|0\rangle ou 1|1\rangle.

5. Codificação superdensa

Praticamente a mesma configuração pode ser usada para um propósito diferente. Suponha que Alice queira enviar a Bob dois bits de informação clássica, mas ela não tem nenhum meio de comunicação clássica com Bob. No entanto, ela compartilha um par entrelaçado com Bob e pode enviar o qubit dela para o local de Bob. Perceba o contraste com o protocolo de teletransporte quântico. No teletransporte, a comunicação clássica estava disponível para os dois, e o objetivo era enviar um estado quântico. Aqui, a comunicação clássica não está acessível, e eles usam a transferência de um qubit para compartilhar dois bits de informação clássica.

5.1 O protocolo de codificação superdensa

Premissa: Alice tem dois bits de informação, digamos, a1a2{00,01,10,11}a_1a_2 \in \{00, 01, 10, 11\}. Alice e Bob compartilham um par entrelaçado (e-bit), mas não podem se comunicar de forma clássica.

  1. Alice realiza uma das seguintes operações na parte dela do e-bit.
    • Se a1a2=00a_1a_2 = 00, ela não faz nada
    • Se a1a2=01a_1a_2 = 01, ela aplica a porta Z
    • Se a1a2=10a_1a_2 = 10, ela aplica a porta X
    • Se a1a2=11a_1a_2 = 11, ela aplica a porta Z e a porta X.
  2. Alice envia a parte dela do e-bit para o local de Bob.
  3. Bob aplica uma porta CNOT com o qubit de Alice como controle e o qubit dele como alvo, depois aplica a porta H ao qubit de Alice e mede os dois qubits. Os possíveis estados iniciais e resultados das operações de Bob são:
00+112CX01H000\frac {|00\rangle + |11\rangle}{\sqrt 2} \rightarrow CX_{01}\otimes H_0 \rightarrow |00\rangle 00112CX01H001\frac {|00\rangle - |11\rangle}{\sqrt 2} \rightarrow CX_{01}\otimes H_0 \rightarrow |01\rangle 10+012CX01H010\frac {|10\rangle + |01\rangle}{\sqrt 2} \rightarrow CX_{01}\otimes H_0 \rightarrow |10\rangle 10012CX01H011\frac {|10\rangle - |01\rangle}{\sqrt 2} \rightarrow CX_{01}\otimes H_0 \rightarrow -|11\rangle

Note que o sinal negativo de 11-|11\rangle é fase global, portanto não é mensurável.

5.2 Circuito quântico simulando a codificação superdensa

Com base no protocolo de codificação superdensa, você pode construir o circuito de codificação superdensa como mostrado abaixo. Tente mudar a mensagem, msg, que Alice quer transmitir a Bob.

from qiskit import QuantumCircuit

Os passos do padrão Qiskit estão identificados nos comentários do 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")

Saída da célula de código anterior

# 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)

Saída da célula de código anterior

Você pode ver que Bob recebeu a mensagem que Alice queria lhe enviar.

Em seguida, vamos tentar em um computador quâ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)

Saída da célula de código anterior

# 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())

Saída da célula de código anterior

O resultado é o que esperávamos. Note que a codificação superdensa em um computador quântico real apresentou menos erros do que no caso do teletransporte quântico em um computador quântico real. Um dos motivos pode ser que o teletransporte quântico usa circuitos dinâmicos, enquanto a codificação superdensa não usa. Aprenderemos mais sobre erros em circuitos quânticos nas lições seguintes.

6. Resumo

Nesta sessão, implementamos dois protocolos quânticos. Embora os cenários para ambos envolvendo amigos distantes estejam um pouco afastados da computação quântica em um único QPU, eles têm aplicações em computação quântica e nos ajudam a entender melhor a transferência de informação quântica.

  • Teletransporte quântico: Embora não possamos copiar estados quânticos, podemos teletransportar estados quânticos desconhecidos por meio de entrelaçamento compartilhado.
  • Codificação superdensa quântica: Um par entrelaçado compartilhado e a transferência de um qubit permitem a comunicação de dois bits de informação clássica.
# See the version of Qiskit
import qiskit

qiskit.__version__
'2.0.2'