El algoritmo de Shor
Para este módulo de Qiskit en el aula, los estudiantes deben tener un entorno de Python funcional con los siguientes paquetes instalados:
qiskitv2.1.0 o más recienteqiskit-ibm-runtimev0.40.1 o más recienteqiskit-aerv0.17.0 o más recienteqiskit.visualizationnumpypylatexenc
Para configurar e instalar los paquetes anteriores, consulta la guía Instalar Qiskit. Para ejecutar trabajos en computadoras cuánticas reales, los estudiantes deberán crear una cuenta en IBM Quantum® siguiendo los pasos de la guía Configura tu cuenta de IBM Cloud.
Este módulo fue probado y utilizó tres segundos de tiempo de QPU. Esta es solo una estimación. Tu uso real puede variar.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime
# Uncomment and modify this line as needed to install dependencies
#!pip install 'qiskit>=2.1.0' 'qiskit-ibm-runtime>=0.40.1' 'qiskit-aer>=0.17.0' 'numpy' 'pylatexenc'
Introducción
A principios de la década de 1990, había una creciente expectativa en torno al potencial de las computadoras cuánticas para resolver problemas que eran difíciles para las computadoras clásicas. Algunos talentosos científicos de la computación habían diseñado algoritmos que demostraban el poder de la computación cuántica para ciertos problemas de nicho y muy específicos, pero nadie había encontrado la «aplicación estrella» de la computación cuántica que fuera a revolucionar el campo. Eso fue hasta 1994, cuando Peter Shor presentó lo que hoy se conoce como el algoritmo de Shor para factorizar números grandes.
Era bien sabido en aquel entonces que encontrar los factores primos de un número grande era extremadamente difícil para una computadora clásica. De hecho, los protocolos de seguridad en internet se basaban en esa dificultad. Shor encontró una manera de hallar esos factores de forma exponencialmente más eficiente, delegando algunos de los pasos más complejos a una teórica computadora cuántica del futuro.
En este módulo exploraremos el algoritmo de Shor. Primero, daremos un poco más de contexto al algoritmo, formalizando el problema que resuelve y explicando su relevancia para la ciberseguridad. Luego, haremos una introducción a las matemáticas modulares y cómo aplicarlas al problema de factorización, mostrando cómo la factorización se reduce a otro problema llamado «búsqueda de orden». Veremos cómo la Transformada de Fourier Cuántica y la Estimación de Fase Cuántica que aprendimos en un módulo anterior entran en juego, y cómo usarlas para resolver el problema de búsqueda de orden.
Por último, ¡ejecutaremos el algoritmo de Shor en una computadora cuántica real! Ten en cuenta, sin embargo, que este algoritmo solo será verdaderamente útil cuando dispongamos de una computadora cuántica grande y tolerante a fallos, lo cual todavía está a varios años de distancia. Así que por ahora factorizaremos un número pequeño para demostrar cómo funciona el algoritmo.
El problema de factorización
El objetivo del problema de factorización es encontrar los factores primos de un número . Para algunos números , esto es bastante sencillo. Por ejemplo, si es par, uno de sus factores primos será 2. Si es una potencia prima, es decir, para algún número primo , también es relativamente fácil encontrar : simplemente aproximamos la raíz -ésima de y buscamos primos cercanos que puedan ser .
Donde las computadoras clásicas tienen dificultades es cuando es impar y no es una potencia prima. Este es el caso que aborda el algoritmo de Shor. El algoritmo encuentra dos factores y tales que . Puede aplicarse de forma recursiva hasta que todos los factores sean primos. En las siguientes secciones veremos cómo se aborda este problema.
Relevancia para la ciberseguridad
Muchos esquemas criptográficos se han construido basándose en el hecho de que factorizar números grandes es difícil, incluyendo uno que se usa comúnmente hoy en día, llamado RSA. En RSA, se crea una clave pública multiplicando dos números primos grandes para obtener . Luego, cualquiera puede usar esa clave pública para cifrar datos. Pero solo alguien con la clave privada, y , puede descifrar esos datos.
Si fuera fácil de factorizar, cualquiera podría determinar cuáles son y y romper el cifrado. Pero no lo es. Este es un problema famosamente difícil. De hecho, los factores primos de un número llamado RSA1024, que tiene 1024 dígitos binarios y 309 dígitos decimales de longitud, aún no han sido encontrados, a pesar de que se ofreció un premio de $100,000 dólares por su factorización allá por 1991.
La solución de Shor
En 1994, Peter Shor se dio cuenta de que una computadora cuántica podría factorizar un número grande de forma exponencialmente más eficiente que una computadora clásica. Su intuición se basó en la relación entre este problema de factorización y la aritmética modular. Haremos una breve introducción a la aritmética modular y luego veremos cómo podemos usarla para factorizar .
Aritmética modular
La aritmética modular es un sistema de conteo cíclico: aunque el conteo comienza de la manera habitual, con los enteros 0, 1, 2, etc., en cierto punto, tras un período , el conteo vuelve a empezar. Veamos cómo funciona esto con un ejemplo. Supongamos que nuestro período es 5. Entonces, al contar, donde normalmente llegaríamos a 5, en cambio empezamos de nuevo en 0:
Esto se debe a que en el mundo «módulo 5», el 5 es equivalente al 0. Decimos que . De hecho, todos los múltiplos de 5 serán equivalentes a .
Comprueba tu comprensión
Lee la(s) pregunta(s) a continuación, piensa en tu respuesta y luego haz clic en el triángulo para revelar la solución.
Usa la aritmética modular para resolver el siguiente problema:
Partes en un largo viaje transcontinental en tren a las 8 AM. El viaje dura 60 horas. ¿A qué hora llegas?
Respuesta:
El período es 24, ya que hay 24 horas en un día. Por lo tanto, este problema se puede escribir en aritmética modular como:
Llegarías a tu destino a las 20:00, o las 8 PM.
y
Con frecuencia es útil introducir dos conjuntos, y . es simplemente el conjunto de números que existen en el mundo «módulo ». Por ejemplo, cuando contábamos módulo 5, el conjunto sería . Otro ejemplo: . Podemos realizar sumas y multiplicaciones (módulo ) sobre los elementos de , y el resultado de cada una de estas operaciones también es un elemento de , lo que convierte a en un objeto matemático llamado anillo.
Hay un subconjunto especial de que nos resulta de particular interés para el algoritmo de Shor. Es el subconjunto de números en tales que el máximo común divisor entre cada elemento y es 1, es decir, cada elemento es «coprimo» con . Si tomamos el conjunto de estos números junto con la operación de multiplicación modular, se forma otro objeto matemático llamado grupo. A este grupo lo denominamos . Resulta que con (y los grupos finitos en general), si tomamos cualquier elemento y lo multiplicamos repetidamente por sí mismo, siempre terminaremos obteniendo el número . El número mínimo de veces que hay que multiplicar por sí mismo para obtener se llama el orden de . Este hecho será muy importante para nuestra discusión sobre cómo factorizar números a continuación.
Comprueba tu comprensión
Lee la(s) pregunta(s) a continuación, piensa en tu respuesta y luego haz clic en el triángulo para revelar la solución.
¿Cuál es ?
Respuesta:
Excluimos los siguientes números:
¿Cuál es el orden de cada uno de los elementos de ?
Respuesta:
El orden es el número más pequeño tal que para cada elemento .
Ten en cuenta que, aunque pudimos encontrar el orden de los números en , esta NO es una tarea sencilla en general para valores más grandes de . Este es el núcleo del problema de factorización y la razón por la que necesitamos una computadora cuántica. Lo veremos a medida que avancemos en el resto del cuaderno.
Aplicar la aritmética modular al problema de factorización
La clave para encontrar los factores y tales que consiste en encontrar algún otro entero tal que
y
¿Cómo nos ayuda encontrar a encontrar los factores y ? Veamos el razonamiento ahora. Como , eso significa que . En otras palabras, es un múltiplo de . Entonces, para algún entero ,
Podemos factorizar para obtener:
Por nuestras suposiciones iniciales sabemos que , así que no divide de forma exacta ni a ni a . Por lo tanto, los dos factores de , y , deben dividir a y respectivamente. O bien es factor de y es factor de , o viceversa. Entonces, si calculamos los máximos comunes divisores (MCD) entre y tanto como , obtendremos los factores y . Calcular el MCD entre dos números es una tarea clásicamente sencilla que puede realizarse, por ejemplo, usando el algoritmo de Euclides.
Comprueba tu comprensión
Lee la(s) pregunta(s) a continuación, piensa en tu respuesta y luego haz clic en el triángulo para revelar la solución.
Puede ser complicado entender cada paso de la lógica anterior, así que intenta trabajarlo con un ejemplo. Usa y . Primero, verifica que y . Luego continúa verificando cada paso. Por último, calcula y verifica que son los factores de .
Respuesta:
, que es , por lo que .
, que no es equivalente a .
, que no es equivalente a .
Ahora sabemos que para algún entero . Esto se verifica cuando sustituimos y : cuando .
Ahora necesitamos calcular y .
¡Así encontramos los factores de !
El algoritmo
Ahora que hemos visto cómo encontrar un entero tal que nos ayuda a factorizar , podemos repasar el algoritmo de Shor. Esencialmente se reduce a encontrar :
- Elige un entero aleatorio Elige un entero aleatorio tal que .
- Calcula de forma clásica.
- Si , ya encontraste un factor. Detente.
- De lo contrario, continúa.
-
Encuentra el orden de módulo Encuentra el entero positivo más pequeño que satisface .
-
Verifica si el orden es par
- Si es impar, regresa al paso 1 y elige un nuevo .
- Si es par, continúa al paso 4.
- Calcula
- Verifica que y .
- Si , regresa al paso 1 y elige un nuevo .
- De lo contrario, calcula los MCD para extraer los factores:
Estos serán factores no triviales de .
- Factoriza recursivamente si es necesario
- Si y/o no son primos, aplica el algoritmo de forma recursiva para factorizarlos completamente.
- Una vez que todos los factores sean primos, la factorización está completa.
Basándose en este procedimiento, puede que no sea obvio por qué se necesita una computadora cuántica para completar esta tarea. Es necesaria porque el paso 2, encontrar el orden de módulo , es clásicamente un problema muy difícil. La complejidad escala exponencialmente con el número . Pero con una computadora cuántica, solo tenemos que usar la Estimación de Fase Cuántica para resolverlo. El paso 4, encontrar el MCD de dos enteros, es en realidad algo bastante sencillo de hacer clásicamente. Entonces, el único paso que realmente necesita el poder de una computadora cuántica es el paso de búsqueda de orden. Decimos que el problema de factorización «se reduce» al problema de búsqueda de orden.
La parte difícil: búsqueda de orden
Ahora veremos cómo podemos usar una computadora cuántica para la búsqueda de orden. Primero, aclaremos qué queremos decir con «orden». Por supuesto, ya te expliqué qué significa el orden matemáticamente: es el primer entero no nulo tal que Pero veamos si podemos ganar un poco más de intuición para este concepto.
Para valores de suficientemente pequeños, podemos determinar el orden simplemente calculando cada potencia de , tomando el módulo de ese número y deteniéndonos cuando encontremos la potencia que satisface . Eso fue exactamente lo que hicimos con nuestro ejemplo más arriba. Echemos un vistazo a algunas gráficas de estas potencias modulares para algunos valores de ejemplo de y :
¿Notas algo? ¡Son funciones periódicas! Y el orden es lo mismo que el período. Entonces, la búsqueda de orden es equivalente a la búsqueda de período.
Las computadoras cuánticas son muy adecuadas para encontrar el período de funciones. Para esto, podemos usar una subrutina algorítmica llamada Estimación de Fase Cuántica. Discutimos la QPE y su relación con la Transformada de Fourier Cuántica en el módulo anterior. Para un repaso detallado, ve al módulo de QFT o a la lección de John Watrous sobre Estimación de Fase Cuántica en su curso de Algoritmos Cuánticos. A continuación repasaremos la esencia del procedimiento:
En la Estimación de Fase Cuántica (QPE), comenzamos con un unitario y un autoestado de ese unitario . Luego, usamos QPE para aproximar el autovalor correspondiente, que, dado que el operador es unitario, tendrá la forma . Entonces, encontrar el autovalor es equivalente a encontrar el valor de en la función periódica. El Circuit se ve así:

