Algoritmos quânticos: Estimativa de fase
Kento Ueda (15 de maio de 2024)
Este notebook apresenta os conceitos fundamentais e a implementação da Transformada de Fourier Quântica (QFT) e da Estimativa de Fase Quântica (QPE).
Baixe o PDF da aula original. Observe que alguns trechos de código podem estar desatualizados, pois são imagens estáticas.
O tempo aproximado de QPU para rodar esse experimento é de 7 segundos.
1. Introdução
Transformada de Fourier Quântica (QFT)
A Transformada de Fourier Quântica é a versão quântica da transformada discreta de Fourier clássica. É uma transformação linear aplicada a estados quânticos que mapeia as bases computacionais para suas representações na base de Fourier. A QFT tem um papel fundamental em muitos algoritmos quânticos, oferecendo um método eficiente para extrair informações de periodicidade de estados quânticos. A QFT pode ser implementada com operações usando portas quânticas como portas de Hadamard e portas de Fase Controlada para qubits, possibilitando uma aceleração exponencial em relação à transformada de Fourier clássica.
- Aplicações: É um componente fundamental em algoritmos quânticos como o algoritmo de Shor para fatoração de inteiros grandes e logaritmo discreto.
Estimativa de Fase Quântica (QPE)
A Estimativa de Fase Quântica é um algoritmo quântico usado para estimar a fase associada a um autovetor de um operador unitário. Esse algoritmo cria uma ponte entre as propriedades matemáticas abstratas dos estados quânticos e suas aplicações computacionais.
- Aplicações: Ele consegue resolver problemas como encontrar autovalores de matrizes unitárias e simular sistemas quânticos.
Juntos, QFT e QPE formam a espinha dorsal essencial de muitos algoritmos quânticos que resolvem problemas inviáveis para computadores clássicos. Ao fim deste notebook, você vai entender como essas técnicas são implementadas.
2. Fundamentos da Transformada de Fourier Quântica (QFT)
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-aer qiskit-ibm-runtime
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit_aer import AerSimulator
from qiskit.visualization import plot_histogram, plot_bloch_multivector
from qiskit.quantum_info import Statevector
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import Sampler
from numpy import pi
Por analogia com a transformada discreta de Fourier, a QFT age sobre um estado quântico para qubits e o mapeia para o estado quântico .
onde .
Ou a representação em matriz unitária:
2.1. Intuição
A transformada de Fourier quântica (QFT) transforma entre duas bases: a base computacional (Z) e a base de Fourier. Mas o que significa a base de Fourier nesse contexto? Você provavelmente lembra que a transformada de Fourier de uma função descreve a convolução de com uma função senoidal de frequência . Em termos simples: a transformada de Fourier é uma função que descreve quanto de cada frequência precisaríamos para construir uma função a partir de funções seno (ou cosseno). Para entender melhor o que a QFT significa nesse contexto, observe as imagens em sequência abaixo, que mostram um número codificado em binário usando quatro qubits:
Na base computacional, armazenamos números em binário usando os estados e .
Observe a frequência com que os diferentes qubits mudam: o qubit mais à esquerda vira a cada incremento no número, o seguinte a cada 2 incrementos, o terceiro a cada 4 incrementos, e assim por diante.
Se aplicarmos uma transformada de Fourier quântica a esses estados, mapeamos
(Costumamos denotar estados na base de Fourier usando o til (~)).
Na base de Fourier, armazenamos números usando diferentes rotações ao redor do eixo Z:
IFRAME
O número que queremos armazenar determina o ângulo em que cada qubit é girado ao redor do eixo Z. No estado , todos os qubits estão no estado . Como visto no exemplo acima, para codificar o estado em 4 qubits, giramos o qubit mais à esquerda em voltas completas ( radianos). O próximo qubit é girado o dobro disso ( radianos, ou voltas completas), esse ângulo é então dobrado para o qubit seguinte, e assim sucessivamente.
Novamente, observe a frequência com que cada qubit muda. O qubit mais à esquerda (qubit 0) tem a menor frequência nesse caso, e o mais à direita tem a maior.
2.2 Exemplo: QFT de 1 qubit
Vamos considerar o caso de .
A matriz unitária pode ser escrita como:
Essa operação é o resultado de aplicar a porta de Hadamard ().
2.3 Representação em produto da QFT
Vamos generalizar uma transformação para , agindo sobre o estado .
2.4 Exemplo: Construção de circuito QFT com 3 qubits
A partir da descrição acima, pode não estar claro como construir um circuito QFT. Por enquanto, basta notar que esperamos que três qubits tenham fases que evoluem em "taxas" diferentes. Entender exatamente como isso se traduz em um circuito fica como exercício para o leitor. Há vários diagramas e exemplos no PDF da aula. Recursos adicionais incluem esta lição do curso de Fundamentos de algoritmos quânticos.
Vamos demonstrar a QFT usando apenas simuladores e, portanto, não usaremos o framework de padrões do Qiskit até chegarmos na estimativa de fase quântica.
# Prepare for 3 qubits circuit
qr = QuantumRegister(3)
cr = ClassicalRegister(3)
qc = QuantumCircuit(qr, cr)
qc.h(2)
qc.cp(pi / 2, 1, 2) # Controlled-phase gate from qubit 1 to qubit 2
qc.cp(pi / 4, 0, 2) # Controlled-phase gate from qubit 0 to qubit 2
qc.draw(output="mpl")
qc.h(1)
qc.cp(pi / 2, 0, 1) # Controlled-phase gate from qubit 0 to qubit 1
qc.draw(output="mpl")
qc.h(0)
qc.draw(output="mpl")
qc.swap(0, 2)
qc.draw(output="mpl")
Vamos aplicar a QFT em como exemplo.
Primeiro, confirmamos a notação binária do inteiro 5 e criamos o circuito que codifica o estado 5:
bin(5)
'0b101'
qc = QuantumCircuit(3)
qc.x(0)
qc.x(2)
qc.draw(output="mpl")
Verificamos os estados quânticos usando o simulador Aer:
statevector = Statevector(qc)
plot_bloch_multivector(statevector)

