{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "6581d769-6f75-4b26-90e3-39c28f22a74c",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Solucionador de valores propios cuánticos variacionales\"\n",
        "description: \"Esta introducción al VQE abarca sus componentes, una implementación básica y analiza los factores que determinan su eficiencia y utilidad.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore ansä IIIZ IZII IIZI ZIII IZIZ IIZZ ZIIZ IZZI ZZII ZIZI XXYY YYXX ansätze ansatze infty IZZX XIYX ZZXZ ZZZX IIXX IIXZ IZXZ IXXZ XZXZ ZIXZ */}\n",
        "\n",
        "<span id=\"the-variational-quantum-eigensolver-vqe\" />\n",
        "\n",
        "# El solucionador de valores propios cuánticos variacionales (VQE)\n",
        "\n",
        "En esta lección se presentará el eigensolver cuántico variacional, se explicará su importancia como algoritmo fundacional de la computación cuántica y también se explorarán sus puntos fuertes y débiles. Es probable que la VQE por sí sola, sin métodos complementarios, no sea suficiente para los cálculos cuánticos modernos a escala comercial. No obstante, es importante como método híbrido clásico-cuántico arquetípico, y es una base importante sobre la que se construyen muchos algoritmos más avanzados.\n",
        "\n",
        "Este vídeo ofrece una visión general del VQE y de los factores que afectan a su eficacia. El texto siguiente añade más detalles e implementa VQE utilizando Qiskit.\n",
        "\n",
        "<IBMVideo id=\"134325519\" title=\"En este vídeo, Chris Porter describe los componentes clave del eigensolver variacional cuántico. Estos incluyen el Hamiltoniano, el ansatz, un optimizador clásico y un estimador. También habla de los factores que afectan a la eficacia del VQE.\" />\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "21115450-3f01-4e19-860d-48e31f401b1f",
      "metadata": {},
      "source": [
        "<span id=\"1-what-is-vqe\" />\n",
        "\n",
        "## 1. ¿Qué es VQE?\n",
        "\n",
        "El solucionador de valores propios cuántico variacional es un algoritmo que utiliza la computación clásica y la cuántica de forma conjunta para realizar una tarea. El cálculo del VQE consta de cuatro componentes principales:\n",
        "\n",
        "* **Un operador** : A menudo un Hamiltoniano, que llamaremos $H$, que describe una propiedad de su sistema que desea optimizar. Otra forma de decirlo es que se busca el vector propio de este operador que corresponde al valor propio mínimo. A menudo llamamos a ese vector propio \"estado básico\".\n",
        "* **Un \"ansatz** \" (palabra alemana que significa \"aproximación\"): se trata de un circuito cuántico que prepara un estado cuántico aproximado al vector propio que se busca. En realidad, el ansatz es una familia de circuitos cuánticos, porque algunas de las puertas del ansatz están parametrizadas, es decir, reciben un parámetro que podemos variar. Esta familia de circuitos cuánticos puede preparar una familia de estados cuánticos aproximados al estado básico.\n",
        "* **Un estimador** : un método para estimar el valor esperado del operador $H$ sobre el estado cuántico variacional actual. A veces, lo que realmente nos importa es simplemente este valor esperado, al que llamamos función de coste. A veces nos interesa una función más compleja que, no obstante, puede expresarse a partir de uno o varios valores esperados.\n",
        "* **Un optimizador clásico** : un algoritmo que varía los parámetros para intentar minimizar la función de coste.\n",
        "\n",
        "Veamos cada uno de estos componentes con más detalle.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7dbd605e-a440-446b-bc05-e2e301796bf2",
      "metadata": {},
      "source": [
        "<span id=\"11-the-operator-hamiltonian\" />\n",
        "\n",
        "### 1.1 El operador (hamiltoniano)\n",
        "\n",
        "En el núcleo de un problema VQE hay un operador que describe un sistema de interés. Supondremos aquí que el valor propio más bajo y el vector propio correspondiente de este operador son útiles para algún fin científico o empresarial. Por ejemplo, un Hamiltoniano químico que describa una molécula, de forma que el valor propio más bajo del operador corresponda a la energía del estado básico de la molécula, y el estado propio correspondiente describa la geometría o la configuración electrónica de la molécula. O bien el operador podría describir un coste de un determinado proceso a optimizar, y los estados propios podrían corresponder a rutas o prácticas. En algunos campos, como la física, un \"hamiltoniano\" casi siempre se refiere a un operador que describe la energía de un sistema físico. Pero en informática cuántica, es habitual ver que los operadores cuánticos que describen un problema comercial o logístico también se denominan \"hamiltonianos\". Adoptaremos aquí esa convención.\n",
        "\n",
        "![Una imagen de orbitales atómicos y otra de una red con numerosos nodos y conexiones entre ellos.](https://quantum.cloud.ibm.com/learning/images/courses/quantum-diagonalization-algorithms/vqe/vqe-fig1.avif)\n",
        "\n",
        "Mapear un problema físico o de optimización a qubits suele ser una tarea no trivial, pero esos detalles no son el objetivo de este curso. En [Quantum computing in practice (La computación cuántica en la práctica](/learning/courses/quantum-computing-in-practice) ) encontrará un análisis general de la asignación de un problema a un operador cuántico. En [Quantum Chemistry with VQE (Química cuántica con VQE](/learning/courses/quantum-chem-with-vqe) ) se ofrece un análisis más detallado de la conversión de problemas químicos en operadores cuánticos.\n",
        "\n",
        "Para los propósitos de este curso, asumiremos que la forma del Hamiltoniano es conocida. Por ejemplo, un Hamiltoniano para una molécula simple de hidrógeno (bajo ciertas suposiciones de espacio activo, y utilizando el mapeador de Jordan-Wigner) es:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "89b425d8-f54f-4f98-996e-c303f77edb25",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.quantum_info import SparsePauliOp\n",
        "\n",
        "hamiltonian = SparsePauliOp(\n",
        "    [\n",
        "        \"IIII\",\n",
        "        \"IIIZ\",\n",
        "        \"IZII\",\n",
        "        \"IIZI\",\n",
        "        \"ZIII\",\n",
        "        \"IZIZ\",\n",
        "        \"IIZZ\",\n",
        "        \"ZIIZ\",\n",
        "        \"IZZI\",\n",
        "        \"ZZII\",\n",
        "        \"ZIZI\",\n",
        "        \"YYYY\",\n",
        "        \"XXYY\",\n",
        "        \"YYXX\",\n",
        "        \"XXXX\",\n",
        "    ],\n",
        "    coeffs=[\n",
        "        -0.09820182 + 0.0j,\n",
        "        -0.1740751 + 0.0j,\n",
        "        -0.1740751 + 0.0j,\n",
        "        0.2242933 + 0.0j,\n",
        "        0.2242933 + 0.0j,\n",
        "        0.16891402 + 0.0j,\n",
        "        0.1210099 + 0.0j,\n",
        "        0.16631441 + 0.0j,\n",
        "        0.16631441 + 0.0j,\n",
        "        0.1210099 + 0.0j,\n",
        "        0.17504456 + 0.0j,\n",
        "        0.04530451 + 0.0j,\n",
        "        0.04530451 + 0.0j,\n",
        "        0.04530451 + 0.0j,\n",
        "        0.04530451 + 0.0j,\n",
        "    ],\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "14c20ee9-51ad-4cc9-be36-7a33fae6c56a",
      "metadata": {},
      "source": [
        "Nótese que en el Hamiltoniano anterior, hay términos como `ZZII` y `YYYY` que no conmutan entre sí. Es decir, para evaluar `ZZII`, necesitaríamos medir el operador Z de Pauli en el qubit 3 (entre otras mediciones). Pero para evaluar `YYYY`, necesitamos medir el operador Y de Pauli en ese mismo qubit, el qubit 3. Existe una relación de incertidumbre entre los operadores Y y Z en el mismo qubit; no podemos medir ambos operadores al mismo tiempo. Volveremos sobre este punto más adelante y, de hecho, a lo largo de todo el curso.\n",
        "El hamiltoniano anterior es un operador matricial $16\\times 16$. Diagonalizar el operador para encontrar su eigenvalor de menor energía no es difícil.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "9703b61e-ff90-4e94-8999-242d0c6766c0",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The ground state energy is  -1.1459778447627311 hartrees\n"
          ]
        }
      ],
      "source": [
        "import numpy as np\n",
        "\n",
        "A = np.array(hamiltonian)\n",
        "eigenvalues, eigenvectors = np.linalg.eigh(A)\n",
        "print(\"The ground state energy is \", min(eigenvalues), \"hartrees\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "01a76c42-29e7-455a-acd6-55490f36121b",
      "metadata": {},
      "source": [
        "Los eigensolvers clásicos de fuerza bruta no pueden escalar para describir las energías o geometrías de sistemas muy grandes de átomos, como medicamentos o proteínas. VQE es uno de los primeros intentos de aprovechar la computación cuántica en este problema.\n",
        "\n",
        "En esta lección nos encontraremos con Hamiltonianos mucho más grandes que el anterior. Pero sería inútil ampliar los límites de lo que VQE puede hacer, antes de que presentemos algunas de las herramientas más avanzadas que pueden aumentar o sustituir a VQE, más adelante en este curso.\n",
        "\n",
        "<span id=\"12-ansatz\" />\n",
        "\n",
        "### 1.2 Ansatz\n",
        "\n",
        "La palabra \"ansatz\" significa \"aproximación\" en alemán. El plural correcto en alemán es \"ansätze\", aunque a menudo se ve \"ansatzes\" o \"ansatze\". En el contexto de VQE, un ansatz es el circuito cuántico que se utiliza para crear una función de onda multiqubit que se aproxime lo más posible al estado fundamental del sistema que se está estudiando y que, por tanto, produzca el valor de expectativa más bajo del operador. Este circuito cuántico contendrá parámetros variacionales (a menudo reunidos en el vector de variables $\\vec{\\theta}$ ).\n",
        "\n",
        "![Una imagen de un circuito cuántico con parámetros variacionales etiquetados como «theta».](https://quantum.cloud.ibm.com/learning/images/courses/quantum-diagonalization-algorithms/vqe/vqe-fig2.avif)\n",
        "\n",
        "Se elige un conjunto inicial de valores $\\vec{\\theta_0}$ de los parámetros variacionales. Llamaremos a la operación unitaria del ansatz sobre el circuito $U_{\\text{var}}(\\vec{\\theta_0})$. Por defecto, todos los qubits de los ordenadores cuánticos IBM® se inicializan en el estado $|0\\rangle$. Cuando se ejecuta el circuito, el estado de los qubits será\n",
        "\n",
        "$$\n",
        "|\\psi(\\vec{\\theta_0})\\rangle=U_{\\text{var}}(\\vec{\\theta_0})|0\\rangle^{\\otimes N}\n",
        "$$\n",
        "\n",
        "Si todo lo que necesitáramos fuera la energía más baja (utilizando el lenguaje de los sistemas físicos), podríamos estimarla simplemente midiendo la energía muchas veces y tomando la más baja. Pero normalmente también queremos la configuración que produce esa energía o valor propio más bajo. Así que el siguiente paso es la estimación del valor de expectativa del hamiltoniano, que se consigue mediante mediciones cuánticas. Hay mucho en juego. Pero podemos entender este proceso cualitativamente observando que la probabilidad $P_j$ de medir una energía $E_j$ (de nuevo utilizando el lenguaje de los sistemas físicos) está relacionada con el valor de expectativa por:\n",
        "\n",
        "$$\n",
        "\\langle \\psi(\\vec{\\theta_0}) |H|\\psi (\\vec{\\theta_0}) \\rangle\n",
        "$$\n",
        "\n",
        "La probabilidad $P_j$ también está relacionada con el solapamiento entre el estado propio $|\\phi_j\\rangle$ y el estado actual del sistema $|\\psi(\\vec{\\theta_0})\\rangle$ :\n",
        "\n",
        "$$\n",
        "P_j=|\\langle \\phi_j|\\psi(\\vec{\\theta_0})\\rangle|^2 = |\\langle \\phi_j|U_{\\text{var}}(\\vec{\\theta_0})|0\\rangle^{\\otimes N}|^2\n",
        "$$\n",
        "\n",
        "Así, realizando muchas mediciones de los operadores de Pauli que componen nuestro Hamiltoniano, podemos estimar el valor de expectativa del Hamiltoniano en el estado actual del sistema $|\\psi(\\vec{\\theta_0})\\rangle$. El siguiente paso es variar los parámetros $\\vec{\\theta}$ y tratar de acercarnos más al estado de mínima energía (tierra) del sistema. Debido a los parámetros variacionales de la ansatz, a veces se la denomina **forma variacional**.\n",
        "\n",
        "Antes de pasar a ese proceso variacional, tenga en cuenta que a menudo es útil comenzar su estado a partir de un estado de \"buena suposición\". Puede que conozcas lo suficiente tu sistema como para hacer una estimación inicial mejor que $|0\\rangle^{\\otimes N}$. Por ejemplo, es común inicializar qubits al estado Hartree-Fock en aplicaciones químicas. Esta suposición inicial, que no contiene ningún parámetro variacional, se denomina **estado de referencia**. Llamemos al circuito cuántico utilizado para crear el estado de referencia $U_{ref}$. Siempre que sea importante distinguir el estado de referencia del resto del ansatz, utilicemos: $U_{\\text{ansatz}}(\\vec{\\theta}) =U_{\\text{var}}(\\vec{\\theta})U_{\\text{ref}}.$ Equivalentemente\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        "|\\psi_{\\text{ref}}\\rangle&=U_{\\text{ref}}|0\\rangle^{\\otimes N}\\\\\n",
        "|\\psi_{\\text{ansatz}}(\\vec{\\theta})\\rangle&=U_{var}(\\vec{\\theta})|\\psi_{\\text{ref}}\\rangle = U_{\\text{var}}(\\vec{\\theta})U_{\\text{ref}}|0\\rangle^{\\otimes N}.\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b7f66a1f-93cb-47ee-81c3-6fbf62392003",
      "metadata": {},
      "source": [
        "<span id=\"13-estimator\" />\n",
        "\n",
        "### 1.3 Estimador\n",
        "\n",
        "Necesitamos una forma de estimar el valor esperado de nuestro Hamiltoniano en un estado variacional particular $|\\psi(\\vec{\\theta})\\rangle$. Si pudiéramos medir directamente todo el operador $H$, esto sería tan sencillo como hacer muchas mediciones (digamos $N$ ) y promediar los valores medidos:\n",
        "\n",
        "$$\n",
        "\\langle \\psi(\\vec{\\theta})|H|\\psi(\\vec{\\theta})\\rangle _N \\approx \\frac{1}{N}\\sum_{j=1}^N {E_j}\n",
        "$$\n",
        "\n",
        "Aquí, el símbolo $\\approx$ nos recuerda que este valor de expectativa sólo sería exactamente correcto en el límite como $N\\rightarrow \\infty$. Pero con miles de mediciones realizadas en un circuito, el error de muestreo del valor esperado es bastante bajo. Hay otras consideraciones, como el ruido, que se convierten en un problema para cálculos muy precisos.\n",
        "\n",
        "Sin embargo, por lo general no es posible medir $H$ todos a la vez. $H$ puede contener múltiples operadores Pauli X, Y y Z no conmutativos. Así pues, el hamiltoniano debe dividirse en grupos de operadores que puedan medirse simultáneamente, y cada uno de esos grupos debe estimarse por separado, combinando los resultados para obtener un valor de expectativa. Volveremos a tratar este tema con más detalle en la próxima lección, cuando analicemos el escalonamiento de los enfoques clásico y cuántico. Esta complejidad en la medición es una de las razones por las que necesitamos un código muy eficiente para llevar a cabo dicha estimación. A partir de esta lección, utilizaremos para ello el estimador primitivo Qiskit Runtime.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8b68d38e-80ad-4e02-815f-576806ec3e1c",
      "metadata": {},
      "source": [
        "<span id=\"14-classical-optimizers\" />\n",
        "\n",
        "### 1.4 Optimizadores clásicos\n",
        "\n",
        "Un optimizador clásico es cualquier algoritmo clásico diseñado para encontrar extremos de una función objetivo (normalmente un mínimo). Buscan a través del espacio de posibles parámetros buscando un conjunto que minimice alguna función de interés. Pueden clasificarse a grandes rasgos en métodos basados en el gradiente, que utilizan la información del gradiente, y métodos sin gradiente, que funcionan como optimizadores de caja negra. La elección del optimizador clásico puede afectar significativamente al rendimiento de un algoritmo, especialmente en presencia de ruido en el hardware cuántico. Entre los optimizadores más populares en este campo se encuentran Adam, AMSGrad y SPSA, que han mostrado resultados prometedores en entornos ruidosos. Otros optimizadores más tradicionales son COBYLA y SLSQP.\n",
        "\n",
        "Un flujo de trabajo común (demostrado en la Sección 3.3 ) es utilizar uno de estos algoritmos como método dentro de un minimizador como la función `minimize` de scipy. Toma como argumentos\n",
        "\n",
        "* Alguna función a minimizar. Suele ser el valor esperado de la energía. Pero generalmente se denominan \"funciones de coste\".\n",
        "* Conjunto de parámetros a partir de los cuales se inicia la búsqueda. A menudo llamado $x_0$ o $\\theta_0$.\n",
        "* Argumentos, incluidos los argumentos de la función de coste. En la computación cuántica con Qiskit, estos argumentos incluirán el ansatz, el hamiltoniano y la primitiva Estimator, que se analiza con más detalle en la siguiente subsección.\n",
        "* Un \"método\" de minimización. Se refiere al algoritmo específico utilizado para buscar en el espacio de parámetros. Aquí es donde especificaríamos, por ejemplo, COBYLA o SLSQP.\n",
        "* Opciones. Las opciones disponibles pueden variar según el método. Pero un ejemplo que prácticamente todos los métodos incluirían es el número máximo de iteraciones del optimizador antes de finalizar la búsqueda: 'maxiter'.\n",
        "\n",
        "![Una imagen en la que se muestra una línea curva que representa la energía, con varios puntos en los que se comprueba el valor para hallar el mínimo.](https://quantum.cloud.ibm.com/learning/images/courses/quantum-diagonalization-algorithms/vqe/vqe-fig3.avif)\n",
        "\n",
        "En cada paso iterativo, el valor esperado del hamiltoniano se estima realizando muchas mediciones. Esta energía estimada es devuelta por la función de coste, y el minimizador actualiza la información que tiene sobre el paisaje energético. Lo que hace exactamente el optimizador para elegir el siguiente paso varía de un método a otro. Algunos utilizan gradientes y seleccionan la dirección de descenso más pronunciada. Otros pueden tener en cuenta el ruido y exigir que el coste disminuya en un gran margen antes de aceptar que la energía real disminuye en esa dirección.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "9e0642f5-8127-4647-b3f2-deceff0651ec",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Example syntax for minimization\n",
        "# from scipy.optimize import minimize\n",
        "# res = minimize(cost_func, x0, args=(ansatz, hamiltonian, estimator), method=\"cobyla\",\n",
        "# options={'maxiter': 200})"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5bbcff76-ea91-4fb6-acca-35e9e6675ed1",
      "metadata": {},
      "source": [
        "<span id=\"15-the-variational-principle\" />\n",
        "\n",
        "### 1.5 El principio variacional\n",
        "\n",
        "En este contexto, el principio variacional es muy importante; establece que ninguna función de onda variacional puede producir un valor de expectativa de energía (o coste) menor que el producido por la función de onda del estado fundamental. Matemáticamente,\n",
        "\n",
        "$$\n",
        "E_\\text{var}=\\langle \\psi_\\text{var}|H|\\psi_\\text{var}\\rangle \\geq E_\\text{min}=\\langle \\psi_\\text{0}|H|\\psi_\\text{0}\\rangle\n",
        "$$\n",
        "\n",
        "Esto es fácil de verificar si observamos que el conjunto de todos los estados propios $\\{|\\psi_0\\rangle, |\\psi_1\\rangle, |\\psi_2\\rangle, ...|\\psi_n \\rangle\\}$ de $H$ forman una base completa para el espacio de Hilbert. En otras palabras, cualquier estado y, en particular, $|\\psi_\\text{var}\\rangle$ puede escribirse como una suma ponderada (normalizada) de estos estados propios de $H$ :\n",
        "\n",
        "$$\n",
        "|\\psi_\\text{var}\\rangle=\\sum_{i=0}^n c_i |\\psi_i\\rangle\n",
        "$$\n",
        "\n",
        "donde $c_i$ son constantes a determinar, y $\\sum_{i=0} |c_i|^2 = 1$. Dejamos esto como ejercicio para el lector. Pero nótese la implicación: el estado variacional que produce el valor de expectativa de energía más bajo *es* la mejor estimación del verdadero estado fundamental.\n",
        "\n",
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Comprueba tu comprensión\n",
        "\n",
        "Demuestra matemáticamente que $E_\\text{var}\\geq E_0$ para cualquier estado variacional $|\\psi_\\text{var}\\rangle$.\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Respuesta\">\n",
        "    Utilizando la expansión dada del estado variacional en términos de los estados propios de energía,\n",
        "\n",
        "    $$\n",
        "    |\\psi_\\text{var}\\rangle=\\sum_{i=0}^n c_i |\\psi_i\\rangle,\n",
        "    $$\n",
        "\n",
        "    podemos escribir el valor de expectativa de la energía variacional como\n",
        "\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    E_\\text{var}&=\\langle \\psi_\\text{var}|H|\\psi_\\text{var}\\rangle =\\left(\\sum_{i=0}^n c^*_i \\langle \\psi_i|\\right)H\\left(\\sum_{j=0}^n c_j |\\psi_j\\rangle\\right)\\\\\n",
        "    &=\\left(\\sum_{i=0}^n c^*_i \\langle \\psi_i|\\right)\\left(\\sum_{j=0}^n c_j E_j|\\psi_j\\rangle\\right)\\\\\n",
        "    &=\\sum_{i,j=0}^n c^*_i c_j E_j \\langle \\psi_i|\\psi_j\\rangle\\\\\n",
        "    &=\\sum_{i,j=0}^n c^*_i c_j E_j \\delta_{i,j}\\\\\n",
        "    &=\\sum_{i=0}^n |c_i|^2 E_i.\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "\n",
        "    Para todos los coeficientes $0\\leq|c_i|^2\\leq 1$. Así que podemos escribir\n",
        "\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    E_\\text{var}&=\\sum_{i=0}^n |c_i|^2 E_i\\geq \\sum_{i=0}^n |c_i|^2 E_0 = E_0 \\sum_{i=0}^n |c_i|^2 = E_0(1) \\\\\n",
        "    E_\\text{var}&\\geq E_0\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4d14d6df-7320-4f57-bc62-fbee4041957b",
      "metadata": {},
      "source": [
        "<span id=\"2-comparison-with-classical-workflow\" />\n",
        "\n",
        "## 2. Comparación con el flujo de trabajo clásico\n",
        "\n",
        "Supongamos que nos interesa una matriz con N filas y N columnas. Supongamos que su matriz es tan grande que la diagonalización exacta no es una opción. Supongamos, además, que usted sabe lo suficiente acerca de su problema como para hacer algunas conjeturas sobre la estructura general del estado propio de destino, y desea sondear estados similares a su conjetura inicial para ver si su coste/energía puede reducirse aún más. Se trata de un enfoque variacional, y es un método que se utiliza cuando la diagonalización exacta no es una opción.\n",
        "\n",
        "<span id=\"21-classical-workflow\" />\n",
        "\n",
        "### 2.1 Flujo de trabajo clásico\n",
        "\n",
        "Utilizando un ordenador clásico, esto funcionaría de la siguiente manera:\n",
        "\n",
        "* Haga una conjetura de estado, con algunos parámetros $\\vec{\\theta}_i$ que usted variará: $|\\psi(\\vec{\\theta}_i)\\rangle$. Aunque esta conjetura inicial podría ser aleatoria, no es aconsejable. Queremos utilizar el conocimiento del problema en cuestión para adaptar nuestras conjeturas en la medida de lo posible.\n",
        "* Calcular el valor esperado del operador con el sistema en ese estado: $\\langle\\psi(\\vec{\\theta}_i)|H|\\psi(\\vec{\\theta}_i)\\rangle$\n",
        "* Altere los parámetros variacionales y repita: $\\vec{\\theta}_i\\rightarrow \\vec{\\theta}_{i+1}$.\n",
        "* Utiliza la información acumulada sobre el paisaje de estados posibles en tu subespacio variacional para hacer conjeturas cada vez mejores y acercarte al estado objetivo. El principio variacional garantiza que nuestro estado variacional no puede arrojar un valor propio inferior al del estado básico objetivo. Por tanto, cuanto menor sea el valor de expectativa, mejor será nuestra aproximación al estado fundamental:\n",
        "\n",
        "$$\n",
        "\\min_{\\vec{\\theta}} \\{ E_{\\text{var},i} = \\langle\\psi(\\vec{\\theta_i})|H|\\psi(\\vec{\\theta_i})\\rangle \\} \\geq E_0\n",
        "$$\n",
        "\n",
        "Examinemos la dificultad de cada paso de este planteamiento. Ajustar o actualizar los parámetros es fácil desde el punto de vista computacional; la dificultad reside en seleccionar parámetros iniciales útiles y motivados físicamente. Utilizar la información acumulada de iteraciones anteriores para actualizar los parámetros de forma que se acerque al estado básico no es una tarea trivial. Pero existen algoritmos de optimización clásicos que lo hacen con bastante eficacia. Esta optimización clásica sólo es cara porque puede requerir muchas iteraciones; en el peor de los casos, el número de iteraciones puede escalar exponencialmente con N. El paso más costoso desde el punto de vista computacional es, casi con toda seguridad, calcular el valor de la expectativa de su matriz utilizando un estado determinado $|\\psi(\\vec{\\theta_i})\\rangle$ : $\\langle\\psi(\\vec{\\theta_i})|H|\\psi(\\vec{\\theta_i})\\rangle.$\n",
        "\n",
        "La matriz $N\\times N$ debe actuar sobre el vector de elementos $N$, lo que corresponde a: $O(N^2)$ operaciones de multiplicación en el peor de los casos. Esto debe hacerse en cada iteración de los parámetros. Para matrices extremadamente grandes, esto tiene un alto coste computacional.\n",
        "\n",
        "<span id=\"22-quantum-workflow-and-commuting-pauli-groups\" />\n",
        "\n",
        "### 2.2 Flujo de trabajo cuántico y grupos de Pauli de conmutación\n",
        "\n",
        "Ahora imagine que relega esta parte del cálculo a un ordenador cuántico. En lugar de calcular este valor de expectativa, se estima preparando el estado $|\\psi(\\vec{\\theta_i})\\rangle$ en el ordenador cuántico utilizando su ansatz variacional y, a continuación, realizando mediciones.\n",
        "\n",
        "Puede parecer más fácil de lo que es. $H$ no suele ser fácil de medir. Por ejemplo, podría estar formado por muchos operadores Pauli X, Y y Z no conmutativos. Pero $H$ **puede** escribirse como una combinación lineal de términos, $h_\\alpha$, cada uno de los cuales es fácilmente mensurable (por ejemplo, operadores de Pauli o grupos de operadores de Pauli conmutables entre qubits).\n",
        "El valor de expectativa de $H$ sobre algún estado $|\\Psi\\rangle$ es la suma ponderada de los valores de expectativa de los términos constituyentes $h_\\alpha$. Esta expresión es válida para cualquier estado $|\\Psi⟩$, pero la utilizaremos específicamente con nuestros estados variacionales $|\\psi(\\theta_i)\\rangle$.\n",
        "\n",
        "$$\n",
        "H = \\sum_{\\alpha = 1}^T{c_\\alpha h_\\alpha}\n",
        "$$\n",
        "\n",
        "donde $h_\\alpha$ es una cadena de Pauli como `IZZX…XIYX`, o varias cadenas de este tipo que conmutan entre sí. Así pues, una descripción del valor de expectativa que se ajusta más a la realidad de la medición en los ordenadores cuánticos es la siguiente\n",
        "\n",
        "$$\n",
        "\\langle \\Psi |H|\\Psi \\rangle =\\sum_{\\alpha} c_\\alpha \\langle \\Psi | h_\\alpha|\\Psi \\rangle.\n",
        "$$\n",
        "\n",
        "Y en el contexto de nuestra función de onda variacional:\n",
        "\n",
        "$$\n",
        "\\langle \\psi(\\vec{\\theta}_i) |H|\\psi(\\vec{\\theta}_i) \\rangle =\\sum_{\\alpha} c_\\alpha \\langle \\psi(\\vec{\\theta}_i) | h_\\alpha|\\psi(\\vec{\\theta}_i) \\rangle\n",
        "$$\n",
        "\n",
        "Cada uno de los términos $h_\\alpha$ puede medirse $M$ veces obteniendo muestras de medición $s_{\\alpha j}$ con $j=1…M$ y devuelve un valor de expectativa $\\mu_\\alpha$ y una desviación típica $\\sigma_\\alpha$. Podemos sumar estos términos y propagar los errores a través de la suma para obtener un valor de expectativa global $\\mu$ y una desviación típica $\\sigma$.\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        "\\langle \\psi(\\vec{\\theta}_i) |h_\\alpha|\\psi(\\vec{\\theta}_i) \\rangle &\\simeq \\mu _\\alpha \\pm \\frac{\\sigma_\\alpha}{\\sqrt{M}} &\\qquad \\mu_\\alpha &=\\frac{1}{M}\\sum_j s_{\\alpha,j} &\\qquad \\sigma^2_\\alpha &=\\frac{1}{M-1}\\sum_j (s_{\\alpha,j}-\\mu_\\alpha)^2\\\\\n",
        "\n",
        "\\langle \\psi(\\vec{\\theta}_i) |H|\\psi(\\vec{\\theta}_i) \\rangle &\\simeq \\mu  \\pm \\sigma &\\qquad \\mu &= \\sum_\\alpha c_\\alpha \\mu_\\alpha &\\qquad \\sigma^2&=\\sum_\\alpha c^2_\\alpha \\frac{\\sigma^2_\\alpha }{M}\n",
        "\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ab02194a-bb0e-4bbc-81e1-c5f8f9582d9b",
      "metadata": {},
      "source": [
        "Esto no requiere ninguna multiplicación a gran escala, ni ningún proceso que escale necesariamente como $N^2$. En su lugar, requiere múltiples mediciones en el ordenador cuántico. Si no necesita demasiados, este enfoque podría ser eficaz. Y esa es la parte cuántica de VQE.\n",
        "\n",
        "Pero hablemos de las razones por las que esto podría no ser eficiente. Una de las razones para realizar muchas mediciones es reducir la incertidumbre estadística en sus estimaciones, para cálculos de muy alta precisión. Otra razón es el número de cadenas de Pauli necesarias para abarcar toda la matriz. Dado que las matrices de Pauli (más la identidad: X, Y, Z e I) abarcan el espacio de todos los operadores de una dimensión dada, tenemos la garantía de que podemos escribir nuestra matriz de interés como una suma ponderada de operadores de Pauli, como hicimos antes.\n",
        "\n",
        "$$\n",
        "H = \\sum_{\\alpha = 1}^T{c_\\alpha h_\\alpha}\n",
        "$$\n",
        "\n",
        "donde $h_\\alpha$ es una cadena de Pauli que actúa sobre todos los qubits que describen su sistema como `IZZX…XIYX`, o varias cadenas de este tipo que conmutan entre sí. Recordemos que Qiskit utiliza notación *little endian*, en la que el operador $n^\\text{th}$ Pauli de la derecha actúa sobre el qubit $n^\\text{th}$. Así que podemos medir nuestro operador midiendo una serie de operadores de Pauli.\n",
        "\n",
        "Pero no podemos medir todos esos operadores de Pauli al mismo tiempo. Los operadores de Pauli (excepto el I) no conmutan entre sí si están asociados al mismo qubit. Por ejemplo, podemos medir `IZIZ` y `ZZXZ` simultáneamente, ya que podemos medir I y Z al mismo tiempo para el tercer qubit, y podemos conocer I y X al mismo tiempo para el primer qubit. Pero no podemos medir `ZZZZ` y `ZZZX` al mismo tiempo, porque Z y X no conmutan, y ambos actúan sobre el qubit 0. Los lectores con experiencia recordarán quizá que dos conjuntos de operadores de Pauli pueden conmutarse entre sí, aunque las mediciones de cada qubit individual no lo hagan. El estimador parte de la base de que las mediciones de Pauli se realizan mediante el producto tensorial (a través de rotaciones de bases), lo que corresponde a agrupar operadores que conmutan a nivel de cada qubit. Por lo tanto, para estimar simultáneamente dos cadenas (A y B) de operadores de Pauli utilizando Estimator, los operadores de Pauli de cada qubit en A y B deben conmutarse. Esto significa que tampoco podemos medir `ZZZZ` y `ZZXX` al mismo tiempo.\n",
        "\n",
        "![Una tabla con diferentes cuerdas de Pauli, algunas de las cuales conmutan y otras no.](https://quantum.cloud.ibm.com/learning/images/courses/quantum-diagonalization-algorithms/vqe/vqe-fig4.avif)\n",
        "\n",
        "Así que descomponemos nuestra matriz $H$ en una suma de Paulis actuando sobre diferentes qubits. Algunos elementos de esa suma pueden medirse todos a la vez; a esto lo llamamos *grupo de Paulis conmutantes*. Dependiendo de cuántos términos no conmutantes haya, es posible que necesitemos muchos grupos de este tipo. Llamemos al número de tales grupos de cadenas de Pauli conmutantes $N_\\text{GCP}$. Si $N_\\text{GCP}$ es pequeño, esto podría funcionar bien. Si $H$ tiene millones de grupos, esto no será útil.\n",
        "\n",
        "Los procesos necesarios para calcular el valor esperado se agrupan en la primitiva de la biblioteca « Qiskit Runtime » denominada «Estimator». Para obtener más información sobre Estimator, consulta la [referencia de la API](/docs/api/qiskit-ibm-runtime/estimator-v2) en la documentación de IBM Quantum®. Se puede utilizar Estimator directamente, pero esta función devuelve mucho más que el valor propio de menor energía. Por ejemplo, también proporciona información sobre el error estándar del conjunto. Por lo tanto, en el contexto de los problemas de minimización, es habitual encontrar un estimador dentro de una función de coste. Para obtener más información sobre los datos de entrada y salida de Estimator, consulta esta [guía](/docs/guides/primitive-input-output#pubs) en la documentación de IBM Quantum.\n",
        "\n",
        "Se registra el valor de la expectativa (o la función de coste) para el conjunto de parámetros $\\vec{\\theta_i}$ utilizados en su estado y, a continuación, se actualizan los parámetros. Con el tiempo, podrías utilizar los valores de las expectativas o los valores de la función de coste que has estimado para aproximar un gradiente de tu función de coste en el subespacio de estados muestreados por tu ansatz. Existen optimizadores clásicos basados en el gradiente y optimizadores clásicos sin gradiente. Ambos adolecen de problemas potenciales de entrenabilidad, como múltiples mínimos locales y grandes regiones del espacio de parámetros con gradiente cercano a cero, denominadas *mesetas estériles*.\n",
        "\n",
        "![Dos imágenes de una línea curva con un valor mínimo. En uno de ellos, se comprueban puntos al azar para encontrar el mínimo; en el otro, se estima un gradiente trazando una línea entre dos puntos adyacentes.](https://quantum.cloud.ibm.com/learning/images/courses/quantum-diagonalization-algorithms/vqe/vqe-fig5.avif)\n",
        "\n",
        "<span id=\"23-factors-that-determine-computational-cost\" />\n",
        "\n",
        "### 2.3 Factores que determinan el coste computacional\n",
        "\n",
        "VQE no resolverá todos sus problemas más difíciles de química cuántica. Núm. Pero ser mejor en todos los cálculos no es la cuestión. Hemos desplazado lo que determina el coste computacional.\n",
        "\n",
        "![Una tabla en la que se comparan los enfoques variacionales clásicos y cuánticos. Ambas requieren buenas suposiciones iniciales. Tradicionalmente, el coste varía proporcionalmente al cuadrado de la dimensión de la matriz, mientras que en el enfoque cuántico depende del número de grupos de operadores de Pauli conmutativos que haya.](https://quantum.cloud.ibm.com/learning/images/courses/quantum-diagonalization-algorithms/vqe/vqe-fig6.avif)\n",
        "\n",
        "Hemos pasado de un proceso cuya complejidad depende sólo de la dimensión de la matriz a otro que depende de la precisión requerida y del número de operadores de Pauli no conmutativos que componen la matriz. El último bit no tiene análogo en la informática clásica.\n",
        "\n",
        "Basándose en estas dependencias, para matrices dispersas, o matrices que implican pocas cadenas de Pauli no conmutativas, este proceso puede ser útil. Este es el caso, por ejemplo, de los sistemas de espines que interactúan. Para matrices densas, puede resultar menos útil. Sabemos, por ejemplo, que los sistemas químicos suelen tener hamiltonianos que implican cientos, miles e incluso millones de cadenas de Pauli. Se han realizado interesantes trabajos para reducir este número de términos. Pero los sistemas químicos pueden ser más adecuados para algunos de los otros algoritmos que discutiremos en este curso.\n",
        "\n",
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Comprueba tu comprensión\n",
        "\n",
        "Consideremos un Hamiltoniano en cuatro qubits que contiene los términos:\n",
        "\n",
        "`IIXX`, `IIXZ`, `IIZZ`, `IZXZ`, `IXXZ`, `ZZXZ`, `XZXZ`, `ZIXZ`, `ZZZZ`, `XXXX`\n",
        "\n",
        "Desea clasificar estos términos en grupos de forma que todos los términos de un grupo puedan medirse simultáneamente. ¿Cuál es el menor número de grupos de este tipo que se puede formar para que se tengan en cuenta todos los términos?\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Respuesta\">\n",
        "    Se puede hacer en 4 grupos. Ten en cuenta que estas soluciones no suelen ser únicas.\n",
        "\n",
        "    `IIXX`, `XXXX`, `IIZZ`, `ZZZZ`\n",
        "\n",
        "    `IIXZ`, `IZXZ`, `ZIXZ`, `ZZXZ`\n",
        "\n",
        "    `IXXZ`\n",
        "\n",
        "    `XZXZ`\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "¿Qué crees que suele dificultar la química cuántica con VQE: el número de términos del hamiltoniano o encontrar un buen ansatz?\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Respuesta\">\n",
        "    Resulta que hay ansätze que están altamente optimizados para contextos químicos. El número de términos del hamiltoniano y, por tanto, el número de mediciones necesarias suelen causar más problemas.\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a9a687e3-ae98-49f7-9b5a-0a2700ecd278",
      "metadata": {},
      "source": [
        "<span id=\"3-example-hamiltonian\" />\n",
        "\n",
        "## 3. Ejemplo de hamiltoniano\n",
        "\n",
        "Pongamos en práctica este algoritmo utilizando una pequeña matriz hamiltoniana para que podamos ver lo que ocurre en cada paso. Emplearemos el marco de patrones Qiskit:\n",
        "\n",
        "* **Paso 1** : Asignar el problema a circuitos y operadores cuánticos - **Paso 2** : Optimización para el hardware de destino - **Paso 3** : Ejecución en el hardware de destino - **Paso 4** : Posprocesamiento de los resultados\n",
        "\n",
        "<span id=\"31-step-1-map-the-problem-to-quantum-circuits-and-operators\" />\n",
        "\n",
        "### 3.1 Paso 1: Asignar el problema a circuitos y operadores cuánticos\n",
        "\n",
        "Utilizaremos la definida anteriormente en el contexto de la química. Comenzamos con algunas importaciones generales.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "acb6fb0c-4b8c-4ab3-b632-ed201e99b45f",
      "metadata": {},
      "outputs": [],
      "source": [
        "# General imports\n",
        "import numpy as np\n",
        "\n",
        "# SciPy minimizer routine\n",
        "from scipy.optimize import minimize\n",
        "\n",
        "# Plotting functions\n",
        "import matplotlib.pyplot as plt"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a713b494-ab7a-49c6-845c-db728c4635eb",
      "metadata": {},
      "source": [
        "De nuevo, asumimos que el Hamiltoniano de interés es conocido. Aquí utilizaremos un Hamiltoniano extremadamente pequeño, porque otros métodos discutidos en este curso serán más eficientes para resolver problemas más grandes.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "0908f251-58af-49f9-98a8-af98110f6c15",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The ground state energy is  -0.702930394459531\n"
          ]
        }
      ],
      "source": [
        "from qiskit.quantum_info import SparsePauliOp\n",
        "import numpy as np\n",
        "\n",
        "hamiltonian = SparsePauliOp.from_list(\n",
        "    [(\"YZ\", 0.3980), (\"ZI\", -0.3980), (\"ZZ\", -0.0113), (\"XX\", 0.1810)]\n",
        ")\n",
        "\n",
        "A = np.array(hamiltonian)\n",
        "eigenvalues, eigenvectors = np.linalg.eigh(A)\n",
        "print(\"The ground state energy is \", min(eigenvalues))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "75bf2097-1a9c-48d5-8eff-393e0c9eb2f9",
      "metadata": {},
      "source": [
        "Hay muchas opciones de ansatz prefabricados en Qiskit. Utilizaremos `efficient_su2`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "84d6380e-8ee8-4340-a416-c07900fe4c5b",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "This circuit has  4 parameters\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/quantum-diagonalization-algorithms/vqe/extracted-outputs/84d6380e-8ee8-4340-a416-c07900fe4c5b-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Pre-defined ansatz circuit and operator class for Hamiltonian\n",
        "from qiskit.circuit.library import efficient_su2\n",
        "\n",
        "# Note that it is more common to place initial 'h' gates outside the ansatz.\n",
        "# Here we specifically wanted this layer structure.\n",
        "ansatz = efficient_su2(\n",
        "    hamiltonian.num_qubits, su2_gates=[\"h\", \"rz\", \"y\"], entanglement=\"circular\", reps=1\n",
        ")\n",
        "\n",
        "num_params = ansatz.num_parameters\n",
        "print(\"This circuit has \", num_params, \"parameters\")\n",
        "\n",
        "ansatz.decompose().draw(\"mpl\", style=\"iqp\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "15394107-6589-4d06-b611-8fa641784b33",
      "metadata": {},
      "source": [
        "Diferentes ansätze tendrán diferentes estructuras de enredo y diferentes puertas de rotación. El que se muestra aquí utiliza puertas CNOT para el enredo, y puertas Y y puertas RZ parametrizadas para las rotaciones. Nótese el tamaño de este espacio de parámetros; significa que debemos minimizar la función de coste sobre 4 variables (los parámetros para las puertas RZ). Esto puede ampliarse, pero no indefinidamente. Ejecutando un problema similar en 4 qubits, utilizando las 3 repeticiones por defecto para `efficient_su2` se obtienen 16 parámetros variacionales.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "12ee2d18-b4db-4b94-ab19-8a0654ca4b2e",
      "metadata": {},
      "source": [
        "<span id=\"32-step-2-optimize-for-target-hardware\" />\n",
        "\n",
        "### 3.2 Paso 2: Optimizar para el hardware de destino\n",
        "\n",
        "El ansatz se escribió utilizando puertas conocidas, pero nuestro circuito debe transpilarse para hacer uso de las puertas base que pueden implementarse en cada ordenador cuántico. Seleccionamos el backend menos ocupado.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "ba1a1493-e807-44b8-b971-69f098981da2",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "<IBMBackend('ibm_torino')>\n"
          ]
        }
      ],
      "source": [
        "# runtime imports\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService, Session\n",
        "from qiskit_ibm_runtime import EstimatorV2 as Estimator\n",
        "\n",
        "# To run on hardware, select the backend with the fewest number of jobs in the queue\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "\n",
        "print(backend)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "84200023-d16f-41d4-9791-c6ef3488da90",
      "metadata": {},
      "source": [
        "Ahora podemos transpilar nuestro circuito para este hardware y visualizar nuestro ansatz transpilado.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "4a1cdcf3-7d94-4a1f-b675-9549bf28d956",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/quantum-diagonalization-algorithms/vqe/extracted-outputs/4a1cdcf3-7d94-4a1f-b675-9549bf28d956-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 9,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "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",
        "ansatz_isa = pm.run(ansatz)\n",
        "\n",
        "ansatz_isa.draw(output=\"mpl\", idle_wires=False, style=\"iqp\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6066c477-2d43-459f-bfc6-650011279587",
      "metadata": {},
      "source": [
        "Obsérvese que las puertas utilizadas han cambiado y que los qubits de nuestro circuito abstracto se han asignado a qubits de diferente número en el ordenador cuántico. Debemos mapear nuestro Hamiltoniano de forma idéntica para que nuestros resultados tengan sentido.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "3543e888-6f84-4f12-88a8-948dec7f3f55",
      "metadata": {},
      "outputs": [],
      "source": [
        "hamiltonian_isa = hamiltonian.apply_layout(layout=ansatz_isa.layout)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ba1ae1c5-c26c-42f8-b3af-411970d4d39d",
      "metadata": {},
      "source": [
        "<span id=\"33-step-3-execute-on-target-hardware\" />\n",
        "\n",
        "### 3.3 Paso 3: Ejecutar en el hardware de destino\n",
        "\n",
        "<span id=\"331-reporting-out-values\" />\n",
        "\n",
        "#### 3.3.1 Comunicación de valores\n",
        "\n",
        "Aquí definimos una función de coste que toma como argumentos las estructuras que hemos construido en los pasos anteriores: los parámetros, el ansatz y el hamiltoniano. También utiliza Estimator, que aún no hemos definido. Incluimos código para realizar un seguimiento del historial de nuestra función de coste, de modo que podamos comprobar su comportamiento de convergencia.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "bb1d8999-77aa-4c1a-adae-8974eda9e63b",
      "metadata": {},
      "outputs": [],
      "source": [
        "def cost_func(params, ansatz, hamiltonian, estimator):\n",
        "    \"\"\"Return estimate of energy from Estimator\n",
        "\n",
        "    Parameters:\n",
        "        params (ndarray): Array of ansatz parameters\n",
        "        ansatz (QuantumCircuit): Parameterized ansatz circuit\n",
        "        hamiltonian (SparsePauliOp): Operator representation of Hamiltonian\n",
        "        estimator (EstimatorV2): Estimator primitive instance\n",
        "        cost_history_dict: Dictionary for storing intermediate results\n",
        "\n",
        "    Returns:\n",
        "        float: Energy estimate\n",
        "    \"\"\"\n",
        "    pub = (ansatz, [hamiltonian], [params])\n",
        "    result = estimator.run(pubs=[pub]).result()\n",
        "    energy = result[0].data.evs[0]\n",
        "\n",
        "    cost_history_dict[\"iters\"] += 1\n",
        "    cost_history_dict[\"prev_vector\"] = params\n",
        "    cost_history_dict[\"cost_history\"].append(energy)\n",
        "    print(f\"Iters. done: {cost_history_dict['iters']} [Current cost: {energy}]\")\n",
        "\n",
        "    return energy\n",
        "\n",
        "\n",
        "cost_history_dict = {\n",
        "    \"prev_vector\": None,\n",
        "    \"iters\": 0,\n",
        "    \"cost_history\": [],\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "95537d30-ba5d-48ab-bf6a-82edc8f0a348",
      "metadata": {},
      "source": [
        "Resulta muy ventajoso poder elegir los valores iniciales de los parámetros basándose en el conocimiento del problema en cuestión y en las características del estado objetivo. No supondremos tal conocimiento y utilizaremos valores iniciales aleatorios.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "01a5d370-2947-4441-b71b-ffbdeddf52f0",
      "metadata": {},
      "outputs": [],
      "source": [
        "x0 = 2 * np.pi * np.random.random(num_params)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "8e0d33b8-7f9c-428d-a76d-d832747b8430",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Iters. done: 1 [Current cost: 0.010575798722044727]\n",
            "Iters. done: 2 [Current cost: 0.004040015974440895]\n",
            "Iters. done: 3 [Current cost: 0.0020213258785942503]\n",
            "Iters. done: 4 [Current cost: 0.18723082446726014]\n",
            "Iters. done: 5 [Current cost: -0.2746792152068885]\n",
            "Iters. done: 6 [Current cost: -0.3094547651648519]\n",
            "Iters. done: 7 [Current cost: -0.05281985428356641]\n",
            "Iters. done: 8 [Current cost: 0.00808560303514377]\n",
            "Iters. done: 9 [Current cost: -0.0014821685303514388]\n",
            "Iters. done: 10 [Current cost: -0.004759824281150161]\n",
            "Iters. done: 11 [Current cost: 0.09942328705995292]\n",
            "Iters. done: 12 [Current cost: 0.01092366214057508]\n",
            "Iters. done: 13 [Current cost: 0.05017497496069776]\n",
            "Iters. done: 14 [Current cost: 0.13028868414310696]\n",
            "Iters. done: 15 [Current cost: 0.013747803514376994]\n",
            "Iters. done: 16 [Current cost: 0.2583072432944498]\n",
            "Iters. done: 17 [Current cost: -0.14422125655131562]\n",
            "Iters. done: 18 [Current cost: -0.0004950150347678081]\n",
            "Iters. done: 19 [Current cost: 0.00681082268370607]\n",
            "Iters. done: 20 [Current cost: -0.0023377795527156544]\n",
            "Iters. done: 21 [Current cost: 0.6027665591169237]\n",
            "Iters. done: 22 [Current cost: 0.00596641373801917]\n",
            "Iters. done: 23 [Current cost: -0.008318769968051117]\n",
            "Iters. done: 24 [Current cost: -0.00026683306709265246]\n",
            "Iters. done: 25 [Current cost: -0.007648222843450479]\n",
            "Iters. done: 26 [Current cost: 0.004121086261980831]\n",
            "Iters. done: 27 [Current cost: -0.004075019968051117]\n",
            "Iters. done: 28 [Current cost: -0.004419369009584665]\n",
            "Iters. done: 29 [Current cost: 0.213185460054037]\n",
            "Iters. done: 30 [Current cost: -0.06505919572162797]\n",
            "Iters. done: 31 [Current cost: -0.5334241316590271]\n",
            "Iters. done: 32 [Current cost: 0.00218370607028754]\n",
            "Iters. done: 33 [Current cost: 0.09579352143666908]\n",
            "Iters. done: 34 [Current cost: -0.009274800319488819]\n",
            "Iters. done: 35 [Current cost: -0.44395141360688106]\n",
            "Iters. done: 36 [Current cost: 0.011747104632587858]\n",
            "Iters. done: 37 [Current cost: -0.003344149361022364]\n",
            "Iters. done: 38 [Current cost: 0.19138183916486304]\n",
            "Iters. done: 39 [Current cost: 0.013513931813145209]\n"
          ]
        }
      ],
      "source": [
        "# This required 13 min, 20 s QPU time on an Eagle processor, 28 min total time.\n",
        "with Session(backend=backend) as session:\n",
        "    estimator = Estimator(mode=session)\n",
        "    estimator.options.default_shots = 10000\n",
        "\n",
        "    res = minimize(\n",
        "        cost_func,\n",
        "        x0,\n",
        "        args=(ansatz_isa, hamiltonian_isa, estimator),\n",
        "        method=\"cobyla\",\n",
        "        options={\"maxiter\": 50},\n",
        "    )"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fe98bde2-b435-47d1-b227-ee897aa3fe3e",
      "metadata": {},
      "source": [
        "Podemos ver los resultados brutos.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "b1d07809-5f98-4c11-9bf6-fc5b5c5fb47f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              " message: Return from COBYLA because the trust region radius reaches its lower bound.\n",
              " success: True\n",
              "  status: 0\n",
              "     fun: -0.5334241316590271\n",
              "       x: [ 1.024e+00  6.459e+00  3.625e+00  4.007e+00]\n",
              "    nfev: 39\n",
              "   maxcv: 0.0"
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "res"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "80c958e5-f70e-4736-889a-f88a69c50890",
      "metadata": {},
      "source": [
        "<span id=\"34-step-4-post-process-results\" />\n",
        "\n",
        "### 3.4 Paso 4: Procesamiento posterior de los resultados\n",
        "\n",
        "Si el procedimiento termina correctamente, los valores de nuestro diccionario deben ser iguales al vector solución y al número total de evaluaciones de la función, respectivamente. Esto es fácil de comprobar:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "e87046c1-bfe9-4bb3-b7fd-1e4da55149fe",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'prev_vector': array([1.02397956, 6.45886604, 3.62479262, 4.00744128]),\n",
              " 'iters': 39,\n",
              " 'cost_history': [np.float64(0.010575798722044727),\n",
              "  np.float64(0.004040015974440895),\n",
              "  np.float64(0.0020213258785942503),\n",
              "  np.float64(0.18723082446726014),\n",
              "  np.float64(-0.2746792152068885),\n",
              "  np.float64(-0.3094547651648519),\n",
              "  np.float64(-0.05281985428356641),\n",
              "  np.float64(0.00808560303514377),\n",
              "  np.float64(-0.0014821685303514388),\n",
              "  np.float64(-0.004759824281150161),\n",
              "  np.float64(0.09942328705995292),\n",
              "  np.float64(0.01092366214057508),\n",
              "  np.float64(0.05017497496069776),\n",
              "  np.float64(0.13028868414310696),\n",
              "  np.float64(0.013747803514376994),\n",
              "  np.float64(0.2583072432944498),\n",
              "  np.float64(-0.14422125655131562),\n",
              "  np.float64(-0.0004950150347678081),\n",
              "  np.float64(0.00681082268370607),\n",
              "  np.float64(-0.0023377795527156544),\n",
              "  np.float64(0.6027665591169237),\n",
              "  np.float64(0.00596641373801917),\n",
              "  np.float64(-0.008318769968051117),\n",
              "  np.float64(-0.00026683306709265246),\n",
              "  np.float64(-0.007648222843450479),\n",
              "  np.float64(0.004121086261980831),\n",
              "  np.float64(-0.004075019968051117),\n",
              "  np.float64(-0.004419369009584665),\n",
              "  np.float64(0.213185460054037),\n",
              "  np.float64(-0.06505919572162797),\n",
              "  np.float64(-0.5334241316590271),\n",
              "  np.float64(0.00218370607028754),\n",
              "  np.float64(0.09579352143666908),\n",
              "  np.float64(-0.009274800319488819),\n",
              "  np.float64(-0.44395141360688106),\n",
              "  np.float64(0.011747104632587858),\n",
              "  np.float64(-0.003344149361022364),\n",
              "  np.float64(0.19138183916486304),\n",
              "  np.float64(0.013513931813145209)]}"
            ]
          },
          "execution_count": 15,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "cost_history_dict"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "a789373a-8d32-4761-ba21-6b2f98a7ae5a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/quantum-diagonalization-algorithms/vqe/extracted-outputs/a789373a-8d32-4761-ba21-6b2f98a7ae5a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, ax = plt.subplots()\n",
        "x = np.linspace(0, 10, 50)\n",
        "\n",
        "# Define the constant function\n",
        "constant = -0.7029\n",
        "y_constant = np.full_like(x, constant)\n",
        "ax.plot(\n",
        "    range(cost_history_dict[\"iters\"]), cost_history_dict[\"cost_history\"], label=\"VQE\"\n",
        ")\n",
        "ax.set_xlabel(\"Iterations\")\n",
        "ax.set_ylabel(\"Cost\")\n",
        "ax.plot(y_constant, label=\"Target\")\n",
        "plt.legend()\n",
        "plt.draw()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4be8cb84-ec8c-4c83-880f-b9c296282040",
      "metadata": {},
      "source": [
        "IBM Quantum dispone de otras ofertas de formación relacionadas con VQE. Si está listo para poner en práctica VQE, consulte nuestro tutorial: [Estimación de la energía en estado básico de la cadena de Heisenberg con VQE](/docs/tutorials/spin-chain-vqe). Si desea más información sobre la creación de Hamiltonianos moleculares, consulte [esta lección](/learning/courses/quantum-chem-with-vqe/hamiltonian-construction) de nuestro curso de [Química Cuántica con VQE](/learning/courses/quantum-chem-with-vqe). Si está interesado en comprender en profundidad cómo funcionan los algoritmos variacionales como VQE, le recomendamos el curso [Diseño de algoritmos variacionales](/learning/courses/variational-algorithm-design/optimization-loops).\n",
        "\n",
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Comprueba tu comprensión\n",
        "\n",
        "En esta sección, calculamos la energía del estado básico a partir de un Hamiltoniano. Si quisiéramos aplicarlo, por ejemplo, a la determinación de la geometría de una molécula, ¿cómo lo ampliaríamos?\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Respuesta\">\n",
        "    Tendríamos que introducir variables para el espaciado interatómico y los ángulos entre enlaces. Tendríamos que variarlas. Para cada variación de éstos, produciríamos un nuevo Hamiltoniano (ya que los operadores que describen la energía dependen ciertamente de la geometría). Para cada uno de estos Hamiltonianos producidos y mapeados en qubits, necesitaríamos llevar a cabo una optimización como la realizada anteriormente. De todos esos muchos problemas de optimización convergentes, la geometría que produjera la energía más baja sería la adoptada por la naturaleza. Esto es bastante más complicado que lo que se muestra arriba. Este cálculo se realiza para la molécula más simple, $\\text{H}_2$, [aquí](/learning/courses/quantum-chem-with-vqe/geometry).\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "33231f54-a9c1-499c-a4ad-b4404b843909",
      "metadata": {},
      "source": [
        "<span id=\"4-vqes-relationship-to-other-methods\" />\n",
        "\n",
        "## 4. Relación de VQE con otros métodos\n",
        "\n",
        "En esta sección repasaremos las ventajas e inconvenientes del enfoque VQE original y señalaremos sus relaciones con otros algoritmos más recientes.\n",
        "\n",
        "<span id=\"41-the-strengths-and-weaknesses-of-vqe\" />\n",
        "\n",
        "### 4.1 Las fortalezas y debilidades de VQE\n",
        "\n",
        "Ya se han señalado algunos puntos fuertes. Incluyen los siguientes:\n",
        "\n",
        "* Adecuación al hardware moderno: Algunos algoritmos cuánticos requieren tasas de error mucho más bajas, acercándose a la tolerancia a fallos a gran escala. VQE no; puede implementarse en los ordenadores cuánticos actuales.\n",
        "* Circuitos poco profundos: VQE suele emplear circuitos cuánticos relativamente poco profundos. Esto hace que VQE sea menos susceptible a los errores de puerta acumulados y lo hace adecuado para muchas técnicas de mitigación de errores. Por supuesto, los circuitos no siempre son poco profundos; esto depende del ansatz utilizado.\n",
        "* Versatilidad: El VQE puede (en principio) aplicarse a cualquier problema que pueda plantearse como un problema de valores propios/vectores propios. Hay muchas advertencias que hacen que el VQE sea poco práctico o desventajoso para algunos problemas. A continuación se resumen algunas de ellas.\n",
        "\n",
        "También se han descrito anteriormente algunos puntos débiles del VQE y problemas para los que no resulta práctico. Esto incluye:\n",
        "\n",
        "* Naturaleza heurística: VQE no garantiza la convergencia a la energía correcta del estado fundamental, ya que su rendimiento depende de la elección del ansatz y de los métodos de optimización [\\[1-2\\]](#references). Si se elige un ansatz deficiente que carezca del entrelazamiento necesario para el estado base deseado, ningún optimizador clásico podrá alcanzar dicho estado base.\n",
        "* Parámetros potencialmente numerosos: Un ansatz muy expresivo puede tener tantos parámetros que las iteraciones de minimización consuman mucho tiempo.\n",
        "* Alta carga computacional: en VQE, se utiliza el estimador para calcular el valor esperado de cada término del hamiltoniano. La mayoría de los hamiltonianos de interés tendrán términos que no pueden estimarse simultáneamente. Esto puede hacer que el método VQE requiera muchos recursos en sistemas grandes con hamiltonianos complejos [\\[1\\]](#references).\n",
        "* Efectos del ruido: Cuando el optimizador clásico busca un mínimo, los cálculos ruidosos pueden confundirlo y alejarlo del verdadero mínimo o retrasar su convergencia. Una posible solución es aprovechar las técnicas más avanzadas de mitigación y supresión de errores [\\[2-3\\]](#references) de IBM.\n",
        "* Mesetas estériles: Estas regiones de gradientes decrecientes [\\[2-3\\]](#references) existen incluso en ausencia de ruido, pero el ruido las hace más problemáticas ya que el cambio en los valores de las expectativas debido al ruido podría ser mayor que el cambio de actualizar los parámetros en estas regiones estériles.\n",
        "\n",
        "<span id=\"42-relationship-to-other-approaches\" />\n",
        "\n",
        "### 4.2 Relación con otros enfoques\n",
        "\n",
        "<span id=\"adapt-vqe\" />\n",
        "\n",
        "#### Adapt-VQE\n",
        "\n",
        "El algoritmo **ADAPT-VQE** (Adaptive Derivative-Assembled Pseudo-Trotter Variational Quantum Eigensolver) es una mejora del algoritmo VQE original, diseñado para mejorar la eficiencia, precisión y escalabilidad de las simulaciones cuánticas, especialmente en química cuántica.\n",
        "\n",
        "El algoritmo VQE original descrito a lo largo de esta lección utiliza un ansatz predefinido y fijo para aproximar el estado fundamental del sistema. En nuestro caso, hemos utilizado `efficient_su2`, con una sola repetición, utilizando puertas de rotación Y y RZ.  Aunque los parámetros de las puertas RZ cambiaron, la estructura de este ansatz y las puertas utilizadas no cambiaron.\n",
        "\n",
        "ADAPT-VQE aborda las limitaciones de VQE mediante la construcción adaptativa de ansatz. En lugar de comenzar con un ansatz fijo, ADAPT-VQE construye dinámicamente el ansatz de forma iterativa. En cada paso, selecciona el operador de un conjunto predefinido (como los operadores de excitación fermiónicos) que tenga el mayor gradiente con respecto a la energía. Esto garantiza que sólo se añadan los operadores de mayor impacto, lo que conduce a un ansatz compacto y eficiente [\\[4-6\\]](#references). Este planteamiento puede tener varios efectos beneficiosos:\n",
        "\n",
        "1. **Reducción de la profundidad del circuito** : Al hacer crecer el ansatz de forma incremental y centrarse sólo en los operadores necesarios, ADAPT-VQE minimiza las operaciones de puerta en comparación con los enfoques VQE tradicionales [\\[5,7\\]](#references).\n",
        "2. **Mayor precisión** : La naturaleza adaptativa permite a ADAPT-VQE recuperar más energía de correlación en cada paso, haciéndolo particularmente efectivo para sistemas fuertemente correlacionados donde el VQE tradicional tiene dificultades [\\[8,9\\]](#references).\n",
        "3. **Escalabilidad y resistencia al ruido** : El ansatz compacto reduce la acumulación de errores de puerta, reduce la sobrecarga computacional y limita el número de parámetros variacionales que deben minimizarse.\n",
        "\n",
        "ADAPT-VQE aún no es perfecto. En algunos casos puede quedar atrapado o ralentizado por mínimos locales, y puede sufrir un exceso de parametrización. También puede consumir bastantes recursos, ya que requiere el cálculo de gradientes y la optimización de parámetros con muchas estructuras de puertas.\n",
        "\n",
        "<span id=\"quantum-phase-estimation-qpe\" />\n",
        "\n",
        "#### Estimación de fase cuántica (QPE)\n",
        "\n",
        "El QPE es similar en su propósito al VQE, pero muy diferente en su aplicación. El QPE requiere ordenadores cuánticos tolerantes a fallos debido a sus circuitos cuánticos generalmente profundos y al alto nivel de coherencia que requiere. Una vez que se pueda implantar el QPE, sería más preciso que el VQE. Una forma de describir la diferencia es a través de la precisión en función de la profundidad del circuito. QPE alcanza la precisión $\\epsilon$ con profundidades de circuito que escalan como $O(1/\\epsilon)$ [\\[10\\]](#references). VQE requiere $O(1/\\epsilon^2)$ muestras para alcanzar la misma precisión [\\[10,11\\]](#references).\n",
        "\n",
        "<span id=\"krylov-sqd-qsci-and-others-in-this-course\" />\n",
        "\n",
        "#### Krylov, SQD, QSCI y otros en este curso\n",
        "\n",
        "El VQE ayudó a establecer algoritmos cuánticos que aún dependen de ordenadores clásicos, no sólo para hacer funcionar el ordenador cuántico, sino para partes sustanciales del algoritmo. El resto de este curso se centra en varios de estos algoritmos. A continuación explicamos brevemente algunas de ellas, simplemente para compararlas y contrastarlas con la VQE. Se explicarán con mucho más detalle en lecciones posteriores.\n",
        "\n",
        "**Diagonalización cuántica de Krylov (KQD)**\n",
        "\n",
        "**Los métodos de subespacios de Krylov** son formas de proyectar una matriz en un subespacio para reducir su dimensión y hacerla más manejable, conservando las características más importantes. Un truco de este método es generar un subespacio que mantenga estas características; resulta que generar este subespacio está estrechamente relacionado con un método bien establecido en los ordenadores cuánticos llamado **Trotterización**.\n",
        "\n",
        "Existen algunas variantes de los métodos cuánticos de Krylov, pero en general el planteamiento es:\n",
        "\n",
        "* Utilizar el ordenador cuántico para generar un subespacio (el subespacio de Krylov) mediante Trotterización\n",
        "* Proyectar la matriz de interés en ese subespacio de Krylov\n",
        "* Diagonalizar el nuevo hamiltoniano proyectado utilizando un ordenador clásico\n",
        "\n",
        "**Diagonalización cuántica basada en muestreo (SQD)**\n",
        "\n",
        "**La diagonalización cuántica basada en el muestreo (SQD** ) está relacionada con el método de Krylov en el sentido de que también intenta reducir la dimensión de una matriz a diagonalizar preservando las características clave. SQD lo hace de la siguiente manera:\n",
        "\n",
        "* Comienza con una estimación inicial de tu estado base y prepara el sistema en ese estado base.\n",
        "* Utiliza Sampler para muestrear las cadenas de bits que componen este estado.\n",
        "* Utilice la colección de estados base computacionales del muestreador como subespacio sobre el que proyectar su matriz de interés.\n",
        "* Diagonaliza la matriz proyectada más pequeña utilizando un ordenador clásico.\n",
        "\n",
        "Está relacionado con VQE en el sentido de que aprovecha la computación clásica y cuántica para componentes sustanciales del algoritmo. Ambos también comparten el requisito de que preparemos una buena conjetura inicial o ansatz. Pero la distribución del trabajo entre los ordenadores clásicos y cuánticos en SQD se parece más a la del método de Krylov.\n",
        "\n",
        "De hecho, el método de Krylov y el SQD se han combinado recientemente en el método de diagonalización cuántica de Krylov basado en muestreo (SKQD) [\\[12\\]](#references).\n",
        "\n",
        "**Interacción de la configuración del subespacio cuántico**\n",
        "\n",
        "**Quantum Selected Configuration Interaction (QSCI)**[ \\[13\\]](#references) es un algoritmo que produce un estado terreno aproximado de un Hamiltoniano muestreando una función de onda de prueba para identificar los estados base computacionales significativos para generar un subespacio para una diagonalización clásica.\n",
        "Tanto SQD como QSCI utilizan un ordenador cuántico para construir un subespacio reducido.  El punto fuerte adicional de QSCI es su preparación estatal, especialmente en el contexto de los problemas de química. Aprovecha varias estrategias, como el uso de estados evolucionados en el tiempo [\\[14\\]](#references) y un conjunto de ansätze inspiradas en la química. Al centrarse en la preparación eficiente de estados, QSCI reduce los costes computacionales cuánticos de los hamiltonianos químicos, al tiempo que mantiene una alta fidelidad y aprovecha la robustez frente al ruido de las técnicas de muestreo cuántico de estados [\\[15\\]](#references). QSCI proporciona también una técnica de construcción adaptativa que proporciona más ansätze para un mejor resultado.\n",
        "\n",
        "El flujo de trabajo por defecto de QSCI para el problema de química es el siguiente:\n",
        "\n",
        "* Construya el Hamiltoniano molecular utilizando el software de su elección (como SciPy ).\n",
        "* Prepare un algoritmo QSCI seleccionando un estado inicial adecuado y un ansatz inspirado en la química con un conjunto preseleccionado de parámetros.\n",
        "* Muestrear los estados base significativos y diagonalizar el Hamiltoniano utilizando un ordenador clásico para obtener la energía del estado base.\n",
        "* A menudo se recurre a la recuperación de la configuración [\\[16\\]](#references) y a la poselección de simetría [\\[15\\]](#references) como técnicas de posprocesamiento.\n",
        "* Opcionalmente, el flujo de trabajo de QSCI adaptativo tiene un bucle de optimización adicional de step2 a step3, mediante el uso de más ansätze con unos estados iniciales aleatorios.\n",
        "\n",
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Comprueba tu comprensión\n",
        "\n",
        "¿Qué tiene en común el VQE con todos los demás métodos mencionados anteriormente (excepto el QPE, que no se describe con gran detalle)?\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Respuesta\">\n",
        "    Todos implican un estado de prueba o una función de onda de algún tipo. Todos funcionan mejor cuando la conjetura inicial para este estado de prueba es excelente.\n",
        "\n",
        "    Otra respuesta correcta es que todos ellos son más fáciles de implementar cuando el Hamiltoniano es fácil de medir (se puede clasificar en relativamente pocos grupos de operadores de Pauli conmutantes).\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "¿Qué tiene en común el VQE con ninguno de los otros métodos mencionados?\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Respuesta\">\n",
        "    Optimizadores clásicos. Ninguno de los demás utiliza algoritmos de optimización clásicos para seleccionar los parámetros variacionales.\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8086b6a5-daf2-457b-bc7a-84db943b333f",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Referencias\n",
        "\n",
        "\\[2] [https://en.wikipedia.org/wiki/Variational\\_quantum\\_eigensolver](https://en.wikipedia.org/wiki/Variational_quantum_eigensolver)\n",
        "\n",
        "\\[3] [https://journals.aps.org/prapplied/abstract/10.1103/PhysRevApplied.19.024047](https://journals.aps.org/prapplied/abstract/10.1103/PhysRevApplied.19.024047)\n",
        "\n",
        "\\[4] [https://arxiv.org/abs/2111.05176](https://arxiv.org/abs/2111.05176)\n",
        "\n",
        "\\[6] [https://inquanto.quantinuum.com/tutorials/InQ\\_tut\\_fe4n2\\_2.html](https://inquanto.quantinuum.com/tutorials/InQ_tut_fe4n2_2.html)\n",
        "\n",
        "\\[7] [https://www.nature.com/articles/s41467-019-10988-2](https://www.nature.com/articles/s41467-019-10988-2)\n",
        "\n",
        "\\[8] [https://arxiv.org/abs/2210.15438](https://arxiv.org/abs/2210.15438)\n",
        "\n",
        "\\[9] [https://journals.aps.org/prresearch/abstract/10.1103/PhysRevResearch.6.013254](https://journals.aps.org/prresearch/abstract/10.1103/PhysRevResearch.6.013254)\n",
        "\n",
        "\\[10] [https://arxiv.org/html/2403.09624v1](https://arxiv.org/html/2403.09624v1)\n",
        "\n",
        "\\[11] [https://www.nature.com/articles/s42005-023-01312-y](https://www.nature.com/articles/s42005-023-01312-y)\n",
        "\n",
        "\\[13] [https://arxiv.org/abs/1802.00171](https://arxiv.org/abs/1802.00171)\n",
        "\n",
        "\\[14] [https://arxiv.org/abs/2103.08505](https://arxiv.org/abs/2103.08505)\n",
        "\n",
        "\\[15] [https://arxiv.org/html/2501.09702v1](https://arxiv.org/html/2501.09702v1)\n",
        "\n",
        "\\[16] [https://quri-sdk.qunasys.com/docs/examples/quri-algo-vm/qsci/](https://quri-sdk.qunasys.com/docs/examples/quri-algo-vm/qsci/)\n",
        "\n",
        "\\[17] [https://arxiv.org/abs/2412.13839](https://arxiv.org/abs/2412.13839)\n",
        "\n",
        "\\[18] [https://arxiv.org/abs/2302.11320v1](https://arxiv.org/abs/2302.11320v1)\n",
        "\n",
        "\\[19] [https://arxiv.org/pdf/2405.05068v1](https://arxiv.org/pdf/2405.05068v1)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "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"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}