Algoritmo de Deutsch
O algoritmo de Deutsch resolve o problema de paridade para o caso especial em que No contexto da computação quântica, esse problema é às vezes chamado de problema de Deutsch, e seguiremos essa nomenclatura nesta lição.
Para ser preciso, a entrada é representada por uma função de um bit para um bit. Existem quatro funções desse tipo:
A primeira e a última dessas funções são constantes, e as duas do meio são balanceadas, o que significa que os dois possíveis valores de saída da função ocorrem o mesmo número de vezes conforme variamos as entradas. O problema de Deutsch consiste em determinar a qual dessas duas categorias a função de entrada pertence: constante ou balanceada.
Se interpretarmos a função de entrada no problema de Deutsch como representando acesso aleatório a uma string, estamos pensando em uma string de dois bits:
Visto dessa forma, o problema de Deutsch consiste em calcular a paridade (ou, equivalentemente, o OU-exclusivo) dos dois bits.
Todo algoritmo de consulta clássico que resolve corretamente esse problema precisa consultar os dois bits: e Se soubermos que por exemplo, a resposta ainda pode ser ou , dependendo se ou , respectivamente. Todo outro caso é semelhante; conhecer apenas um dos dois bits não fornece nenhuma informação sobre a paridade deles. Portanto, o circuito booleano descrito na seção anterior é o melhor que podemos fazer em termos do número de consultas necessárias para resolver esse problema.
Descrição do circuito quântico
O algoritmo de Deutsch resolve o problema de Deutsch usando uma única consulta, fornecendo assim uma vantagem quantificável da computação quântica sobre a clássica. Essa pode ser uma vantagem modesta — uma consulta em vez de duas — mas precisamos começar em algum lugar. Avanços científicos às vezes têm origens aparentemente modestas.
Aqui está um circuito quântico que descreve o algoritmo de Deutsch:
Análise
Para analisar o algoritmo de Deutsch, vamos percorrer a ação do circuito acima e identificar os estados dos qubits nos momentos sugeridos por esta figura:
O estado inicial é e as duas operações de Hadamard no lado esquerdo do circuito transformam esse estado em
(Como sempre, seguimos a convenção de ordenação de qubits do Qiskit, que coloca o qubit do topo à direita e o qubit de baixo à esquerda.) Pode parecer pouco intuitivo escrever esse estado de produto parcialmente distribuído (deixando os estados do qubit 1 fatorados fora), mas isso tornará nossas expressões posteriores mais compactas.
Em seguida, a porta é aplicada. De acordo com a definição da porta , o valor da função para o estado clássico do qubit do topo/mais à direita é aplicado via XOR no qubit de baixo/mais à esquerda, o que transforma no estado
Podemos simplificar essa expressão observando que a fórmula
é válida para ambos os possíveis valores De forma mais explícita, os dois casos são os seguintes.
Assim, podemos expressar alternativamente da seguinte forma:
Algo interessante acabou de acontecer! Embora a ação da porta sobre estados da base padrão deixe o qubit do topo/mais à direita intacto e aplique o XOR do valor da função no qubit de baixo/mais à esquerda, aqui vemos que o estado do qubit do topo/mais à direita mudou (em geral), enquanto o estado do qubit de baixo/mais à esquerda permanece o mesmo — especificamente no estado antes e depois da aplicação da porta . Esse fenômeno é conhecido como phase kickback (retrocesso de fase), e falaremos mais sobre ele em breve.
Com uma simplificação final, que consiste em extrair o fator para fora da soma, obtemos esta expressão para o estado :
Observe que nessa expressão temos no expoente de , em vez de que seria o esperado de um ponto de vista puramente algébrico, mas obtemos o mesmo resultado de qualquer forma. Isso ocorre porque o valor para qualquer inteiro depende apenas de ser par ou ímpar.
Aplicando a porta de Hadamard final ao qubit do topo, chegamos ao estado
o que leva ao resultado correto com probabilidade quando o qubit da direita/topo é medido.
Observações adicionais sobre o phase kickback
Antes de prosseguir, vamos analisar a discussão acima sob um ângulo ligeiramente diferente, o que pode lançar alguma luz sobre o fenômeno do phase kickback.
Primeiro, observe que a seguinte fórmula é válida para todas as escolhas de bits
Isso pode ser verificado checando os dois possíveis valores e :
Usando essa fórmula, vemos que
para toda escolha de bits Como essa fórmula é verdadeira para e , vemos por linearidade que
para todos os vetores de estado de qubit e portanto
A chave que faz isso funcionar é que Em termos matemáticos, o vetor é um autovetor da matriz com autovalor
Discutiremos autovetores e autovalores com mais detalhes na próxima lição sobre Estimativa de fase e fatoração, onde o fenômeno do phase kickback é generalizado para outras operações unitárias.
Lembrando que escalares se propagam livremente pelos produtos tensoriais, encontramos uma forma alternativa de raciocinar sobre como a operação transforma em na análise acima:
Implementação no Qiskit
Agora vamos ver como podemos implementar o algoritmo de Deutsch no Qiskit. Começaremos com uma verificação de versão e em seguida faremos as importações necessárias exclusivamente para esta implementação. Para as implementações de outros algoritmos que se seguem, faremos as importações necessárias separadamente em prol de maior modularidade.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-aer
from qiskit import __version__
print(__version__)
2.1.1
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
Primeiro definiremos um circuito quântico que implementa uma porta de consulta para uma das quatro funções ou de um bit para um bit descritas anteriormente. Como já mencionamos, a implementação de portas de consulta não é realmente parte do próprio algoritmo de Deutsch; aqui estamos essencialmente mostrando uma maneira de preparar a entrada, na forma de uma implementação em circuito de uma porta de consulta.
def deutsch_function(case: int):
# This function generates a quantum circuit for one of the 4 functions
# from one bit to one bit
if case not in [1, 2, 3, 4]:
raise ValueError("`case` must be 1, 2, 3, or 4.")
f = QuantumCircuit(2)
if case in [2, 3]:
f.cx(0, 1)
if case in [3, 4]:
f.x(1)
return f
Podemos ver como cada circuito se parece usando o método draw. Aqui está o circuito para a função
display(deutsch_function(3).draw(output="mpl"))
Em seguida, criaremos o circuito quântico real para o algoritmo de Deutsch, substituindo a porta de consulta por uma implementação em circuito quântico fornecida como argumento. Em breve, vamos conectar um dos quatro circuitos definidos pela função deutsch_function que definimos anteriormente.
Barreiras são incluídas para mostrar a separação visual entre a implementação da porta de consulta e o restante do circuito.
def compile_circuit(function: QuantumCircuit):
# Compiles a circuit for use in Deutsch's algorithm.
n = function.num_qubits - 1
qc = QuantumCircuit(n + 1, n)
qc.x(n)
qc.h(range(n + 1))
qc.barrier()
qc.compose(function, inplace=True)
qc.barrier()
qc.h(range(n))
qc.measure(range(n), range(n))
return qc
Novamente podemos ver como o circuito se parece usando o método draw.
display(compile_circuit(deutsch_function(3)).draw(output="mpl"))
Por fim, criaremos uma função que executa o circuito definido anteriormente uma única vez e retorna o resultado apropriado: "constant" ou "balanced."
def deutsch_algorithm(function: QuantumCircuit):
# Determine if a one-bit function is constant or balanced.
qc = compile_circuit(function)
result = AerSimulator().run(qc, shots=1, memory=True).result()
measurements = result.get_memory()
if measurements[0] == "0":
return "constant"
return "balanced"
Agora podemos executar o algoritmo de Deutsch em qualquer uma das quatro funções definidas acima.
f = deutsch_function(3)
display(deutsch_algorithm(f))
'balanced'