Implementação com Qiskit
Nesta aula, implementamos algumas das ideias da aula sobre entrelaçamento em ação, usando o Qiskit.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-aer
from qiskit import __version__
print(__version__)
2.1.1
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit_aer import AerSimulator
from qiskit.visualization import plot_histogram, array_to_latex
from qiskit.result import marginal_distribution
from qiskit.circuit.library import UGate
from numpy import pi, random
A seguir, uma implementação em Circuit quântico do protocolo de teleportação.
qubit = QuantumRegister(1, "Q")
ebit0 = QuantumRegister(1, "A")
ebit1 = QuantumRegister(1, "B")
a = ClassicalRegister(1, "a")
b = ClassicalRegister(1, "b")
protocol = QuantumCircuit(qubit, ebit0, ebit1, a, b)
# Prepare ebit used for teleportation
protocol.h(ebit0)
protocol.cx(ebit0, ebit1)
protocol.barrier()
# Alice's operations
protocol.cx(qubit, ebit0)
protocol.h(qubit)
protocol.barrier()
# Alice measures and sends classical bits to Bob
protocol.measure(ebit0, a)
protocol.measure(qubit, b)
protocol.barrier()
# Bob uses the classical bits to conditionally apply gates
with protocol.if_test((a, 1)):
protocol.x(ebit1)
with protocol.if_test((b, 1)):
protocol.z(ebit1)
display(protocol.draw(output="mpl"))

O Circuit utiliza alguns recursos do Qiskit que ainda não havíamos visto nas aulas anteriores, incluindo as funções barrier e if_test.
A função barrier cria uma separação visual que torna o diagrama do Circuit mais legível, e também impede que o Qiskit realize diversas simplificações e otimizações através da barreira durante a compilação, quando os Circuits são executados em hardware real.
A função if_test aplica uma operação de forma condicional, dependendo de um bit ou registrador clássico.
O Circuit primeiro inicializa no estado (o que não faz parte do protocolo em si), seguido pelas operações de Alice, depois suas medições e, por fim, as operações de Bob. Para testar que o protocolo funciona corretamente, aplicaremos uma Gate de um único Qubit gerada aleatoriamente ao estado inicializado de , obtendo um vetor de estado quântico aleatório a ser teleportado. Ao aplicar a inversa (ou seja, a transposta conjugada) dessa Gate em após a execução do protocolo, podemos verificar que o estado foi teleportado medindo para confirmar que ele retornou ao estado .
Primeiro, vamos escolher aleatoriamente uma Gate unitária de um Qubit.
random_gate = UGate(
theta=random.random() * 2 * pi,
phi=random.random() * 2 * pi,
lam=random.random() * 2 * pi,
)
display(array_to_latex(random_gate.to_matrix()))
Agora criaremos um novo Circuit de teste que primeiro aplica nossa Gate aleatória a depois executa o Circuit de teleportação e, por fim, aplica a inversa da nossa Gate aleatória ao Qubit e realiza a medição. O resultado deve ser com certeza.
# Create a new circuit including the same bits and qubits used in the
# teleportation protocol.
test = QuantumCircuit(qubit, ebit0, ebit1, a, b)
# Start with the randomly selected gate on Q
test.append(random_gate, qubit)
test.barrier()
# Append the entire teleportation protocol from above.
test = test.compose(protocol)
test.barrier()
# Finally, apply the inverse of the random unitary to B and measure.
test.append(random_gate.inverse(), ebit1)
result = ClassicalRegister(1, "Result")
test.add_register(result)
test.measure(ebit1, result)
display(test.draw(output="mpl"))