donde el número de Qubits de control (los Qubits superiores en la figura anterior) determina la precisión de la aproximación.
En el algoritmo de Shor, usamos QPE sobre el operador unitario :
Aquí, denota un estado de la base computacional del registro de múltiples Qubits, donde el valor binario de los Qubits corresponde al entero . Por ejemplo, si y , entonces está representado por el estado de base de cuatro Qubits , ya que se necesitan cuatro Qubits para codificar números hasta 15. (Si este concepto no te resulta familiar, ve al módulo introductorio de Qiskit en el aula para un repaso sobre la codificación binaria de estados cuánticos.)
Ahora necesitamos encontrar un autoestado de este unitario. Si comenzamos en el estado , podemos ver que cada aplicación sucesiva de multiplicará el estado de nuestro registro por , y después de aplicaciones llegaremos al estado nuevamente. Por ejemplo con y :
Entonces las superposiciones de los estados en este ciclo () de la forma:
son todos autoestados de . (Hay más autoestados además de estos. Pero solo nos interesan los de la forma anterior.)
Comprueba tu comprensión
Lee la(s) pregunta(s) a continuación, piensa en tu respuesta y luego haz clic en el triángulo para revelar la solución.
Encuentra un autoestado del unitario correspondiente a y .
Respuesta:
Entonces, el orden es . Los autoestados que nos interesan serán una superposición equitativa de todos los estados por los que se pasó anteriormente, con varias fases:
Supongamos que pudiéramos inicializar el estado de nuestros Qubits en uno de estos autoestados (spoiler: no podemos, o al menos no fácilmente; explicaremos por qué y qué podemos hacer en su lugar en breve). Entonces podríamos usar QPE para estimar el autovalor correspondiente, donde . Luego, podríamos determinar el orden con la simple ecuación:
Pero recuerda, dije que QPE estima , no nos da un valor exacto. Necesitamos que la estimación sea lo suficientemente buena como para diferenciar entre y . Cuantos más Qubits de control tengamos, mejor será la estimación. En los problemas al final de la lección se te pedirá que determines el mínimo necesario para factorizar un número .
Ahora tenemos que resolver un problema. Toda la explicación anterior sobre cómo encontrar comienza con preparar el autoestado . Pero no sabemos cómo hacer eso sin ya conocer . La lógica es circular. Necesitamos una manera de estimar el autovalor sin inicializar el autoestado.
En lugar de comenzar con un autoestado de , podemos preparar el estado inicial en el estado de Qubits correspondiente a en binario (es decir, ). Aunque este estado en sí mismo evidentemente no es un autoestado de , sí es una superposición de todos los autoestados :
Comprueba tu comprensión
Lee la(s) pregunta(s) a continuación, piensa en tu respuesta y luego haz clic en el triángulo para revelar la solución.
Verifica que es equivalente a la superposición de los autoestados que encontraste para y en la pregunta de verificación anterior.
Respuesta:
Los cuatro autoestados eran:
Entonces,
¿Cómo nos permite esto encontrar el orden ? Dado que el estado inicial es una superposición de todos los autoestados de la forma indicada arriba, el algoritmo QPE estima simultáneamente cada uno de los correspondientes a esos autoestados. Así, la medición de los Qubits de control al final producirá una aproximación al valor donde es uno de los autovalores elegido aleatoriamente. Si repetimos este Circuit unas pocas veces y obtenemos varias muestras con diferentes valores de , podremos deducir rápidamente.
Implementar en Qiskit
Como mencionamos antes, nuestro hardware todavía no está en el punto en que podamos factorizar números grandes como RSA1024. Solo vamos a factorizar un número pequeño para demostrar cómo funciona el algoritmo. Para esta demostración, usaremos una versión simplificada del código presentado en el tutorial del algoritmo de Shor. Si quieres más detalles, visita el tutorial.
Ejecutaremos el algoritmo usando nuestro marco de trabajo estándar para resolver problemas cuánticos, llamado el marco de patrones de Qiskit. Este consta de cuatro pasos:
- Mapear tu problema a un circuito cuántico
- Optimizar el circuito para ejecutarlo en hardware cuántico
- Ejecutar tu circuito en el computador cuántico
- Posprocesar las mediciones
1. Mapear
Vamos a factorizar , seleccionando como nuestro entero coprimo.
Primero, necesitamos construir el circuito que implementará la unitaria de multiplicación modular, . Esta es, de hecho, la parte más complicada de toda la implementación y puede ser computacionalmente muy costosa, dependiendo de cómo se haga. Para esto, vamos a hacer trampa un poco: sabemos que empezamos en el estado , y a partir de una pregunta de comprensión anterior,
Así que construiremos una unitaria que realice las operaciones correctas sobre estos cuatro estados, pero que deje todos los demás estados sin cambios. Esto es hacer trampa porque estamos usando nuestro conocimiento del orden de para simplificar la unitaria. Si realmente estuviéramos intentando factorizar un número cuyos factores nos fueran desconocidos, no podríamos hacer esto.
Comprueba tu comprensión
Lee la(s) pregunta(s) a continuación, piensa en tu respuesta y luego haz clic en el triángulo para ver la solución.
Con tu conocimiento de cómo el operador transforma los estados anteriores, construye el operador a partir de una serie de puertas SWAP, que intercambian los estados de dos qubits. (Pista: escribir cada estado en binario te ayudará.)
Respuesta:
Reescribamos la acción de sobre los estados en binario:
Cada una de estas acciones puede lograrse con un simple SWAP. se obtiene intercambiando los estados del qubit y . se obtiene intercambiando los estados del qubit y . Y así sucesivamente. Por lo tanto, podemos descomponer la matriz en la siguiente serie de puertas SWAP:
Recordando que los operadores actúan de derecha a izquierda, comprobemos que esto tiene el efecto deseado sobre cada uno de los estados:
Ahora podemos programar el circuito equivalente a este operador en Qiskit.
Primero, importamos los paquetes necesarios:
# Import necessary packages
import numpy as np
from fractions import Fraction
from math import floor, gcd, log
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.library import QFTGate
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.visualization import plot_histogram
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler
Luego, construimos el operador :
def M2mod15():
"""
M2 (mod 15)
"""
b = 2
U = QuantumCircuit(4)
U.swap(2, 3)
U.swap(1, 2)
U.swap(0, 1)
U = U.to_gate()
U.name = f"M_{b}"
return U
# Get the M2 operator
M2 = M2mod15()
# Add it to a circuit and plot
circ = QuantumCircuit(4)
circ.compose(M2, inplace=True)
circ.decompose(reps=2).draw(output="mpl", fold=-1)
El algoritmo QPE usa una puerta controlada. Así que, ahora que tenemos un circuito , necesitamos convertirlo en un circuito controlado:
def controlled_M2mod15():
"""
Controlled M2 (mod 15)
"""
b = 2
U = QuantumCircuit(4)
U.swap(2, 3)
U.swap(1, 2)
U.swap(0, 1)
U = U.to_gate()
U.name = f"M_{b}"
c_U = U.control()
return c_U
# Get the controlled-M2 operator
controlled_M2 = controlled_M2mod15()
# Add it to a circuit and plot
circ = QuantumCircuit(5)
circ.compose(controlled_M2, inplace=True)
circ.decompose(reps=1).draw(output="mpl", fold=-1)
Ahora tenemos nuestra puerta controlada. Pero para ejecutar el algoritmo de Estimación de Fase Cuántica, necesitaremos controlada, controlada, hasta controlada, donde es el número de qubits usados para estimar la fase. Cuantos más qubits, más precisa será la estimación de la fase. Usaremos qubits de control para nuestro procedimiento de estimación de fase. Así que necesitamos:
donde el índice , con , corresponde al qubit de control. Ahora calculemos para cada valor de :
def a2kmodN(a, k, N):
"""Compute a^{2^k} (mod N) by repeated squaring"""
for _ in range(k):
a = int(np.mod(a**2, N))
return a
k_list = range(8)
b_list = [a2kmodN(2, k, 15) for k in k_list]
print(b_list)
[2, 4, 1, 1, 1, 1, 1, 1]
Como para , todos los operadores correspondientes ( y superiores) son equivalentes a la identidad. Así que solo necesitamos construir una matriz más,
Nota: Esta simplificación solo funciona aquí porque el orden de es . Una vez que (es decir, ), cada potencia posterior del operador es la identidad. En general, para números más grandes o diferentes elecciones de , no se puede omitir la construcción de las potencias superiores. Esta es una de las razones por las que esto se considera un ejemplo de juguete: los números pequeños permiten atajos que no funcionarían para casos más grandes.
def M4mod15():
"""
M4 (mod 15)
"""
b = 4
U = QuantumCircuit(4)
U.swap(1, 3)
U.swap(0, 2)
U = U.to_gate()
U.name = f"M_{b}"
return U
# Get the M4 operator
M4 = M4mod15()
# Add it to a circuit and plot
circ = QuantumCircuit(4)
circ.compose(M4, inplace=True)
circ.decompose(reps=2).draw(output="mpl", fold=-1)
Y, como antes, lo convertimos en un operador controlado:
def controlled_M4mod15():
"""
Controlled M4 (mod 15)
"""
b = 4
U = QuantumCircuit(4)
U.swap(1, 3)
U.swap(0, 2)
U = U.to_gate()
U.name = f"M_{b}"
c_U = U.control()
return c_U
# Get the controlled-M4 operator
controlled_M4 = controlled_M4mod15()
# Add it to a circuit and plot
circ = QuantumCircuit(5)
circ.compose(controlled_M4, inplace=True)
circ.decompose(reps=1).draw(output="mpl", fold=-1)
Ahora podemos juntarlo todo para encontrar el orden de con un circuito cuántico, usando la estimación de fase:
# Order finding problem for N = 15 with a = 2
N = 15
a = 2
# Number of qubits
num_target = floor(log(N - 1, 2)) + 1 # for modular exponentiation operators
num_control = 2 * num_target # for enough precision of estimation
# List of M_b operators in order
k_list = range(num_control)
b_list = [a2kmodN(2, k, 15) for k in k_list]
# Initialize the circuit
control = QuantumRegister(num_control, name="C")
target = QuantumRegister(num_target, name="T")
output = ClassicalRegister(num_control, name="out")
circuit = QuantumCircuit(control, target, output)
# Initialize the target register to the state |1>
circuit.x(num_control)
# Add the Hadamard gates and controlled versions of the
# multiplication gates
for k, qubit in enumerate(control):
circuit.h(k)
b = b_list[k]
if b == 2:
circuit.compose(
M2mod15().control(), qubits=[qubit] + list(target), inplace=True
)
elif b == 4:
circuit.compose(
M4mod15().control(), qubits=[qubit] + list(target), inplace=True
)
else:
continue # M1 is the identity operator
# Apply the inverse QFT to the control register
circuit.compose(QFTGate(num_control).inverse(), qubits=control, inplace=True)
# Measure the control register
circuit.measure(control, output)
circuit.draw("mpl", fold=-1)
2. Optimizar
Ahora que hemos mapeado nuestro circuito, el siguiente paso es optimizarlo para ejecutarlo en un computador cuántico específico. Primero necesitamos cargar el backend.
service = QiskitRuntimeService()
backend = service.backend("ibm_marrakesh")
Si no tienes tiempo disponible en tu cuenta o quieres usar un simulador por cualquier razón, puedes ejecutar la celda a continuación para configurar un simulador que imitará el dispositivo cuántico que seleccionamos arriba:
pm = generate_preset_pass_manager(optimization_level=2, backend=backend)
transpiled_circuit = pm.run(circuit)
print(f"2q-depth: {transpiled_circuit.depth(lambda x: x.operation.num_qubits==2)}")
print(f"2q-size: {transpiled_circuit.size(lambda x: x.operation.num_qubits==2)}")
print(f"Operator counts: {transpiled_circuit.count_ops()}")
transpiled_circuit.draw(output="mpl", fold=-1, style="clifford", idle_wires=False)
2q-depth: 188
2q-size: 281
Operator counts: OrderedDict({'sx': 548, 'rz': 380, 'cz': 281, 'measure': 8, 'x': 6})

