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 esse problema tem o formato para um número inteiro positivo arbitrário Como no problema de Deutsch, a tarefa é produzir se for constante e se for equilibrado, o que significa novamente que o número de cadeias de caracteres de entrada nas quais a função assume o valor é igual ao número de cadeias de caracteres de entrada nas quais a função assume o valor .
Observe que, quando é maior que , há funções do formato que não são 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 das medições forem , então a função é constante; caso contrário, se pelo menos um dos resultados das medições 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 é verdadeiro 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, a propósito, 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 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 se pode 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 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 para cada string caso em que o valor da soma é ou para cada string e, nesse caso, o valor da soma é Dividindo por e tomando o quadrado do valor absoluto, obtém-se
Se, por outro lado, estiver equilibrado, então assume o valor em metade das cadeias de caracteres 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 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 as cadeias de entrada uniformemente ao acaso, avaliarmos e respondermos se os valores da função forem todos iguais e se não forem, então sempre estaremos corretos quando for constante e errados no caso de ser equilibrado com probabilidade igual a Se considerarmos , por exemplo, esse algoritmo responderá corretamente com probabilidade maior que %.
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 introduzir 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 de ponto binário como sendo a paridade dos bits de nas posições em que a cadeia de caracteres tem um ou, de forma equivalente, a paridade dos bits de nas posições em que a cadeia de caracteres tem 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. Prometemos que para alguma string para que possamos 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:
A etapa final é usar outra fórmula, que funciona para cada string binária
Aqui estamos usando uma notação simples para cadeias de caracteres que usaremos várias outras vezes na lição: é a cadeia de caracteres totalmente zero de comprimento
Uma maneira simples de argumentar que essa fórmula funciona é considerar os dois casos separadamente. Se , então para cada cadeia de caracteres , então o valor de cada termo na soma é e obtemos somando e dividindo por Por outro lado, se qualquer um dos bits de for igual a , então o produto de ponto binário é igual a para exatamente metade das escolhas possíveis para e para a outra metade - porque o valor do produto de ponto binário inverte (de para ou de para ) se invertermos 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 Portanto, as medições revelam precisamente a string 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 em cada uma das cadeias de caracteres com um único em cada posição possível e para todos os outros bits, o que revela os bits de um de cada vez. Portanto, a vantagem do quantum sobre os algoritmos clássicos para esse problema é query versus queries.
Bernstein-Vazirani com Qiskit
Já implementamos o circuito Deutsch-Jozsa acima e aqui o utilizaremos para resolver o problema Bernstein-Vazirani. Primeiro, definiremos uma função que implementa uma porta de consulta para o problema de Bernstein-Vazirani com 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"))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ó.