Por fim, adicionamos a QFT e visualizamos o estado final dos nossos qubits:
qc.h(2)
qc.cp(pi / 2, 1, 2)
qc.cp(pi / 4, 0, 2)
qc.h(1)
qc.cp(pi / 2, 0, 1)
qc.h(0)
qc.swap(0, 2)
qc.draw(output="mpl")
statevector = Statevector(qc)
plot_bloch_multivector(statevector)

Podemos ver que nossa função QFT funcionou corretamente. O qubit 0 foi girado em de uma volta completa, o qubit 1 em voltas completas (equivalente a de volta completa), e o qubit 2 em voltas completas (equivalente a de volta completa).
2.5 Exercício: QFT
(1) Implemente a QFT de 4 qubits.
##your code goes here##
(2) Aplique a QFT em , simule e plote o vetor de estado usando a esfera de Bloch.
##your code goes here##
Solução do exercício: QFT
(1)
qr = QuantumRegister(4)
cr = ClassicalRegister(4)
qc = QuantumCircuit(qr, cr)
qc.h(3)
qc.cp(pi / 2, 2, 3)
qc.cp(pi / 4, 1, 3)
qc.cp(pi / 8, 0, 3)
qc.h(2)
qc.cp(pi / 2, 1, 2)
qc.cp(pi / 4, 0, 2)
qc.h(1)
qc.cp(pi / 2, 0, 1)
qc.h(0)
qc.swap(0, 3)
qc.swap(1, 2)
qc.draw(output="mpl")
(2)
bin(14)
'0b1110'
qc = QuantumCircuit(4)
qc.x(1)
qc.x(2)
qc.x(3)
qc.draw("mpl")
qc.h(3)
qc.cp(pi / 2, 2, 3)
qc.cp(pi / 4, 1, 3)
qc.cp(pi / 8, 0, 3)
qc.h(2)
qc.cp(pi / 2, 1, 2)
qc.cp(pi / 4, 0, 2)
qc.h(1)
qc.cp(pi / 2, 0, 1)
qc.h(0)
qc.swap(0, 3)
qc.swap(1, 2)
qc.draw(output="mpl")
statevector = Statevector(qc)
plot_bloch_multivector(statevector)

