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 em vez de duas. O algoritmo de Deutsch-Jozsa amplia essa vantagem — e, de fato, pode ser usado para resolver alguns problemas de consulta diferentes.
Aqui está a descrição em circuito quântico do algoritmo de Deutsch-Jozsa. Uma etapa adicional de pós-processamento clássico, não mostrada na figura, também pode ser necessária dependendo do problema específico que está sendo resolvido.
Claro, ainda não discutimos quais problemas esse algoritmo resolve; isso é feito nas duas seções a seguir.
O problema de Deutsch-Jozsa
Vamos começar com o problema de consulta que o algoritmo de Deutsch-Jozsa foi originalmente criado para resolver, conhecido como o problema de Deutsch-Jozsa.
A função de entrada para esse problema tem a forma para um inteiro positivo arbitrário Assim como no problema de Deutsch, a tarefa é retornar se for constante e se for balanceada, o que novamente significa que o número de strings de entrada nas quais a função assume o valor é igual ao número de strings de entrada nas 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 balanceadas. Por exemplo, a função definida como
não se enquadra em nenhuma dessas duas categorias. Para o problema de Deutsch-Jozsa, simplesmente não nos preocupamos com funções assim — elas são consideradas entradas "não importa". Ou seja, para esse problema temos uma promessa de que é constante ou balanceada.
O algoritmo de Deutsch-Jozsa, com sua única consulta, resolve esse problema da seguinte forma: se todos os resultados de medição forem então a função é constante; caso contrário, se pelo menos um dos resultados de medição for então a função é balanceada. Outra forma de dizer isso é que o circuito descrito acima é seguido por uma etapa de pós-processamento clássico na qual o OR dos resultados de medição é calculado para produzir a saída.
Análise do algoritmo
Para analisar o desempenho do algoritmo de Deutsch-Jozsa para o problema de 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 forma usual,
mas também podemos expressar essa operação em termos de sua ação sobre os estados da base padrão:
Essas duas equações podem ser combinadas em uma única fórmula,
que vale para ambas as escolhas de
Agora suponha que, em vez de apenas um único qubit, temos qubits, e uma operação Hadamard é executada em cada um. A operação combinada nos qubits é descrita pelo produto tensorial ( vezes), que escrevemos como para concisão e clareza. Usando a fórmula acima e expandindo e simplificando, podemos expressar a ação dessa operação combinada sobre os estados da base padrão de qubits da seguinte forma:
Aqui, aliás, estamos escrevendo strings 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 dos demais) é
Quando a operação é executada, esse estado é transformado em
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 concluir muito sobre as probabilidades de obter diferentes resultados de medição sem saber mais sobre a função
Felizmente, tudo o que precisamos saber é a probabilidade de que todos os resultados de medição sejam — pois essa é a probabilidade de o algoritmo determinar que é constante. Essa probabilidade tem uma fórmula simples.
Em mais detalhes, 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 calculando o quadrado do valor absoluto, obtemos
Se, por outro lado, for balanceada, então assume o valor em metade das strings e o valor na outra metade, de modo que os termos e na soma se cancelam e ficamos com o valor
Concluímos que o algoritmo opera corretamente desde que a promessa seja cumprida.
Dificuldade clássica
O algoritmo de Deutsch-Jozsa funciona sempre, fornecendo sempre a resposta correta quando a promessa é cumprida, e requer apenas uma única consulta. Como isso se compara aos algoritmos de consulta clássicos para o problema de Deutsch-Jozsa?
Primeiro, qualquer algoritmo clássico determinístico que resolva corretamente o problema de Deutsch-Jozsa deve fazer um número exponencialmente grande de consultas: consultas são necessárias no pior caso. O raciocínio é que, se um algoritmo determinístico consulta em ou menos strings diferentes, e obtém o mesmo valor da função em todos os casos, então ambas as respostas ainda são possíveis. A função pode ser constante, ou pode ser balanceada mas, por azar, todas as consultas retornam o mesmo valor.
A segunda possibilidade pode parecer improvável — mas para algoritmos determinísticos não há aleatoriedade ou incerteza, então eles falharão sistematicamente em certas funções. Portanto, temos uma vantagem significativa dos algoritmos quânticos sobre os clássicos nesse aspecto.
Há, no entanto, uma ressalva: algoritmos clássicos probabilísticos conseguem resolver o problema de Deutsch-Jozsa com probabilidade muito alta usando apenas algumas consultas. Em particular, se simplesmente escolhermos algumas strings diferentes de comprimento aleatoriamente e consultarmos nessas strings, é improvável que obtenhamos o mesmo valor da função para todas elas quando for balanceada.
Para ser mais preciso, se escolhermos strings de entrada uniformemente ao acaso, avaliarmos e respondermos se os valores da função forem todos iguais, e caso contrário, estaremos sempre corretos quando for constante, e errados no caso em que é balanceada com probabilidade de apenas Se tomarmos por exemplo, esse algoritmo responderá corretamente com probabilidade superior a %.
Por essa razão, ainda temos uma vantagem bastante modesta dos algoritmos quânticos sobre os clássicos — mas é, mesmo assim, uma vantagem quantificável que representa uma melhoria em relação ao algoritmo de Deutsch.
Deutsch-Jozsa com Qiskit
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-aer
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
import numpy as np
Para implementar o algoritmo de Deutsch-Jozsa no Qiskit, vamos começar definindo uma função dj_query que gera um circuito quântico implementando um porta de consulta, para uma função selecionada aleatoriamente que satisfaça a promessa do problema de Deutsch-Jozsa.
Com 50% de chance, a função é constante, e com 50% de chance a função é balanceada.
Para cada uma dessas duas possibilidades, a função é selecionada de forma uniforme dentre as funções daquele 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 qc
Podemos visualizar a implementação em Circuit quântico do porta de consulta usando o método draw como de costume.
display(dj_query(3).draw(output="mpl"))
A seguir, definimos uma função que cria o circuito do Deutsch-Jozsa, recebendo como argumento um circuito quântico que implementa um 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 qc
Por fim, definimos uma função que executa o circuito do Deutsch-Jozsa uma única 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 em Circuit quântico do porta de consulta para essa função e, em seguida, executando o algoritmo de Deutsch-Jozsa sobre ela.
f = dj_query(3)
display(f.draw("mpl"))
display(dj_algorithm(f))

