{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "dd33e9e7-3e4c-48ea-81a9-70b74c34b130",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Transformada de Fourier cuántica\"\n",
        "description: \"Aprenda sobre la transformada de Fourier cuántica y cómo se utiliza como subrutina en algoritmos tales como la estimación de fase cuántica.\"\n",
        "---\n",
        "\n",
        "<span id=\"quantum-fourier-transform\" />\n",
        "\n",
        "# Transformada de Fourier cuántica\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a920f884-f6b0-46fd-a4ef-126f9c281f17",
      "metadata": {},
      "source": [
        "Para este módulo de Qiskit en las aulas, los estudiantes deben tener un entorno Python en funcionamiento con los siguientes paquetes instalados:\n",
        "\n",
        "* `qiskit` v2.1.0 o más reciente\n",
        "* `qiskit-ibm-runtime` v0.40.1 o más reciente\n",
        "* `qiskit-aer` v0.17.0 o más reciente\n",
        "* `qiskit.visualization`\n",
        "* `numpy`\n",
        "* `pylatexenc`\n",
        "\n",
        "Para configurar e instalar los paquetes anteriores, consulta 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 en IBM Quantum® siguiendo los pasos de la guía [Configure su cuenta en IBM Cloud](/docs/guides/cloud-setup).\n",
        "\n",
        "Este módulo fue probado y utilizó 13 segundos de tiempo QPU. Se trata de una estimación de buena fe; su uso real puede variar.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "46231c35-a3f5-4b04-83c5-6f15d6a5785c",
      "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'"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d77edd39-2574-41bb-9338-13d139f1a6a9",
      "metadata": {},
      "source": [
        "<span id=\"introduction\" />\n",
        "\n",
        "## Introducción\n",
        "\n",
        "La transformada de Fourier es una herramienta omnipresente con aplicaciones en matemáticas, física, procesamiento de señales, compresión de datos y otros innumerables campos. Una versión *cuántica* de la transformada de Fourier, acertadamente denominada transformada cuántica de Fourier, constituye la base de algunos de los algoritmos cuánticos más importantes.\n",
        "\n",
        "Hoy, tras un recordatorio de la transformada de Fourier clásica, hablaremos de cómo implementar la transformada de Fourier cuántica en un ordenador cuántico. A continuación, hablaremos de una de las aplicaciones de la transformada cuántica de Fourier a un algoritmo llamado algoritmo de estimación de fase. La estimación cuántica de fase es una subrutina del famoso algoritmo de factorización de Shor, al que a veces se hace referencia como la \"joya de la corona\" de la computación cuántica. Este módulo se basa en otro módulo sobre el algoritmo de Shor, pero también puede utilizarse de forma independiente. La transformada cuántica de Fourier es un algoritmo fascinante y útil por derecho propio\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "24e8de09-0876-46cd-9346-c0d46ffce8a9",
      "metadata": {},
      "source": [
        "<span id=\"the-classical-fourier-transform\" />\n",
        "\n",
        "## La transformada de Fourier clásica\n",
        "\n",
        "Antes de pasar a la transformada cuántica de Fourier, recordemos la versión clásica. La transformada de Fourier es un método de transformación de una \"base\" a otra. Se puede pensar en dos bases como diferentes perspectivas del mismo problema: ambas son formas válidas de expresar una función, pero una u otra pueden ser más esclarecedoras, dependiendo del problema que se trate. Algunos ejemplos de pares de bases que se conectan mediante la transformada de Fourier son la posición y el momento, y el tiempo y la frecuencia.\n",
        "\n",
        "Veamos un ejemplo de cómo la transformada de Fourier puede ayudarnos a averiguar qué nota está tocando un instrumento basándonos en su forma de onda de audio. Normalmente, vemos las formas de onda representadas en base temporal, es decir, la amplitud de la onda se expresa en función del tiempo.\n",
        "\n",
        "![Señal sinusoidal única trazada en función del tiempo.](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/qft/Cnote.avif)\n",
        "\n",
        "Podemos transformar de Fourier esta forma de onda para pasar de la base temporal a la base frecuencial:\n",
        "\n",
        "![Espectro de frecuencia de la forma de onda de audio. Un pico nítido y claro a 260 Hz.](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/qft/Cnotefreq.avif)\n",
        "\n",
        "En la base de frecuencia, podemos ver fácilmente un pico claro en torno a 260 Hz. ¡Eso es un do central!\n",
        "\n",
        "Ahora bien, es posible que haya podido determinar que se estaba tocando un Do central sin utilizar una transformada de Fourier, pero ¿qué ocurre si se tocan varias notas a la vez? Entonces, la forma de onda se complica cuando la trazamos en base temporal:\n",
        "\n",
        "![Gráfico de desplazamiento en función del tiempo de múltiples ondas sinusoidales a la vez, creando un patrón periódico más complicado.](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/qft/Cchord.avif)\n",
        "\n",
        "Pero el espectro de frecuencias identifica claramente tres picos:\n",
        "\n",
        "![Espectro de frecuencias de la forma de onda de audio anterior. Tres picos aproximadamente a 260 Hz, 330 Hz y 392 Hz. El último pico es muy débil, pero visible.](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/qft/Cchordfreq.avif)\n",
        "\n",
        "Se trataba de un acorde de Do mayor, tocando las notas Do, Mi y Sol.\n",
        "\n",
        "Este tipo de análisis de Fourier puede ayudarnos a extraer los componentes de frecuencia de cualquier tipo de señal complicada.\n",
        "\n",
        "<span id=\"discrete-fourier-transform\" />\n",
        "\n",
        "### Transformada discreta de Fourier\n",
        "\n",
        "La transformada de Fourier es útil para numerosas aplicaciones de tratamiento de señales. Pero en la mayoría de estas aplicaciones del mundo real (incluido el ejemplo de la música que hemos utilizado antes), queremos transformar un conjunto discreto de puntos de datos $N$, no una función continua. En este caso, utilizamos la transformada *discreta* de Fourier. La transformada discreta de Fourier (DFT) actúa sobre un vector $(x_0, ..., x_{N-1})$ y lo mapea al vector $(y_0, ..., y_{N-1})$ según la fórmula:\n",
        "\n",
        "$y_k = \\frac{1}{\\sqrt{N}}\\sum_{j=0}^{N-1}x_j\\omega_N^{jk}$\n",
        "\n",
        "donde tomamos $\\omega_N^{jk} = e^{2\\pi i \\frac{jk}{N}}$. (Tenga en cuenta que hay otras convenciones que tienen un signo menos en el exponencial, así que tenga cuidado cuando vea la DFT en la naturaleza) Recordemos que $e^{2\\pi i \\frac{jk}{N}}$ es una función periódica, con período $\\frac{N}{k}$. Por tanto, al multiplicar por esta función, la transformada de Fourier es esencialmente una forma de descomponer la función (discreta) $\\{x_{j}\\}$ en una combinación lineal de sus funciones periódicas constituyentes, cada una con periodo $\\frac{N}{k}$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e1271322-48e3-47e1-90e1-7cf7117ccd7c",
      "metadata": {},
      "source": [
        "<span id=\"the-quantum-fourier-transform\" />\n",
        "\n",
        "## La transformada de Fourier cuántica\n",
        "\n",
        "Ya hemos visto cómo se utiliza la transformada de Fourier para representar una función como combinación lineal de un nuevo conjunto de las llamadas \"funciones base\" Las transformaciones de base también se realizan regularmente en los estados qubit. Por ejemplo, el estado de un único qubit $|\\psi\\rangle$ puede expresarse en la base computacional $|\\psi\\rangle = c_0 |0\\rangle + c_1 |1\\rangle$, con estados de base $|0\\rangle$ y $|1\\rangle$, o en la base $X$ $|\\psi\\rangle = c_+ |+\\rangle + c_- |-\\rangle$ con estados de base $|+\\rangle = \\frac{1}{\\sqrt{2}} (|0\\rangle + |1\\rangle)$ y $|-\\rangle = \\frac{1}{\\sqrt{2}} (|0\\rangle - |1\\rangle)$. Ambas son igualmente válidas, pero una puede ser más natural que la otra, dependiendo del tipo de problema que se intente resolver.\n",
        "\n",
        "Los estados Qubit también pueden expresarse en la base de Fourier, donde un estado se expresa en términos de una combinación lineal de los estados de la base de Fourier $|\\phi_y\\rangle$, en lugar de los estados habituales de la base computacional, $|x\\rangle$. Para ello, es necesario aplicar una transformada cuántica de Fourier (QFT):\n",
        "\n",
        "$ | \\phi_y \\rangle =  \\frac{1}{\\sqrt{N}}\\sum_{x=0}^{N-1}\\omega_N^{y x} \\vert x \\rangle$\n",
        "\n",
        "con $\\omega_N^{yx} = e^{\\frac{2\\pi i y x}{N}}$ como arriba, y $N$ es el número de estados básicos en su sistema cuántico. Tenga en cuenta que, dado que ahora estamos trabajando con qubits, $m$ qubits le da $2^m$ estados básicos, por lo que $N=2^m$. Aquí, los estados básicos se escriben como un solo número $|x\\rangle$ donde $x$ varía de $0$ a $N-1$, pero es más habitual ver los estados básicos expresados como $|00...00\\rangle$, $|00...01\\rangle$, $|00...11\\rangle$,..., $|11...11\\rangle$, donde cada dígito binario representa el estado del qubit 0 a $m-1$, de derecha a izquierda. Hay una forma fácil de convertir estos estados binarios en un solo número: ¡simplemente trátalos como números binarios! Por lo tanto, $|00...00\\rangle = |0\\rangle$, $|00...01\\rangle = |1\\rangle$, $|00...10\\rangle = |2\\rangle$, $|00...11\\rangle = |3\\rangle$, y así sucesivamente, hasta $|11...11\\rangle = |2^m -1\\rangle = |N-1\\rangle$.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "07d09c65-9ba8-41cb-983d-b5dd33d99458",
      "metadata": {},
      "source": [
        "<span id=\"build-intuition-for-the-fourier-basis-states\" />\n",
        "\n",
        "### Desarrollar la intuición para los estados básicos de Fourier\n",
        "\n",
        "Así pues, acabamos de repasar qué son los estados base computacionales y cómo se ordenan: son el conjunto de estados en los que cada qubit está en $0$ o $1$, y los ordenamos desde el estado en el que todos los qubits están en $0$, $|00...00\\rangle$, hasta el estado en el que todos están en $1$, $|11...11\\rangle$.\n",
        "\n",
        "Pero, ¿cómo dar sentido a los estados de la base de *Fourier*? Todos los estados de la base de Fourier son superposiciones iguales de todos los estados de la base computacional, pero cada estado difiere del otro en la periodicidad en la *fase* de los componentes. Para entenderlo más concretamente, veamos los cuatro estados de la base de Fourier de un sistema de dos qubits. El estado de Fourier más bajo es aquel cuya fase no varía en absoluto:\n",
        "\n",
        "$|\\phi_0\\rangle = \\frac{1}{2} (|00\\rangle + |01\\rangle + |10\\rangle + |11\\rangle)$\n",
        "\n",
        "Podemos visualizar este estado trazando la amplitud compleja de cada uno de los términos. La línea roja guía al ojo para mostrarle cómo la fase de esta amplitud serpentea por el plano complejo en función del estado base de cálculo. Para $|\\phi_0\\rangle$, la fase permanece constante:\n",
        "\n",
        "![Gráfico de barras de la amplitud compleja (plano x-y) de cada estado base de cálculo (eje z) para phi\\_0. Todos son reales, por lo que las barras apuntan a +1 en el eje x](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/qft/phi0.avif)\n",
        "\n",
        "El siguiente estado de la base de Fourier es aquel cuyas fases de los componentes giran de $0$ a $2\\pi$ una sola vez:\n",
        "\n",
        "$|\\phi_1\\rangle = \\frac{1}{2} (|00\\rangle + e^{i\\pi/2}|01\\rangle + e^{i\\pi}|10\\rangle + e^{3i\\pi/2}|11\\rangle) = \\frac{1}{2}(|00\\rangle + i|01\\rangle - |10\\rangle - i|11\\rangle)$\n",
        "\n",
        "Y podemos ver esta sinuosidad en el gráfico de la amplitud compleja frente al estado base computacional:\n",
        "\n",
        "![Gráfico de barras de la amplitud compleja (plano x-y) de cada estado base de cálculo (eje z) para phi\\_1. La línea roja muestra cómo la fase compleja se acumula de tal manera que serpentea alrededor de 2\\pi una vez al pasar por todos los estados de la base de cálculo.](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/qft/phi1.avif)\n",
        "\n",
        "Así, cada estado tiene una fase que es $2\\pi/4$ radianes mayor que el estado que le precede cuando se ordenan de la forma estándar, ya que en este ejemplo tenemos cuatro estados base ( $N=4$ ). El siguiente estado base gira de 0 a 2 $\\pi$ dos veces:\n",
        "\n",
        "$|\\phi_2\\rangle = \\frac{1}{2} (|00\\rangle + e^{i\\pi}|01\\rangle + e^{2i\\pi}|10\\rangle + e^{3i\\pi}|11\\rangle) = \\frac{1}{2} (|00\\rangle - |01\\rangle + |10\\rangle - |11\\rangle)$\n",
        "\n",
        "![Gráfico de barras de la amplitud compleja (plano x-y) de cada estado base de cálculo (eje z) para phi\\_2. La línea roja muestra cómo la fase compleja se acumula de tal manera que serpentea alrededor de 2\\pi dos veces a medida que se recorren todos los estados de la base de cálculo.](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/qft/phi2.avif)\n",
        "\n",
        "Por último, la componente de Fourier más alta es la que varía más rápidamente de fase. Para nuestro ejemplo con dos qubits, es aquel cuyas fases dan tres vueltas de 0 a $2\\pi$ :\n",
        "\n",
        "$|\\phi_3\\rangle = \\frac{1}{2} (|00\\rangle + e^{3i\\pi/2}|01\\rangle + e^{6i\\pi/2}|10\\rangle + e^{9i\\pi/2}|11\\rangle) = \\frac{1}{2} (|00\\rangle - i|01\\rangle - |10\\rangle + i|11\\rangle)$\n",
        "\n",
        "![Gráfico de barras de la amplitud compleja (plano x-y) de cada estado base de cálculo (eje z) para phi\\_3. La línea roja muestra cómo la fase compleja se acumula de tal forma que serpentea alrededor de 2\\pi tres veces a medida que se recorren todos los estados base computacionales.](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/qft/phi3.avif)\n",
        "\n",
        "En general, para un estado de qubit e $m$, habrá e $2^m$ es estados de base de Fourier, cuya frecuencia en la variación de fase varía desde constante, para $|\\phi_0\\rangle$, hasta rápidamente variable para $|\\phi_{2^m-1}\\rangle$, completando $2^m-1$ vueltas alrededor de $2\\pi$ sobre la superposición de estados. Por lo tanto, cuando tomamos una QFT de un estado cuántico, esencialmente estamos haciendo el mismo análisis básico que hicimos para la forma de onda musical en la introducción. Estamos determinando los componentes de frecuencia de Fourier que contribuyen a crear el estado cuántico de interés.\n",
        "\n",
        "<span id=\"try-some-example-qfts\" />\n",
        "\n",
        "### Prueba algunos ejemplos de QFT\n",
        "\n",
        "Intentemos seguir construyendo nuestra intuición para la transformada cuántica de Fourier haciendo un estado en la base computacional, y luego viendo qué ocurre cuando le aplicamos la QFT. Por ahora, nos limitaremos a tratar la QFT como una caja negra que aplicamos utilizando la `QFTGate` de la [biblioteca de circuitos Qiskit.](/docs/guides/circuit-library) Más adelante veremos cómo se implementa.\n",
        "\n",
        "Comenzamos cargando los paquetes necesarios y seleccionando un dispositivo en el que ejecutar nuestro circuito:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "3b420ee9-fe1f-4f29-aa73-1306e7a86688",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "from qiskit import QuantumCircuit\n",
        "from qiskit.visualization import plot_histogram\n",
        "from qiskit.circuit.library import QFTGate"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "f93e8786-96e4-4adf-97b1-1d6c1222b3dc",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "ibm_pinguino2\n"
          ]
        }
      ],
      "source": [
        "# Load the Qiskit Runtime service\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "\n",
        "# Load the Runtime primitive and session\n",
        "from qiskit_ibm_runtime import SamplerV2 as Sampler\n",
        "\n",
        "service = QiskitRuntimeService()\n",
        "\n",
        "# Use the least busy backend\n",
        "# backend = service.least_busy(operational=True, simulator=False, min_num_qubits = 127)\n",
        "backend = service.backend(\"ibm_pinguino2\")\n",
        "\n",
        "print(backend.name)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ea5beb2c-d7b2-4d7b-9015-02dad90d44b6",
      "metadata": {},
      "source": [
        "Si no tienes tiempo disponible en tu cuenta o quieres utilizar un simulador por cualquier motivo, puedes ejecutar la celda que aparece a continuación para configurar un simulador que imitará el dispositivo cuántico que seleccionamos anteriormente:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "7f33f044-a798-4b9c-baad-6676650cd322",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Load the backend sampler\n",
        "from qiskit.primitives import BackendSamplerV2\n",
        "\n",
        "# Load the Aer simulator and generate a noise model based on the currently-selected backend.\n",
        "from qiskit_aer import AerSimulator\n",
        "from qiskit_aer.noise import NoiseModel\n",
        "\n",
        "noise_model = NoiseModel.from_backend(backend)\n",
        "\n",
        "# Define a simulator using Aer, and use it in Sampler.\n",
        "backend_sim = AerSimulator(noise_model=noise_model)\n",
        "sampler_sim = BackendSamplerV2(backend=backend_sim)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "1d4d986a-6164-40cd-8bba-0289af2430f4",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Alternatively, load a fake backend with generic properties and define a simulator.\n",
        "from qiskit.providers.fake_provider import GenericBackendV2\n",
        "\n",
        "backend_gen = GenericBackendV2(num_qubits=18)\n",
        "sampler_gen = BackendSamplerV2(backend=backend_gen)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bce6f65e-1105-4abf-80ab-476385ff5b60",
      "metadata": {},
      "source": [
        "<span id=\"single-computational-basis-state\" />\n",
        "\n",
        "#### Estado de base computacional único\n",
        "\n",
        "En primer lugar, vamos a intentar transformar un único estado de base computacional. Empezaremos creando un estado computacional aleatorio:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "79943d18-2d57-41f4-ba6e-9c40aca24d38",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/qft/extracted-outputs/79943d18-2d57-41f4-ba6e-9c40aca24d38-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Step 1: Map\n",
        "\n",
        "qubits = 4\n",
        "N = 2**qubits\n",
        "\n",
        "\n",
        "qc = QuantumCircuit(qubits)\n",
        "\n",
        "# flip state of random qubits to put in a random single computational basis state\n",
        "for i in range(1, qubits):\n",
        "    if np.random.randint(0, 2):\n",
        "        qc.x(i)\n",
        "\n",
        "\n",
        "# make a copy of the above circuit. (to be used when we apply the QFT in next part)\n",
        "qc_qft = qc.copy()\n",
        "\n",
        "\n",
        "qc.measure_all()\n",
        "qc.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "c16bbbb1-99fe-4824-b9a5-b74fa79eeedf",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/qft/extracted-outputs/c16bbbb1-99fe-4824-b9a5-b74fa79eeedf-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Step 2: Transpile\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "target = backend.target\n",
        "pm = generate_preset_pass_manager(target=target, optimization_level=3)\n",
        "\n",
        "qc_isa = pm.run(qc)\n",
        "\n",
        "# Step 3: Run the job on a real quantum computer OR try fake backend\n",
        "\n",
        "sampler = Sampler(mode=backend)\n",
        "pubs = [qc_isa]\n",
        "\n",
        "# Run the job on real quantum device\n",
        "\n",
        "job = sampler.run(pubs, shots=1000)\n",
        "res = job.result()\n",
        "counts = res[0].data.meas.get_counts()\n",
        "\n",
        "# OR Run the job on the Aer simulator with noise model from real backend\n",
        "\n",
        "# job = sampler_sim.run([qc_isa])\n",
        "# res = job.result()\n",
        "# counts = res[0].data.meas.get_counts()\n",
        "\n",
        "# Step 4: Post-Process\n",
        "plot_histogram(counts)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f3723281-56f2-42ac-afd9-44f6d0fe147b",
      "metadata": {},
      "source": [
        "Ahora, transformemos este estado en Fourier con `QFTGate`:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "51b45910-624c-40ee-ad56-d9af094490d2",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/qft/extracted-outputs/51b45910-624c-40ee-ad56-d9af094490d2-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 9,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Step 1: Map\n",
        "\n",
        "qc_qft.compose(QFTGate(qubits), inplace=True)\n",
        "qc_qft.measure_all()\n",
        "qc_qft.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "198a4223-96ab-475e-a83c-75596bf569cb",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/qft/extracted-outputs/198a4223-96ab-475e-a83c-75596bf569cb-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Step 2: Transpile\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "target = backend.target\n",
        "pm = generate_preset_pass_manager(target=target, optimization_level=3)\n",
        "\n",
        "qc_isa = pm.run(qc_qft)\n",
        "\n",
        "# Step 3: Run the job on a real quantum computer - try fake backend\n",
        "\n",
        "sampler = Sampler(mode=backend)\n",
        "pubs = [qc_isa]\n",
        "\n",
        "# Run the job on real quantum device\n",
        "\n",
        "job = sampler.run(pubs, shots=1000)\n",
        "res = job.result()\n",
        "counts = res[0].data.meas.get_counts()\n",
        "\n",
        "# OR Run the job on the Aer simulator with noise model from real backend\n",
        "\n",
        "# job = sampler_sim.run([qc_isa])\n",
        "# res = job.result()\n",
        "# counts = res[0].data.meas.get_counts()\n",
        "\n",
        "# Step 4: Post-Process\n",
        "plot_histogram(counts)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "89965746-e7d1-409f-a893-5766759a8ce3",
      "metadata": {},
      "source": [
        "Como puede ver, medimos las poblaciones de cada estado para que sean más o menos iguales, más o menos algo de ruido experimental y estadístico. Por tanto, si se toma la QFT de un único estado base computacional, el resultado es una superposición igual de todos los estados. Si está familiarizado con las transformadas de Fourier, probablemente esto no le sorprenda. Un principio básico que puede ayudarnos a establecer una conexión intuitiva entre una función y su transformada de Fourier es que la anchura de una función es inversamente proporcional a la anchura de su transformada de Fourier. Así, algo que está muy localizado en el tiempo, por ejemplo, como un pulso muy corto, requerirá una amplia gama de frecuencias para generar ese pulso. Esa señal será muy amplia en el espacio de Fourier.\n",
        "\n",
        "Este hecho está relacionado con la incertidumbre cuántica El principio de incertidumbre de Heisenberg suele enunciarse como $\\Delta x \\Delta p \\ge \\hbar / 2 $. Así, si la incertidumbre en $x$ ( $\\Delta x$ ) es pequeña, la incertidumbre en el momento ( $\\Delta p$ ) debe ser grande, y viceversa. Resulta que la transformación de la base de posición $x$ a la base de momento $p$ se realiza mediante una transformada de Fourier.\n",
        "\n",
        "Nota: Ten en cuenta que estamos midiendo poblaciones en cada uno de los estados base, por lo que estamos perdiendo información sobre las fases relativas entre las distintas partes de la superposición. Así, mientras que la QFT de cualquier estado base computacional dará como resultado la misma dispersión uniforme de la población en todos los estados base, las *fases* no serán necesariamente las mismas.\n",
        "\n",
        "<span id=\"two-computational-basis-states\" />\n",
        "\n",
        "#### Dos estados de base computacional\n",
        "\n",
        "Veamos ahora qué ocurre cuando preparamos una superposición de estados de base computacional. ¿Cómo crees que será la transformada de Fourier en este caso?\n",
        "\n",
        "Elijamos la superposición:\n",
        "\n",
        "$|\\psi\\rangle = \\frac{1}{\\sqrt{2}} (|0\\rangle + |N/2\\rangle) = \\frac{1}{\\sqrt{2}} (|000...0\\rangle + |100...0\\rangle)$\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "cd0b237b-b139-451a-8170-69babdc29e56",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/qft/extracted-outputs/cd0b237b-b139-451a-8170-69babdc29e56-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 11,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Step 1: Map\n",
        "qubits = 4\n",
        "N = 2**qubits\n",
        "\n",
        "\n",
        "qc = QuantumCircuit(qubits)\n",
        "\n",
        "# To make this state, we just need to apply a Hadamard to the last qubit\n",
        "\n",
        "qc.h(qubits - 1)\n",
        "\n",
        "\n",
        "qc_qft = qc.copy()\n",
        "\n",
        "\n",
        "qc.measure_all()\n",
        "\n",
        "qc.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "0ad8006b-caaf-4e4c-b222-a9224d3f6af8",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/qft/extracted-outputs/0ad8006b-caaf-4e4c-b222-a9224d3f6af8-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 12,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# First, let's go through steps 2-4 for the first circuit, qc\n",
        "\n",
        "# Step 2: Transpile\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "target = backend.target\n",
        "pm = generate_preset_pass_manager(target=target, optimization_level=3)\n",
        "\n",
        "qc_isa = pm.run(qc)\n",
        "\n",
        "# Step 3: Run the job on a real quantum computer - try fake backend\n",
        "\n",
        "sampler = Sampler(mode=backend)\n",
        "pubs = [qc_isa]\n",
        "\n",
        "# Run the job on real quantum device\n",
        "\n",
        "job = sampler.run(pubs, shots=1000)\n",
        "res = job.result()\n",
        "counts = res[0].data.meas.get_counts()\n",
        "\n",
        "# OR run the job on the Aer simulator with noise model from real backend\n",
        "\n",
        "# job = sampler_sim.run([qc_isa])\n",
        "# res = job.result()\n",
        "# counts = res[0].data.meas.get_counts()\n",
        "\n",
        "# Step 4: Post-process\n",
        "plot_histogram(counts)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "93aaa06c-7de3-4c0f-950c-e48768ca50c5",
      "metadata": {},
      "source": [
        "Ahora, transformemos este estado en Fourier con `QFTGate`:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "11793bc2-53ea-4c34-aed7-06e1ce630557",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/qft/extracted-outputs/11793bc2-53ea-4c34-aed7-06e1ce630557-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Step 1: Map\n",
        "\n",
        "qc_qft.compose(QFTGate(qubits), inplace=True)\n",
        "qc_qft.measure_all()\n",
        "qc_qft.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "15b9688a-8c71-457d-87e7-8bfec6f4ee76",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/qft/extracted-outputs/15b9688a-8c71-457d-87e7-8bfec6f4ee76-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Step 2: Transpile\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "target = backend.target\n",
        "pm = generate_preset_pass_manager(target=target, optimization_level=3)\n",
        "\n",
        "qc_isa = pm.run(qc_qft)\n",
        "\n",
        "# Step 3: Run the job on a real quantum computer OR try fake backend\n",
        "\n",
        "sampler = Sampler(mode=backend)\n",
        "pubs = [qc_isa]\n",
        "\n",
        "# Run the job on real quantum device\n",
        "\n",
        "job = sampler.run(pubs, shots=1000)\n",
        "res = job.result()\n",
        "counts = res[0].data.meas.get_counts()\n",
        "\n",
        "# OR run the job on the Aer simulator with noise model from real backend\n",
        "\n",
        "# job = sampler_sim.run([qc_isa])\n",
        "# res = job.result()\n",
        "# counts = res[0].data.meas.get_counts()\n",
        "\n",
        "# Step 4: Post-process\n",
        "plot_histogram(counts)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "74008f89-5634-4e52-8061-87976888c39f",
      "metadata": {},
      "source": [
        "Esta puede ser un poco más sorprendente. Parece que la QFT del estado $|\\psi\\rangle = \\frac{1}{\\sqrt{2}} (|0\\rangle + |N/2\\rangle)$ es una superposición de todos los estados de la base par. Pero si pensamos de nuevo en nuestra visualización de cada estado base $|\\phi_y\\rangle$, y en cómo la fase de cada componente gira alrededor de $2\\pi$ $y$ veces, entonces la razón por la que obtenemos este resultado puede quedar clara.\n",
        "\n",
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Comprueba tu comprensión\n",
        "\n",
        "Utilizando la pista anterior, explica por qué es previsible el resultado que obtuvimos para la teoría cuántica de campos de un campo de tipo « $|\\psi\\rangle = \\frac{1}{\\sqrt{2}} (|0\\rangle + |N/2\\rangle)$ ».\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Respuesta\">\n",
        "    El estado original tiene una fase relativa de 0 (o un múltiplo entero de $2\\pi$ ) entre las dos partes de la superposición. Así, sabemos que este estado tiene componentes de Fourier cuyas fases también coinciden de esa manera: las que tienen desplazamiento de fase 0 entre el término |0000> y el término |1000>. Cada estado de la base de Fourier $|\\phi_y\\rangle$ se compone de términos cuya fase se acumula a un ritmo de $2\\pi y/N$, lo que significa que, ordenados de la forma habitual, cada término de la superposición tiene una fase de $2\\pi y/N$ mayor que el término anterior. Así, en el punto medio $N/2$, queremos que la fase $2\\pi y/N * N/2$ sea un múltiplo entero de $2\\pi$. Esto ocurre cuando $y$ es par.\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "¿Qué superposición de estados computacional correspondería a una teoría cuántica de campos con picos en cada número binario impar?\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Respuesta\">\n",
        "    Si se tomara la QFT del estado $\\psi = |0\\rangle - |N/2\\rangle$, entonces se verían picos en cada estado binario impar.\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "af84bf95-c211-4a58-a851-1e827a42fcdb",
      "metadata": {},
      "source": [
        "<span id=\"break-down-the-qft-algorithm\" />\n",
        "\n",
        "## Desglosar el algoritmo QFT\n",
        "\n",
        "Ahora que hemos adquirido más intuición sobre la relación entre los estados de los qubits en la base computacional y la base de Fourier, profundicemos en el algoritmo QFT en sí. En otras palabras, ¿qué puertas implementamos realmente en el ordenador cuántico para lograr esta transformación?\n",
        "\n",
        "Empecemos poco a poco, con un solo qubit. Entonces, eso significa que tendremos dos estados base. QFT $_2$ transforma los estados de base computacional $|0\\rangle$ y $|1\\rangle$ en estados de base de Fourier $\\phi_0$ y $\\phi_1$ :\n",
        "\n",
        "$|\\phi_0\\rangle = \\frac{1}{\\sqrt{2}}(|0\\rangle + |1\\rangle)$\n",
        "\n",
        "$|\\phi_1\\rangle = \\frac{1}{\\sqrt{2}}(|0\\rangle - |1\\rangle)$\n",
        "\n",
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Comprueba tu comprensión\n",
        "\n",
        "Utiliza la ecuación de la teoría cuántica de campos de la sección anterior para verificar estos dos estados de base de Fourier mencionados anteriormente.\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Respuesta\">\n",
        "    La fórmula general de QFT es:\n",
        "\n",
        "    $ | \\phi_y \\rangle =  \\frac{1}{\\sqrt{N}}\\sum_{x=0}^{N-1}\\omega_N^{y x} \\vert x \\rangle$\n",
        "\n",
        "    Para un único qubit ( $n=1$ ), $N=2^n=2$, y $\\omega_N^{xy} = e^{2\\pi i \\frac {y x}{2}}$. Así pues, tenemos\n",
        "\n",
        "    $ | \\phi_0 \\rangle = \\frac{1}{\\sqrt{2}}(e^{2\\pi i \\frac {0 \\times 0}{2}}|0\\rangle + e^{2\\pi i \\frac {0 \\times 1}{2}}|1\\rangle) = \\frac{1}{\\sqrt{2}}(|0\\rangle + |1\\rangle)$\n",
        "\n",
        "    $ | \\phi_1 \\rangle = \\frac{1}{\\sqrt{2}}(e^{2\\pi i \\frac {1 \\times 0}{2}}|0\\rangle + e^{2\\pi i \\frac {1 \\times 1}{2}}|1\\rangle) = \\frac{1}{\\sqrt{2}}(|0\\rangle - |1\\rangle)$\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "Echa un vistazo a esas dos ecuaciones. Es posible que ya conozcas una puerta cuántica que puede utilizarse para aplicar esta transformación. Es decir, existe una puerta que transforma los estados de base computacional $|0\\rangle$ y $|1\\rangle$ en los respectivos estados de base de Fourier $|\\phi_0\\rangle$ y $|\\phi_1\\rangle$. ¡Es una puerta Hadamard! Esto queda aún más claro si introducimos una representación matricial de la operación QFT $_N$ :\n",
        "\n",
        "$ \\text{QFT}_N = \\frac{1}{\\sqrt{N}} \\sum_{x=0}^{N-1} \\sum_{y=0}^{N-1} \\omega_N^{xy} \\vert x \\rangle \\langle y \\vert$\n",
        "\n",
        "Si no estás familiarizado con esta notación para expresar un operador cuántico, ¡no pasa nada! Es una forma de representar una matriz $N \\times N$, donde $x$ y $y$ indexan las columnas y filas de la matriz, de $0$ a $N-1$, y $\\omega_N^{xy}$ es el valor de esa entrada concreta. Así, la entrada de la columna 0 y la fila 2, por ejemplo, sería $\\omega_N^{0,2} = e^{2 \\pi i \\frac{0 \\times 2}{N}} = 1$.\n",
        "\n",
        "En esta representación, cada uno de los estados base computacionales se asocia a uno de los vectores base:\n",
        "\n",
        "$$|0\\rangle =\n",
        "\\begin{pmatrix}\n",
        "1 \\\\ 0 \\\\ \\vdots \\\\ 0\n",
        "\\end{pmatrix},\n",
        "|1\\rangle =\n",
        "\\begin{pmatrix}\n",
        "0 \\\\ 1 \\\\ \\vdots \\\\ 0\n",
        "\\end{pmatrix},\n",
        "|N-1\\rangle =\n",
        "\\begin{pmatrix}\n",
        "0 \\\\ 0 \\\\ \\vdots \\\\ 1\n",
        "\\end{pmatrix}.\n",
        "$$\n",
        "\n",
        "Si desea conocer más a fondo esta representación, consulte la lección de John Watrous sobre sistemas múltiples en el curso [Fundamentos de la información cuántica](/learning/courses/basics-of-quantum-information/multiple-systems/introduction).\n",
        "\n",
        "Intentemos construir la matriz para QFT $_4$. Utilizando la fórmula anterior, encontramos que\n",
        "\n",
        "$\\text{QFT}_4 = \\frac{1}{2} \\begin{pmatrix}     1 & 1 & 1 & 1 \\\\     1 & i & -1 & -i \\\\     1 & -1 & 1 & -1 \\\\     1 & -i & -1 & i \\\\ \\end{pmatrix} $\n",
        "\n",
        "Para implementar esta matriz en un ordenador cuántico, tendremos que averiguar qué combinación de puertas aplicadas a qué qubits nos dará una transformación unitaria que coincida con la matriz anterior. Ya conocemos una de las puertas que serán necesarias: la Hadamard. Otra puerta que necesitaremos es la puerta de fase controlada, que aplica una fase relativa $\\alpha$ al estado del qubit objetivo, siempre que el qubit de control esté en el estado $|1\\rangle$. En forma de matriz esto se ve así:\n",
        "\n",
        "$\\text{CP}_\\alpha = \\begin{pmatrix}     1 & 0 & 0 & 0 \\\\     0 & 1 & 0 & 0 \\\\     0 & 0 & 1 & 0 \\\\     0 & 0 & 0 & e^{i\\alpha} \\\\ \\end{pmatrix} $\n",
        "\n",
        "Dado que sólo se cambia el estado $|11\\rangle$, en realidad no importa qué qubit se considera el \"control\" y cuál es el \"objetivo\" El resultado será el mismo en ambos casos.\n",
        "\n",
        "Por último, también necesitaremos algunas puertas SWAP. Una puerta SWAP intercambia los estados de dos qubits. Eso parece:\n",
        "\n",
        "$\\text{SWAP}_\\alpha = \\begin{pmatrix}     1 & 0 & 0 & 0 \\\\     0 & 0 & 1 & 0 \\\\     0 & 1 & 0 & 0 \\\\     0 & 0 & 0 & 1 \\\\ \\end{pmatrix} $\n",
        "\n",
        "El procedimiento para construir un circuito QFT $_{2^m}$ en qubits $m$ es iterativo - primero se aplica la QFT $_{2^{m-1}}$ a los qubits $1$ a $m-1$, luego se añaden algunas puertas entre el qubit $0$ y los otros qubits $m-1$. Pero para aplicar la QFT $_{2^{m-1}}$, primero hay que aplicar la QFT $_{2^{m-2}}$ a los qubits 2 a $m-1$, y luego añadir algunas puertas entre el qubit 1 y los qubits restantes $2$ a $m-1$. Es como una muñeca rusa anidada: cada muñeca añade un factor de dos en la dimensión del circuito QFT, con la muñeca más pequeña en el centro, siendo QFT $_2$, o la puerta de Hadamard.\n",
        "\n",
        "Para meter un muñeco dentro del siguiente de mayor tamaño, aumentando así la dimensión de la QFT en un factor de dos, se sigue siempre el mismo procedimiento:\n",
        "\n",
        "1. En primer lugar, aplique la QFT $_{2^{m-1}}$ a los qubits de la parte inferior $m-1$. Esta es tu \"muñeca más pequeña\" del juego de muñecas rusas que pronto meterás dentro de la siguiente muñeca más grande.\n",
        "2. Utilice el qubit siguiente como control y aplique puertas de fase controlada a cada uno de los qubits inferiores $m-1$, con fases a los estados de base estándar de cada uno de los qubits restantes $m-1$.\n",
        "3. Realiza un Hadamard en el mismo qubit superior que se utilizó como control en las puertas de fase.\n",
        "4. Utiliza las puertas SWAP para permutar el orden de los qubits de modo que el bit menos significativo (superior) se convierta en el más significativo (inferior), y todos los demás se desplacen uno hacia arriba.\n",
        "\n",
        "Ya hemos estado utilizando la función `QFTGate` de la librería de circuitos Qiskit, pero ahora vamos a echar un vistazo al interior de algunas de estas puertas QFT para verificar el procedimiento anterior. Podemos hacerlo con `decompose()`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "6de41e8b-2900-4600-bd35-96df80b1b409",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/qft/extracted-outputs/6de41e8b-2900-4600-bd35-96df80b1b409-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 15,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "qc = QuantumCircuit(1)\n",
        "qc.compose(QFTGate(1), inplace=True)\n",
        "qc.decompose().draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "066a1c6b-864e-4cf3-b9f1-2751b9b00998",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/qft/extracted-outputs/066a1c6b-864e-4cf3-b9f1-2751b9b00998-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 16,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "qc = QuantumCircuit(2)\n",
        "qc.compose(QFTGate(2), inplace=True)\n",
        "qc.decompose().draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "dffb70da-0107-4aeb-b433-20f4b82f6abf",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/qft/extracted-outputs/dffb70da-0107-4aeb-b433-20f4b82f6abf-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 17,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "qc = QuantumCircuit(3)\n",
        "qc.compose(QFTGate(3), inplace=True)\n",
        "qc.decompose().draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "b3375193-b230-4dda-a676-ae350c3a9b93",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/qft/extracted-outputs/b3375193-b230-4dda-a676-ae350c3a9b93-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 18,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "qc = QuantumCircuit(4)\n",
        "qc.compose(QFTGate(4), inplace=True)\n",
        "qc.decompose().draw(\"mpl\")"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "0f29ce85-e9a8-4445-b3ac-5625bdca9c8a",
      "metadata": {},
      "source": [
        "Así que, esperemos que a partir de las cuatro primeras QFT puedas empezar a ver cómo cada una está anidada dentro de la siguiente más grande. Sin embargo, te habrás dado cuenta de que algunas de las puertas de fase no son exactamente como se indica en el procedimiento que hemos descrito anteriormente, y los SWAPs no aparecen después de cada subrutina, sino que se encuentran al final de la QFT completa. Esto nos ahorra puertas innecesarias, que harían que el circuito tardara más y fuera más propenso a errores. En lugar de implementar el SWAP después de cada muñeca anidada, el circuito realiza un seguimiento de dónde *debería* estar cada estado de qubit y ajusta los qubits a los que está aplicando las puertas de fase en consecuencia. Luego, un último conjunto de SWAPs al final pone todo en su sitio.\n",
        "\n",
        "<span id=\"apply-the-qft-phase-estimation\" />\n",
        "\n",
        "## Aplicar el QFT: Estimación de fase\n",
        "\n",
        "Veamos cómo puede utilizarse la QFT para resolver un problema útil en computación cuántica. El cálculo de la transformada cuántica de Fourier inversa es un paso necesario en un algoritmo conocido como Estimación Cuántica de Fase (QPE), que es a su vez una subrutina en muchos otros algoritmos, incluida la \"joya de la corona\" de los algoritmos cuánticos, el algoritmo de factorización de Shor.\n",
        "\n",
        "El objetivo del QPE es estimar los valores propios de un operador unitario. Los operadores unitarios son omnipresentes en la computación cuántica y, a menudo, encontrar los valores propios de sus vectores propios asociados es un paso necesario en un algoritmo más amplio. Dependiendo del problema, un valor propio puede representar una energía de un Hamiltoniano en un problema de tipo simulación, puede ayudarnos a encontrar factores primos de un número en el algoritmo de Shor o puede contener otra información esencial. QPE es una de las subrutinas más importantes y utilizadas en computación cuántica.\n",
        "\n",
        "¿Qué tiene esto que ver con la transformada cuántica de Fourier? Bien, como recordarás, cualquier valor propio $\\lambda$ de un operador unitario tiene una magnitud $|\\lambda| = 1$. Así que podemos escribir cada valor propio como un número complejo con magnitud uno:\n",
        "\n",
        "$\\lambda = e^{2\\pi i \\theta}$\n",
        "\n",
        "donde $\\theta$ es un número real entre 0 y 1. Si desea más información sobre matrices unitarias, consulte [la lección de John Watrous sobre el tema](/learning/courses/basics-of-quantum-information/multiple-systems/quantum-information) en Fundamentos de la información cuántica.\n",
        "\n",
        "Nótese que $\\lambda$ es *periódica* en $\\theta$. Esto ya podría sugerirte que una QFT podría estar implicada, puesto que vimos lo útiles que son las QFT para analizar funciones periódicas. A continuación, recorreremos el algoritmo y veremos con precisión cómo entra en juego la QFT.\n",
        "\n",
        "<span id=\"how-qpe-works\" />\n",
        "\n",
        "### Cómo funciona QPE\n",
        "\n",
        "En primer lugar, empezaremos con el algoritmo QPE más sencillo, que estima aproximadamente la fase con un solo dígito binario de precisión. En otras palabras, este algoritmo puede distinguir entre $\\theta = 0 $ y $\\theta = 1/2$, pero no puede hacerlo mejor. Aquí está el diagrama del circuito:\n",
        "\n",
        "![Diagrama de circuito del algoritmo QPE para un único qubit de datos. Se aplica un Hadamard al qubit de datos. A continuación, el algoritmo utiliza otro qubit ayudante, sobre el que se aplica una puerta controlada-U, con el qubit de datos como control. Tras otro Hadamard en el qubit 0, se miden los qubits.](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/qft/QPE1qubit.avif)\n",
        "\n",
        "Los qubits se preparan en el estado $|\\pi_0\\rangle = |\\psi\\rangle|0\\rangle$, donde el qubit $0$ está en el estado $|0\\rangle$ y los qubits restantes están en el estado $|\\psi\\rangle$, que es un estado propio de $U$. Después del primer Hadamard, el estado de los qubits pasa a ser:\n",
        "\n",
        "$|\\pi_1\\rangle = \\frac{1}{\\sqrt{2}}|\\psi\\rangle (|0\\rangle + |1\\rangle)$\n",
        "\n",
        "La siguiente puerta es una puerta \"controlada- $U$ \". Esto aplica la operación unitaria $U$ a los qubits inferiores que están en el estado $|\\psi\\rangle$ si el qubit 0 está en el estado $|1\\rangle$, pero no hace nada a $|\\psi\\rangle$ si el qubit 0 está en el estado $|0\\rangle$. Esto transforma los qubits al estado:\n",
        "\n",
        "$|\\pi_2\\rangle = \\frac{1}{\\sqrt{2}}( |\\psi\\rangle|0\\rangle + e^{2\\pi i \\theta}|\\psi\\rangle|1\\rangle)$\n",
        "$=  \\frac{1}{\\sqrt{2}}|\\psi\\rangle (|0\\rangle + e^{2\\pi i \\theta}|1\\rangle)$\n",
        "\n",
        "Algo extraño acaba de suceder: la puerta controlada- $U$ sólo utiliza el qubit $0$ como qubit de control, por lo que se podría pensar que esta puerta no cambiaría el estado del qubit 0 en absoluto. Pero de alguna manera, ¡lo hace! Aunque la operación se haya aplicado a los qubits inferiores, el efecto global de la puerta es cambiar la fase del qubit $0$. Esto se conoce como \"mecanismo de retroceso de fase\" y se utiliza en muchos algoritmos cuánticos, incluidos los algoritmos de Deutsch-Josza y Grover. Si desea obtener más información sobre el mecanismo phase-kickback, consulte la lección de John Watrous sobre [Algoritmos cuánticos de consulta](/learning/courses/fundamentals-of-quantum-algorithms/quantum-query-algorithms/deutsch-algorithm) en Fundamentos de los algoritmos cuánticos.\n",
        "\n",
        "Después del retroceso de fase, aplicamos un Hadamard más al qubit $0$, lo que da como resultado el estado:\n",
        "\n",
        "$|\\pi_3\\rangle = |\\psi\\rangle ( \\frac{1+e^{2\\pi i \\theta}}{2} |0\\rangle + \\frac{1 - e^{2\\pi i \\theta}}{2}|1\\rangle) = |\\psi\\rangle ( \\cos(\\pi\\theta) |0\\rangle - i \\sin(\\pi\\theta)|1\\rangle)$\n",
        "\n",
        "Así, cuando midamos el qubit $0$ al final, mediremos $|0\\rangle$ con un 100% de certeza si $\\theta = 0$ y mediremos $|1\\rangle$ con un 100% de certeza si $\\theta = \\frac{1}{2}$ (y si nuestro ordenador cuántico es perfecto, sin ruido). Si $\\theta$ es algo distinto de esto, la medición final es sólo probabilística y sólo nos dice una parte.\n",
        "\n",
        "<span id=\"qpe-with-more-precision-more-qubits\" />\n",
        "\n",
        "### QPE con mayor precisión: más qubits\n",
        "\n",
        "Podemos ampliar este sencillo concepto a un algoritmo más complicado con una precisión arbitraria. Si en lugar de utilizar sólo el qubit $0$ para medir la fase, utilizamos $m$ qubits $0$ a $m-1$, y entonces podremos estimar la fase con $m$ bits de precisión. Veamos cómo funciona:\n",
        "\n",
        "![Esquema del algoritmo QPE para múltiples qubits. Los Hadamards se aplican a los qubits de datos 0 a m-1. A continuación, se aplica una serie de compuertas controladas-U a los m qubits ayudantes. Por último, se aplica una QFT inversa a los qubits y se miden.](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/qft/QPE_withpi.avif)\n",
        "\n",
        "Este circuito QPE más preciso comienza igual que la versión de un solo bit: Se aplican Hadamards a los primeros $m$ qubits, y los qubits restantes se preparan en el estado $|\\psi\\rangle$, creando el estado:\n",
        "\n",
        "$|\\pi_1\\rangle = \\frac{1}{2^{m/2}}|\\psi\\rangle(|0\\rangle+|1\\rangle)(|0\\rangle+|1\\rangle)...(|0\\rangle+|1\\rangle)$\n",
        "\n",
        "Ahora se aplican los unitarios controlados. Qubit $0$ es el control para el mismo unitario $U$ que antes. Pero ahora, el qubit $1$ es el control para el unitario $U^2$, que es simplemente $U$ aplicado dos veces. Así, el valor propio de $U^2$ es $e^{2*2\\pi i \\theta}$. En general, cada qubit $k$ desde 0 hasta $m-1$ será el control del unitario $U^{2^k}$. Esto significa que cada uno de estos qubits experimentará un retroceso de fase de $e^{2^k*2\\pi i \\theta}$. Esto resulta en el estado:\n",
        "\n",
        "$|\\pi_2\\rangle = |\\psi\\rangle \\otimes \\frac{1}{2^{m/2}} (|0\\rangle+e^{2^{m-1}2\\pi i \\theta}|1\\rangle)(|0\\rangle+e^{2^{m-2}2\\pi i \\theta}|1\\rangle)...(|0\\rangle+e^{2\\pi i \\theta}|1\\rangle)$\n",
        "\n",
        "Esto puede reescribirse como una suma sobre los estados base computacionales:\n",
        "\n",
        "$|\\pi_2\\rangle = |\\psi\\rangle \\otimes \\frac{1}{2^{m/2}} \\sum_{k=0}^{2^{m}-1} e^{2\\pi i k \\theta} |k\\rangle $\n",
        "\n",
        "¿Le suena la suma? ¡Es un QFT! Recordemos la ecuación de una transformada cuántica de Fourier:\n",
        "\n",
        "$ \\text{QFT}_{2^m}| y \\rangle =  \\frac{1}{\\sqrt{2^m}}\\sum_{x=0}^{2^m-1}\\omega_{2^m}^{y x} \\vert x \\rangle$\n",
        "\n",
        "Entonces, si la fase $\\theta = y/2^m$ para algún entero $y$ entre $0$ y $2^m-1$, entonces tomando la QFT inversa de este estado resultará en el estado:\n",
        "\n",
        "$|\\pi_3\\rangle = |\\psi\\rangle \\otimes |y\\rangle $\n",
        "\n",
        "y de $|y\\rangle$, podemos deducir $\\theta$.\n",
        "\n",
        "Sin embargo, si $\\theta/2^m$ *no* es un múltiplo entero, la QFT inversa sólo *aproximará* $\\theta$. Lo bien que se aproxime a $\\theta$ será probabilístico, lo que significa que no siempre obtendremos la mejor aproximación, pero estará bastante cerca, y cuantos más qubits $m$ utilices, mejor será la aproximación que obtengas. Para saber cómo cuantificar esta aproximación de $\\theta$, consulte la lección de John Watrous sobre [Estimación de fase y factorización](/learning/courses/fundamentals-of-quantum-algorithms/phase-estimation-and-factoring/phase-estimation-procedure) en Fundamentos de algoritmos cuánticos.\n",
        "\n",
        "<span id=\"conclusion\" />\n",
        "\n",
        "### Conclusión\n",
        "\n",
        "Este módulo ofreció una visión general de lo que es una QFT, cómo se implementa en un ordenador cuántico y lo útil que puede ser para resolver problemas. Ya le dimos una idea de su utilidad cuando vimos cómo puede utilizarse en la estimación cuántica de fase para conocer los valores propios de una matriz unitaria.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "33b2c0e7-b48a-4426-8472-ad9a52bf48ea",
      "metadata": {},
      "source": [
        "<span id=\"critical-concepts\" />\n",
        "\n",
        "### Conceptos fundamentales\n",
        "\n",
        "* La transformada cuántica de Fourier es el análogo cuántico de la transformada discreta de Fourier.\n",
        "* La QFT es un ejemplo de transformación de bases.\n",
        "* El procedimiento de estimación cuántica de fase se basa en el mecanismo de retroceso de fase de las operaciones unitarias controladas, así como en una QFT inversa.\n",
        "* QFT y QPE son subrutinas ampliamente utilizadas en numerosos algoritmos cuánticos.\n",
        "\n",
        "<span id=\"questions\" />\n",
        "\n",
        "## Preguntas\n",
        "\n",
        "<span id=\"true/false\" />\n",
        "\n",
        "### True/False\n",
        "\n",
        "1. T/F La transformada cuántica de Fourier es el análogo cuántico de la transformada discreta de Fourier (DFT) clásica.\n",
        "2. T/F QFT puede implementarse utilizando sólo puertas Hadamard y CNOT.\n",
        "3. T/F La QFT es un componente clave del algoritmo de Shor.\n",
        "4. T/F La salida de la Estimación Cuántica de Fase es un estado cuántico que representa el vector propio del operador.\n",
        "5. T/F QPE requiere el uso de la transformada cuántica de Fourier inversa (QFT $^\\dag$ ).\n",
        "6. T/F En QPE, si la fase $\\phi$ es representable exactamente con $n$ bits, el algoritmo da el resultado correcto con probabilidad 1.\n",
        "\n",
        "<span id=\"short-answers\" />\n",
        "\n",
        "### Respuestas breves\n",
        "\n",
        "1. ¿Cuántos qubits se necesitan para realizar una QFT en un sistema con $2^n$ puntos de datos?\n",
        "2. ¿Se puede utilizar la QFT en un estado que no sea un estado base de cálculo? Si es así, ¿qué ocurre?\n",
        "3. ¿Cómo afecta el número de qubits de control utilizados en QPE a la resolución de la estimación de fase resultante?\n",
        "\n",
        "<span id=\"problems\" />\n",
        "\n",
        "### Problemas\n",
        "\n",
        "1. Utilice la multiplicación de matrices para verificar que los pasos del algoritmo QFT dan como resultado la matriz $\\text{QFT}_4$ :\n",
        "\n",
        "$$\n",
        "\\text{QFT}_4 = \\frac{1}{2}\n",
        "\\begin{pmatrix}\n",
        "    1 & 1 & 1 & 1 \\\\\n",
        "    1 & i & -1 & -i \\\\\n",
        "    1 & -1 & 1 & -1 \\\\\n",
        "    1 & -i & -1 & i \\\\\n",
        "\\end{pmatrix}\n",
        "$$\n",
        "\n",
        "(¡No hace falta que lo hagas a mano!)\n",
        "\n",
        "<span id=\"challenge-problems\" />\n",
        "\n",
        "### Problemas desafiantes\n",
        "\n",
        "1. Hacer un estado de cuatro qubits que sea una superposición igual de todas las bases computacionales impares: $|\\psi\\rangle = |0001\\rangle + |0011\\rangle + |0101\\rangle + |0111\\rangle +|1001\\rangle +|1011\\rangle +|1101\\rangle +|1111\\rangle$. A continuación, realice una QFT en el estado. ¿Cuál es el estado resultante? Explica por qué tu resultado tiene sentido, utilizando tus conocimientos de las transformadas de Fourier.\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"
    },
    "hours": 2
  },
  "nbformat": 4,
  "nbformat_minor": 5
}