Por fim, vamos executar o simulador Aer neste Circuit e plotar um histograma das saídas. Veremos as estatísticas para todos os três bits clássicos: o bit inferior/mais à esquerda deve ser sempre indicando que o Qubit foi teleportado com sucesso para enquanto os outros dois bits devem ser aproximadamente uniformes.
result = AerSimulator().run(test).result()
statistics = result.get_counts()
display(plot_histogram(statistics))
Também podemos filtrar as estatísticas para focar apenas no Qubit de resultado do teste, se desejarmos, assim:
filtered_statistics = marginal_distribution(statistics, [2])
display(plot_histogram(filtered_statistics))
Codificação superdensa
A codificação superdensa é um protocolo que, em certo sentido, alcança um objetivo complementar ao da teleportação. Em vez de permitir a transmissão de um Qubit usando dois bits clássicos de comunicação (ao custo de um e-bit de entrelaçamento), ela permite a transmissão de dois bits clássicos usando um Qubit de comunicação quântica (novamente, ao custo de um e-bit de entrelaçamento).
Em mais detalhes, temos um remetente (Alice) e um receptor (Bob) que compartilham um e-bit de entrelaçamento. De acordo com as convenções estabelecidas para a aula, isso significa que Alice possui um Qubit Bob possui um Qubit e juntos o par está no estado Alice deseja transmitir dois bits clássicos a Bob, que denotaremos por e e ela fará isso enviando a ele um Qubit.
É razoável considerar essa façanha menos interessante do que a que a teleportação realiza. Enviar Qubits provavelmente será muito mais difícil do que enviar bits clássicos no futuro previsível, de modo que trocar um Qubit de comunicação quântica por dois bits de comunicação clássica, ao custo de um e-bit, mal parece valer a pena. No entanto, isso não implica que a codificação superdensa não seja interessante, pois ela certamente é.
Em consonância com o tema da aula, uma das razões pelas quais a codificação superdensa é interessante é que ela demonstra um uso concreto e (no contexto da teoria da informação) bastante notável do entrelaçamento. Um famoso teorema da teoria quântica da informação, conhecido como teorema de Holevo, implica que, sem o uso de um estado entrelaçado compartilhado, é impossível comunicar mais de um bit de informação clássica enviando um único Qubit. (O teorema de Holevo é mais geral do que isso. Sua declaração precisa é técnica e requer explicação, mas essa é uma de suas consequências.) Portanto, por meio da codificação superdensa, o entrelaçamento compartilhado efetivamente permite a duplicação da capacidade de transmissão de informação clássica ao enviar Qubits.
Protocolo
O seguinte diagrama de Circuit quântico descreve o protocolo de codificação superdensa:

Em palavras, eis o que Alice faz:
-
Se Alice aplica uma Gate ao seu Qubit (e se ela não faz nada).
-
Se Alice aplica uma Gate ao seu Qubit (e se ela não faz nada).
Alice então envia seu Qubit a Bob.
O que Bob faz ao receber o Qubit é primeiro aplicar uma Gate NOT controlada, com sendo o controle e sendo o alvo, e depois aplica uma Gate de Hadamard a Ele então mede para obter e para obter usando medições na base padrão em ambos os casos.
Análise
A ideia por trás deste protocolo é simples: Alice efetivamente escolhe qual estado de Bell ela deseja compartilhar com Bob, ela envia a ele seu Qubit, e Bob mede para determinar qual estado de Bell Alice escolheu.
Ou seja, eles inicialmente compartilham e dependendo dos bits e Alice deixa esse estado inalterado ou o desloca para um dos outros estados de Bell aplicando ou ao seu Qubit
As ações de Bob têm os seguintes efeitos sobre os quatro estados de Bell:
Isso pode ser verificado diretamente, calculando os resultados das operações de Bob sobre esses estados um a um.
Portanto, quando Bob realiza suas medições, ele consegue determinar qual estado de Bell Alice escolheu. Verificar que o protocolo funciona corretamente é uma questão de checar cada caso:
-
Se então o estado de quando Bob recebe é Ele transforma esse estado em e obtém
-
Se então o estado de quando Bob recebe é Ele transforma esse estado em e obtém
-
Se então o estado de quando Bob recebe é Ele transforma esse estado em e obtém
-
Se então o estado de quando Bob recebe é Ele transforma esse estado em e obtém (O fator de fase negativo não tem efeito aqui.)
Implementação da codificação superdensa
A seguir, uma implementação simples da codificação superdensa, em que especificamos o Circuit em si de acordo com os bits a serem transmitidos. Primeiro, escolheremos dois bits a serem transmitidos. (Mais adiante os escolheremos aleatoriamente, mas por enquanto faremos apenas uma escolha arbitrária.)
c = "1"
d = "0"
Agora construiremos o Circuit de acordo com isso. Aqui permitiremos que o Qiskit use os nomes padrão para os Qubits: para o Qubit superior e para o inferior.
protocol = QuantumCircuit(2)
# Prepare ebit used for superdense coding
protocol.h(0)
protocol.cx(0, 1)
protocol.barrier()
# Alice's operations
if d == "1":
protocol.z(0)
if c == "1":
protocol.x(0)
protocol.barrier()
# Bob's actions
protocol.cx(0, 1)
protocol.h(0)
protocol.measure_all()
display(protocol.draw(output="mpl"))