'balanced'
O problema de Bernstein-Vazirani
A seguir, vamos discutir um problema conhecido como o 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 recebem esse nome.
Primeiro, vamos introduzir uma notação. Para quaisquer duas strings binárias e de comprimento definimos
Vamos nos referir a essa operação como o produto interno binário. Uma forma alternativa de defini-lo é a seguinte.
Observe que essa é uma operação simétrica, ou seja, o resultado não muda se trocarmos e o que nos dá liberdade para fazer isso sempre que for conveniente. Às vezes é útil pensar no produto interno binário como a paridade dos bits de nas posições em que a string tem um ou equivalentemente, a paridade dos bits de nas posições em que a string tem um
Com essa notação em mãos, podemos agora definir o problema de Bernstein-Vazirani.
Na verdade, não precisamos de um novo algoritmo quântico para esse problema; o algoritmo de Deutsch-Jozsa já o resolve. Para maior clareza, vamos nos referir ao Circuit quântico descrito acima — que não inclui a etapa de pós-processamento clássico de calcular o OR — como o Circuit de Deutsch-Jozsa.
Análise do algoritmo
Para analisar como o circuito de Deutsch-Jozsa funciona para uma função que satisfaz a promessa do problema de Bernstein-Vazirani, vamos começar com uma observação rápida. Usando o produto interno binário, podemos descrever de forma alternativa a ação de Gates Hadamard sobre os estados da base computacional de qubits da seguinte maneira.
Assim como vimos na análise do algoritmo de Deutsch, isso se deve ao fato de que o valor para qualquer inteiro depende apenas de ser par ou ímpar.
Voltando ao Circuit de Deutsch-Jozsa, após a execução da primeira camada de Gates Hadamard, o estado dos qubits é
O porta de consulta é então aplicado, o que (por meio do fenômeno de phase kickback) transforma o estado em
Usando nossa fórmula para a ação de uma camada de Gates Hadamard, vemos que a segunda camada de Gates Hadamard transforma esse estado em
Agora podemos fazer algumas simplificações no expoente de dentro da soma. Nos é prometido que para alguma string então podemos expressar o estado como
Como e são valores binários, podemos substituir a adição pelo ou-exclusivo — novamente porque o que importa para um inteiro no expoente de é apenas se ele é par ou ímpar. Fazendo uso da simetria do produto interno binário, obtemos a seguinte expressão para o estado:
(Os parênteses foram adicionados para maior clareza, embora não sejam estritamente necessários, pois é convencional tratar o produto interno binário como tendo precedência maior do que o ou-exclusivo.)
Neste ponto, faremos uso da seguinte fórmula.
Podemos obter essa fórmula a partir de uma fórmula análoga para bits,
junto com uma expansão do produto interno binário e do ou-exclusivo bit a bit:
Isso nos permite expressar o estado do circuito imediatamente antes das medições da seguinte forma:
O passo final é usar mais uma fórmula, que vale para toda string binária
Aqui estamos usando uma notação simples para strings que usaremos mais algumas vezes nesta lição: é a string de comprimento composta inteiramente de zeros.
Uma maneira simples de verificar que essa fórmula funciona é considerar os dois casos separadamente. Se então para toda string logo o valor de cada termo da soma é e obtemos ao somar e dividir por Por outro lado, se algum dos bits de for igual a então o produto interno binário é igual a para exatamente metade das possíveis escolhas de e igual a para a outra metade — pois o valor do produto interno binário se inverte (de para ou de para ) quando invertemos qualquer bit de em uma posição onde tem um
Ao aplicar essa fórmula para simplificar o estado do circuito antes das medições, obtemos
pois se e somente se Assim, as medições revelam exatamente a string que estávamos procurando.
Dificuldade clássica
Enquanto o circuito de Deutsch-Jozsa resolve o problema de Bernstein-Vazirani com uma única consulta, qualquer algoritmo de consulta clássico precisa fazer pelo menos consultas para resolver esse problema.
Isso pode ser demonstrado por meio de um argumento chamado de teórico da informação, que é bastante simples neste 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 classicamente consultando a função em cada uma das strings que possuem um único em cada posição possível, e em todos os outros bits, o que revela os bits de um por vez. Portanto, a vantagem do algoritmo quântico sobre o clássico para este problema é de consulta contra consultas.
Bernstein-Vazirani com Qiskit
Já implementamos o circuito de Deutsch-Jozsa acima, e aqui vamos usá-lo para resolver o problema de Bernstein-Vazirani. Primeiro, vamos definir uma função que implementa um gate de consulta para o problema de Bernstein-Vazirani a partir de qualquer string 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"))
Agora podemos criar uma função que executa o circuito de Deutsch-Jozsa sobre a função, usando a função compile_circuit 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")))
'1011'
Observação sobre nomenclatura
No contexto do problema de Bernstein-Vazirani, é comum que o algoritmo de Deutsch-Jozsa seja chamado de "algoritmo de Bernstein-Vazirani". Isso é um pouco enganoso, pois o algoritmo é o algoritmo de Deutsch-Jozsa — e os próprios Bernstein e Vazirani foram bem claros sobre isso em seu trabalho.
O que Bernstein e Vazirani fizeram, após demonstrar que o algoritmo de Deutsch-Jozsa resolve o problema de Bernstein-Vazirani (como enunciado acima), foi definir um problema muito mais complexo, conhecido como o problema de amostragem de Fourier recursiva. Trata-se de um problema bastante artificial, em que as soluções para diferentes instâncias do problema desbloqueiam efetivamente novos níveis, organizados em uma estrutura de árvore. O problema de Bernstein-Vazirani é, essencialmente, apenas o caso base desse problema mais complexo.
O problema de amostragem de Fourier recursiva foi o primeiro exemplo conhecido de um problema de consulta em que algoritmos quânticos possuem uma vantagem dita super-polinomial sobre algoritmos probabilísticos, superando assim a vantagem quântica sobre a clássica oferecida pelo algoritmo de Deutsch-Jozsa. De forma intuitiva, a versão recursiva do problema amplifica a vantagem de versus dos algoritmos quânticos para algo muito maior.
O aspecto mais desafiador da análise matemática que estabelece essa vantagem é demonstrar que algoritmos clássicos de consulta não conseguem resolver o problema sem fazer muitas consultas. Isso é bastante típico: 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, fornece um exemplo bem mais simples de uma vantagem super-polinomial (e, na verdade, exponencial) dos algoritmos quânticos sobre os clássicos — e, por essa razão, o problema de amostragem de Fourier recursiva é menos frequentemente discutido. Ainda assim, é um problema computacional interessante por mérito próprio.