3. Ejecutar
# Sampler primitive to obtain the probability distribution
sampler = Sampler(backend)
# Turn on dynamical decoupling with sequence XpXm
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XpXm"
# Enable gate twirling
sampler.options.twirling.enable_gates = True
pub = transpiled_circuit
job = sampler.run([pub], shots=1024)
result = job.result()[0]
counts = result.data["out"].get_counts()
plot_histogram(counts, figsize=(35, 5))

Vemos cuatro picos claros en 00000000, 01000000, 10000000 y 11000000, con algunos conteos en otras cadenas de bits debidos al ruido en el computador cuántico. Ignoraremos estos y nos quedaremos solo con los cuatro dominantes imponiendo un umbral: solo los conteos por encima de este umbral se consideran una señal verdadera por encima del ruido.
# Dictionary of bitstrings and their counts to keep
counts_keep = {}
# Threshold to filter
threshold = np.max(list(counts.values())) / 2
for key, value in counts.items():
if value > threshold:
counts_keep[key] = value
print(counts_keep)
4. Posprocesar
En el algoritmo de Shor, una gran parte del algoritmo se ejecuta de forma clásica. Por eso, pondremos el resto en el paso de "posprocesamiento", después de haber obtenido nuestras mediciones del computador cuántico. Cada una de las mediciones anteriores puede convertirse en enteros que, después de dividir entre , son nuestras aproximaciones de , donde es aleatorio en cada ocasión.
a = 2
N = 15
FACTOR_FOUND = False
num_attempt = 0
while not FACTOR_FOUND:
print(f"\nATTEMPT {num_attempt}:")
# Here, we get the bitstring by iterating over outcomes
# of a previous hardware run with multiple shots.
# Instead, we can also perform a single-shot measurement
# here in the loop.
bitstring = list(counts_keep.keys())[num_attempt]
num_attempt += 1
# Find the phase from measurement
decimal = int(bitstring, 2)
phase = decimal / (2**num_control) # phase = k / r
print(f"Phase: theta = {phase}")
# Guess the order from phase
frac = Fraction(phase).limit_denominator(N)
r = frac.denominator # order = r
print(f"Order of {a} modulo {N} estimated as: r = {r}")
if phase != 0:
# Guesses for factors are gcd(a^{r / 2} ± 1, 15)
if r % 2 == 0:
x = pow(a, r // 2, N) - 1
d = gcd(x, N)
if d > 1:
FACTOR_FOUND = True
print(f"*** Non-trivial factor found: {x} ***")
ATTEMPT 0:
Phase: theta = 0.0
Order of 2 modulo 15 estimated as: r = 1
ATTEMPT 1:
Phase: theta = 0.75
Order of 2 modulo 15 estimated as: r = 4
*** Non-trivial factor found: 3 ***
Conclusión
Después de recorrer el módulo, puede que te haya llegado una nueva apreciación por el ingenio de Peter Shor al haber concebido un algoritmo tan brillante. Pero esperamos que también hayas alcanzado un nuevo nivel de comprensión de su engañosa sencillez. Aunque el algoritmo puede parecer impresionantemente (o intimidantemente) complejo, si lo desglosas en cada paso de lógica y lo recorres poco a poco, tú también serás capaz de ejecutar el algoritmo de Shor.
Si bien estamos lejos de usar este algoritmo para factorizar números como RSA1024, nuestros computadores cuánticos mejoran cada día, y una vez que se alcance un umbral llamado tolerancia a fallos, algoritmos como estos no tardarán en seguir. ¡Es un momento apasionante para aprender sobre computación cuántica!
Problemas
Conceptos clave:
- Los sistemas criptográficos modernos se basan en la dificultad clásica de factorizar enteros grandes.
- La aritmética modular —incluyendo las estructuras y — proporciona los fundamentos matemáticos del algoritmo de Shor.
- El problema de factorizar un entero puede reducirse al problema de encontrar el orden de un número módulo .
- La búsqueda cuántica del orden usa técnicas de estimación de fase cuántica para determinar el período de la función .
- El algoritmo de Shor consiste en un flujo de trabajo híbrido clásico-cuántico que selecciona una base, realiza la búsqueda cuántica del orden y luego calcula los factores clásicamente a partir del resultado.
Verdadero/Falso:
- V/F La eficiencia del algoritmo de Shor amenaza la seguridad del cifrado RSA.
- V/F El algoritmo de Shor puede ejecutarse eficientemente en cualquier computador cuántico actual.
- V/F El algoritmo de Shor usa la estimación de fase cuántica (QPE) como subrutina clave.
- V/F La parte clásica del algoritmo de Shor implica calcular el máximo común divisor (MCD).
- V/F El algoritmo de Shor solo funciona para factorizar números pares.
- V/F Una ejecución exitosa del algoritmo de Shor siempre garantiza los factores correctos.
Respuesta corta:
- ¿Por qué se considera el algoritmo de Shor una potencial amenaza futura para el cifrado RSA?
- ¿Por qué encontrar el período, u orden, de una función exponencial modular es útil para factorizar un número en el algoritmo de Shor?
Problemas de desafío:
- ¿Cuántos qubits de control necesitamos para un número dado que queremos factorizar, con el fin de obtener la precisión en el QPE necesaria para encontrar el valor correcto del orden ?