Não há muitas novidades aqui, exceto a função measure_all, que mede todos os Qubits e coloca os resultados em um único registrador clássico (neste caso, com dois bits).
Executar o simulador Aer produz a saída esperada.
result = AerSimulator().run(protocol).result()
statistics = result.get_counts()
for outcome, frequency in statistics.items():
print(f"Measured {outcome} with frequency {frequency}")
display(plot_histogram(statistics))
Measured 10 with frequency 1024
Agora vamos usar um Qubit adicional como gerador de bits aleatórios — essencialmente para lançar moedas justas. Vamos usá-lo para escolher aleatoriamente e e então executar o protocolo de codificação superdensa.
rbg = QuantumRegister(1, "coin")
ebit0 = QuantumRegister(1, "A")
ebit1 = QuantumRegister(1, "B")
Alice_c = ClassicalRegister(1, "Alice c")
Alice_d = ClassicalRegister(1, "Alice d")
test = QuantumCircuit(rbg, ebit0, ebit1, Alice_d, Alice_c)
# Initialize the ebit
test.h(ebit0)
test.cx(ebit0, ebit1)
test.barrier()
# Use the 'coin' qubit twice to generate Alice's bits c and d.
test.h(rbg)
test.measure(rbg, Alice_c)
test.h(rbg)
test.measure(rbg, Alice_d)
test.barrier()
# Now the protocol runs, starting with Alice's actions, which depend
# on her bits.
with test.if_test((Alice_d, 1), label="Z"):
test.z(ebit0)
with test.if_test((Alice_c, 1), label="X"):
test.x(ebit0)
test.barrier()
# Bob's actions
test.cx(ebit0, ebit1)
test.h(ebit0)
test.barrier()
Bob_c = ClassicalRegister(1, "Bob c")
Bob_d = ClassicalRegister(1, "Bob d")
test.add_register(Bob_d)
test.add_register(Bob_c)
test.measure(ebit0, Bob_d)
test.measure(ebit1, Bob_c)
display(test.draw(output="mpl"))

Executar o simulador Aer mostra os resultados: os bits clássicos de Alice e Bob sempre concordam.
result = AerSimulator().run(test).result()
statistics = result.get_counts()
display(plot_histogram(statistics))

