{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "78090c95-8fea-4731-a74a-515150e4f899",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Algoritmo de Shor\"\n",
        "description: \"Aprende a utilizar el algoritmo de Shor para factorizar números compuestos.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore checkmark */}\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "64abf08d-8e69-41f9-b802-8c0fbbc37555",
      "metadata": {},
      "source": [
        "<span id=\"shors-algorithm\" />\n",
        "\n",
        "# Algoritmo de Shor\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c257cbc3-7730-40cc-a240-19e57033d582",
      "metadata": {},
      "source": [
        "Para este módulo de Qiskit in Classrooms, los estudiantes deben disponer de un entorno de Python trabajo con los siguientes paquetes instalados:\n",
        "\n",
        "* v2.1.0`qiskit` o más reciente\n",
        "* v0.40.1`qiskit-ibm-runtime` o más reciente\n",
        "* v0.17.0`qiskit-aer` o más reciente\n",
        "* `qiskit.visualization`\n",
        "* `numpy`\n",
        "* `pylatexenc`\n",
        "\n",
        "Para configurar e instalar los paquetes anteriores, consulte la guía [Instalar Qiskit](/docs/guides/install-qiskit).\n",
        "Para ejecutar trabajos en ordenadores cuánticos reales, los estudiantes deberán crear una cuenta siguiendo los pasos que IBM Quantum® se [indican](/docs/guides/cloud-setup) en la guía Configurar su IBM Cloud cuenta.\n",
        "\n",
        "Este módulo se probó y utilizó tres segundos de tiempo de QPU. Esto es solo una estimación. Su uso real puede variar.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "500d10c9-17ca-4787-90e0-4c57bf86c80d",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Uncomment and modify this line as needed to install dependencies\n",
        "#!pip install 'qiskit>=2.1.0' 'qiskit-ibm-runtime>=0.40.1' 'qiskit-aer>=0.17.0' 'numpy' 'pylatexenc'"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "72231ac3-5b60-4bcb-a072-0c2ecdd176aa",
      "metadata": {
        "jp-MarkdownHeadingCollapsed": true
      },
      "source": [
        "<span id=\"intro\" />\n",
        "\n",
        "## Introducción\n",
        "\n",
        "A principios de la década 1990s, crecía el entusiasmo en torno al potencial de los ordenadores cuánticos para resolver problemas que resultaban difíciles para los ordenadores clásicos. Algunos informáticos con talento habían ideado algoritmos que demostraban el poder de la computación cuántica para algunos problemas específicos y artificiales, pero nadie había encontrado una única «aplicación revolucionaria» de la computación cuántica que fuera capaz de revolucionar el campo. Así fue hasta 1994, cuando Peter Shor ideó lo que hoy se conoce como el algoritmo de Shor para factorizar números grandes.\n",
        "\n",
        "En aquella época era bien sabido que encontrar los factores primos de un número grande resultaba extremadamente difícil para un ordenador clásico. De hecho, los protocolos de seguridad de Internet se basaban en esta dificultad. Shor encontró una forma de hallar estos factores de manera exponencialmente más eficiente al descargar algunos de los pasos más difíciles en un ordenador cuántico teórico futuro.\n",
        "\n",
        "En este módulo, exploraremos el algoritmo de Shor. En primer lugar, daremos un poco más de contexto al algoritmo, formalizando el problema que resuelve y explicando su relevancia para la ciberseguridad. A continuación, ofreceremos una introducción a las matemáticas modulares y cómo aplicarlas al problema de la factorización, mostrando cómo la factorización se reduce a otro problema denominado «búsqueda de orden» Mostraremos cómo se aplican la transformada de Fourier cuántica y la estimación de fase cuántica que aprendimos en un módulo anterior, y cómo utilizarlas para resolver el problema de búsqueda de orden.\n",
        "\n",
        "¡Por fin ejecutaremos el algoritmo de Shor en un ordenador cuántico real! Sin embargo, hay que tener en cuenta que este algoritmo solo será realmente útil cuando dispongamos de un ordenador cuántico grande y tolerante a fallos, lo que aún tardará algunos años en llegar. Por lo tanto, solo factorizaremos un número pequeño para demostrar cómo funciona el algoritmo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "22a38013-7bd5-46db-9a44-e89d66c2d06b",
      "metadata": {},
      "source": [
        "<span id=\"the-factoring-problem\" />\n",
        "\n",
        "## El problema del factoring\n",
        "\n",
        "El objetivo del problema de factorización es encontrar los factores primos de un número $N$. Para algunos números $N$, esto es bastante fácil. Por ejemplo, si $N$ es par, uno de sus factores primos será 2. Si $N$ es una potencia prima, es decir, $N=p^k$ para algún número primo $p$, también es bastante fácil encontrar $p$ : solo hay que aproximar la raíz $k^{\\text{th}}$ de $N$ y buscar números primos cercanos que podrían ser $p$.\n",
        "\n",
        "Sin embargo, donde los ordenadores clásicos tienen dificultades es cuando $N$ es impar y *no* es una potencia prima. Este es el caso que aborda el algoritmo de Shor. El algoritmo encuentra dos factores $p$ y $q$ tales que $N=pq$. Se puede aplicar de forma recursiva hasta que todos los factores sean primos. En las siguientes secciones veremos cómo se aborda este problema.\n",
        "\n",
        "<span id=\"relevance-to-cyber-security\" />\n",
        "\n",
        "### Relevancia para la ciberseguridad\n",
        "\n",
        "Se han creado muchos sistemas criptográficos basados en el hecho de que factorizar números grandes es difícil, incluido uno que se utiliza habitualmente en la actualidad, llamado RSA. En RSA, se crea una clave pública multiplicando dos números primos grandes entre sí para obtener $N = p\\cdot q$. A continuación, cualquiera puede utilizar esta clave pública para cifrar datos. Pero solo alguien que tenga la clave privada, $p$ y $q$, puede descifrar esos datos.\n",
        "\n",
        "Si $N$ fuera fácil de factorizar, entonces cualquiera podría determinar cuáles $p$ son $q$ y y descifrar la encriptación. Pero no lo es. Este es un problema famoso por su dificultad. De hecho, los factores primos de un número llamado RSA1024, que tiene 1024 dígitos binarios y 309 dígitos decimales, aún no se han encontrado, a pesar de que en 1991 se ofreció un premio de 100 000 dólares por su factorización.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "01a0c5d4-922b-47af-8a8b-d87df8d520b0",
      "metadata": {},
      "source": [
        "<span id=\"shors-solution\" />\n",
        "\n",
        "## La solución de Shor\n",
        "\n",
        "En 1994, Peter Shor se dio cuenta de que un ordenador cuántico podía factorizar un número grande de forma exponencialmente más eficiente que un ordenador clásico. Su visión se basaba en la relación entre este problema de factorización y *la aritmética modular*. Repasaremos brevemente los fundamentos de la aritmética modular y luego veremos cómo podemos utilizarla para factorizar $N$.\n",
        "\n",
        "<span id=\"modular-arithmetic\" />\n",
        "\n",
        "### Aritmética modular\n",
        "\n",
        "La aritmética modular es un sistema de conteo cíclico, lo que significa que, aunque el conteo comienza de la forma habitual, con los números enteros 0, 1, 2, etc., En algún momento, tras un periodo de tiempo $N$, el recuento vuelve a empezar. Veamos cómo funciona esto con un ejemplo. Digamos que nuestro período es 5. Entonces, mientras contamos, donde normalmente llegaríamos a 5, en su lugar volvemos a empezar desde 0:\n",
        "\n",
        "$0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, ...$\n",
        "\n",
        "Esto se debe a que en el mundo «» modulo-5, 5 equivale a 0. Decimos que $5\\bmod 5 \\ = 0$. De hecho, todos los múltiplos de 5 serán equivalentes a $0\\bmod 5$.\n",
        "\n",
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Comprueba tu comprensión\n",
        "\n",
        "Utiliza la aritmética modular para resolver el siguiente problema:\n",
        "\n",
        "Sale en un largo viaje transcontinental en tren a las 8 de la mañana. El viaje en tren dura 60 horas. ¿A qué hora llegas?\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Respuesta\">\n",
        "    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:\n",
        "\n",
        "    $(8+60)\\text{mod}(24) = 20$\n",
        "\n",
        "    Por lo tanto, llegarías a tu destino a las 20:00, o las 8 de la tarde.\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "<span id=\"$mathbb{z}_n$-and-$mathbb{z}_n^*$\" />\n",
        "\n",
        "#### $\\mathbb{Z}_N$ y $\\mathbb{Z}_N^*$\n",
        "\n",
        "A menudo resulta útil introducir dos conjuntos, $\\mathbb{Z}_N$ y $\\mathbb{Z}_N^*$. $\\mathbb{Z}_N$ es simplemente el conjunto de números que existen en un mundo «módulo $N$ ». Por ejemplo, cuando contábamos modulo-5, el conjunto sería $\\mathbb{Z}_5=\\{0,1,2,3,4\\}$. Otro ejemplo: $\\mathbb{Z}_{15} = \\{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14\\}$. Podemos realizar sumas y multiplicaciones (módulo $N$ ) con los elementos de $\\mathbb{Z}_N$, y el resultado de cada una de estas operaciones también es un elemento de $\\mathbb{Z}_N$, lo que convierte a $\\mathbb{Z}_N$ en un objeto matemático denominado *anillo*.\n",
        "\n",
        "Hay un subconjunto especial de $\\mathbb{Z}_N$ que nos interesa especialmente para el algoritmo de Shor. Es el subconjunto de números en $\\mathbb{Z}_N$ tal que el máximo común divisor entre cada elemento y $N$ es 1, por lo que cada elemento es «coprimario» con respecto a $N$. 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 $\\mathbb{Z}_N^*$ llamamos. Resulta que con $\\mathbb{Z}_N^*$ (y con los grupos finitos en general), si elegimos cualquier elemento $a \\in \\mathbb{Z}_N^*$ y multiplicamos repetidamente $a$ por sí mismo, siempre acabaremos obteniendo el número $1$. El número mínimo de veces que hay que multiplicar $a$ por sí mismo para obtener $1$ se denomina **orden** de $a$. Este hecho será muy importante para nuestro análisis sobre cómo factorizar números más adelante.\n",
        "\n",
        "<span id=\"check-your-understanding-1\" />\n",
        "\n",
        "#### Comprueba tu comprensión\n",
        "\n",
        "¿Qué es $\\mathbb{Z}_{15}^*$?\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Respuesta\">\n",
        "    $\\mathbb{Z}_{15}^* = \\{1,2,4,7,8,11,13,14\\} $\n",
        "\n",
        "    Hemos excluido los siguientes números:\n",
        "\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    3: GCD(3,15)=3 \\\\\n",
        "    5: GCD(5,15)=5 \\\\\n",
        "    6: GCD(6,15)=3 \\\\\n",
        "    9: GCD(9,15)=3 \\\\\n",
        "    10: GCD(10,15)=5 \\\\\n",
        "    12: GCD(12,15)=3 \\\\\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "¿Cuál es el orden de cada uno de los elementos en $\\mathbb{Z}_{15}^*$?\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Respuesta\">\n",
        "    El orden $r$ es el número más bajo tal que $a^r\\text{mod}(15)=1$ para cada elemento $a$.\n",
        "\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    1^1\\text{mod}(15) = 1, r=1 \\\\\n",
        "    2^4\\text{mod}(15) = 1, r=4 \\\\\n",
        "    4^2\\text{mod}(15) = 1, r=2 \\\\\n",
        "    7^4\\text{mod}(15) = 1, r=4 \\\\\n",
        "    8^4\\text{mod}(15) = 1, r=4 \\\\\n",
        "    11^2\\text{mod}(15) = 1, r=2 \\\\\n",
        "    13^4\\text{mod}(15) = 1, r=4 \\\\\n",
        "    14^2\\text{mod}(15) = 1, r=2 \\\\\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "\n",
        "    Tenga en cuenta que, aunque pudimos encontrar el orden de los números en $\\mathbb{Z}_{15}^*$, esto NO es una tarea fácil en general, para números más grandes $N$. Este es el quid de la cuestión del problema de la factorización y la razón por la que necesitamos un ordenador cuántico. Veremos por qué a medida que avancemos con el resto del cuaderno.\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c19d2032-3c40-48ee-823b-607b1ed8b4f0",
      "metadata": {},
      "source": [
        "<span id=\"apply-modular-arithmetic-to-the-factoring-problem\" />\n",
        "\n",
        "### Aplicar la aritmética modular al problema de la factorización\n",
        "\n",
        "La clave para encontrar factores $p$ y $q$ tales que $N=pq$ se reduce a encontrar algún *otro* entero $x$ tal que\n",
        "\n",
        "$x^2 \\equiv 1 \\bmod N$ y $x \\not\\equiv \\pm 1 \\bmod N.$\n",
        "\n",
        "¿Cómo nos ayuda encontrar $x$ a hallar los factores $p$ y $q$? Analicemos ahora el razonamiento. Dado $x^2 \\equiv 1 \\bmod N$ que, eso significa que $x^2 - 1 \\equiv 0 \\bmod N $. En otras palabras, $x^2 - 1$ es un múltiplo de $N$. Por lo tanto, para algún entero $l$,\n",
        "\n",
        "$x^2 - 1 = l N$\n",
        "\n",
        "Podemos factorizar $x^2 - 1$ para obtener:\n",
        "\n",
        "$(x+1)(x-1) = l N$\n",
        "\n",
        "A partir de nuestras hipótesis iniciales sabemos que $x \\not\\equiv \\pm 1 \\bmod N$, por lo que $N$ no se divide uniformemente ni en $x+1$ ni $x-1$ en. Por lo tanto, los dos factores de $N$, $p$ y, $q$ deben dividirse cada uno en $x-1$ y $x+1$. O bien $p$ es un factor de $x-1$ y $q$ es un factor de $x+1$, o viceversa. Por lo tanto, si calculamos los máximos comunes divisores (MCD) entre $N$ y tanto $x-1$ como $x+1$, obtendremos los factores $p$ y $q$. Calcular el MCD entre dos números es una tarea clásicamente fácil que se puede realizar, por ejemplo, utilizando [el algoritmo de Euclides](https://en.wikipedia.org/wiki/Euclidean_algorithm).\n",
        "\n",
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Comprueba tu comprensión\n",
        "\n",
        "Puede resultar complicado comprender cada paso de la lógica anterior, así que intente aplicarla con un ejemplo. Utilice $N=15$ y $x=11$. En primer lugar, compruebe que $x^2 \\equiv 1 \\text{mod}(N)$ y $x \\not\\equiv \\pm 1 \\bmod N$. A continuación, continúe verificando cada paso. Por último, calcula $\\text{GCD}(11\\pm1,15)$ y comprueba que son los factores de $15$.\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Respuesta\">\n",
        "    $11^2 = 121$, que es $15*8 + 1$, por lo que $11^2\\bmod 15 = 1$. $\\checkmark$\n",
        "\n",
        "    $ 11 - 1 = 10$, que no es equivalente a $0\\bmod 15$. $\\checkmark$\n",
        "\n",
        "    $ 11 + 1 = 12$, que no es equivalente a $0\\bmod 15$. $\\checkmark$\n",
        "\n",
        "    Ahora sabemos que $(x+1)(x-1) = l N$ para algún entero $l$. Esto se verifica cuando sustituimos $x$ y $N$ : $(12)(10) = l 15$ cuando $l = 8$. $\\checkmark$\n",
        "\n",
        "    Ahora, necesitamos calcular $\\text{GCD}(12,15)$ y $\\text{GCD}(10,15)$.\n",
        "\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    \\text{GCD}(12,15) = 3 \\\\\n",
        "    \\text{GCD}(10,15) = 5\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "\n",
        "    Así que, ¡encontramos nuestros factores de $15$!\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "<span id=\"the-algorithm\" />\n",
        "\n",
        "### El algoritmo\n",
        "\n",
        "Ahora que hemos visto cómo encontrar un número entero $x$ tal que nos $x^2 \\equiv 1\\bmod N$ ayuda a factorizar $N$, podemos repasar el algoritmo de Shor. Básicamente, se trata de encontrar $x$ :\n",
        "\n",
        "1. **Elige un número entero aleatorio**\n",
        "   Elige un número entero $a$ aleatorio tal que $1 < a < N$.\n",
        "\n",
        "* Calcular $\\text{GCD}(a, N)$ de forma clásica.\n",
        "  * Si $\\text{GCD}(a, N) > 1$, ya has encontrado un factor. Alto.\n",
        "  * De lo contrario, continúe.\n",
        "\n",
        "2. **Encuentra el orden $r$ del $a$ módulo**\n",
        "   $N$ Encuentra el entero positivo más $r$ pequeño que satisfaga $a^r \\equiv 1 \\pmod N$.\n",
        "\n",
        "3. **Comprueba si el pedido es par.**\n",
        "\n",
        "* Si $r$ es impar, vuelve al paso 1 y elige un nuevo $a$.\n",
        "* Si $r$ es par, continúe con el paso 4.\n",
        "\n",
        "4. **Calcular $x = a^{r/2} \\bmod N$**\n",
        "\n",
        "* Comprueba que $x \\not\\equiv 1 \\pmod N$ y $x \\not\\equiv -1 \\pmod N$.\n",
        "  * Si $x \\equiv \\pm 1 \\pmod N$, vuelve al paso 1 y elige uno nuevo $a$.\n",
        "* De lo contrario, calcula los mcd para extraer los factores:\n",
        "\n",
        "$$\n",
        "p = \\text{GCD}(x-1, N), \\quad q = \\text{GCD}(x+1, N)\n",
        "$$\n",
        "\n",
        "Estos serán factores no triviales de $N$.\n",
        "\n",
        "5. **Factorizar recursivamente si es necesario.**\n",
        "\n",
        "* Si $p$ y/o no $q$ son primos, aplique el algoritmo de forma recursiva para factorizarlos completamente.\n",
        "* Una vez que todos los factores son primos, el factorización está completa.\n",
        "\n",
        "Basándonos en este procedimiento, podría no resultar obvio por qué se necesita un ordenador cuántico para completar esta tarea. Es necesario porque el paso 2, encontrar el orden del $a$ módulo $N$, es clásicamente un problema muy difícil. La complejidad aumenta exponencialmente con el número $N$. Pero con un ordenador cuántico, solo tenemos que utilizar la estimación de fase cuántica para resolverlo. El paso 4, hallar el MCD de dos números enteros, es en realidad algo bastante fácil de hacer de forma clásica. Por lo tanto, el único paso que realmente necesita la potencia de un ordenador cuántico es el paso de búsqueda de órdenes. Decimos que el problema del factoraje «se reduce» al problema de encontrar el orden.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "d4771244-0604-4b2e-bd99-a52280456f43",
      "metadata": {},
      "source": [
        "<span id=\"the-hard-part-order-finding\" />\n",
        "\n",
        "### La parte difícil: encontrar el pedido\n",
        "\n",
        "Ahora veremos cómo podemos utilizar un ordenador cuántico para la búsqueda. En primer lugar, aclaremos qué entendemos por «orden» Por supuesto, ya te he explicado lo que significa matemáticamente el orden: es el primer entero distinto $r$ de cero tal que $a^r = 1 \\pmod N.$ Pero veamos si podemos entender un poco mejor este concepto.\n",
        "\n",
        "Para valores suficientemente pequeños $N$, podemos determinar el orden calculando cada potencia de $a$, tomando el módulo $N$ de ese número y deteniéndonos cuando encontramos la potencia $r$ que satisface $a^r = 1 \\text{mod}(N)$. Eso es lo que hicimos con nuestro ejemplo, $N=15$, arriba. Veamos algunos gráficos de estas potencias modulares para algunos valores de muestra de $a$ y $N$ :\n",
        "\n",
        "![Valor de a elevado a la potencia k módulo N frente a la potencia k, donde a=2 y N=15. Vemos que a medida que k aumenta, surge un patrón repetitivo, lo que demuestra que a^k módulo N es periódico en k.](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/shors-algorithm/a2n15.avif)\n",
        "\n",
        "![Valor de a elevado a la potencia k módulo N frente a la potencia k, donde a=5 y N=21. Vemos que a medida que k aumenta, surge un patrón repetitivo, lo que demuestra que a^k módulo N es periódico en k.](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/shors-algorithm/a5n21.avif)\n",
        "\n",
        "¿Notas algo? ¡Son funciones periódicas! ¡Y el orden $r$ es el mismo que el período! **Por lo tanto, encontrar el orden equivale a encontrar el período.**\n",
        "\n",
        "Las computadoras cuánticas son muy adecuadas para hallar el período de las funciones. Para ello, podemos utilizar una subrutina algorítmica denominada «estimación de fase cuántica». En el módulo anterior hablamos sobre QPE y su relación con la transformada de Fourier cuántica. Para obtener información detallada, consulte el módulo QFT o la lección de John Watrous sobre [estimación de fase cuántica](/learning/courses/fundamentals-of-quantum-algorithms/phase-estimation-and-factoring/shor-algorithm) en su curso sobre algoritmos cuánticos. Ahora repasaremos los puntos principales del procedimiento:\n",
        "\n",
        "En la estimación de fase cuántica (QPE), comenzamos con un operador unitario $U$ y un estado propio de ese operador unitario $|\\psi\\rangle$. A continuación, utilizamos la QPE para aproximar el valor propio correspondiente, que, dado que el operador es unitario, tendrá la forma $e^{2\\pi i \\theta}$. Por lo tanto, hallar el valor propio equivale a hallar el valor de $\\theta$ en la función periódica. El circuito tiene el siguiente aspecto:\n",
        "\n",
        "![Diagrama del circuito del procedimiento de estimación de fase cuántica. Los qubits de control superiores m se preparan en superposiciones con puertas Hadamard, luego se aplican puertas unitarias controladas a los qubits inferiores, que se encuentran en un estado propio de la unidad. Por último, se aplica una transformada de Fourier cuántica inversa a los qubits superiores y se miden.](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/shors-algorithm/QPE.avif)\n",
        "\n",
        "donde el número de qubits de control (los qubits $m$ superiores en la figura anterior) determina la precisión de la aproximación.\n",
        "\n",
        "En el algoritmo de Shor, utilizamos QPE en el operador unitario $M_a$ :\n",
        "\n",
        "$ M_a|y\\rangle \\equiv |ay \\mod N \\rangle .$\n",
        "\n",
        "Aquí, $|y\\rangle$ denota un estado de base computacional del registro multiqubit, donde el valor binario de los qubits corresponde al entero $y$. Por ejemplo, si $N=15$ y $y = 2$, entonces $|y\\rangle$ se representa mediante el estado de base de cuatro $|0010\\rangle$ qubits, ya que se necesitan cuatro qubits para codificar números hasta 15. (Si este concepto le resulta desconocido, consulte el [módulo introductorio Qiskit en las aulas](/learning/modules/quantum-mechanics/get-started-with-qiskit) para refrescar sus conocimientos sobre la codificación binaria de los estados cuánticos)\n",
        "\n",
        "Ahora, necesitamos averiguar un estado propio de esta unidad. Si comenzamos en el estado $|1\\rangle$, podemos ver que cada aplicación sucesiva de $U$ multiplicará el estado de nuestro registro por $a \\pmod N$, y después de $r$ aplicaciones llegaremos nuevamente al $|1\\rangle$ estado. Por ejemplo, con $a = 3$ y $N = 35$ :\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        "M_3|1\\rangle &= |3\\rangle & \\\\\n",
        "M_3^2|1\\rangle &= |9\\rangle \\\\\n",
        "M_3^3|1\\rangle &= |27\\rangle \\\\\n",
        "& \\vdots \\\\\n",
        "M_3^{(r-1)}|1\\rangle &= |12\\rangle \\\\\n",
        "M_3^r|1\\rangle &= |1\\rangle\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n",
        "Así, las superposiciones de los estados en este ciclo ( $|\\psi_j\\rangle$ ) de la forma:\n",
        "\n",
        "$|\\psi_j\\rangle = \\tfrac{1}{\\sqrt{r}}\\sum_{k=0}^{r-1}{e^{\\frac{2 \\pi i j k}{r}} |a^k \\rangle} $\n",
        "\n",
        "son todos los estados propios de $M_a$. (Hay más estados propios además de estos. Pero solo nos interesan los que tienen la forma anterior)\n",
        "\n",
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Comprueba tu comprensión\n",
        "\n",
        "Encuentre un estado propio del unitario correspondiente a $a=2$ y $N = 15$.\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Respuesta\">\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    M_2|1\\rangle &= |2\\rangle & \\\\\n",
        "    M_2^2|1\\rangle &= |4\\rangle \\\\\n",
        "    M_2^3|1\\rangle &= |8\\rangle \\\\\n",
        "    M_2^4|1\\rangle &= |1\\rangle \\\\\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "\n",
        "    Entonces, el orden $r=4$. Los estados propios que nos interesan serán una superposición igual de todos los estados que se han repetido anteriormente, con varias fases:\n",
        "\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    |\\psi_0\\rangle &= \\frac{1}{2}(|1\\rangle+|2\\rangle+|4\\rangle+|8\\rangle) \\\\\n",
        "    |\\psi_1\\rangle &= \\frac{1}{2}(e^{2 \\pi i \\frac{0}{4}}|1\\rangle+e^{2 \\pi i \\frac{1}{4}}|2\\rangle+e^{2 \\pi i \\frac{2}{4}}|4\\rangle+e^{2 \\pi i \\frac{3}{4}}|8\\rangle) \\\\\n",
        "    &= \\frac{1}{2}(|1\\rangle+i|2\\rangle-|4\\rangle-i|8\\rangle) \\\\\n",
        "    |\\psi_2\\rangle &= \\frac{1}{2}(e^{2 \\pi i \\frac{0}{4}}|1\\rangle+e^{2 \\pi i \\frac{2}{4}}|2\\rangle+e^{2 \\pi i \\frac{4}{4}}|4\\rangle+e^{2 \\pi i \\frac{6}{4}}|8\\rangle) \\\\\n",
        "    &= \\frac{1}{2}(|1\\rangle-|2\\rangle+|4\\rangle-|8\\rangle) \\\\\n",
        "    |\\psi_3\\rangle &= \\frac{1}{2}(e^{2 \\pi i \\frac{0}{4}}|1\\rangle+e^{2 \\pi i \\frac{3}{4}}|2\\rangle+e^{2 \\pi i \\frac{6}{4}}|4\\rangle+e^{2 \\pi i \\frac{9}{4}}|8\\rangle) \\\\\n",
        "    &= \\frac{1}{2}(|1\\rangle-i|2\\rangle-|4\\rangle+i|8\\rangle) \\\\\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "Supongamos que pudiéramos inicializar nuestro estado cuántico en uno de estos estados propios (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 valor propio correspondiente, $\\omega_j = e^{2 \\pi i \\theta_j}$ donde $\\theta_j = \\frac{j}{r}$. A continuación, podremos determinar el orden $r$ mediante la sencilla ecuación:\n",
        "\n",
        "$r = \\frac{j}{\\theta_j}.$\n",
        "\n",
        "Pero recuerde, dije que se trata de $\\theta_j$\\* estimaciones\\* de QPE, no nos da un valor exacto. Necesitamos que la estimación sea lo suficientemente buena como para diferenciar entre $r$ y $r+1$. Cuantos más qubits de control tengamos, mejor $m$ será la estimación. En los problemas al final de la lección, se te pedirá que determines el mínimo $m$ necesario para factorizar un número $N$.\n",
        "\n",
        "Ahora tenemos que resolver un problema. Toda la explicación anterior sobre cómo encontrar $r$ comienza con la preparación del estado propio $|\\psi_j\\rangle = \\tfrac{1}{\\sqrt{r}}\\sum_{k=0}^{r-1}{e^{\\frac{2 \\pi i j k}{r}} |a^k \\rangle}$. Pero no sabemos cómo hacerlo sin saber ya qué $r$ es. La lógica es circular. Necesitamos una forma de estimar el valor propio *sin* inicializar el estado propio.\n",
        "\n",
        "En lugar de comenzar con un estado propio de $M_a$, podemos preparar el estado inicial en el estado $n$ de -qubit correspondiente a $|1\\rangle$ en binario (como en ) $|000...01\\rangle$. Aunque este estado en sí mismo obviamente no es un estado propio de $M_a$, es una superposición sobre todos los estados propios $|\\psi_k\\rangle$ :\n",
        "\n",
        "$|1\\rangle = \\frac{1}{\\sqrt{r}} \\sum\\limits_{k=0}^{r-1}{|\\psi_k\\rangle}$\n",
        "\n",
        "<span id=\"check-your-understanding-1\" />\n",
        "\n",
        "#### Comprueba tu comprensión\n",
        "\n",
        "Verifica que $|1\\rangle$ es equivalente a la superposición sobre los estados propios que encontraste para $N=15$ y $a=2$ en la pregunta anterior.\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Respuesta\">\n",
        "    Los cuatro estados propios eran:\n",
        "\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    |\\psi_0\\rangle &= \\frac{1}{2}(|1\\rangle+|2\\rangle+|4\\rangle+|8\\rangle) \\\\\n",
        "    |\\psi_1\\rangle &= \\frac{1}{2}(|1\\rangle+i|2\\rangle-|4\\rangle-i|8\\rangle) \\\\\n",
        "    |\\psi_2\\rangle &= \\frac{1}{2}(|1\\rangle-|2\\rangle+|4\\rangle-|8\\rangle) \\\\\n",
        "    |\\psi_3\\rangle &= \\frac{1}{2}(|1\\rangle-i|2\\rangle-|4\\rangle+i|8\\rangle) \\\\\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "\n",
        "    Entonces,\n",
        "\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    \\frac{1}{\\sqrt{r}} \\sum\\limits_{k=0}^{r-1}{|\\psi_k\\rangle} &= \\frac{1}{2}(|\\psi_0\\rangle + |\\psi_1\\rangle + |\\psi_2\\rangle + |\\psi_3\\rangle ) \\\\\n",
        "    &= \\frac{1}{4}(|1\\rangle+|2\\rangle+|4\\rangle+|8\\rangle+|1\\rangle+i|2\\rangle-|4\\rangle-i|8\\rangle+|1\\rangle-|2\\rangle+|4\\rangle-|8\\rangle + |1\\rangle-i|2\\rangle-|4\\rangle+i|8\\rangle) \\\\\n",
        "    &= \\frac{1}{4}(4|1\\rangle) = |1\\rangle\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "¿Cómo nos permite esto encontrar el orden $r$? Dado que el estado inicial es una superposición de todos los estados propios de la forma indicada anteriormente, el algoritmo QPE estima simultáneamente cada uno de los $\\theta_k$ correspondientes a estos estados propios. Por lo tanto, la medición de los qubits $m$ de control al final dará como resultado una aproximación al valor $k/r$ donde $k \\in \\{0,1,2,...,r-1\\}$ es uno de los valores propios elegidos al azar. Si repetimos este circuito varias veces y obtenemos algunas muestras con diferentes valores de $k$, rápidamente podremos deducir $r$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "099389d1-1370-472c-af29-a642532beff4",
      "metadata": {},
      "source": [
        "<span id=\"implement-in-qiskit\" />\n",
        "\n",
        "## Implementar en Qiskit\n",
        "\n",
        "Como mencionamos anteriormente, nuestro hardware no está en condiciones de factorizar números tan grandes como RSA1024. Vamos a factorizar un número pequeño para demostrar cómo funciona el algoritmo. Para esta demostración, utilizaremos una versión simplificada del código presentado en el [tutorial del algoritmo de Shor](/docs/tutorials/shors-algorithm). Si desea obtener más detalles, visite el tutorial.\n",
        "\n",
        "Ejecutaremos el algoritmo utilizando nuestro marco estándar para resolver problemas cuánticos, denominado marco de patrones Qiskit. Esto consta de cuatro pasos:\n",
        "\n",
        "1. Asignación de su problema a un circuito cuántico\n",
        "2. Optimizar el circuito para que se ejecute en hardware cuántico\n",
        "3. Ejecuta tu circuito en el ordenador cuántico\n",
        "4. Procesar posteriormente las mediciones\n",
        "\n",
        "<span id=\"1-map\" />\n",
        "\n",
        "### 1. Mapa\n",
        "\n",
        "Factorizemos $N=15$, seleccionando $a=2$ como nuestro entero coprimo.\n",
        "\n",
        "En primer lugar, debemos construir el circuito que implementará la unidad de multiplicación modular. $M_a$ Esta es, en realidad, la parte más complicada de toda la implementación y puede requerir un gran esfuerzo computacional, dependiendo de cómo se haga. Para ello, haremos un poco de trampa: sabemos que estamos empezando en el estado $|1\\rangle$, y por una pregunta anterior,\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        "M_2|1\\rangle &= |2\\rangle & \\\\\n",
        "M_2|2\\rangle &= |4\\rangle \\\\\n",
        "M_2|4\\rangle &= |8\\rangle \\\\\n",
        "M_2|8\\rangle &= |1\\rangle \\\\\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n",
        "Por lo tanto, construiremos una unidad que realice las operaciones correctas en estos cuatro estados, pero que deje todos los demás estados tal cual. Esto es hacer trampa porque estamos utilizando nuestro conocimiento del orden de $2\\bmod 15$ para simplificar el unitario. Si realmente estuviéramos tratando de factorizar un número cuyos factores nos fueran desconocidos, no podríamos hacerlo.\n",
        "\n",
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Comprueba tu comprensión\n",
        "\n",
        "Con tu conocimiento de cómo el $M_2$ 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 $|i\\rangle$ en binario te ayudará)\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Respuesta\">\n",
        "    Reescribamos la acción de $M_2$ sobre los estados en binario:\n",
        "\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    M_2|0001\\rangle &= |0010\\rangle \\\\\n",
        "    M_2|0010\\rangle &= |0100\\rangle \\\\\n",
        "    M_2|0100\\rangle &= |1000\\rangle \\\\\n",
        "    M_2|1000\\rangle &= |0001\\rangle \\\\\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "\n",
        "    Cada una de estas acciones se puede realizar con un simple SWAP. $M_2|0001\\rangle$ se consigue intercambiando los estados de los qubits $0$ y $1$. $M_2|0010\\rangle$ se consigue intercambiando los estados de los qubits $1$ y $2$. Y así sucesivamente. Por lo tanto, podemos descomponer la $M_2$ matriz en la siguiente serie de puertas SWAP:\n",
        "\n",
        "    $$\n",
        "    M_2 = SWAP(0,1)SWAP(1,2)SWAP(2,3)\n",
        "    $$\n",
        "\n",
        "    Recordando que los operadores actúan de derecha a izquierda, comprobemos que esto tiene el efecto que queremos en cada uno de los estados:\n",
        "\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    M_2|0001\\rangle &= SWAP(0,1)SWAP(1,2)SWAP(2,3)|0001\\rangle \\\\\n",
        "    &= SWAP(0,1)SWAP(1,2)|0001\\rangle \\\\\n",
        "    &= SWAP(0,1)|0001\\rangle \\\\\n",
        "    &=|0010\\rangle  \\checkmark \\\\\n",
        "    M_2|0010\\rangle &= SWAP(0,1)SWAP(1,2)SWAP(2,3)|0010\\rangle \\\\\n",
        "    &= SWAP(0,1)SWAP(1,2)|0010\\rangle \\\\\n",
        "    &= SWAP(0,1)|0100\\rangle \\\\\n",
        "    &=|0100\\rangle  \\checkmark \\\\\n",
        "    M_2|0100\\rangle &= SWAP(0,1)SWAP(1,2)SWAP(2,3)|0100\\rangle \\\\\n",
        "    &= SWAP(0,1)SWAP(1,2)|1000\\rangle \\\\\n",
        "    &= SWAP(0,1)|1000\\rangle \\\\\n",
        "    &=|1000\\rangle  \\checkmark \\\\\n",
        "    M_2|1000\\rangle &= SWAP(0,1)SWAP(1,2)SWAP(2,3)|1000\\rangle \\\\\n",
        "    &= SWAP(0,1)SWAP(1,2)|0100\\rangle \\\\\n",
        "    &= SWAP(0,1)|0010\\rangle \\\\\n",
        "    &=|0001\\rangle  \\checkmark \\\\\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "Ahora podemos codificar el circuito equivalente a este operador en Qiskit.\n",
        "\n",
        "Primero, importamos los paquetes necesarios:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "id": "95c8d21b-d2e6-45c3-a2d2-a7288db4171f",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Import necessary packages\n",
        "\n",
        "import numpy as np\n",
        "from fractions import Fraction\n",
        "from math import floor, gcd, log\n",
        "\n",
        "from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister\n",
        "from qiskit.circuit.library import QFTGate\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "from qiskit.visualization import plot_histogram\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import SamplerV2 as Sampler"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f8d46443-d3d3-4d27-8026-27974f5d6702",
      "metadata": {},
      "source": [
        "A continuación, creamos el $M_2$ operador:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 30,
      "id": "1396a7ff-718a-4169-9c63-85ae218b58a4",
      "metadata": {},
      "outputs": [],
      "source": [
        "def M2mod15():\n",
        "    \"\"\"\n",
        "    M2 (mod 15)\n",
        "    \"\"\"\n",
        "    b = 2\n",
        "    U = QuantumCircuit(4)\n",
        "\n",
        "    U.swap(2, 3)\n",
        "    U.swap(1, 2)\n",
        "    U.swap(0, 1)\n",
        "\n",
        "    U = U.to_gate()\n",
        "    U.name = f\"M_{b}\"\n",
        "\n",
        "    return U"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 31,
      "id": "e7521fba-fe3e-45bc-b9ce-fe876bddad33",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/shors-algorithm/extracted-outputs/e7521fba-fe3e-45bc-b9ce-fe876bddad33-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 31,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Get the M2 operator\n",
        "M2 = M2mod15()\n",
        "\n",
        "# Add it to a circuit and plot\n",
        "circ = QuantumCircuit(4)\n",
        "circ.compose(M2, inplace=True)\n",
        "circ.decompose(reps=2).draw(output=\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6f1e1a5d-bd34-4379-9237-d91ecba9d435",
      "metadata": {},
      "source": [
        "El algoritmo QPE utiliza una puerta $U$ controlada. Ahora que tenemos un $M_2$ circuito, necesitamos convertirlo en un circuito $M_2$\\* controlado\\* :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "id": "36d1020c-c0af-4211-bd50-be865d057f75",
      "metadata": {},
      "outputs": [],
      "source": [
        "def controlled_M2mod15():\n",
        "    \"\"\"\n",
        "    Controlled M2 (mod 15)\n",
        "    \"\"\"\n",
        "    b = 2\n",
        "    U = QuantumCircuit(4)\n",
        "\n",
        "    U.swap(2, 3)\n",
        "    U.swap(1, 2)\n",
        "    U.swap(0, 1)\n",
        "\n",
        "    U = U.to_gate()\n",
        "    U.name = f\"M_{b}\"\n",
        "    c_U = U.control()\n",
        "\n",
        "    return c_U"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 33,
      "id": "5bf4f10d-5d52-406d-b62d-ad4a8d366c02",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/shors-algorithm/extracted-outputs/5bf4f10d-5d52-406d-b62d-ad4a8d366c02-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 33,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Get the controlled-M2 operator\n",
        "controlled_M2 = controlled_M2mod15()\n",
        "\n",
        "# Add it to a circuit and plot\n",
        "circ = QuantumCircuit(5)\n",
        "circ.compose(controlled_M2, inplace=True)\n",
        "circ.decompose(reps=1).draw(output=\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fd98c560-530f-424f-b8f8-0f9fe9615c5e",
      "metadata": {},
      "source": [
        "Ahora tenemos nuestra puerta $U$ controlada. Pero para ejecutar el algoritmo de estimación de fase cuántica, necesitaremos controlado $U^2$, controlado $U^4$, hasta controlado $U^{2^{m-1}}$, donde $m$ es el número de qubits utilizados para estimar la fase. Cuantos más qubits, más precisa será la estimación de fase. Utilizaremos qubits $m=8$ de control para nuestro procedimiento de estimación de fase. Por lo tanto, necesitamos:\n",
        "\n",
        "$$\n",
        "M_{a^{2^k}}|y\\rangle \\equiv |a^{2^k} y \\bmod N \\rangle\n",
        "$$\n",
        "\n",
        "donde el índice $k$, con $0 \\le k \\le m-1 = 7$, corresponde al qubit de control. Ahora calculemos $a^{2^k}\\bmod N $ para cada valor de $k$ :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 34,
      "id": "1463f000-c7ab-4e09-b111-5cb3d279e06b",
      "metadata": {},
      "outputs": [],
      "source": [
        "def a2kmodN(a, k, N):\n",
        "    \"\"\"Compute a^{2^k} (mod N) by repeated squaring\"\"\"\n",
        "    for _ in range(k):\n",
        "        a = int(np.mod(a**2, N))\n",
        "    return a"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 35,
      "id": "ac0f44f1-e5fa-46b8-a1d2-5255ed63e3b9",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[2, 4, 1, 1, 1, 1, 1, 1]\n"
          ]
        }
      ],
      "source": [
        "k_list = range(8)\n",
        "b_list = [a2kmodN(2, k, 15) for k in k_list]\n",
        "\n",
        "print(b_list)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a2654cb9-a2ad-4947-8441-a1e2799501b8",
      "metadata": {},
      "source": [
        "Dado que $a^{2^k} \\bmod N = 1$ para $k \\ge 2$, todos los operadores correspondientes ( $M_8$ y superiores) son equivalentes a la identidad. Por lo tanto, solo necesitamos construir una matriz más, $M_4.$\n",
        "\n",
        "**Nota:** Esta simplificación solo funciona aquí porque el orden de $2 \\bmod 15 $ es $4$. Una vez que $k=2$ (por lo tanto, $2^k = 4$ ), cada potencia posterior del operador es la identidad. En general, para números más grandes $N$ o diferentes elecciones de $a$, no se puede omitir la construcción de las potencias superiores. Esta es una de las razones por las que se considera un *ejemplo simplificado* : los números pequeños permiten atajos que no funcionarían en casos más grandes.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 36,
      "id": "29bc2fb5-511d-4593-8c48-7b1c7c7eeed0",
      "metadata": {},
      "outputs": [],
      "source": [
        "def M4mod15():\n",
        "    \"\"\"\n",
        "    M4 (mod 15)\n",
        "    \"\"\"\n",
        "    b = 4\n",
        "    U = QuantumCircuit(4)\n",
        "\n",
        "    U.swap(1, 3)\n",
        "    U.swap(0, 2)\n",
        "\n",
        "    U = U.to_gate()\n",
        "    U.name = f\"M_{b}\"\n",
        "\n",
        "    return U"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 37,
      "id": "ea4fc641-e97c-400d-a761-5f67a0b7d65e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/shors-algorithm/extracted-outputs/ea4fc641-e97c-400d-a761-5f67a0b7d65e-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 37,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Get the M4 operator\n",
        "M4 = M4mod15()\n",
        "\n",
        "# Add it to a circuit and plot\n",
        "circ = QuantumCircuit(4)\n",
        "circ.compose(M4, inplace=True)\n",
        "circ.decompose(reps=2).draw(output=\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f6712a0c-a695-4c1d-a99a-a9fd67a95c92",
      "metadata": {},
      "source": [
        "Y, como antes, lo convertimos en un operador $M_4$\\* controlado\\* :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 38,
      "id": "c8ec99c7-5e03-4623-b6f7-22dae558de04",
      "metadata": {},
      "outputs": [],
      "source": [
        "def controlled_M4mod15():\n",
        "    \"\"\"\n",
        "    Controlled M4 (mod 15)\n",
        "    \"\"\"\n",
        "    b = 4\n",
        "    U = QuantumCircuit(4)\n",
        "\n",
        "    U.swap(1, 3)\n",
        "    U.swap(0, 2)\n",
        "\n",
        "    U = U.to_gate()\n",
        "    U.name = f\"M_{b}\"\n",
        "    c_U = U.control()\n",
        "\n",
        "    return c_U"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 39,
      "id": "37caf888-276e-4f19-a0d0-59517c5ee44e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/shors-algorithm/extracted-outputs/37caf888-276e-4f19-a0d0-59517c5ee44e-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 39,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Get the controlled-M4 operator\n",
        "controlled_M4 = controlled_M4mod15()\n",
        "\n",
        "# Add it to a circuit and plot\n",
        "circ = QuantumCircuit(5)\n",
        "circ.compose(controlled_M4, inplace=True)\n",
        "circ.decompose(reps=1).draw(output=\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "db3f989d-ef17-48af-99c6-aae08eb59001",
      "metadata": {},
      "source": [
        "Ahora, podemos juntarlo todo para encontrar el orden de $2\\bmod 15$ con un circuito cuántico, utilizando la estimación de fase:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 48,
      "id": "d1b111c8-1a12-420b-bbc2-4ecdd1ac5a97",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/shors-algorithm/extracted-outputs/d1b111c8-1a12-420b-bbc2-4ecdd1ac5a97-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 48,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Order finding problem for N = 15 with a = 2\n",
        "N = 15\n",
        "a = 2\n",
        "\n",
        "# Number of qubits\n",
        "num_target = floor(log(N - 1, 2)) + 1  # for modular exponentiation operators\n",
        "num_control = 2 * num_target  # for enough precision of estimation\n",
        "\n",
        "# List of M_b operators in order\n",
        "k_list = range(num_control)\n",
        "b_list = [a2kmodN(2, k, 15) for k in k_list]\n",
        "\n",
        "# Initialize the circuit\n",
        "control = QuantumRegister(num_control, name=\"C\")\n",
        "target = QuantumRegister(num_target, name=\"T\")\n",
        "output = ClassicalRegister(num_control, name=\"out\")\n",
        "circuit = QuantumCircuit(control, target, output)\n",
        "\n",
        "# Initialize the target register to the state |1>\n",
        "circuit.x(num_control)\n",
        "\n",
        "# Add the Hadamard gates and controlled versions of the\n",
        "# multiplication gates\n",
        "for k, qubit in enumerate(control):\n",
        "    circuit.h(k)\n",
        "    b = b_list[k]\n",
        "    if b == 2:\n",
        "        circuit.compose(\n",
        "            M2mod15().control(), qubits=[qubit] + list(target), inplace=True\n",
        "        )\n",
        "    elif b == 4:\n",
        "        circuit.compose(\n",
        "            M4mod15().control(), qubits=[qubit] + list(target), inplace=True\n",
        "        )\n",
        "    else:\n",
        "        continue  # M1 is the identity operator\n",
        "\n",
        "# Apply the inverse QFT to the control register\n",
        "circuit.compose(QFTGate(num_control).inverse(), qubits=control, inplace=True)\n",
        "\n",
        "# Measure the control register\n",
        "circuit.measure(control, output)\n",
        "\n",
        "circuit.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b3659616-39b4-4544-8def-54012b40dbfa",
      "metadata": {},
      "source": [
        "<span id=\"2-optimize\" />\n",
        "\n",
        "### 2. Optimizar\n",
        "\n",
        "Ahora que hemos mapeado nuestro circuito, el siguiente paso es optimizarlo para que se ejecute en un ordenador cuántico concreto. Primero tenemos que cargar el backend.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 49,
      "id": "92b92fd3-0dca-43db-a304-0a933f293d8f",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()\n",
        "\n",
        "backend = service.backend(\"ibm_marrakesh\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a4e27bee-66de-4ffc-b1e3-40a458840e7a",
      "metadata": {},
      "source": [
        "Si no dispone de tiempo en su cuenta o desea utilizar un simulador por cualquier motivo, puede ejecutar la celda siguiente para configurar un simulador que imitará el dispositivo cuántico que hemos seleccionado anteriormente:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 50,
      "id": "4aa86bd6-1240-49cb-aad0-c3dbc64602c5",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "2q-depth: 188\n",
            "2q-size: 281\n",
            "Operator counts: OrderedDict({'sx': 548, 'rz': 380, 'cz': 281, 'measure': 8, 'x': 6})\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/shors-algorithm/extracted-outputs/4aa86bd6-1240-49cb-aad0-c3dbc64602c5-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 50,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "pm = generate_preset_pass_manager(optimization_level=2, backend=backend)\n",
        "\n",
        "transpiled_circuit = pm.run(circuit)\n",
        "\n",
        "print(f\"2q-depth: {transpiled_circuit.depth(lambda x: x.operation.num_qubits==2)}\")\n",
        "print(f\"2q-size: {transpiled_circuit.size(lambda x: x.operation.num_qubits==2)}\")\n",
        "print(f\"Operator counts: {transpiled_circuit.count_ops()}\")\n",
        "transpiled_circuit.draw(output=\"mpl\", fold=-1, style=\"clifford\", idle_wires=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b0425c46-2f2e-436f-9d37-a29fbc3a0006",
      "metadata": {},
      "source": [
        "<span id=\"3-execute\" />\n",
        "\n",
        "### 3. Ejecutar\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 51,
      "id": "6ee53841-d7e0-4a23-a3eb-bed31465374f",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Sampler primitive to obtain the probability distribution\n",
        "sampler = Sampler(backend)\n",
        "\n",
        "# Turn on dynamical decoupling with sequence XpXm\n",
        "sampler.options.dynamical_decoupling.enable = True\n",
        "sampler.options.dynamical_decoupling.sequence_type = \"XpXm\"\n",
        "# Enable gate twirling\n",
        "sampler.options.twirling.enable_gates = True\n",
        "\n",
        "pub = transpiled_circuit\n",
        "job = sampler.run([pub], shots=1024)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 52,
      "id": "7fb6178e-76bd-4067-9607-1d7e0ac051f3",
      "metadata": {},
      "outputs": [],
      "source": [
        "result = job.result()[0]\n",
        "counts = result.data[\"out\"].get_counts()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 53,
      "id": "6744e165-6929-46c6-9cad-55e78fe21f07",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/shors-algorithm/extracted-outputs/6744e165-6929-46c6-9cad-55e78fe21f07-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 53,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "plot_histogram(counts, figsize=(35, 5))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2eae47e9-1161-44e5-9e00-7b0d225bcf5f",
      "metadata": {},
      "source": [
        "Vemos cuatro picos claros en `00000000`, `01000000`, `10000000` y `11000000`, con algunos recuentos en otras cadenas de bits debido al ruido en el ordenador cuántico. Ignoraremos estos y mantendremos solo los cuatro dominantes imponiendo un umbral: solo los recuentos por encima de este umbral se consideran una señal verdadera por encima del ruido.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "948f404b-a2a9-4645-a034-31b696ba24b7",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Dictionary of bitstrings and their counts to keep\n",
        "counts_keep = {}\n",
        "# Threshold to filter\n",
        "threshold = np.max(list(counts.values())) / 2\n",
        "\n",
        "for key, value in counts.items():\n",
        "    if value > threshold:\n",
        "        counts_keep[key] = value\n",
        "\n",
        "print(counts_keep)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f9bac651-2d89-4b4e-9930-ea585a4c042e",
      "metadata": {},
      "source": [
        "<span id=\"4-post-process\" />\n",
        "\n",
        "### 4. Postprocesamiento\n",
        "\n",
        "En el caso del algoritmo de Shor, gran parte del algoritmo se ejecuta de forma clásica. Por lo tanto, pondremos el resto en el paso de «posprocesamiento», después de haber obtenido nuestras mediciones del ordenador cuántico. Cada una de las mediciones anteriores se puede convertir en números enteros que, tras dividirlos por $2^m$, son nuestras aproximaciones para $\\frac{k}{r}$, donde $k$ es aleatorio cada vez.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 55,
      "id": "aad78e69-adb8-416f-bc68-c2086f900bef",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "ATTEMPT 0:\n",
            "Phase: theta = 0.0\n",
            "Order of 2 modulo 15 estimated as: r = 1\n",
            "\n",
            "ATTEMPT 1:\n",
            "Phase: theta = 0.75\n",
            "Order of 2 modulo 15 estimated as: r = 4\n",
            "*** Non-trivial factor found: 3 ***\n"
          ]
        }
      ],
      "source": [
        "a = 2\n",
        "N = 15\n",
        "\n",
        "FACTOR_FOUND = False\n",
        "num_attempt = 0\n",
        "\n",
        "while not FACTOR_FOUND:\n",
        "    print(f\"\\nATTEMPT {num_attempt}:\")\n",
        "    # Here, we get the bitstring by iterating over outcomes\n",
        "    # of a previous hardware run with multiple shots.\n",
        "    # Instead, we can also perform a single-shot measurement\n",
        "    # here in the loop.\n",
        "    bitstring = list(counts_keep.keys())[num_attempt]\n",
        "    num_attempt += 1\n",
        "    # Find the phase from measurement\n",
        "    decimal = int(bitstring, 2)\n",
        "    phase = decimal / (2**num_control)  # phase = k / r\n",
        "    print(f\"Phase: theta = {phase}\")\n",
        "\n",
        "    # Guess the order from phase\n",
        "    frac = Fraction(phase).limit_denominator(N)\n",
        "    r = frac.denominator  # order = r\n",
        "    print(f\"Order of {a} modulo {N} estimated as: r = {r}\")\n",
        "\n",
        "    if phase != 0:\n",
        "        # Guesses for factors are gcd(a^{r / 2} ± 1, 15)\n",
        "        if r % 2 == 0:\n",
        "            x = pow(a, r // 2, N) - 1\n",
        "            d = gcd(x, N)\n",
        "            if d > 1:\n",
        "                FACTOR_FOUND = True\n",
        "                print(f\"*** Non-trivial factor found: {x} ***\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c88c65c3-2dfb-47fd-bef9-59f02440f418",
      "metadata": {},
      "source": [
        "<span id=\"conclusion\" />\n",
        "\n",
        "## Conclusión\n",
        "\n",
        "Después de completar el módulo, es posible que te sorprenda una nueva apreciación de la genialidad de Peter Shor al haber ideado un algoritmo tan inteligente. Pero esperamos que también hayas alcanzado un nuevo nivel de comprensión de su engañosa simplicidad. Aunque el algoritmo pueda parecer impresionantemente (o intimidantemente) complejo, si lo desglosas en cada paso lógico y lo sigues lentamente, tú también podrás ejecutar el algoritmo de Shor.\n",
        "\n",
        "Aunque aún estamos lejos de utilizar este algoritmo para factorizar números como RSA1024, nuestros ordenadores cuánticos mejoran cada día y, una vez que se alcance un umbral denominado *«tolerancia a fallos»*, algoritmos como estos no tardarán en llegar. ¡Es un momento emocionante para aprender sobre la computación cuántica!\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9f1e3bb2-9732-406f-9f17-862178724285",
      "metadata": {},
      "source": [
        "<span id=\"problems\" />\n",
        "\n",
        "## Problemas\n",
        "\n",
        "<span id=\"critical-concepts\" />\n",
        "\n",
        "### Conceptos fundamentales:\n",
        "\n",
        "* Los sistemas criptográficos modernos se basan en la dificultad clásica de factorizar números enteros grandes.\n",
        "* La aritmética modular —incluidas las estructuras $\\mathbb{Z}_N$ y $\\mathbb{Z}_N^*$ — proporciona la base matemática para el algoritmo de Shor.\n",
        "* El problema de factorizar un número entero $N$ puede reducirse al problema de hallar el orden de un número módulo $N$.\n",
        "* La búsqueda de orden cuántico utiliza técnicas de estimación de fase cuántica para determinar el periodo de la función $a^x \\mod N$.\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 del orden cuántico y, a continuación, calcula de forma clásica los factores a partir del resultado.\n",
        "\n",
        "<span id=\"true/false\" />\n",
        "\n",
        "### Verdadero/Falso:\n",
        "\n",
        "1. V/F La eficiencia del algoritmo de Shor amenaza la seguridad del cifrado RSA.\n",
        "2. V/F El algoritmo de Shor se puede ejecutar de manera eficiente en cualquier ordenador cuántico moderno.\n",
        "3. El algoritmo de T/F Shor utiliza la estimación de fase cuántica (QPE) como subrutina clave.\n",
        "4. V/F La parte clásica del algoritmo de Shor implica calcular el máximo común divisor (MCD).\n",
        "5. V/F El algoritmo de Shor solo funciona para factorizar números pares.\n",
        "6. V/F Una ejecución correcta del algoritmo de Shor siempre garantiza los factores correctos.\n",
        "\n",
        "<span id=\"short-answer\" />\n",
        "\n",
        "### Respuesta breve:\n",
        "\n",
        "1. ¿Por qué se considera que el algoritmo de Shor es una amenaza potencial futura para el cifrado RSA?\n",
        "2. ¿Por qué es útil encontrar el período, o orden, de una función exponencial modular para factorizar un número en el algoritmo de Shor?\n",
        "\n",
        "<span id=\"challenge-problems\" />\n",
        "\n",
        "### Problemas desafiantes:\n",
        "\n",
        "1. ¿Cuántos qubits de control $m$ necesitamos para un número dado $N$ que estamos tratando de factorizar para obtener la precisión en el QPE necesaria para encontrar el valor correcto del orden $r$?\n",
        "\n",
        "2. Siguiendo el procedimiento que hemos descrito aquí para factorizar 15, ahora intenta factorizar 21.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "in_page_toc_max_heading_level": 2,
    "in_page_toc_min_heading_level": 2,
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    },
    "widgets": {
      "application/vnd.jupyter.widget-state+json": {
        "state": {},
        "version_major": 2,
        "version_minor": 0
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}