Algoritmos cuánticos: estimación de fase
Kento Ueda (15 de mayo de 2024)
Este notebook presenta los conceptos fundamentales y la implementación de la transformada cuántica de Fourier (QFT) y la estimación de fase cuántica (QPE).
Descargar el PDF de la clase original. Ten en cuenta que algunos fragmentos de código pueden estar desactualizados, ya que son imágenes estáticas.
Tiempo aproximado de QPU para este experimento: 7 segundos.
1. Introducción
Transformada cuántica de Fourier (QFT)
La transformada cuántica de Fourier es el equivalente cuántico de la transformada discreta de Fourier clásica. Es una transformación lineal que se aplica a estados cuánticos y convierte representaciones en la base computacional a sus representaciones en la base de Fourier. La QFT desempeña un papel central en muchos algoritmos cuánticos y ofrece un método eficiente para extraer información de periodicidad de los estados cuánticos. La QFT puede implementarse con operaciones mediante puertas cuánticas como puertas Hadamard y puertas de fase controladas para qubits, lo que permite una aceleración exponencial frente a la transformada de Fourier clásica.
- Aplicaciones: Es un componente fundamental en algoritmos cuánticos como el algoritmo de Shor para la factorización de números enteros grandes y el logaritmo discreto.
Estimación de fase cuántica (QPE)
La estimación de fase cuántica es un algoritmo cuántico que estima la fase de un eigenvector de un operador unitario. Este algoritmo establece un puente entre las propiedades matemáticas abstractas de los estados cuánticos y sus aplicaciones computacionales.
- Aplicaciones: Puede resolver problemas como encontrar eigenvalores de matrices unitarias y la simulación de sistemas cuánticos.
Juntas, la QFT y la QPE forman la columna vertebral esencial de muchos algoritmos cuánticos que resuelven problemas que son intratables para los computadores clásicos. Al final de este notebook, comprenderás cómo se implementan estas técnicas.
2. Fundamentos de la transformada cuántica de Fourier (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
En analogía con la transformada discreta de Fourier, la QFT actúa sobre un estado cuántico para qubits y lo transforma en el estado cuántico .
donde .
O representado como una matriz unitaria:
2.1. Intuición
La transformada cuántica de Fourier (QFT) transforma entre dos bases: la base computacional (base Z) y la base de Fourier. ¿Qué significa la base de Fourier en este contexto? Seguramente recuerdas que la transformada de Fourier de una función describe la convolución de con una función sinusoidal de frecuencia . En términos simples: la transformada de Fourier es una función que describe cuánto de cada frecuencia se necesitaría para construir a partir de funciones seno (o coseno). Para obtener una mejor comprensión de lo que significa la QFT en este contexto, considera las imágenes paso a paso a continuación, que muestran un número codificado en binario con cuatro qubits:
En la base computacional almacenamos números en forma binaria utilizando los estados y .
Observa la frecuencia con la que cambian los diferentes qubits: el qubit más a la izquierda cambia con cada incremento del número, el siguiente cada dos incrementos, el tercero cada cuatro incrementos, etc.
Si aplicamos una transformada cuántica de Fourier a estos estados, mapeamos:
(Los estados en la base de Fourier a menudo se denotan con una tilde (~).)
En la base de Fourier almacenamos números mediante diferentes rotaciones alrededor del eje Z:
IFRAME
El número a almacenar determina el ángulo en el que cada qubit se rota alrededor del eje Z. En el estado , todos los qubits se encuentran en el estado . Como se ve en el ejemplo anterior, para codificar el estado en 4 qubits, el qubit más a la izquierda fue rotado vueltas completas ( radianes). El siguiente qubit se rota el doble ( radianes o vueltas completas), este ángulo se duplica nuevamente para el siguiente qubit, etc.
Observa nuevamente la frecuencia con la que cambia cada qubit. El qubit más a la izquierda (qubit 0) tiene en este caso la frecuencia más baja, el más a la derecha la más alta.
2.2 Ejemplo: QFT de 1 qubit
Consideremos el caso .
La matriz unitaria se puede escribir como:
Esta operación corresponde al resultado de aplicar la puerta Hadamard ().
2.3 Representación como producto de la QFT
Generalicemos la transformación para , aplicada al estado .
2.4 Ejemplo: construcción del circuito QFT de 3 qubits
A partir de la descripción anterior, puede no ser inmediatamente claro cómo construir un circuito QFT. Primero, observa que con tres qubits esperamos fases que evolucionan a diferentes "ritmos". La traducción exacta a un circuito se deja como ejercicio para el lector. Hay varios diagramas y ejemplos en el PDF de la clase. Recursos adicionales se encuentran en esta lección del curso "Fundamentos de algoritmos cuánticos".
Demostramos la QFT solo con simuladores y utilizamos el framework Qiskit Patterns cuando pasemos a la estimación de fase cuá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")
Como ejemplo, aplicamos la QFT a .
Primero confirmamos la representación binaria del entero 5 y creamos el circuito que codifica el estado 5:
bin(5)
'0b101'
qc = QuantumCircuit(3)
qc.x(0)
qc.x(2)
qc.draw(output="mpl")
Verificamos los estados cuánticos con el simulador Aer:
statevector = Statevector(qc)
plot_bloch_multivector(statevector)

Finalmente, añadimos la QFT y observamos el estado final de nuestros 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)