O jogo CHSH
O último exemplo a ser discutido nesta lição não é um protocolo, mas um jogo conhecido como jogo CHSH.
Quando falamos de jogo neste contexto, não estamos falando de algo que se joga por diversão ou esporte, mas sim de uma abstração matemática no sentido da teoria dos jogos. Abstrações matemáticas de jogos são estudadas em economia e ciência da computação, por exemplo, e são ao mesmo tempo fascinantes e úteis.
As letras CHSH se referem aos autores — John Clauser, Michael Horne, Abner Shimony e Richard Holt — de um artigo de 1969 onde o exemplo foi descrito pela primeira vez. Eles não descreveram o exemplo como um jogo, mas como um experimento. Sua descrição como jogo, porém, é natural e intuitiva.
O jogo CHSH pertence a uma classe de jogos conhecidos como jogos não-locais. Jogos não-locais são incrivelmente interessantes e têm conexões profundas com física, ciência da computação e matemática — guardando mistérios que ainda permanecem sem solução. Começaremos a seção explicando o que são jogos não-locais, e então nos concentraremos no jogo CHSH e no que o torna interessante.
Jogos não-locais
Um jogo não-local é um jogo cooperativo em que dois jogadores, Alice e Bob, trabalham juntos para alcançar um determinado resultado. O jogo é conduzido por um árbitro, que age de acordo com regras rígidas conhecidas por Alice e Bob.
Alice e Bob podem se preparar para o jogo como quiserem, mas uma vez que o jogo começa eles estão proibidos de se comunicar. Podemos imaginar o jogo acontecendo em algum tipo de instalação segura — como se o árbitro fosse um detetive e Alice e Bob fossem suspeitos sendo interrogados em salas separadas. Mas outra forma de pensar na configuração é que Alice e Bob estão separados por uma grande distância, e a comunicação é proibida porque a velocidade da luz não permite isso dentro do tempo de execução do jogo. Ou seja, se Alice tentar enviar uma mensagem para Bob, o jogo já terá terminado quando ele a receber, e vice-versa.
O modo como um jogo não-local funciona é que o árbitro primeiro faz uma pergunta a Alice e uma a Bob. Usaremos a letra para a pergunta de Alice e para a pergunta de Bob. Aqui estamos pensando em e como estados clássicos, e no jogo CHSH e são bits.
O árbitro usa aleatoriedade para selecionar essas perguntas. Mais precisamente, existe uma probabilidade associada a cada par possível de perguntas, e o árbitro se comprometeu a escolher as perguntas aleatoriamente, no momento do jogo, dessa forma. Todos, incluindo Alice e Bob, conhecem essas probabilidades — mas ninguém sabe especificamente qual par será escolhido até que o jogo comece.
Após Alice e Bob receberem suas perguntas, eles devem fornecer respostas: a resposta de Alice é e a de Bob é Novamente, esses são estados clássicos em geral, e bits no jogo CHSH.
Nesse ponto o árbitro toma uma decisão: Alice e Bob ganham ou perdem dependendo de o par de respostas ser considerado correto para o par de perguntas de acordo com um conjunto fixo de regras. Regras diferentes significam jogos diferentes, e as regras do jogo CHSH especificamente são descritas na seção seguinte. Como já foi sugerido, as regras são conhecidas por todos.
O diagrama a seguir fornece uma representação gráfica das interações.

