{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "8d86bf32",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Simular la evolución temporal del modelo de Ising de campo transversal\"\n",
        "description: \"Utiliza Qiskit.jl para simular la evolución temporal del modelo de Ising de campo transversal en el hardware de IBM Quantum\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore mktempdir Néel spdiagm Runge Kutta tspan saveat Neel maxdim tensornetworkstate println clims xlabel ylabel colorbar juliaup vcat nsteps ylims topright findfirst */}\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "65550e2a",
      "metadata": {},
      "source": [
        "<span id=\"simulate-time-evolution-of-the-transverse-field-ising-model\" />\n",
        "\n",
        "# Simular la evolución temporal del modelo de Ising de campo transversal\n",
        "\n",
        "Estimación *de tiempo de ejecución: 105 segundos en un procesador Nighthawk r2 (NOTA: Se trata únicamente de una estimación. (El tiempo de ejecución puede variar.)*\n",
        "\n",
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Resultados del aprendizaje\n",
        "\n",
        "1. Aprende a transpilar y ejecutar circuitos cuánticos en el hardware utilizando Julia\n",
        "2. Aprende a procesar los resultados de las mediciones para calcular los valores esperados\n",
        "3. Descubre cómo comparar los resultados del hardware con los de la simulación clásica para cuantificar los efectos combinados del error de aproximación de Trotter y el ruido del hardware\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "efde28b8",
      "metadata": {},
      "source": [
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Requisitos previos\n",
        "\n",
        "Familiarízate con los siguientes temas antes de comenzar este tutorial:\n",
        "\n",
        "* [Simulación cuántica](/learning/courses/utility-scale-quantum-computing/quantum-simulation)\n",
        "\n",
        "* [El modelo de Ising de campo transversal](https://en.wikipedia.org/wiki/Transverse-field_Ising_model)\n",
        "\n",
        "* [Introducción a la transpilación](/docs/guides/transpile#introduction-to-transpilation)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "727912c7",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## En segundo plano\n",
        "\n",
        "Julia es un lenguaje de programación dinámico diseñado principalmente para la computación numérica y científica. Sus capacidades de cálculo numérico de alto rendimiento lo convierten en la opción ideal para los flujos de trabajo de simulación cuántica. En este tutorial, mostramos cómo se utiliza Julia tanto para el preprocesamiento y el posprocesamiento clásicos (por ejemplo, para construir hamiltonianos, ejecutar solucionadores de ecuaciones diferenciales ordinarias (EDO) y calcular valores esperados) como para coordinar tareas en hardware cuántico, lo que elimina la necesidad de cambiar de lenguaje o de entorno.\n",
        "\n",
        "Para interactuar con el hardware de IBM Quantum® desde Julia, este tutorial utiliza dos paquetes del ecosistema de Qiskit: `Qiskit.jl` envuelve la biblioteca C de Qiskit y ofrece funcionalidades de construcción de circuitos y transpilación en Julia; se `QiskitIBMRuntime.jl` conecta al hardware de IBM Quantum a través del cliente del servicio IBM Quantum Compute, lo que permite enviar trabajos y recuperar resultados directamente desde Julia.\n",
        "\n",
        "En este tutorial, analizamos la evolución «trotterizada» del modelo de Ising de campo transversal en una cadena « 1D » con interacciones entre vecinos más cercanos:\n",
        "\n",
        "$$\n",
        "    H = \\sum_{\\langle i,j\\rangle}J_{ij}Z_iZ_j + \\sum_i h_i X_i\n",
        "$$\n",
        "\n",
        "Para implementar la evolución temporal $e^{-iH\\tau}$, dividimos el intervalo de tiempo $\\tau$ en $r$ pasos y definimos $\\Delta\\tau=\\tau/r$. La descomposición de Trotter-Suzuki de segundo orden da como resultado lo siguiente:\n",
        "\n",
        "$$\n",
        "    e^{-iH\\Delta\\tau}\\approx \\prod_i e^{-ih_i X_i\\Delta\\tau/2 } \\prod_{\\langle i,j\\rangle} e^{-iJ_{ij}Z_iZ_j\\Delta\\tau} \\prod_i e^{-ih_iX_i\\Delta\\tau/2}\n",
        "$$\n",
        "\n",
        "Para la construcción del circuito, cada paso de Trotter se implementa como una secuencia de rotaciones de un solo qubit $R_x$ y puertas de dos qubits $R_{ZZ}$. El circuito comienza preparando el estado de Néel $|0101\\cdots\\rangle$ mediante puertas X en qubits alternos. En cada paso sucesivo del algoritmo de Trotter se aplica: (1) $R_x(h_i\\Delta\\tau)$ en cada qubit, (2) $R_{ZZ}(2J_{ij}\\Delta\\tau)$ en cada par vecino a lo largo de la cadena, y (3) $R_x(h_i\\Delta\\tau)$ de nuevo en cada qubit. La profundidad total del circuito crece linealmente con el número de pasos de Trotter $r$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "020f72af",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Requisitos\n",
        "\n",
        "Ten en cuenta que este tutorial requiere macOS o Linux. Qiskit.jl Actualmente no es compatible con Windows (se está siguiendo en [esta incidencia abierta](https://github.com/Qiskit/Qiskit.jl/issues/15) ).\n",
        "\n",
        "Para empezar, instala Julia siguiendo las instrucciones que aparecen en la [página de descargas de Julia](https://julialang.org/downloads/). Este tutorial se ha desarrollado con Julia 1.11; instálalo con `juliaup add 1.11`.\n",
        "\n",
        "A continuación, ejecuta el siguiente comando en un terminal para instalar el paquete de Julia en `IJulia` el entorno global, de modo que puedas ejecutar Julia dentro del cuaderno de Jupyter.\n",
        "\n",
        "```\n",
        "julia -e 'using Pkg; Pkg.add(\"IJulia\")'\n",
        "```\n",
        "\n",
        "Utilizamos el gestor de paquetes integrado de Julia para configurar el entorno del proyecto. Hay dos formas de configurar el entorno.\n",
        "\n",
        "*Opción 1* : entorno temporal. Puedes ejecutar la siguiente celda de código para configurar un entorno temporal e instalar los paquetes necesarios;\n",
        "\n",
        "*Opción 2* : reproducir exactamente el entorno en el que se han realizado las pruebas. Indícalo en `download_toml_files = true` la celda de abajo. La celda descargará y `Project.toml` `Manifest.toml` del [repositorio](https://github.com/Qiskit/documentation/tree/main/docs/tutorials/assets/time-evolution/julia) de documentación en una `env_tutorial/time-evolution/` carpeta situada junto a este cuaderno; a continuación, activará ese entorno e instalará las versiones exactas de los paquetes que indique. El archivo del proyecto describe el entorno a gran escala; por ejemplo, en la sección `[deps]` se enumeran todas las dependencias. El archivo de manifiesto especifica la versión exacta de cada paquete (incluidas las dependencias indirectas), lo que hace que el entorno sea reproducible. Consulta la [documentación de Julia](https://pkgdocs.julialang.org/v1/toml-files/) para obtener más detalles.\n",
        "\n",
        "Se instalarán las siguientes dependencias en el entorno.\n",
        "\n",
        "Para la construcción y ejecución de circuitos cuánticos:\n",
        "\n",
        "* `Qiskit.jl`\n",
        "* `QiskitIBMRuntime.jl`\n",
        "\n",
        "Para la simulación clásica:\n",
        "\n",
        "* `OrdinaryDiffEq.jl`\n",
        "* `TensorNetworkQuantumSimulator.jl`\n",
        "\n",
        "Para el posprocesamiento de los resultados y su visualización:\n",
        "\n",
        "* `StatsBase.jl`\n",
        "* `Plots.jl`\n",
        "\n",
        "Este tutorial se ha probado con la versión `Qiskit.jl` 0.6.0 y la versión `QiskitIBMRuntime.jl` 0.3.1.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "1454cc50",
      "metadata": {},
      "outputs": [],
      "source": [
        "using Pkg\n",
        "using Downloads\n",
        "\n",
        "download_toml_files = false\n",
        "\n",
        "if !download_toml_files\n",
        "    # Option 1: Install the latest versions of the required packages into a temporary environment\n",
        "    Pkg.activate(mktempdir(); io=devnull)\n",
        "    Pkg.add([\n",
        "        PackageSpec(name=\"Qiskit\"),\n",
        "        PackageSpec(name=\"QiskitIBMRuntime\"),\n",
        "        PackageSpec(name=\"Python_jll\"),\n",
        "        PackageSpec(name=\"OrdinaryDiffEq\"),\n",
        "        PackageSpec(name=\"TensorNetworkQuantumSimulator\"),\n",
        "        PackageSpec(name=\"StatsBase\"),\n",
        "        PackageSpec(name=\"Plots\"),\n",
        "    ]; io=devnull)\n",
        "else\n",
        "    # Option 2: Install the exact tested versions pinned in the downloaded Project.toml and Manifest.toml\n",
        "    base_url = \"https://raw.githubusercontent.com/Qiskit/documentation/main/docs/tutorials/assets/time-evolution/julia\"\n",
        "    env_dir = joinpath(@__DIR__, \"env_tutorial\", \"time-evolution\")\n",
        "    mkpath(env_dir)\n",
        "    for file in (\"Project.toml\", \"Manifest.toml\")\n",
        "        Downloads.download(\"$base_url/$file\", joinpath(env_dir, file))\n",
        "    end\n",
        "\n",
        "    Pkg.activate(env_dir; io=devnull)\n",
        "    Pkg.instantiate(; io=devnull) # installs the exact versions recorded in Manifest.toml\n",
        "end"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a13bff0e",
      "metadata": {},
      "source": [
        "Hasta ahora, hemos configurado el entorno del proyecto de Julia para ejecutar el cuaderno. Para ejecutar el flujo de trabajo en una unidad de procesamiento cuántico de IBM, necesitas una cuenta de IBM Quantum y un token de API para instanciar el servicio desde `qiskit-ibm-runtime`. Sigue los pasos de «Instalar y autenticar» del tema «[Ejecuta tu primer circuito en hardware](/docs/guides/hello-world) » para generar tu token de API y localizar tu instancia en CRN.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "613853a6",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configuración\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "4ea0efac",
      "metadata": {},
      "outputs": [],
      "source": [
        "using Qiskit\n",
        "using Qiskit.Operations\n",
        "using QiskitIBMRuntime\n",
        "using StatsBase\n",
        "using OrdinaryDiffEq\n",
        "using SparseArrays\n",
        "using LinearAlgebra\n",
        "using TensorNetworkQuantumSimulator\n",
        "using Plots: plot, plot!, heatmap, @layout, mm"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5016929d",
      "metadata": {},
      "source": [
        "También definimos la siguiente función de utilidad, que devuelve el valor del bit de una cadena de bits situado en `v` la posición `i`. Por ejemplo, con ( `v = 6` binario `110`),\n",
        "\n",
        "* `bit_at(6, 1)` devuelve `0`,\n",
        "* `bit_at(6, 2)` devuelve `1`,\n",
        "* `bit_at(6, 3)` devuelve `1`.\n",
        "\n",
        "Esto sigue la [convención «little-endian»](/docs/guides/bit-ordering) utilizada en Qiskit: la posición `i` se indexa a partir del bit menos significativo (el «más a la derecha»).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "143aecbe",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "bit_at"
            ]
          },
          "execution_count": 21,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "\"\"\"\n",
        "    bit_at(v::Integer, i::Integer) = (v >> (i-1)) & 1\n",
        "\n",
        "Return the value of the bit at position `i` in `v`.\n",
        "\"\"\"\n",
        "bit_at(v::Integer, i::Integer) = (v >> (i-1)) & 1"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6a17088d",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulator-example\" />\n",
        "\n",
        "## Ejemplo de simulador a pequeña escala\n",
        "\n",
        "Consideramos una cadena « 1D » de qubits « $N$ », descrita por el modelo de Ising de campo transversal mencionado anteriormente. Para el sistema en cuestión, a continuación especificamos el tamaño del sistema `N`, el tamaño del paso de `δt` Trotter y el número total de pasos de Trotter `r_max`. El tiempo total de evolución es `δt * r_max`. Ten en cuenta que Julia admite identificadores Unicode como `δt`; en el cuaderno o en el REPL de Julia, escribe seguido `\\delta` de la tecla Tab para introducir `δ`. Para obtener información más detallada, consulta la [documentación sobre la entrada de Unicode en Julia](https://docs.julialang.org/en/v1/manual/unicode-input/).\n",
        "\n",
        "<span id=\"exact-solution\" />\n",
        "\n",
        "### Solución exacta\n",
        "\n",
        "Para establecer una referencia que permita comparar los resultados obtenidos con el hardware cuántico, primero mostramos el flujo de trabajo de la simulación clásica para un problema a pequeña escala. Construimos el hamiltoniano de Ising como una matriz dispersa y, a continuación, obtenemos la evolución temporal exacta integrando numéricamente la ecuación de Schrödinger utilizando `ODEProblem` a partir de `OrdinaryDiffEq.jl`. Este enfoque crece exponencialmente con el número de qubits $N$. Requiere almacenar el vector de estado completo de $2^N$ dimensiones. En el caso de $N=20$, el espacio de Hilbert ya tiene más de un millón de dimensiones, lo que lo hace poco práctico para sistemas más grandes.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "aa6b4db3",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "1048576×1048576 SparseMatrixCSC{ComplexF64, Int64} with 22020096 stored entries:\n",
              "⎡⣿⣿⣾⢦⡀⠳⣄⠀⠀⠀⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎤\n",
              "⎢⠺⣟⢻⣶⣿⡂⠈⠳⣄⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥\n",
              "⎢⢤⡈⠻⠻⠿⣧⣤⣠⡈⠳⠄⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥\n",
              "⎢⠀⠙⢦⡀⠀⣻⣿⣿⣙⣦⡀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥\n",
              "⎢⠀⠀⠀⠙⢦⡈⠳⣼⣿⣿⡆⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥\n",
              "⎢⠙⢦⡀⠀⠀⠁⠀⠈⠈⠉⣿⣿⣾⢦⡀⠳⣄⠀⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⎥\n",
              "⎢⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠺⣟⢻⣶⣿⡂⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⎥\n",
              "⎢⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⢤⡈⠻⠻⠿⣧⣤⣠⡈⠳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⎥\n",
              "⎢⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠙⢦⡀⠀⣻⣿⣿⣙⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⎥\n",
              "⎢⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠙⢦⡈⠳⣼⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⎥\n",
              "⎢⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⡟⢦⡈⠳⣄⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⎥\n",
              "⎢⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⣍⣿⣿⣯⠀⠈⠳⣄⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⎥\n",
              "⎢⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢦⡈⠋⠛⢻⣶⣦⣦⡈⠓⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⎥\n",
              "⎢⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠨⣿⠿⣧⣽⡦⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⎥\n",
              "⎢⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠙⢦⠈⠳⡿⣿⣿⣀⡀⡀⠀⢀⠀⠀⠈⠳⣄⎥\n",
              "⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠸⣿⣿⡟⢦⡈⠳⣄⠀⠀⠀⎥\n",
              "⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠈⠻⣍⣿⣿⣯⠀⠈⠳⣄⠀⎥\n",
              "⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠐⢦⡈⠋⠛⢻⣶⣦⣦⡈⠓⎥\n",
              "⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠙⢦⡀⠨⣿⠿⣧⣽⡦⎥\n",
              "⎣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⠀⠀⠀⠙⢦⠈⠳⡿⣿⣿⎦"
            ]
          },
          "execution_count": 22,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "N = 20\n",
        "δt = 0.05 # Trotter step size\n",
        "r_max = 10 # total number of Trotter steps\n",
        "\n",
        "h = fill(1.0, N)\n",
        "J = fill(1.0, N-1)\n",
        "\n",
        "# Build the Ising Hamiltonian as a sparse 2^n × 2^n matrix\n",
        "function build_ising_hamiltonian(h::AbstractVector{<:Real}, J::AbstractVector{<:Real}, n::Int)\n",
        "    dim = 2^n\n",
        "\n",
        "    # diagonal ZZ terms\n",
        "    diag_terms = zeros(Float64, dim)\n",
        "    for i in 1:n-1\n",
        "        for b in 0:dim-1\n",
        "            bi = bit_at(b, i) # bit at position i\n",
        "            bi_next = bit_at(b, i+1) # bit at position i + 1\n",
        "            diag_terms[b+1] += J[i] * (1-2bi) * (1-2bi_next)\n",
        "        end\n",
        "    end\n",
        "    H = spdiagm(0 => complex(diag_terms))\n",
        "\n",
        "    # off-diagonal local X terms\n",
        "    for i in 1:n\n",
        "        mask = 1 << (i-1)\n",
        "        cols = [xor(b, mask) + 1 for b in 0:dim-1]\n",
        "        H += h[i] * sparse(1:dim, cols, ones(ComplexF64, dim), dim, dim)\n",
        "    end\n",
        "    return H\n",
        "end\n",
        "\n",
        "H_ising = build_ising_hamiltonian(h, J, N)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bbe38420",
      "metadata": {},
      "source": [
        "Definimos el lado derecho de la ecuación de Schrödinger en su forma «in-place» `schrodinger!(dψ, ψ, H, t)`, que calcula $d\\psi/dt = -iH\\psi$ mediante una multiplicación de matriz y vector dispersa. A continuación, configuramos un `ODEProblem` con el estado de Néel como condición inicial y lo resolvemos durante el intervalo de tiempo $[0, r \\cdot \\delta t]$, guardando el estado en cada paso temporal $\\delta t$. El solucionador utilizado es `Tsit5()`, un método explícito estándar de Runge-Kutta de cuarto/quinto orden adecuado para problemas no rígidos.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "959f77e9",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "retcode: Success\n",
              "Interpolation: 1st order linear\n",
              "t: 11-element Vector{Float64}:\n",
              " 0.0\n",
              " 0.05\n",
              " 0.1\n",
              " 0.15\n",
              " 0.2\n",
              " 0.25\n",
              " 0.3\n",
              " 0.35\n",
              " 0.4\n",
              " 0.45\n",
              " 0.5\n",
              "u: 11-element Vector{Vector{ComplexF64}}:\n",
              " [0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im  …  0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im]\n",
              " [-7.612535273941705e-14 + 2.5737401150886516e-29im, 7.616818423232204e-14 - 1.6944517510822304e-12im, -8.565903349044157e-17 + 3.2368190823993794e-15im, -7.61682079584434e-14 - 1.078515326911689e-15im, 1.523363843119942e-13 - 1.6912125988569567e-12im, 3.5867824743714624e-11 + 5.083352813839524e-12im, -7.608249752495255e-14 - 4.317561743447096e-15im, 7.616820798431766e-14 - 1.6944516451719627e-12im, -8.570258514925147e-17 + 3.2384113624188233e-15im, -7.606534127496737e-14 - 5.398093819616815e-15im  …  -7.591102113501652e-14 - 7.55501666902211e-15im, 1.5233640806402347e-13 - 1.691212597789024e-12im, -4.283149391646935e-17 + 3.2390475588024777e-15im, -7.61253567131055e-14 - 4.319154099332238e-15im, 1.28498500555455e-16 + 4.773388002812907e-18im, -8.570258413862945e-17 + 3.2384113624488572e-15im, -7.612534879063769e-14 - 3.2390464923709225e-15im, 1.5233638826330585e-13 - 1.6912127047672224e-12im, -4.283149290692347e-17 + 3.237455101084436e-15im, -7.612535273941595e-14 - 1.8338992189508272e-31im]\n",
              " [-8.680854672628291e-11 - 3.427254579020333e-25im, 8.709110140864061e-11 - 8.699033104505336e-10im, -5.649235901782656e-13 + 8.601572275260816e-12im, -8.709221846767839e-11 - 2.859732693854631e-12im, 1.7418295344900034e-10 - 8.612608133903897e-10im, 8.41248317362033e-9 + 2.6096671235273274e-9im, -8.652487588621059e-11 - 1.1500395899126693e-11im, 8.709222358844944e-11 - 8.699014655672987e-10im, -5.669821718119223e-13 + 8.629638578162805e-12im, -8.641073444337035e-11 - 1.4395664161950265e-11im  …  -8.538755211456945e-11 - 2.0113079067784756e-11im, 1.7418407565004836e-10 - 8.612606969068827e-10im, -2.825548799186401e-13 + 8.640787361594646e-12im, -8.680873675466659e-11 - 1.152847038256832e-11im, 8.478635815548866e-13 + 8.382372443494577e-14im, -5.669819735592012e-13 + 8.629638588149376e-12im, -8.680836165042751e-11 - 8.640671375786309e-12im, 1.7418313902498964e-10 - 8.61262658275938e-10im, -2.82554682353394e-13 + 8.612701741862262e-12im, -8.680854672628374e-11 - 2.811149115586865e-25im]\n",
              " [-4.2861942928308275e-9 - 4.7678984538985196e-24im, 4.318192995176613e-9 - 2.8269371315624182e-8im, -6.395173522788358e-11 + 6.447233357075582e-10im, -4.318468070877377e-9 - 2.1364257686596578e-10im, 8.636571975410676e-9 - 2.7617721500979347e-8im, 1.7289660717976908e-7 + 8.480086489483005e-8im, -4.253920910159066e-9 - 8.649865429907717e-10im, 4.318470318624579e-9 - 2.826906169988642e-8im, -6.446071076018044e-11 + 6.495016281145607e-10im, -4.240844230135151e-9 - 1.0846764703342651e-9im  …  -4.124170626784771e-9 - 1.5115818225612714e-9im, 8.636849310525618e-9 - 2.7617681592276622e-8im, -3.199878870600196e-11 + 6.513863923787342e-10im, -4.2862418335791054e-9 - 8.697676141630365e-10im, 9.60478168658149e-11 + 1.4206678132640617e-11im, -6.446062402203318e-11 + 6.495016332435747e-10im, -4.286148929585427e-9 - 6.513467397401749e-10im, 8.636617557977306e-9 - 2.7618031117900408e-8im, -3.1998702345812497e-11 + 6.466014984949206e-10im, -4.286194292830829e-9 + 4.870909626742163e-24im]\n",
              " [-6.079348924748445e-8 + 6.709717322272516e-24im, 6.162943323939352e-8 - 2.9592110353110093e-7im, -1.6696556398383817e-9 + 1.2400459826013194e-8im, -6.164292787255694e-8 - 4.088131560944853e-9im, 1.2326810978249792e-7 - 2.832733452226872e-7im, 1.253532043962043e-6 + 8.875042569125039e-7im, -5.994408800994472e-8 - 1.6725258647088304e-8im, 6.164314112979357e-8 - 2.9591021577421156e-7im, -1.6948387141108349e-9 + 1.2572865374243683e-8im, -5.959595610442699e-8 - 2.1031419496566967e-8im  …  -5.651357562833814e-8 - 2.9192023780832225e-8im, 1.2328181992163385e-7 - 2.832706038202527e-7im, -8.359520177503641e-10 + 1.2640019589288568e-8im, -6.079590106381668e-8 - 1.689785040734888e-8im, 2.5106376181130386e-9 + 5.084778396177926e-10im, -1.6948306165735295e-9 + 1.2572866042727832e-8im, -6.079128573701664e-8 - 1.2637312624047932e-8im, 1.2327033399936622e-7 - 2.8328423313378375e-7im, -8.359439919088748e-10 + 1.2467163914771088e-8im, -6.079348924748447e-8 + 1.0331673240130025e-23im]\n",
              " [-4.193552407608965e-7 - 1.840220498495959e-23im, 4.2862657866184504e-7 - 1.605297190776032e-6im, -1.8502762435969943e-8 + 1.0856276308591437e-7im, -4.288690355597115e-7 - 3.554458373697522e-8im, 8.574220964863143e-7 - 1.4932465582703908e-6im, 4.849187224415274e-6 + 4.812233679729101e-6im, -4.098425461298137e-7 - 1.4745065418194435e-7im, 4.288753395920834e-7 - 1.6051467128773302e-6im, -1.896035285782807e-8 + 1.1102835700521827e-7im, -4.0588320553359593e-7 - 1.8611022850670223e-7im  …  -3.711838427223188e-7 - 2.569391306045151e-7im, 8.57670971098675e-7 - 1.4931825847645462e-6im, -9.271568570757063e-9 + 1.1197289579952942e-7im, -4.194005371452928e-7 - 1.4992045110994755e-7im, 2.787088237718031e-8 + 7.194933454521717e-9im, -1.8960118645415406e-8 + 1.1102838165568638e-7im, -4.193161709409199e-7 - 1.1191024844294344e-7im, 8.574617742181924e-7 - 1.4933970418540967e-6im, -9.271337900947239e-9 + 1.0949690324053836e-7im, -4.193552407608964e-7 + 1.6524848989693985e-22im]\n",
              " [-1.7823579016922897e-6 - 1.087640585040274e-21im, 1.840838369770109e-6 - 5.569854913729441e-6im, -1.1658899308162594e-7 + 5.632670364049069e-7im, -1.843111911436975e-6 - 1.8277839588324167e-7im, 3.6832950716080163e-6 - 4.979757871286645e-6im, 1.1823232043972897e-5 + 1.668138856075027e-5im, -1.7216192586061922e-6 - 7.718155525067347e-7im, 1.8432004842500001e-6 - 5.56872697042903e-6im, -1.209408931510706e-7 + 5.825677201706816e-7im, -1.6958321412209815e-6 - 9.789484717969276e-7im  …  -1.4727630743780977e-6 - 1.3421173522554028e-6im, 3.6856596566327488e-6 - 4.9790105233659736e-6im, -5.848359517965976e-8 + 5.898080282235565e-7im, -1.7828068643880377e-6 - 7.911632992433939e-7im, 1.760517112427114e-7 + 5.557422909554899e-8im, -1.209376913040035e-7 + 5.825681268981909e-7im, -1.7819976541714096e-6 - 5.890838036292954e-7im, 3.6836637838442504e-6 - 4.980885908360778e-6im, -5.848046807781978e-8 + 5.703874412047737e-7im, -1.7823579016922876e-6 + 1.4997773495573015e-22im]\n",
              " [-5.2719847869895295e-6 + 8.772351797140282e-21im, 5.515796864069832e-6 - 1.3769890958036113e-5im, -4.854583030811284e-7 + 1.984102714841576e-6im, -5.52914007682216e-6 - 6.365426377065764e-7im, 1.1041341496086288e-5 - 1.1652942788167796e-5im, 1.9151485012220565e-5 + 4.1169949873337434e-5im, -5.0149560721693026e-6 - 2.748560607544284e-6im, 5.529875137426081e-6 - 1.3764484293927507e-5im, -5.114444270677655e-7 + 2.0817439894898367e-6im, -4.903067916522327e-6 - 3.508133202204623e-6im  …  -3.950715241692067e-6 - 4.767013437270117e-6im, 1.1055449702075777e-5 - 1.1647607717886785e-5im, -2.4383689790869913e-7 + 2.1174362562762014e-6im, -5.274804158840664e-6 - 2.846520575369006e-6im, 7.355815857851513e-7 + 2.7656731682993024e-7im, -5.114187341304747e-7 + 2.0817477909234415e-6im, -5.2699143882793144e-6 - 2.112332735357189e-6im, 1.1043481329588082e-5 - 1.1658350328429614e-5im, -2.438120770802614e-7 + 2.018953103020384e-6im, -5.271984786989539e-6 + 6.138686657683503e-21im]\n",
              " [-1.1617825704147756e-5 + 1.0312565819538484e-21im, 1.2349337633136464e-5 - 2.5744303624623147e-5im, -1.454293291805138e-6 + 5.123664098726546e-6im, -1.2403650700016836e-5 - 1.6202581807656819e-6im, 2.473960492472325e-5 - 2.0155561130978555e-5im, 1.9144096039811722e-5 + 7.6742969783544e-5im, -1.0832694804431494e-5 - 7.1930875309620025e-6im, 1.2407719644833958e-5 - 2.572612053989065e-5im, -1.56229208977908e-6 + 5.474515092672054e-6im, -1.0480130191816845e-5 - 9.254685626031852e-6im  …  -7.537537188446326e-6 - 1.2435206581306264e-5im, 2.4798220145598703e-5 - 2.0129513964044365e-5im, -7.316420138432232e-7 + 5.598791194770797e-6im, -1.1630271281791021e-5 - 7.545396775206743e-6im, 2.2142322134173096e-6 + 9.742099793903422e-7im, -1.5621555243617513e-6 + 5.474538012007301e-6im, -1.1609612670484607e-5 - 5.574263610620754e-6im, 2.4748196626943606e-5 - 2.0173749496261283e-5im, -7.315119289888009e-7 + 5.243914596674148e-6im, -1.1617825704147746e-5 + 2.0077178062353968e-21im]\n",
              " [-1.9812558848038747e-5 - 9.187798066332235e-22im, 2.147271081932555e-5 - 3.757217609507472e-5im, -3.2943974661872274e-6 + 1.0138423201098428e-5im, -2.163583224409355e-5 - 3.1479593611068202e-6im, 4.3072956212126986e-5 - 2.6216146005671797e-5im, 4.770433131249609e-6 + 0.00011143181451766211im, -1.7992013464296718e-5 - 1.4466566267165694e-5im, 2.1652013724844488e-5 - 3.752687773861608e-5im, -3.6269134427165884e-6 + 1.1086241359275966e-5im, -1.7142579310500252e-5 - 1.8804566182208357e-5im  …  -1.0216935244391546e-5 - 2.491043927093904e-5im, 4.325353440439837e-5 - 2.6122963951736878e-5im, -1.6606359562313976e-6 + 1.140941830342574e-5im, -1.9853711381944784e-5 - 1.541920404485947e-5im, 5.050007317756615e-6 + 2.569541043839736e-6im, -3.6263960453836696e-6 + 1.1086337766500889e-5im, -1.97886585017898e-5 - 1.1323323936752868e-5im, 4.309833408283902e-5 - 2.6261466566058982e-5im, -1.6601519712867477e-6 + 1.044756873253024e-5im, -1.981255884803875e-5 + 7.339651656276922e-20im]\n",
              " [-2.6605994524636198e-5 + 1.1289591858171765e-19im, 2.9532145628332138e-5 - 4.333877076489454e-5im, -5.793688435837011e-6 + 1.572023652812328e-5im, -2.9906819721527362e-5 - 4.767746092973603e-6im, 5.9370303731772103e-5 - 2.51584831603933e-5im, -2.1873141629015348e-5 + 0.00012741720406900944im, -2.3313208885217603e-5 - 2.288272975975095e-5im, 2.995521974547466e-5 - 4.325274969426522e-5im, -6.580507052690018e-6 + 1.770923159800008e-5im, -2.1702668886267368e-5 - 3.0142728729320425e-5im  …  -8.927183090969304e-6 - 3.921197964153728e-5im, 5.979856139686108e-5 - 2.4903653161242186e-5im, -2.9274853045629046e-6 + 1.8356702741523666e-5im, -2.6711940545518465e-5 - 2.488368314305582e-5im, 8.967724203429812e-6 + 5.238269352768273e-6im, -6.579046665900031e-6 + 1.770952822000892e-5im, -2.6553193423229552e-5 - 1.812661956307733e-5im, 5.942741740138485e-5 - 2.5244572524328294e-5im, -2.9261511036959703e-6 + 1.6330572728121542e-5im, -2.660599452463617e-5 - 3.440870688152987e-20im]"
            ]
          },
          "execution_count": 23,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# initial state |0101...01⟩\n",
        "ψ0 = zeros(ComplexF64, 2^N)\n",
        "neel_index = sum(1 << (i-1) for i in 1:2:N)\n",
        "ψ0[neel_index + 1] = 1.0\n",
        "\n",
        "function schrodinger!(dψ::AbstractVector, ψ::AbstractVector, H::AbstractMatrix, t::Real)\n",
        "    mul!(dψ, H, ψ)\n",
        "    dψ .*= -im\n",
        "end\n",
        "\n",
        "tspan = (0.0, r_max * δt)\n",
        "prob = ODEProblem(schrodinger!, ψ0, tspan, H_ising)\n",
        "sol = solve(prob, Tsit5(), saveat=δt)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8054932d",
      "metadata": {},
      "source": [
        "A partir de la solución, que describe el vector de estado $\\psi(t)$, podemos obtener la magnetización por sitio, expresada como los valores esperados del qubit único $\\langle Z\\rangle$ en función del tiempo. Comparamos esto con los resultados obtenidos a partir de los circuitos «trotterizados».\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "27b209db",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "11×20 Matrix{Float64}:\n",
              " -1.0       1.0       -1.0       1.0       …  1.0       -1.0       1.0\n",
              " -0.995021  0.995034  -0.995034  0.995034     0.995034  -0.995034  0.995021\n",
              " -0.980189  0.980386  -0.980386  0.980386     0.980386  -0.980386  0.980189\n",
              " -0.955994  0.956968  -0.956968  0.956968     0.956968  -0.956968  0.955994\n",
              " -0.922667  0.925652  -0.925653  0.925653     0.925653  -0.925652  0.922667\n",
              " -0.881106  0.888117  -0.88812   0.88812   …  0.88812   -0.888117  0.881106\n",
              " -0.832251  0.846116  -0.846129  0.846129     0.846129  -0.846116  0.832251\n",
              " -0.776957  0.801257  -0.801298  0.801298     0.801298  -0.801257  0.776957\n",
              " -0.715858  0.754749  -0.75486   0.75486      0.75486   -0.754749  0.715858\n",
              " -0.649744  0.707628  -0.707895  0.707895     0.707895  -0.707628  0.649744\n",
              " -0.580117  0.661272  -0.661841  0.661842  …  0.661841  -0.661272  0.580117"
            ]
          },
          "execution_count": 24,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# get a single-qubit expectation value ⟨Z_qubit⟩ from a full state vector, weighting ±1 by |amplitude|²\n",
        "function z_expval_from_state(ψ::AbstractVector{<:Complex}, qubit::Int, n::Int)\n",
        "    s = 0.0\n",
        "    for b in 0:2^n-1\n",
        "        bit = bit_at(b, qubit)\n",
        "        s += (1 - 2bit) * abs2(ψ[b+1])\n",
        "    end\n",
        "    return s\n",
        "end\n",
        "\n",
        "classical_magnetizations = [z_expval_from_state(sol.u[r+1], q, N)\n",
        "                             for r in 0:r_max, q in 1:N]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c3568ea2",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulation-of-the-trotterized-circuits\" />\n",
        "\n",
        "### Simulación a pequeña escala de los circuitos de Trotter\n",
        "\n",
        "A continuación, mostramos la simulación clásica de los circuitos sin ruido utilizando métodos de redes tensoriales con el apoyo de `TensorNetworkQuantumSimulator.jl`, lo que nos permite validar nuestra construcción de circuitos. Estos métodos sirven de referencia para comparar los resultados obtenidos con el hardware cuántico.\n",
        "\n",
        "En primer lugar, definimos el retículo como un grafo de cadenas « 1D » utilizando `named_grid((N,))`, donde cada vértice es una tupla `(i,)`. A continuación, especificamos las puertas del circuito como una lista de tuplas `(gate_name, qubit_indices, gate_parameter)`, que sirve como formato de entrada para el simulador de redes tensoriales.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "id": "47e7b661",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "make_trotter_circuit_tn (generic function with 1 method)"
            ]
          },
          "execution_count": 25,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# 1D chain graph — vertices are named (1,), (2,), ..., (N,)\n",
        "g = named_grid((N,))\n",
        "\n",
        "# Gates to prepare Néel state |0101…⟩, X on every other site\n",
        "neel_state_gates(n::Int) = [(\"X\", [(i,)]) for i in 1:2:n]\n",
        "\n",
        "# Gates for one second-order Trotter step of size δt\n",
        "trotter_step_gates(h::AbstractVector{<:Real}, J::AbstractVector{<:Real}, n::Int, δt::Real) = vcat(\n",
        "    [(\"Rx\",  [(i,)],         h[i] * δt) for i in 1:n],\n",
        "    [(\"Rzz\", [(i,), (i+1,)], 2 * J[i] * δt) for i in 1:n-1],\n",
        "    [(\"Rx\",  [(i,)],         h[i] * δt) for i in 1:n])\n",
        "\n",
        "# Make a list of gates: Néel state preparation followed by n_trotter_steps Trotter steps\n",
        "function make_trotter_circuit_tn(h::AbstractVector{<:Real}, J::AbstractVector{<:Real}, n::Int, δt::Real,\n",
        "                                 n_trotter_steps::Int)\n",
        "    circuit = []\n",
        "\n",
        "    # Neel state initialization\n",
        "    append!(circuit, neel_state_gates(n))\n",
        "\n",
        "    for _ in 1:n_trotter_steps\n",
        "        append!(circuit, trotter_step_gates(h, J, n, δt))\n",
        "    end\n",
        "\n",
        "    return circuit\n",
        "end"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2425014b",
      "metadata": {},
      "source": [
        "Utilizamos el algoritmo de propagación de creencias para la contracción de redes tensoriales. Este método resulta eficaz para circuitos con un entrelazamiento limitado, pero su precisión disminuye a medida que el entrelazamiento aumenta con la profundidad del circuito. Los parámetros `cutoff` y `maxdim` controlan el equilibrio entre la precisión y el coste computacional. Del mismo modo, calculamos la magnetización en cada sitio para compararlas más adelante.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "id": "5e3ea2c3",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "fidelity at Trotter step 0 was 1.0\n",
            "fidelity at Trotter step 1 was 1.0\n",
            "fidelity at Trotter step 2 was 1.0\n",
            "fidelity at Trotter step 3 was 0.9999999999999679\n",
            "fidelity at Trotter step 4 was 0.9999999999976941\n",
            "fidelity at Trotter step 5 was 0.9999999999476229\n",
            "fidelity at Trotter step 6 was 0.9999999993741544\n",
            "fidelity at Trotter step 7 was 0.9999999993647009\n",
            "fidelity at Trotter step 8 was 0.9999999992323603\n",
            "fidelity at Trotter step 9 was 0.9999999980892764\n",
            "fidelity at Trotter step 10 was 0.9999999980892698\n"
          ]
        }
      ],
      "source": [
        "apply_kwargs = (; maxdim=32, cutoff=1e-10, normalize_tensors=true)\n",
        "tn_magnetizations = zeros(r_max+1, N)\n",
        "\n",
        "# |↑↑…↑⟩ product state, wrapped in a belief propagation cache\n",
        "tn_initial_state(g::NamedGraph) = BeliefPropagationCache(\n",
        "    tensornetworkstate(ComplexF32, v -> \"↑\", g, \"S=1/2\"))\n",
        "\n",
        "# Apply a gate list to a TN state; returns the evolved state and the\n",
        "# truncation fidelity, such as ∏(1 - ε) over all gate applications\n",
        "function apply_gates_to_tn_state(circuit::Vector, ψ_bpc::BeliefPropagationCache; apply_kwargs::NamedTuple)\n",
        "    ψ_bpc, errs = apply_gates(circuit, ψ_bpc; apply_kwargs)\n",
        "    return ψ_bpc, prod(1.0 .- errs)\n",
        "end\n",
        "\n",
        "# ⟨Z_q⟩ on every site of a tensor-network state\n",
        "z_expvals_from_tn_state(ψ_bpc::BeliefPropagationCache, n::Int) =\n",
        "    [real(expect(ψ_bpc, [(\"Z\", [(q,)])])[1]) for q in 1:n]\n",
        "\n",
        "for r in 0:r_max\n",
        "    circuit = make_trotter_circuit_tn(h, J, N, δt, r)\n",
        "    ψ_bpc, fidelity = apply_gates_to_tn_state(circuit, tn_initial_state(g); apply_kwargs)\n",
        "    println(\"fidelity at Trotter step $(r) was $(fidelity)\")\n",
        "    tn_magnetizations[r+1, :] = z_expvals_from_tn_state(ψ_bpc, N)\n",
        "end"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "aeb0878b",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "### Paso 1: Asignar entradas clásicas a un problema cuántico\n",
        "\n",
        "Ahora construimos el circuito de evolución temporal «trotterizado» utilizando `Qiskit.jl`. El circuito es un reflejo de la versión de la red tensorial: inicializa el estado de Néel, aplica un $r$ es pasos de Trotter con las puertas $R_x$ y $R_{ZZ}$, y, por último, mide todos los qubits en la base Z.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "id": "6b680069",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "QuantumCircuit with 20 qubits, 20 clbits\n",
              "  instructions: 89"
            ]
          },
          "execution_count": 27,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "function make_trotter_circuit(h::AbstractVector{<:Real}, J::AbstractVector{<:Real}, n::Int, δt::Real, n_trotter_steps::Int)\n",
        "    qc = QuantumCircuit(n, n)\n",
        "\n",
        "    # Neel state initialization\n",
        "    for i in 1:2:n\n",
        "        x!(qc, i)\n",
        "    end\n",
        "\n",
        "    # Trotter evolution\n",
        "    for _ in 1:n_trotter_steps\n",
        "        for i in 1:n\n",
        "            rx!(qc, h[i] * δt, i)\n",
        "        end\n",
        "\n",
        "        for i in 1:n-1\n",
        "            rzz!(qc, 2* J[i] * δt, i, i+1)\n",
        "        end\n",
        "\n",
        "        for i in 1:n\n",
        "            rx!(qc, h[i] * δt, i)\n",
        "        end\n",
        "    end\n",
        "\n",
        "    # measure in Z basis\n",
        "    for i in 1:n\n",
        "        measure!(qc, i, i)\n",
        "    end\n",
        "    return qc\n",
        "end\n",
        "\n",
        "\n",
        "qc = make_trotter_circuit(h, J, N, δt, 1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7a7e4d8f",
      "metadata": {},
      "source": [
        "Elaboramos una lista de circuitos para los pasos 0 a 10 de Trotter, que se corresponden con tiempos de evolución de $\\tau = 0, \\delta t, 2\\delta t, \\ldots, 10\\delta t$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "id": "704485a7",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "11-element Vector{QuantumCircuit}:\n",
              " QuantumCircuit(20, 20; 30 instructions)\n",
              " QuantumCircuit(20, 20; 89 instructions)\n",
              " QuantumCircuit(20, 20; 148 instructions)\n",
              " QuantumCircuit(20, 20; 207 instructions)\n",
              " QuantumCircuit(20, 20; 266 instructions)\n",
              " QuantumCircuit(20, 20; 325 instructions)\n",
              " QuantumCircuit(20, 20; 384 instructions)\n",
              " QuantumCircuit(20, 20; 443 instructions)\n",
              " QuantumCircuit(20, 20; 502 instructions)\n",
              " QuantumCircuit(20, 20; 561 instructions)\n",
              " QuantumCircuit(20, 20; 620 instructions)"
            ]
          },
          "execution_count": 28,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# prepare a list of circuits with different Trotter steps\n",
        "qc_list = [make_trotter_circuit(h, J, N, δt, r) for r in 0:r_max]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3b7100d9",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "### Paso 2: Optimizar el problema para su ejecución en hardware cuántico\n",
        "\n",
        "Para que puedan ejecutarse en hardware cuántico, los circuitos deben compilarse primero. Esto incluye los siguientes pasos: seleccionar un conjunto de qubits físicos sobre los que mapear el circuito, recompilar las puertas en el conjunto de instrucciones nativo del backend y optimizar la profundidad del circuito resultante. Utilizamos `least_busy()` para seleccionar automáticamente el backend disponible menos ocupado, `target_from_backend()` para recuperar su conjunto de puertas nativas y la conectividad de los qubits, y `transpile()` para llevar a cabo la compilación.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "a63ecabe",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "backend.name = \"ibm_phoenix\"\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "\"ibm_phoenix\""
            ]
          },
          "execution_count": 95,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "service = Service()\n",
        "search_results = backend_search(service)\n",
        "backend = least_busy(search_results)\n",
        "@show backend.name"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 37,
      "id": "14205fe7",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Target with 120 qubits\n",
              "  instructions: 8"
            ]
          },
          "execution_count": 37,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "target = target_from_backend(backend, service)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 38,
      "id": "8828900e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "11-element Vector{QuantumCircuit}:\n",
              " QuantumCircuit(120, 20; 30 instructions)\n",
              " QuantumCircuit(120, 20; 211 instructions)\n",
              " QuantumCircuit(120, 20; 344 instructions)\n",
              " QuantumCircuit(120, 20; 475 instructions)\n",
              " QuantumCircuit(120, 20; 606 instructions)\n",
              " QuantumCircuit(120, 20; 737 instructions)\n",
              " QuantumCircuit(120, 20; 868 instructions)\n",
              " QuantumCircuit(120, 20; 999 instructions)\n",
              " QuantumCircuit(120, 20; 1130 instructions)\n",
              " QuantumCircuit(120, 20; 1261 instructions)\n",
              " QuantumCircuit(120, 20; 1392 instructions)"
            ]
          },
          "execution_count": 38,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "tqc_list = [transpile(qc, target)[1] for qc in qc_list]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "501e8dc5",
      "metadata": {},
      "source": [
        "Tras la transpilación, comprobamos dos propiedades de los circuitos compilados. `get_circuit_layout()` devuelve el conjunto de índices de qubits físicos seleccionados para el circuito. `two_qubit_depth()` calcula la profundidad de la puerta de dos qubits —la longitud de la cadena más larga de operaciones de dos qubits en el circuito—, lo cual constituye un indicador útil de la acumulación de ruido en el hardware.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 39,
      "id": "bf4c910c",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Set{Int64} with 20 elements:\n",
              "  35\n",
              "  110\n",
              "  58\n",
              "  12\n",
              "  24\n",
              "  37\n",
              "  23\n",
              "  22\n",
              "  47\n",
              "  69\n",
              "  36\n",
              "  80\n",
              "  109\n",
              "  90\n",
              "  57\n",
              "  34\n",
              "  13\n",
              "  59\n",
              "  70\n",
              "  100"
            ]
          },
          "execution_count": 39,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "function get_circuit_layout(tqc::QuantumCircuit)\n",
        "    return Set(q for inst in tqc.data for q in inst.qubits)\n",
        "end\n",
        "\n",
        "get_circuit_layout(tqc_list[2])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1f5318ec",
      "metadata": {},
      "source": [
        "A continuación mostramos el recuento de puertas de dos qubits y la profundidad del circuito en cada paso de Trotter; tal y como era de esperar, ambos crecen linealmente con el número de pasos. Ten en cuenta que la puerta « $R_\\mathrm{ZZ}$ » fraccional aún no está disponible a través de la API de C (véase [qiskit-ibm-runtime-c#29](https://github.com/Qiskit/qiskit-ibm-runtime-c/issues/29) ). Como resultado, cada una de ellas `RZZGate` se compila en dos puertas de dos qubits en lugar de una, lo que aumenta el número de puertas de dos qubits.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 40,
      "id": "5fa03372",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "r=0: 2q gate count=0, 2q gate depth=0\n",
            "r=1: 2q gate count=38, 2q gate depth=38\n",
            "r=2: 2q gate count=76, 2q gate depth=42\n",
            "r=3: 2q gate count=114, 2q gate depth=46\n",
            "r=4: 2q gate count=152, 2q gate depth=50\n",
            "r=5: 2q gate count=190, 2q gate depth=54\n",
            "r=6: 2q gate count=228, 2q gate depth=58\n",
            "r=7: 2q gate count=266, 2q gate depth=62\n",
            "r=8: 2q gate count=304, 2q gate depth=66\n",
            "r=9: 2q gate count=342, 2q gate depth=70\n",
            "r=10: 2q gate count=380, 2q gate depth=74\n"
          ]
        }
      ],
      "source": [
        "two_qubit_count(qc::QuantumCircuit) = count(inst -> length(inst.qubits) == 2, qc.data)\n",
        "\n",
        "function two_qubit_depth(qc::QuantumCircuit)\n",
        "    qubit_depth = Dict{Int,Int}()\n",
        "    for inst in qc.data\n",
        "        length(inst.qubits) == 2 || continue   # skip non-two-qubit gates\n",
        "        d = maximum(get(qubit_depth, q, 0) for q in inst.qubits)\n",
        "        for q in inst.qubits\n",
        "            qubit_depth[q] = d + 1\n",
        "        end\n",
        "    end\n",
        "    return isempty(qubit_depth) ? 0 : maximum(values(qubit_depth))\n",
        "end\n",
        "\n",
        "for (i, tqc) in enumerate(tqc_list)\n",
        "    println(\"r=$(i-1): 2q gate count=$(two_qubit_count(tqc)), 2q gate depth=$(two_qubit_depth(tqc))\")\n",
        "end"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "37aabbc8",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Paso 3: Ejecutar con el comando « Qiskit primitives »\n",
        "\n",
        "Ahora podemos enviar los circuitos transpilados al backend como `Sampler` tareas con los parámetros `shots` especificados.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 41,
      "id": "7bcdead5",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "11-element Vector{QiskitIBMRuntime.Job}:\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000003427f72f0)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b2d1b4e0)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b2dcb180)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b1e24840)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b266e640)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b2d1b690)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c17980)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b315b9b0)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c16090)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c195f0)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b2d33f70)"
            ]
          },
          "execution_count": 41,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "shots = 1024\n",
        "job_list = [run_sampler_job(service, backend, tqc, shots) for tqc in tqc_list]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 46,
      "id": "9bca942a",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Job 1: Completed\n",
            "Job 2: Completed\n",
            "Job 3: Completed\n",
            "Job 4: Completed\n",
            "Job 5: Completed\n",
            "Job 6: Completed\n",
            "Job 7: Completed\n",
            "Job 8: Completed\n",
            "Job 9: Completed\n",
            "Job 10: Completed\n",
            "Job 11: Completed\n"
          ]
        }
      ],
      "source": [
        "for (i, job) in enumerate(job_list)\n",
        "    status = get_job_status(job, service)\n",
        "    println(\"Job $i: \", status)\n",
        "end"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f1429d09",
      "metadata": {},
      "source": [
        "A medida que se completan los trabajos, podemos recuperar sus resultados. Ten en cuenta que la función `get_sampler_job_results` permanecerá bloqueada hasta que finalice el trabajo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 47,
      "id": "e8f81320",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "11-element Vector{QiskitIBMRuntime.Samples}:\n",
              " [[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]  …  [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]\n",
              " [[1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]  …  [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]\n",
              " [[1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0]  …  [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]\n",
              " [[1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]  …  [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]\n",
              " [[1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]  …  [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]\n",
              " [[1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0], [1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]  …  [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1], [1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]\n",
              " [[0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]  …  [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]\n",
              " [[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0], [1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0], [1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1], [0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0], [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0]  …  [1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1], [0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]\n",
              " [[0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1], [0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]  …  [1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0], [1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0]]\n",
              " [[0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0], [1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1], [0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0], [0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0], [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1], [0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0]  …  [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1]]\n",
              " [[1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0]  …  [1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0], [1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0]]"
            ]
          },
          "execution_count": 47,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "all_samples = [get_sampler_job_results(job, service) for job in job_list]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "64b46f68",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "### Paso 4: Realizar el posprocesamiento y obtener el resultado en el formato clásico deseado\n",
        "\n",
        "A partir de las muestras de cadenas de bits obtenidas del hardware cuántico, calculamos la magnetización por sitio (los valores esperados de la función de $\\langle Z\\rangle$ para un solo qubit) promediando $(-1)^{b_i}$ sobre todos los disparos, donde $b_i$ es el bit medido para el qubit $i$. A continuación, representamos gráficamente la magnetización en forma de mapa de calor sobre los qubits y los pasos de Trotter, comparando los tres métodos en paralelo: simulación clásica exacta, simulación con red tensorial sin ruido y ejecución en hardware.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 48,
      "id": "e7ee3928",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "11×20 Matrix{Float64}:\n",
              " -0.996094  0.998047  -0.990234  0.998047  …  1.0       -0.988281  0.994141\n",
              " -0.925781  0.980469  -0.878906  0.96875      0.970703  -0.976562  0.988281\n",
              " -0.916016  0.992188  -0.837891  0.957031     0.962891  -0.966797  0.953125\n",
              " -0.884766  0.970703  -0.824219  0.90625      0.908203  -0.939453  0.892578\n",
              " -0.835938  0.96875   -0.814453  0.884766     0.931641  -0.902344  0.908203\n",
              " -0.777344  0.958984  -0.78125   0.871094  …  0.935547  -0.876953  0.939453\n",
              " -0.732422  0.933594  -0.767578  0.8125       0.884766  -0.835938  0.890625\n",
              " -0.650391  0.916016  -0.771484  0.771484     0.853516  -0.773438  0.837891\n",
              " -0.537109  0.902344  -0.705078  0.742188     0.875     -0.662109  0.833984\n",
              " -0.472656  0.923828  -0.652344  0.728516     0.839844  -0.695312  0.808594\n",
              " -0.4375    0.923828  -0.613281  0.681641  …  0.890625  -0.658203  0.8125"
            ]
          },
          "execution_count": 48,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Compute expectation values\n",
        "# 0 -> 1, 1 -> -1\n",
        "z_expval(samples, i) = mean((-1)^s[i] for s in samples)\n",
        "\n",
        "magnetizations = [z_expval(all_samples[i], q) for i in 1:length(all_samples), q in 1:N]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a1b2c3d4",
      "metadata": {},
      "source": [
        "Los tres gráficos siguientes muestran la magnetización del sitio $\\langle Z_i \\rangle$ en función del índice del qubit (eje x) y del paso de Trotter (eje y). En $\\delta t = 0.05$ con $r_{\\max} = 10$, el tiempo total de evolución es $\\tau = r_{\\max} \\cdot \\delta t = 0.5$, lo cual es lo suficientemente breve como para que el patrón antiferromagnético inicial aún no se haya desvanecido; los tres métodos muestran un patrón fuertemente alternante. Los resultados de la red clásica y de la red tensorial coinciden ahora en gran medida, lo que confirma que el error de Trotter es pequeño con este tamaño de paso. Los resultados del hardware coinciden en líneas generales con los de los otros dos, aunque algunos qubits se desvían más de las simulaciones que otros, lo que refleja las variaciones en la calidad de los qubits en todo el sistema. Estas desviaciones aumentan en los pasos posteriores del método de Trotter a medida que crece la profundidad del circuito.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 49,
      "id": "cc5496cc",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/time-evolution/extracted-outputs/cc5496cc-0.svg\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 49,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# plot magnetization as a function of time\n",
        "l = @layout [a{0.3w} b{0.3w} c{0.44w}]\n",
        "plot(\n",
        "    heatmap(classical_magnetizations, title=\"Classical\", clims=(-1,1), color=:RdBu,\n",
        "        xlabel=\"Qubit\", ylabel=\"Trotter steps\", colorbar=false),\n",
        "    heatmap(tn_magnetizations, title=\"Tensor Network\", clims=(-1,1), color=:RdBu,\n",
        "        xlabel=\"Qubit\", ylabel=\"Trotter steps\", colorbar=false),\n",
        "    heatmap(magnetizations, title=\"Hardware\", clims=(-1,1), color=:RdBu,\n",
        "        xlabel=\"Qubit\", ylabel=\"Trotter steps\", colorbar=true),\n",
        "    layout=l, size=(900,300),\n",
        "    bottom_margin=5mm, left_margin=5mm, right_margin=6mm\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5e9fc890",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## Ejemplo de hardware a gran escala\n",
        "\n",
        "<span id=\"steps-1–4-in-a-single-workflow\" />\n",
        "\n",
        "### Pasos 1 a 4 en un único flujo de trabajo\n",
        "\n",
        "Ahora combinamos los cuatro pasos anteriores en un único flujo de trabajo, a una escala que supera las posibilidades de la simulación clásica exacta. En lugar de calcular la magnetización sitio por sitio, hacemos un seguimiento de una única magnitud escalar del orden antiferromagnético: la magnetización escalonada:\n",
        "\n",
        "$$\n",
        "    \\langle M_s \\rangle = \\frac{1}{N} \\sum_{i=1}^{N} (-1)^i \\langle Z_i \\rangle\n",
        "$$\n",
        "\n",
        "El signo que cambia en la suma hace que la señal sea visible. Para el estado inicial de Néel $|0101\\ldots01\\rangle$, cada término contribuye con $+1$, por lo que $\\langle M_s\\rangle = 1$, mientras que la media simple $\\frac{1}{N}\\sum_i \\langle Z_i\\rangle$ se anula identicamente para todos los $t$. A medida que el campo transversal altera el patrón alterno, $\\langle M_s\\rangle$ decae hacia $0$, por lo que la magnetización escalonada nos indica qué parte del orden inicial se conserva durante la evolución temporal.\n",
        "\n",
        "También utilizamos este ejemplo para ver cómo influye el tamaño del paso de Trotter en la precisión. Fijamos el tiempo total de evolución $T = 1.5$ y variamos el número de pasos de Trotter $r \\in \\{3, 6, 12\\}$, de modo que $\\delta t = T/r$. En el hardware, compiten dos fuentes de error: un valor más pequeño de $\\delta t$ reduce el error de Trotter, pero requiere proporcionalmente más puertas de dos qubits, lo que acumula más ruido de hardware.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 50,
      "id": "f840b38f",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "  δt = 0.5  →  4 time points, deepest circuit = 3 Trotter steps, 2q count = 594, 2q depth = 206\n",
            "  δt = 0.25  →  7 time points, deepest circuit = 6 Trotter steps, 2q count = 1188, 2q depth = 218\n",
            "  δt = 0.125  →  13 time points, deepest circuit = 12 Trotter steps, 2q count = 2376, 2q depth = 242\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "24-element Vector{QiskitIBMRuntime.Job}:\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c1a750)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e0a270)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c21a50)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e05120)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c2a6e0)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e0d140)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e0b640)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c1a200)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b312eb90)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c22060)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b31ea4e0)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c1ccf0)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c2af50)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b312ffb0)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b0e89700)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e15070)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e10d40)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004af5a5d10)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e12500)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e14870)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e10730)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b237ca60)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b2c713a0)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e13e90)"
            ]
          },
          "execution_count": 50,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# -------------------------Step 1-------------------------\n",
        "# Map classical inputs to a quantum problem.\n",
        "N_large = 100\n",
        "g_large = named_grid((N_large,))\n",
        "h_large = fill(1.0, N_large)       # transverse field on every site\n",
        "J_large = fill(1.0, N_large - 1)   # nearest-neighbor ZZ couplings on the chain\n",
        "\n",
        "T_total = 1.5                    # fixed total evolution time\n",
        "r_list = [3, 6, 12]       # varying Trotter steps; δt = T_total/r\n",
        "sweep = [(r, k) for r in r_list for k in 0:r]\n",
        "\n",
        "qc_list_large = [make_trotter_circuit(h_large, J_large, N_large, T_total/r, k)\n",
        "                 for (r, k) in sweep]\n",
        "\n",
        "# -------------------------Step 2-------------------------\n",
        "# Optimize the problem for quantum hardware execution.\n",
        "tqc_list_large = [transpile(qc, target)[1] for qc in qc_list_large]\n",
        "# Print the 2q gate count and depth of the deepest circuit at each δt\",\n",
        "for r in r_list\n",
        "    i = findfirst(==((r, r)), sweep)   # the k = r circuit reaches the full T_total\n",
        "    println(\"  δt = $(round(T_total/r, digits=4))  →  $(r+1) time points, \",\n",
        "        \"deepest circuit = $(r) Trotter steps, \",\n",
        "        \"2q count = $(two_qubit_count(tqc_list_large[i])), \",\n",
        "        \"2q depth = $(two_qubit_depth(tqc_list_large[i]))\")\n",
        "end\n",
        "\n",
        "# -------------------------Step 3-------------------------\n",
        "# Execute using Qiskit primitives.\n",
        "shots_large = 4096\n",
        "\n",
        "job_list_large = [run_sampler_job(service, backend, tqc, shots_large)\n",
        "                  for tqc in tqc_list_large]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 53,
      "id": "10004db8",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Job 1 (δt=0.5, k=0): Completed\n",
            "Job 2 (δt=0.5, k=1): Completed\n",
            "Job 3 (δt=0.5, k=2): Completed\n",
            "Job 4 (δt=0.5, k=3): Completed\n",
            "Job 5 (δt=0.25, k=0): Completed\n",
            "Job 6 (δt=0.25, k=1): Completed\n",
            "Job 7 (δt=0.25, k=2): Completed\n",
            "Job 8 (δt=0.25, k=3): Completed\n",
            "Job 9 (δt=0.25, k=4): Completed\n",
            "Job 10 (δt=0.25, k=5): Completed\n",
            "Job 11 (δt=0.25, k=6): Completed\n",
            "Job 12 (δt=0.125, k=0): Completed\n",
            "Job 13 (δt=0.125, k=1): Completed\n",
            "Job 14 (δt=0.125, k=2): Completed\n",
            "Job 15 (δt=0.125, k=3): Completed\n",
            "Job 16 (δt=0.125, k=4): Completed\n",
            "Job 17 (δt=0.125, k=5): Completed\n",
            "Job 18 (δt=0.125, k=6): Completed\n",
            "Job 19 (δt=0.125, k=7): Completed\n",
            "Job 20 (δt=0.125, k=8): Completed\n",
            "Job 21 (δt=0.125, k=9): Completed\n",
            "Job 22 (δt=0.125, k=10): Completed\n",
            "Job 23 (δt=0.125, k=11): Completed\n",
            "Job 24 (δt=0.125, k=12): Completed\n"
          ]
        }
      ],
      "source": [
        "# Run this cell to check the job status\n",
        "# Run the following cell for post-processing after all jobs complete\n",
        "for (i, job) in enumerate(job_list_large)\n",
        "    r, k = sweep[i]\n",
        "    println(\"Job $i (δt=$(round(T_total/r, digits=4)), k=$k): \",\n",
        "            get_job_status(job, service))\n",
        "end"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 54,
      "id": "a3646d14",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "24-element Vector{Float64}:\n",
              " 0.9880371093750097\n",
              " 0.697231445312484\n",
              " 0.44886718750000276\n",
              " 0.29333496093750067\n",
              " 0.9882324218750095\n",
              " 0.8109619140625324\n",
              " 0.6457958984374791\n",
              " 0.4808593749999992\n",
              " 0.36695312500000055\n",
              " 0.2963671875000012\n",
              " 0.23831542968750055\n",
              " 0.9884472656250093\n",
              " 0.8566455078125492\n",
              " 0.7951171875000282\n",
              " 0.7050732421874865\n",
              " 0.610673828124981\n",
              " 0.529980468749995\n",
              " 0.4656054687500018\n",
              " 0.4149316406250024\n",
              " 0.3799462890625011\n",
              " 0.35178710937500185\n",
              " 0.3185888671875003\n",
              " 0.31287597656250016\n",
              " 0.29621093750000077"
            ]
          },
          "execution_count": 54,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# -------------------------Step 4-------------------------\n",
        "# Post-process and return the result in the desired classical format.\n",
        "# Run this cell after all jobs are completed\n",
        "\n",
        "# ⟨M_s⟩ = (1/N) Σ (-1)^i ⟨Z_i⟩, averaged over hardware shots\n",
        "function staggered_magnetization(samples::AbstractVector{<:AbstractVector}, n::Int)\n",
        "    s = 0.0\n",
        "    for sample in samples\n",
        "        s += sum((-1)^i * (1 - 2 * sample[i]) for i in 1:n) / n\n",
        "    end\n",
        "    return s / length(samples)\n",
        "end\n",
        "\n",
        "all_samples_large = [get_sampler_job_results(job, service) for job in job_list_large]\n",
        "mags_hardware = [staggered_magnetization(s, N_large) for s in all_samples_large]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "89c310c8",
      "metadata": {},
      "source": [
        "En $N=100$, la solución exacta obtenida con el solucionador de ecuaciones diferenciales ordinarias utilizado anteriormente es inalcanzable, ya que solo el vector de estado necesitaría amplitudes de $2^{100} \\approx 10^{30}$. En su lugar, utilizamos como referencia una simulación con una red tensorial sin ruido de la misma cadena de 1D con un paso de Trotter mucho más preciso ( $r = 96$, $\\delta t \\approx 0.016$ ), en la que el error de Trotter es insignificante en comparación con cualquier $\\delta t$ que ejecutemos en hardware. Esta referencia es, en sí misma, aproximada: su error principal es ahora el truncamiento de la dimensión de los enlaces mencionado anteriormente, que se indica como «fidelidad de truncamiento» para cada ejecución.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 55,
      "id": "fbcc23a1",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Tensor-network reference:\n",
            "  δt=0.01562, 96 steps: truncation fidelity ≈ 1.0\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "([0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.125, 1.25, 1.375, 1.5], Float32[1.0, 0.9695576, 0.8870231, 0.77430177, 0.6560442, 0.5499543, 0.46285573, 0.39298016, 0.33518773, 0.28527063, 0.24147007, 0.20369667, 0.17206171])"
            ]
          },
          "execution_count": 55,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "apply_kwargs = (; maxdim=64, cutoff=1e-10, normalize_tensors=true)\n",
        "\n",
        "# Calculate the staggered magnetization given a tensor network state\n",
        "staggered_magnetization(ψ_bpc::BeliefPropagationCache, n::Int) =\n",
        "    sum((-1)^q * m for (q, m) in enumerate(z_expvals_from_tn_state(ψ_bpc, n))) / n\n",
        "\n",
        "# Evolve a TN state and record the staggered magnetization at each step.\n",
        "function compute_staggered_magnetization_tn(δt::Real, nsteps::Int; record_every::Int = 1)\n",
        "    init_gates = neel_state_gates(N_large)\n",
        "    step_gates = trotter_step_gates(h_large, J_large, N_large, δt)\n",
        "\n",
        "    ψ_bpc, fid = apply_gates_to_tn_state(init_gates, tn_initial_state(g_large); apply_kwargs)\n",
        "    times = [0.0]\n",
        "    mags = [staggered_magnetization(ψ_bpc, N_large)]\n",
        "    for k in 1:nsteps\n",
        "        ψ_bpc, fid_step = apply_gates_to_tn_state(step_gates, ψ_bpc; apply_kwargs)\n",
        "        fid *= fid_step\n",
        "        if k % record_every == 0\n",
        "            push!(times, k * δt)\n",
        "            push!(mags, staggered_magnetization(ψ_bpc, N_large))\n",
        "        end\n",
        "    end\n",
        "    println(\"  δt=$(round(δt, digits=5)), $(nsteps) steps: truncation fidelity ≈ $(round(fid, digits=5))\")\n",
        "    (times, mags)\n",
        "end\n",
        "\n",
        "# If the fidelity drifts from 1, raise `maxdim` in `apply_kwargs`.\n",
        "# Under current setting, the tensor network simulation takes ~ 3 minutes on a laptop.\n",
        "println(\"Tensor-network reference:\")\n",
        "r_ref = 96\n",
        "times_ref, mags_ref = compute_staggered_magnetization_tn(T_total / r_ref, r_ref; record_every = r_ref ÷ 12)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "af333ad4",
      "metadata": {},
      "source": [
        "El gráfico siguiente muestra la magnetización escalonada a lo largo del tiempo para los tres tamaños de paso de Trotter, en comparación con la referencia de la red tensorial sin ruido (línea discontinua negra).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 56,
      "id": "94cd122e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/time-evolution/extracted-outputs/94cd122e-0.svg\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 56,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "plt = plot(xlabel = \"Time\", ylabel = \"Staggered magnetization\",\n",
        "    title = \"N = $(N_large) on $(backend.name), T = $(T_total)\",\n",
        "    legend = :topright, ylims = (-0.05, 1.05), size = (820, 480),\n",
        "    bottom_margin = 5mm, left_margin = 5mm)\n",
        "\n",
        "# Plot tensor network reference\n",
        "plot!(plt, times_ref, mags_ref, lw = 2, ls = :dash, color = :black,\n",
        "      label = \"tensor network, δt → 0\")\n",
        "\n",
        "# Plot hardware result per Trotter step size\n",
        "for (r, stop) in zip(r_list, cumsum(r_list .+ 1))\n",
        "    plot!(plt, range(0, T_total, length = r + 1), mags_hardware[(stop - r):stop],\n",
        "          marker = :circle, markersize = 4, lw = 2,\n",
        "          label = \"hardware, δt = $(round(T_total / r, digits = 4))\")\n",
        "end\n",
        "plt"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "603fd062",
      "metadata": {},
      "source": [
        "En general, el paso de Trotter más grueso $\\delta t = 0.5$ (puntos naranjas) muestra la mayor desviación respecto a la referencia de la red tensorial, lo que se debe, presumiblemente, a una importante contribución del error de Trotter. En el paso más preciso $\\delta t = 0.25$ (puntos verdes), los resultados del hardware coinciden en mayor medida con los de referencia. En el paso más fino $\\delta t = 0.125$ (puntos morados), el error de Trotter es mínimo, pero la concordancia es peor que en $\\delta t = 0.25$. Con la mitad del tamaño del paso, cada punto temporal requiere el doble de puertas de dos qubits, y el ruido adicional compensa la reducción del error de Trotter. Por lo tanto, elegir un « $\\delta t$ » para un circuito «Trotterizado» en hardware supone un compromiso entre el error de Trotter y el ruido acumulado por las puertas adicionales.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8defe5bb",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Próximos pasos\n",
        "\n",
        "Quizá te interese el siguiente material:\n",
        "\n",
        "* [Fórmulas multiproducto para reducir el error de Trotter](/docs/tutorials/multi-product-formula)\n",
        "\n",
        "* [Curso sobre la integración de la computación cuántica y la computación de alto rendimiento](/learning/courses/integrating-quantum-and-high-performance-computing)\n",
        "\n",
        "* [Qiskit.jl](https://github.com/Qiskit/Qiskit.jl) y [QiskitIBMRuntime.jl](https://github.com/Qiskit/QiskitIBMRuntime.jl) en GitHub\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Julia 1.11",
      "language": "julia",
      "name": "julia-1.11"
    },
    "language_info": {
      "file_extension": ".jl",
      "mimetype": "application/julia",
      "name": "julia",
      "version": "1.11.5"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}