Vemos que la función QFT ha funcionado correctamente. El qubit 0 fue rotado de una vuelta completa, el qubit 1 fue rotado vueltas completas (equivalente a de una vuelta completa) y el qubit 2 fue rotado vueltas completas (equivalente a de una vuelta completa).
2.5 Ejercicio: QFT
(1) Implementa la QFT para 4 qubits.
##your code goes here##
(2) Aplica la QFT a , simula y dibuja el vector de estado en la esfera de Bloch.
##your code goes here##
Solución del ejercicio: 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 de la estimación de fase cuántica (QPE)
Dado un operador unitario , la QPE estima en ; como es unitario, todos los eigenvalores tienen módulo 1.
3.1 Procedimiento
1. Configuración
se encuentra en un registro de qubits. Otro registro de qubits forma el registro contador, donde almacenaremos el valor :
2. Superposición
Aplicamos una operación de puerta Hadamard de bits al registro contador:
3. Operaciones unitarias controladas
Introducimos el unitario controlado , que aplica el operador unitario al registro objetivo cuando el bit de control correspondiente es . Como es un operador unitario con eigenvector tal que , se cumple:
3.2 Ejemplo: QPE con puerta T
Utilicemos la puerta como ejemplo para la QPE y estimemos su fase .
Esperamos:
donde
Inicializamos como eigenvector de la puerta aplicando una puerta :
qpe = QuantumCircuit(4, 3)
qpe.x(3)
qpe.draw(output="mpl")
A continuación, aplicamos puertas Hadamard a los qubits contadores:
for qubit in range(3):
qpe.h(qubit)
qpe.draw(output="mpl")
Realizamos las operaciones unitarias 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 la transformada cuántica de Fourier inversa para convertir el estado del registro contador, y luego medimos el registro contador:
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 con el 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)
Obtenemos un resultado (001) con certeza, que como número decimal es 1. Ahora debemos dividir nuestro resultado (1) por para obtener :
El algoritmo de Shor nos permite factorizar un número construyendo un circuito con desconocido y determinando .
3.3 Ejercicio
Estima con 3 qubits para contar y un qubit para un eigenvector.
. Como queremos implementar , debemos establecer .
##your code goes here##
Solución del ejercicio:
# 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. Ejecución con el Sampler Primitive de Qiskit Runtime
Ejecutaremos QPE en un dispositivo cuántico real siguiendo los 4 pasos de Qiskit Patterns.
- Convertir el problema a un formato cuántico nativo
- Optimizar los circuitos
- Ejecutar el circuito objetivo
- Postprocesar los resultados
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import Sampler
# Loading your IBM Quantum® account(s)
service = QiskitRuntimeService()
4.1 Paso 1: Mapear el problema a circuitos cuánticos y operadores
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 Paso 2: Optimizar para el hardware objetivo
# 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 Paso 3: Ejecutar en el hardware objetivo
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 Paso 4: Postprocesar los 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'