O algoritmo de Deutsch-Jozsa
O algoritmo de Deutsch supera todos os algoritmos clássicos para um problema de consulta, mas a vantagem é bastante modesta: uma consulta contra duas. O algoritmo Deutsch-Jozsa amplia essa vantagem e, de fato, pode ser usado para resolver alguns problemas de consulta diferentes.
Aqui está uma descrição do circuito quântico do algoritmo Deutsch-Jozsa. Uma etapa adicional clássica de pós-processamento, não mostrada na figura, também pode ser necessária, dependendo do problema específico que está sendo resolvido.
É claro que ainda não discutimos quais problemas esse algoritmo resolve; isso será feito nas duas seções a seguir.
O problema de Deutsch-Jozsa
Começaremos com o problema de consulta que o algoritmo Deutsch-Jozsa foi originalmente planejado para resolver, conhecido como o problema Deutsch-Jozsa.
A função de entrada para este problema tem a forma , para um inteiro positivo arbitrário . Assim como no problema de Deutsch, a tarefa consiste em gerar a saída se for constante e se for equilibrado, o que, mais uma vez, significa que o número de cadeias de caracteres de entrada para as quais a função assume o valor é igual ao número de cadeias de caracteres de entrada para as quais a função assume o valor .
Observe que, quando é maior que , existem funções da forma que não são nem constantes nem equilibradas. Por exemplo, a função definida como
não se enquadra em nenhuma dessas duas categorias. No caso do problema Deutsch-Jozsa, simplesmente não nos preocupamos com funções como essa - elas são consideradas entradas "indiferentes". Ou seja, para esse problema, temos a promessa de que é constante ou equilibrado.
Entrada: uma função \ Promessa: é constante ou equilibrada \ Saída: se for constante, se for equilibrado
O algoritmo de Deutsch-Jozsa, com sua única consulta, resolve esse problema da seguinte maneira: se todos os resultados da medição forem , então a função é constante; e, caso contrário, se pelo menos um dos resultados da medição for , então a função é equilibrada. Outra forma de expressar isso é dizer que o circuito descrito acima é seguido por uma etapa clássica de pós-processamento, na qual se calcula a operação OR dos resultados das medições para produzir o bit de saída do problema de Deutsch-Jozsa.
Análise de algoritmos
Para analisar o desempenho do algoritmo Deutsch-Jozsa para o problema Deutsch-Jozsa, é útil começar pensando na ação de uma única camada de portas Hadamard. Uma operação Hadamard pode ser expressa como uma matriz da maneira usual,
mas também podemos expressar essa operação em termos de sua ação nos estados da base padrão:
Essas duas equações podem ser combinadas em uma única fórmula,
o que é válido para ambas as opções de .
Agora, suponha que, em vez de apenas um único qubit, tenhamos qubits e que uma operação Hadamard seja realizada em cada um deles. A operação combinada nos qubits é descrita pelo produto tensorial ( vezes), que escrevemos como para fins de concisão e clareza. Usando a fórmula acima, seguida de expansão e simplificação, podemos expressar a ação dessa operação combinada nos estados da base padrão dos qubits da seguinte forma:
Aqui, aliás, estamos escrevendo cadeias binárias de comprimento como e , seguindo a convenção de indexação do Qiskit.
Essa fórmula nos fornece uma ferramenta útil para analisar o circuito quântico acima. Após a execução da primeira camada de portas Hadamard, o estado dos qubits (incluindo o qubit mais à esquerda/inferior, que é tratado separadamente do restante) é
Quando a operação é realizada, esse estado é transformado em
exatamente pelo mesmo fenômeno de retorno de fase que vimos na análise do algoritmo de Deutsch.
Em seguida, a segunda camada de portas Hadamard é executada, o que (pela fórmula acima) transforma esse estado em
Essa expressão parece um pouco complicada, e não é possível tirar muitas conclusões sobre as probabilidades de se obter diferentes resultados de medição sem saber mais sobre a função .
Felizmente, tudo o que precisamos saber é a probabilidade de que cada um dos resultados da medição seja - porque essa é a probabilidade de que o algoritmo determine que é constante. Essa probabilidade tem uma fórmula simples.
Observe que esses valores correspondem à probabilidade de se medir o estado , e não diretamente ao bit de saída clássico final do problema de Deutsch-Jozsa. O algoritmo gera o resultado “ ” quando todos os resultados das medições são “ ” (indicando que “ ” é constante) e, caso contrário, gera “ ” (indicando que “ ” está em equilíbrio).
Mais detalhadamente, se for constante, então ou para toda string , caso em que o valor da soma é , ou para toda string , caso em que o valor da soma é . Dividindo por e elevando ao quadrado o valor absoluto, obtém-se .
Se, por outro lado, for equilibrada, então assume o valor em metade das sequências e o valor na outra metade; assim, os termos e na soma se cancelam, e ficamos com o valor .
Concluímos que o algoritmo funciona corretamente desde que a promessa seja cumprida.
Dificuldade clássica
O algoritmo Deutsch-Jozsa funciona todas as vezes, sempre nos dando a resposta correta quando a promessa é cumprida, e requer uma única consulta. Como isso se compara aos algoritmos de consulta clássicos para o problema Deutsch-Jozsa?
Primeiro, qualquer algoritmo clássico determinístico que resolva corretamente o problema Deutsch-Jozsa deve fazer um número exponencial de consultas: consultas são necessárias no pior dos casos. O raciocínio é que, se um algoritmo determinístico consultar em ou menos cadeias de caracteres diferentes e obtiver o mesmo valor de função todas as vezes, então ambas as respostas ainda serão possíveis. A função pode ser constante ou equilibrada, mas, por azar, todas as consultas retornam o mesmo valor de função.
A segunda possibilidade pode parecer improvável, mas para algoritmos determinísticos não há aleatoriedade ou incerteza, portanto, eles falharão sistematicamente em determinadas funções. Portanto, temos uma vantagem significativa dos algoritmos quânticos em relação aos clássicos nesse aspecto.
No entanto, há um problema: os algoritmos clássicos probabilísticos podem resolver o problema Deutsch-Jozsa com uma probabilidade muito alta usando apenas algumas consultas. Em particular, se simplesmente escolhermos algumas cadeias diferentes de comprimento aleatoriamente e consultarmos nessas cadeias, é improvável que obtenhamos o mesmo valor de função para todas elas quando estiver equilibrado.
Para ser mais específico, se escolhermos sequências de entrada de forma aleatória e uniforme, calcularmos e respondermos caso os valores da função sejam todos iguais, e caso contrário, estaremos sempre corretos quando for constante, e errados no caso em que for equilibrada com probabilidade de apenas . Se considerarmos , por exemplo, esse algoritmo responderá corretamente com probabilidade superior a %.
Por esse motivo, ainda temos uma vantagem bastante modesta dos algoritmos quânticos em relação aos clássicos, mas, ainda assim, é uma vantagem quantificável que representa um aprimoramento em relação ao algoritmo de Deutsch.
Deutsch-Jozsa com Qiskit
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
import numpy as npPara implementar o algoritmo Deutsch-Jozsa no Qiskit, começaremos definindo uma função dj_query que gera um circuito quântico implementando uma porta de consulta, para uma função selecionada aleatoriamente que satisfaça a promessa do problema Deutsch-Jozsa.
Com 50% de chance, a função é constante, e com 50% de mudança, a função é equilibrada.
Para cada uma dessas duas possibilidades, a função é selecionada uniformemente entre as funções desse tipo.
O argumento é o número de bits de entrada da função.
def dj_query(num_qubits):
# Create a circuit implementing for a query gate for a random function
# satisfying the promise for the Deutsch-Jozsa problem.
qc = QuantumCircuit(num_qubits + 1)
if np.random.randint(0, 2):
# Flip output qubit with 50% chance
qc.x(num_qubits)
if np.random.randint(0, 2):
# return constant circuit with 50% chance
return qc
# Choose half the possible input strings
on_states = np.random.choice(
range(2**num_qubits), # numbers to sample from
2**num_qubits // 2, # number of samples
replace=False, # makes sure states are only sampled once
)
def add_cx(qc, bit_string):
for qubit, bit in enumerate(reversed(bit_string)):
if bit == "1":
qc.x(qubit)
return qc
for state in on_states:
qc.barrier() # Barriers are added to help visualize how the functions are created.
qc = add_cx(qc, f"{state:0b}")
qc.mcx(list(range(num_qubits)), num_qubits)
qc = add_cx(qc, f"{state:0b}")
qc.barrier()
return qcPodemos mostrar a implementação do circuito quântico da porta de consulta usando o método draw como de costume.
display(dj_query(3).draw(output="mpl"))Output:
Em seguida, definimos uma função que cria o circuito Deutsch-Jozsa, tendo como argumento uma implementação de circuito quântico de uma porta de consulta.
def compile_circuit(function: QuantumCircuit):
# Compiles a circuit for use in the Deutsch-Jozsa algorithm.
n = function.num_qubits - 1
qc = QuantumCircuit(n + 1, n)
qc.x(n)
qc.h(range(n + 1))
qc.compose(function, inplace=True)
qc.h(range(n))
qc.measure(range(n), range(n))
return qcPor fim, é definida uma função que executa o circuito Deutsch-Jozsa uma vez.
def dj_algorithm(function: QuantumCircuit):
# Determine if a function is constant or balanced.
qc = compile_circuit(function)
result = AerSimulator().run(qc, shots=1, memory=True).result()
measurements = result.get_memory()
if "1" in measurements[0]:
return "balanced"
return "constant"Podemos testar nossa implementação escolhendo uma função aleatoriamente, exibindo a implementação do circuito quântico de uma porta de consulta para essa função e, em seguida, executando o algoritmo Deutsch-Jozsa nessa função.
f = dj_query(3)
display(f.draw("mpl"))
display(dj_algorithm(f))Output:
'balanced'
O problema de Bernstein-Vazirani
A seguir, discutiremos um problema conhecido como problema de Bernstein-Vazirani. Ele também é chamado de problema de amostragem de Fourier, embora existam formulações mais gerais desse problema que também têm esse nome.
Primeiro, vamos apresentar algumas notações. Para quaisquer duas cadeias binárias e de comprimento , definimos
Vamos nos referir a essa operação como o produto de ponto binário. Uma maneira alternativa de defini-lo é assim.
Observe que essa é uma operação simétrica, o que significa que o resultado não muda se trocarmos e ; portanto, podemos fazer isso sempre que for conveniente. Às vezes, é útil pensar no produto escalar binário como sendo a paridade dos bits de nas posições em que a sequência contém um , ou, de forma equivalente, a paridade dos bits de nas posições em que a sequência contém um .
Com essa notação em mãos, podemos agora definir o problema de Bernstein-Vazirani.
Entrada: uma função \ Promessa: existe uma string binária para a qual para todos os \ Saída: a string
Na verdade, não precisamos de um novo algoritmo quântico para esse problema; o algoritmo Deutsch-Jozsa o resolve. Para fins de clareza, vamos nos referir ao circuito quântico acima, que não inclui a etapa clássica de pós-processamento de computação do OU, como o circuito Deutsch-Jozsa.
Análise de algoritmos
Para analisar como o circuito Deutsch-Jozsa funciona para uma função que satisfaz a promessa do problema Bernstein-Vazirani, começaremos com uma observação rápida. Usando o produto de ponto binário, podemos, alternativamente, descrever a ação de Hadamard gates nos estados de base padrão de qubits da seguinte forma.
Semelhante ao que vimos ao analisar o algoritmo de Deutsch, isso ocorre porque o valor para qualquer número inteiro depende apenas do fato de ser par ou ímpar.
Voltando ao circuito Deutsch-Jozsa, após a execução da primeira camada de portas Hadamard, o estado dos qubits é
A porta de consulta é então executada, o que (por meio do fenômeno de retrocesso de fase) transforma o estado em
Usando nossa fórmula para a ação de uma camada de portas Hadamard, vemos que a segunda camada de portas Hadamard transforma esse estado em
Agora podemos fazer algumas simplificações no expoente de dentro da soma. Foi-nos prometido que para uma determinada sequência , de modo que podemos expressar o estado como
Como e são valores binários, podemos substituir a adição pelo exclusivo-OR - novamente porque a única coisa que importa para um número inteiro no expoente de é se ele é par ou ímpar. Usando a simetria do produto de ponto binário, obtemos essa expressão para o estado:
(Os parênteses foram adicionados para fins de clareza, embora não sejam realmente necessários, pois é convencional tratar o produto de ponto binário como tendo precedência mais alta do que o exclusivo-OR)
Neste ponto, usaremos a seguinte fórmula.
Podemos obter a fórmula por meio de uma fórmula semelhante para bits,
juntamente com uma expansão do produto de ponto binário e do bitwise exclusive-OR:
Isso nos permite expressar o estado do circuito imediatamente antes das medições da seguinte forma:
O passo final é utilizar mais uma fórmula, que funciona para qualquer sequência binária .
Aqui, estamos usando uma notação simples para cadeias de caracteres que utilizaremos várias outras vezes nesta aula: é a cadeia composta inteiramente por zeros, de comprimento .
Uma maneira simples de demonstrar que essa fórmula funciona é considerar os dois casos separadamente. Se , então para toda string ; portanto, o valor de cada termo da soma é , e obtemos somando e dividindo por . Por outro lado, se qualquer um dos bits de for igual a , então o produto escalar binário é igual a para exatamente metade das opções possíveis de e para a outra metade — porque o valor do produto escalar binário inverte-se (de para ou de para ) se invertemos qualquer bit de em uma posição em que tenha um .
Se agora aplicarmos essa fórmula para simplificar o estado do circuito antes das medições, obteremos
devido ao fato de que se, e somente se, . Assim, as medições revelam precisamente a sequência que estamos procurando.
Dificuldade clássica
Enquanto o circuito Deutsch-Jozsa resolve o problema Bernstein-Vazirani com uma única consulta, qualquer algoritmo de consulta clássico precisa fazer pelo menos consultas para resolver esse problema.
Isso pode ser explicado por meio do chamado argumento da teoria da informação, que é muito simples nesse caso. Cada consulta clássica revela um único bit de informação sobre a solução, e há bits de informação que precisam ser descobertos - portanto, são necessárias pelo menos consultas.
De fato, é possível resolver o problema de Bernstein-Vazirani de forma clássica, consultando a função para cada uma das sequências que contenham um único , em cada posição possível, e para todos os demais bits, o que revela os bits de um por um. Portanto, a vantagem dos algoritmos quânticos em relação aos clássicos para esse problema é de uma consult , contra consultas .
Bernstein-Vazirani com Qiskit
Já implementamos o circuito de Deutsch-Jozsa acima e, aqui, vamos utilizá-lo para resolver o problema de Bernstein-Vazirani. Primeiro, definiremos uma função que implementa um portão de consulta para o problema de Bernstein-Vazirani, dada qualquer sequência binária .
def bv_query(s):
# Create a quantum circuit implementing a query gate for the
# Bernstein-Vazirani problem.
qc = QuantumCircuit(len(s) + 1)
for index, bit in enumerate(reversed(s)):
if bit == "1":
qc.cx(index, len(s))
return qc
display(bv_query("1011").draw(output="mpl"))Output:
Agora podemos criar uma função que executa o circuito Deutsch-Jozsa na função, usando a função compile_circuit que foi definida anteriormente.
def bv_algorithm(function: QuantumCircuit):
qc = compile_circuit(function)
result = AerSimulator().run(qc, shots=1, memory=True).result()
return result.get_memory()[0]
display(bv_algorithm(bv_query("1011")))Output:
'1011'
Observação sobre a nomenclatura
No contexto do problema Bernstein-Vazirani, é comum que o algoritmo Deutsch-Jozsa seja chamado de "algoritmo Bernstein-Vazirani" Isso é um pouco enganoso, pois o algoritmo é o algoritmo Deutsch-Jozsa, como Bernstein e Vazirani deixaram bem claro em seu trabalho.
O que Bernstein e Vazirani fizeram depois de mostrar que o algoritmo Deutsch-Jozsa resolve o problema de Bernstein-Vazirani (como foi dito acima) foi definir um problema muito mais complicado, conhecido como problema de amostragem recursiva de Fourier. Esse é um problema altamente planejado em que as soluções para diferentes instâncias do problema efetivamente desbloqueiam novos níveis do problema organizados em uma estrutura semelhante a uma árvore. O problema de Bernstein-Vazirani é essencialmente apenas o caso básico desse problema mais complicado.
O problema de amostragem recursiva de Fourier foi o primeiro exemplo conhecido de um problema de consulta em que os algoritmos quânticos têm a chamada vantagem superpolinomial sobre os algoritmos probabilísticos, superando assim a vantagem do quântico sobre o clássico oferecida pelo algoritmo Deutsch-Jozsa. Intuitivamente falando, a versão recursiva do problema amplia a vantagem versus dos algoritmos quânticos para algo muito maior.
O aspecto mais desafiador da análise matemática que estabelece essa vantagem é mostrar que os algoritmos de consulta clássicos não conseguem resolver o problema sem fazer muitas consultas. Isso é bastante comum; para muitos problemas, pode ser muito difícil descartar abordagens clássicas criativas que os resolvam de forma eficiente.
O problema de Simon e o algoritmo para ele descrito na próxima seção fornecem um exemplo muito mais simples de uma vantagem superpolinomial (e, na verdade, exponencial) do quantum em relação aos algoritmos clássicos e, por esse motivo, o problema de amostragem recursiva de Fourier é discutido com menos frequência. No entanto, esse é um problema computacional interessante por si só.