É a incerteza sobre quais perguntas serão feitas, e especificamente o fato de que cada jogador não sabe a pergunta do outro, que torna os jogos não-locais desafiadores para Alice e Bob — assim como suspeitos conluiados em salas diferentes tentando manter a mesma versão da história.
Uma descrição precisa do árbitro define uma instância de um jogo não-local. Isso inclui uma especificação das probabilidades para cada par de perguntas, juntamente com as regras que determinam se cada par de respostas ganha ou perde para cada par de perguntas possível
Vamos dar uma olhada no jogo CHSH em um momento, mas antes disso vamos reconhecer brevemente que também é interessante considerar outros jogos não-locais. Na verdade, é extremamente interessante; há alguns jogos não-locais bastante simples para os quais atualmente não se sabe o quão bem Alice e Bob podem jogar usando entrelaçamento. A configuração é simples, mas há complexidade em ação — e para alguns jogos pode ser impossivelmente difícil calcular as melhores estratégias ou estratégias próximas das melhores para Alice e Bob. Essa é a natureza surpreendentemente contraintuitiva do modelo de jogos não-locais.
Descrição do jogo CHSH
Aqui está a descrição precisa do jogo CHSH, onde (como acima) é a pergunta de Alice, é a pergunta de Bob, é a resposta de Alice e é a resposta de Bob:
-
As perguntas e respostas são todas bits:
-
O árbitro escolhe as perguntas uniformemente ao acaso. Ou seja, cada uma das quatro possibilidades, e é selecionada com probabilidade
-
As respostas vencem para as perguntas se e perdem caso contrário. A tabela a seguir expressa essa regra listando as condições de vitória e derrota sobre as respostas para cada par de perguntas
Limitação das estratégias clássicas
Agora vamos considerar estratégias para Alice e Bob no jogo CHSH, começando pelas estratégias clássicas.
Estratégias determinísticas
Começaremos com estratégias determinísticas, onde a resposta de Alice é uma função da pergunta que ela recebe, e igualmente a resposta de Bob é uma função da pergunta que ele recebe. Assim, por exemplo, podemos escrever para representar a resposta de Alice quando sua pergunta é e para representar a resposta de Alice quando sua pergunta é
Nenhuma estratégia determinística pode vencer o jogo CHSH todas as vezes. Uma forma de entender isso é simplesmente percorrer uma a uma todas as estratégias determinísticas possíveis e verificar que cada uma delas perde para pelo menos um dos quatro pares de perguntas possíveis. Alice e Bob podem cada um escolher entre quatro funções possíveis de um bit para um bit — que encontramos na lição sobre Sistemas únicos — e portanto há estratégias determinísticas diferentes no total para verificar.
Também podemos raciocinar analiticamente. Se a estratégia de Alice e Bob vence quando então deve ser que se a estratégia deles vence quando então e similarmente, se a estratégia vence para então Portanto, se a estratégia deles vence para as três possibilidades, então
Isso implica que a estratégia perde no caso final pois vencer aqui requer Assim, não pode existir uma estratégia determinística que vença sempre.
Por outro lado, é fácil encontrar estratégias determinísticas que vencem em três dos quatro casos, como Com isso concluímos que a probabilidade máxima de Alice e Bob vencerem usando uma estratégia determinística é
Estratégias probabilísticas
Como acabamos de concluir, Alice e Bob não conseguem fazer melhor do que vencer o jogo CHSH 75% das vezes usando uma estratégia determinística. Mas e uma estratégia probabilística? Poderia ajudar Alice e Bob usar aleatoriedade — incluindo a possibilidade de aleatoriedade compartilhada, onde suas escolhas aleatórias são correlacionadas?
Acontece que estratégias probabilísticas não ajudam em nada a aumentar a probabilidade de Alice e Bob vencerem. Isso ocorre porque toda estratégia probabilística pode ser alternativamente vista como uma seleção aleatória de uma estratégia determinística, assim como (como foi mencionado na lição de Sistemas únicos) operações probabilísticas podem ser vistas como seleções aleatórias de operações determinísticas. A média nunca é maior que o máximo, e por isso estratégias probabilísticas não oferecem nenhuma vantagem em termos de probabilidade de vitória geral.
Portanto, vencer com probabilidade é o melhor que Alice e Bob podem fazer usando qualquer estratégia clássica, seja determinística ou probabilística.
Estratégia para o jogo CHSH
Uma pergunta natural a se fazer neste ponto é se Alice e Bob podem se sair melhor usando uma estratégia quântica. Em particular, se eles compartilham um estado quântico entrelaçado como a figura a seguir sugere, que eles poderiam ter preparado antes de jogar, eles conseguem aumentar sua probabilidade de vitória?

A resposta é sim, e esse é o ponto principal do exemplo e o motivo pelo qual é tão interessante. Então vamos ver exatamente como Alice e Bob podem se sair melhor neste jogo usando entrelaçamento.
Vetores e matrizes necessários
A primeira coisa que precisamos fazer é definir um vetor de estado de Qubit para cada número real (que vamos pensar como um ângulo medido em radianos) da seguinte forma.
Aqui estão alguns exemplos simples:
Também temos os exemplos a seguir, que surgem na análise abaixo:
Observando a forma geral, vemos que o produto interno entre quaisquer dois desses vetores tem esta fórmula:
Em detalhes, há apenas entradas de números reais nesses vetores, portanto não há conjugados complexos com os quais se preocupar: o produto interno é o produto dos cossenos mais o produto dos senos. Usar uma das fórmulas de adição de ângulos da trigonometria leva à simplificação acima. Essa fórmula revela a interpretação geométrica do produto interno entre vetores unitários reais como o cosseno do ângulo entre eles.
Se calcularmos o produto interno do produto tensorial de quaisquer dois desses vetores com o estado obtemos uma expressão similar, exceto que tem um no denominador:
Nosso interesse neste produto interno específico ficará claro em breve, mas por ora estamos simplesmente observando isso como uma fórmula.
Em seguida, defina uma matriz unitária para cada ângulo da seguinte forma.
Intuitivamente, essa matriz transforma em e em Para verificar que essa é uma matriz unitária, uma observação fundamental é que os vetores e são ortogonais para todo ângulo :
Assim, encontramos que