3. Fundamentos da Estimativa de Fase Quântica (QPE)
Dado uma operação unitária , a QPE estima em . Como é unitária, todos os seus autovalores têm norma 1.
3.1 Procedimento
1. Configuração
está em um conjunto de registradores de qubits. Um conjunto adicional de qubits forma o registrador de contagem, onde armazenaremos o valor :
2. Superposição
Aplique uma operação Hadamard de bits no registrador de contagem:
3. Operações Unitárias Controladas
Precisamos introduzir a unitária controlada , que aplica o operador unitário no registrador-alvo somente quando o bit de controle correspondente é . Como é um operador unitário com autovetor tal que , temos:
3.2 Exemplo: QPE com a porta T
Vamos usar a porta como exemplo de QPE e estimar sua fase .
Esperamos encontrar:
onde
Inicializamos como o autovetor da porta aplicando uma porta :
qpe = QuantumCircuit(4, 3)
qpe.x(3)
qpe.draw(output="mpl")
Em seguida, aplicamos portas Hadamard aos qubits de contagem:
for qubit in range(3):
qpe.h(qubit)
qpe.draw(output="mpl")
Realizamos as operações unitárias controladas:
repetitions = 1
for counting_qubit in range(3):
for i in range(repetitions):
qpe.cp(pi / 4, counting_qubit, 3) # This is C-U
repetitions *= 2
qpe.draw(output="mpl")
Aplicamos a transformada quântica de Fourier inversa para converter o estado do registrador de contagem e, em seguida, medimos esse registrador:
from qiskit.circuit.library import QFT
# Apply inverse QFT
qpe.append(QFT(3, inverse=True), [0, 1, 2])
qpe.draw(output="mpl")
for n in range(3):
qpe.measure(n, n)
qpe.draw(output="mpl")
Podemos simular usando o simulador Aer:
aer_sim = AerSimulator()
shots = 2048
pm = generate_preset_pass_manager(backend=aer_sim, optimization_level=1)
t_qpe = pm.run(qpe)
sampler = Sampler(mode=aer_sim)
job = sampler.run([t_qpe], shots=shots)
result = job.result()
answer = result[0].data.c.get_counts()
plot_histogram(answer)
Observamos que obtemos um único resultado (001) com certeza, o que corresponde ao decimal 1. Agora precisamos dividir nosso resultado (1) por para obter :
O algoritmo de Shor nos permite fatorar um número construindo um circuito com desconhecido e obtendo .
3.3 Exercício
Estime usando 3 qubits para contagem e um qubit para o autovetor.
. Como queremos implementar , precisamos definir .
##your code goes here##
Solução do exercício:
# Create and set up circuit
qpe = QuantumCircuit(4, 3)
# Apply H-Gates to counting qubits:
for qubit in range(3):
qpe.h(qubit)
# Prepare our eigenstate |psi>:
qpe.x(3)
# Do the controlled-U operations:
angle = 2 * pi / 3
repetitions = 1
for counting_qubit in range(3):
for i in range(repetitions):
qpe.cp(angle, counting_qubit, 3)
repetitions *= 2
# Do the inverse QFT:
qpe.append(QFT(3, inverse=True), [0, 1, 2])
for n in range(3):
qpe.measure(n, n)
qpe.draw(output="mpl")
aer_sim = AerSimulator()
shots = 4096
pm = generate_preset_pass_manager(backend=aer_sim, optimization_level=1)
t_qpe = pm.run(qpe)
sampler = Sampler(mode=aer_sim)
job = sampler.run([t_qpe], shots=shots)
result = job.result()
answer = result[0].data.c.get_counts()
plot_histogram(answer)
4. Execução com a primitiva Sampler do Qiskit Runtime
Vamos executar a QPE em um dispositivo quântico real seguindo as 4 etapas dos padrões Qiskit.
- Mapear o problema para um formato nativo quântico
- Otimizar os circuitos
- Executar o circuito-alvo
- Pós-processar os resultados
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import Sampler
# Loading your IBM Quantum® account(s)
service = QiskitRuntimeService()
4.1 Passo 1: Mapear o problema para circuitos e operadores quânticos
qpe = QuantumCircuit(4, 3)
qpe.x(3)
for qubit in range(3):
qpe.h(qubit)
repetitions = 1
for counting_qubit in range(3):
for i in range(repetitions):
qpe.cp(pi / 4, counting_qubit, 3) # This is C-U
repetitions *= 2
qpe.append(QFT(3, inverse=True), [0, 1, 2])
for n in range(3):
qpe.measure(n, n)
qpe.draw(output="mpl")
backend = service.least_busy(simulator=False, operational=True, min_num_qubits=4)
print(backend)
<IBMBackend('ibm_strasbourg')>
4.2 Passo 2: Otimizar para o hardware-alvo
# Transpile the circuit into basis gates executable on the hardware
pm = generate_preset_pass_manager(backend=backend, optimization_level=2)
qc_compiled = pm.run(qpe)
qc_compiled.draw("mpl", idle_wires=False)

4.3 Passo 3: Executar no hardware-alvo
real_sampler = Sampler(mode=backend)
job = real_sampler.run([qc_compiled], shots=1024)
job_id = job.job_id()
print("job id:", job_id)
job id: d13p4zb5z6q00087ag00
job = service.job(job_id) # Input your job-id between the quotations
job.status()
'DONE'
result_real = job.result()
print(result_real)
PrimitiveResult([SamplerPubResult(data=DataBin(c=BitArray(<shape=(), num_shots=1024, num_bits=3>)), metadata={'circuit_metadata': {}})], metadata={'execution': {'execution_spans': ExecutionSpans([DoubleSliceSpan(<start='2025-06-09 22:39:00', stop='2025-06-09 22:39:00', size=1024>)])}, 'version': 2})
4.4 Passo 4: Pós-processar os resultados
from qiskit.visualization import plot_histogram
plot_histogram(result_real[0].data.c.get_counts())
# See the version of Qiskit
import qiskit
qiskit.__version__
'2.0.2'