Show code cell source
!pip install ket-lang
!pip install numpy
import numpy as np
Import math
from ket import *
Algoritmo de Busca de Grover#
Esta seção aborda o Algoritmo de Grover, um algoritmo quântico que permite encontrar um elemento em uma lista não ordenada. Uma descrição mais rigorosa desse problema, bem como as alternativas clássicas para o mesmo são apresentadas neste texto.
Problema de Grover#
O problema de Grover consiste em encontrar um elemento específico em uma lista de \(2^n\) itens. A lista é formada por palavras binárias de \(n\) dígitos. Há uma função booleana \(f\colon \{ 0,1 \}^n \to \{ 0,1 \}\) que só sinaliza ‘1’ para uma entrada \(x_0\) em particular, que pode ser desconhecida. Em outras palavras, a função \(f\) pode ser descrita por
A maneira de saber se o item \(x\) considerado corresponde ao desejado é por meio da aplicação de \(f\). Essa função poderia ser chamada de ``teste’’, e caso se queira saber se o item \(x\) da lista é o desejado (em outras palavras, se \(x = x_0\)), deve-se fazer o teste em \(x\) e ver se o teste resulta em ‘1’ (item desejado foi encontrado) ou ‘0’ (o item testado não é o desejado).
Problema de Grover
Encontrar a única entrada \(x_0 \in \{ 0,1 \}^n\) tal que o resultado do teste \(f\) sinaliza ‘1’. Ou seja, encontrar único \(x_0\) com
Em seguida, um algoritmo quântico é apresentado para resolver o problema de forma mais eficiente do que seria possível classicamente.
Algoritmo de Grover#
O algoritmo de Grover é um algoritmo quântico para resolução do problema de Grover, que consiste em encontrar o elemento em uma lista por meio de um teste \(f\), e o elemento desejado é o único \(x_0\) que faz com que o teste sinalize 1. O algoritmo quântico consiste em aplicar uma subrotina quântica, o operador de Grover, por um número de vezes da ordem de \(\sqrt{N}\), em que \(N=2^n\) é o número de itens na lista, indexados por \(n\) bits.
Algoritmo de Grover#
Entrada: \(O_\text{F}(f) = O\) \ (oráculo de fase associado à função booleana \(f\))
Procedimento:
Saída: Leitura do registrador fornece o item \(x_0\) buscado, com probabilidade de acerto
EXERCÍCIO
Circuito#
Notação compacta
Notação auxiliar#
Para facilitar, lista-se abaixo a notação utilizada nesse algoritmo:
Contas auxiliares#
Vale que:
A aplicação de \(O_\text{F}\) em \(\ket{\alpha}\) e \(\ket{\beta}\) fica:
O operador \(G\) pode ser escrito como:
em que foi definido \(\ket{\psi} = \ket{+}^{\otimes n}\).
Primeira aplicação do operador \(G\)#
Antes de aplicar o operador \(G\) pela primeira vez, prepara-se o estado inicial fazendo uma superposição com pesos iguais em cada qubit:
Nota-se que \( \ket{\psi_0}\) pertence ao subespaço vetorial \(S\) gerado por \(\ket{\alpha},\ket{\beta}\) e com escalares reais. Isso permitirá uma interpretação geométrica muito útil para o entendimento do algoritmo.
Na etapa 1 de \(G\), aplica-se o oráculo de fase ao estado inicial \(\ket{\psi}\):
O vetor \(\ket{\psi_1}\) continua no espaço \(S\), e a aplicação do oráculo de fase pode ser visualizada como uma reflexão em torno do eixo \(\ket{\alpha}\).
As etapas 2, 3 e 4 de \(G\) redundam em aplicar \(2 \ket{\psi}\bra{\psi} - I\) em \( \ket{\psi_1} \), conforme equação (\(G\)). E a aplicação desse operador pode ser vista geometricamente de acordo com a figura abaixo.
Dessa forma, a primeira aplicação do operador \(G\) fornece o seguinte.
EXERCÍCIO
Aplicações subsequentes do operador \(G\)#
As aplicações sucessivas do operador de Grover têm o mesmo efeito descrito anteriormente. Cada aplicação de \(G\) corresponde a uma rotação no sentido anti-horário. A figura a seguir ilustra a \(l\)-ésima aplicação de \(G\).
O operador \(G\) é aplicado por \(k\) vezes até que o vetor resultante esteja o mais próximo possível do eixo \(\ket{\beta}\).
Número necessário de aplicações de \(G\)#
O número de aplicações \(k\) necessário é dado por
em que se arredonda \(k\) para o inteiro mais próximo.
O ângulo \(\theta\) (em radianos) é o ângulo que \(G\ket{\psi}\) faz com \(\ket{\psi}\). Pode ser obtido por:
Fazendo-se a seguinte manipulação algébrica, pode-se encontrar outra expressão equivalente para o ângulo.
Portanto, pode-se escrever
e o valor de \(k\) é dado por
ou por
É possível verificar que o número \(k\) é da ordem de \(\sqrt{N}\). Tem-se
Probabilidade de acerto#
Ao aplicar a subrotina \(G\) por um número \(k\) de vezes especificado anteriormente, obtém-se o estado final o mais próximo possível de \(\ket{\beta} = \ket{x_0}\), o item desejado. A projeção na direção desse vetor permite encontrar a probabilidade de acerto do algoritmo. O estado final \(G^k \ket{\psi}\) faz um ângulo com o estado \(\ket{x_0}\) menor que \(\theta/2\), pois se fosse maior ou igual, seria possível aplicar novamente o operador \(G\) para reduzi-lo. Portanto, o ângulo \(\cos \theta\big(\ket{x_0},G^k\ket{\psi}\big)\) entre o estado desejado e o estado final é menor que \(\theta/2\), o que significa que seu cosseno é maior que \(\cos (\theta/2)\).
Com isso, tem-se a probabilidade de acerto estimada em
Generalização#
O algoritmo de Grover também funciona para o caso em que há mais de um elemento marcado, isto é, há \(x_0\), \(x_1\), \(\ldots\), \(x_{m-1}\) elementos tais que \(f(x_0) = f(x_1) = \ldots = f(x_{m-1})\) e os demais valores anulam \(f\). É possível adaptar a análise do algoritmo para esse caso. A interpretação geométrica continua valendo, mas desta vez, tem-se
EXERCÍCIO
Algoritmo Clássico#
Algoritmo Clássico Determinístico#
Classicamente, se o teste é dado como uma caixa preta, em que não se sabe a estrutura interna de \(f\), a maneira de se encontrar \(x_0\) é por busca exaustiva. Para garantir que o item \(x_0\) seja encontrado, deve-se, no pior caso, olhar todas as \(N = 2^n\) entradas possíveis. Portanto são necessários \(N\) aplicações do teste \(f\).
Algoritmo Clássico Probabilístico#
Considere um algoritmo que sorteie aleatoriamente as entradas a serem testadas (e sem repetir entradas). Após o teste de \(k\) entradas, a probabilidade de que se tenha localizado o item desejado \(x_0\) é de
Para que haja probabilidade de encontrar a resposta seja maior que \(1/2\), deve-se testar pelo menos por um número de vezes
Desse modo, ainda deve-se aplicar o teste \(f\) da ordem de \(N\) vezes.
\begin{subsection}{Comparação de Desempenho}
O desempenho dos algoritmos são comparados na tabela abaixo.
Dessa forma, o algoritmo de Grover apresentaria ganho quadrático de desempenho em relação aos algoritmos clássicos, se a aplicação de \(f\) nos casos clássico e quântico forem equivalentes em termos de custos computacionais.
Simulação do algorítimo de Grover#
Para simular o algorítimo de Grover usaremos a línguagem Ket de computação quântica, para isso precisamos ter a mesma instalada, caso não possua o pacote instalado rode o seguinte código:
pip install ket-lang
Com a biblioteca instalada, importa-se a mesma para ser usada dentro do seu código:
from ket import *
Primeiro iremos inicializar o oráculo:
def oracle(qubits, target):
"""Oráculo: inverte a amplitude do estado desejado."""
X(
qubits[i]
for i, bit in enumerate(bin(target)[2:].zfill(len(qubits)))
if bit == "0"
)
H(qubits[-1])
ctrl(qubits[:-1], X, qubits[-1]) # Controle multi-qubit
H(qubits[-1])
X(
qubits[i]
for i, bit in enumerate(bin(target)[2:].zfill(len(qubits)))
if bit == "0"
)
Em seguida o operador de difusão
def diffusion_operator(qubits):
"""Operador de difusão: amplifica as amplitudes dos estados marcados."""
H(qubits)
X(qubits)
H(qubits[-1])
ctrl(qubits[:-1], X, qubits[-1]) # Controle multi-qubit
H(qubits[-1])
X(qubits)
H(qubits)
Por fim aplicamos o algorítimo de Grover utilizando-se das portas quânticas já conhecidas, do oráculo e do operador de difusão:
def grover(n, target):
"""Executa o algoritmo de Grover."""
# Inicialização do processo
process = Process()
# Alocação de n qubits
qubits = process.alloc(n)
# Superposição inicial
H(qubits)
# Número ideal de iterações: sqrt(2^n)
iterations = int((2**n) ** 0.5)
# Iterações do algoritmo de Grover
for _ in range(iterations):
oracle(qubits, target)
diffusion_operator(qubits)
# Medição
result = [measure(q).value for q in qubits]
print("Estado encontrado:", result)