{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "8d86bf32",
      "metadata": {},
      "source": [
        "---\n",
        "title: Simulate time evolution of the transverse-field Ising model\n",
        "description: Use Qiskit.jl to simulate time evolution of the transverse-field Ising model on IBM Quantum hardware\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": [
        "# Simulate time evolution of the transverse-field Ising model\n",
        "\n",
        "*Usage estimate: 105 seconds on a Nighthawk r2 processor (NOTE: This is an estimate only. Your runtime might vary.)*\n",
        "\n",
        "## Learning outcomes\n",
        "\n",
        "1. Learn how to transpile and run quantum circuits on the hardware using Julia\n",
        "2. Learn how to post-process measurement outcomes to compute expectation values\n",
        "3. Learn how to benchmark hardware results against classical simulation to quantify the combined effects of Trotter approximation error and hardware noise\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "efde28b8",
      "metadata": {},
      "source": [
        "## Prerequisites\n",
        "\n",
        "Familiarize yourself with the following topics before starting this tutorial:\n",
        "\n",
        "* [Quantum simulation](/learning/courses/utility-scale-quantum-computing/quantum-simulation)\n",
        "\n",
        "* [The transverse field Ising model](https://en.wikipedia.org/wiki/Transverse-field_Ising_model)\n",
        "\n",
        "* [Introduction to transpilation](/docs/guides/transpile#introduction-to-transpilation)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "727912c7",
      "metadata": {},
      "source": [
        "## Background\n",
        "\n",
        "Julia is a dynamic programming language designed primarily for numerical and scientific computing. Its high-performance numerical computing capabilities make it a natural fit for quantum simulation workflows. In this tutorial, we show how Julia is used for both classical pre- and post-processing (for example, building Hamiltonians, running ODE (ordinary differential equation) solvers, and computing expectation values) and for orchestrating quantum hardware jobs, eliminating the need to switch between languages or environments.\n",
        "\n",
        "To interface with IBM Quantum® hardware from Julia, this tutorial uses two packages from the Qiskit ecosystem: `Qiskit.jl` wraps the Qiskit C library and provides circuit construction and transpilation functionality in Julia; `QiskitIBMRuntime.jl` connects to IBM Quantum hardware through the IBM Quantum Compute Service client, enabling job submission and result retrieval directly from Julia.\n",
        "\n",
        "In this tutorial, we consider the trotterized evolution of the transverse-field Ising model on a 1D chain with nearest-neighbor interactions:\n",
        "\n",
        "$$\n",
        "    H = \\sum_{\\langle i,j\\rangle}J_{ij}Z_iZ_j + \\sum_i h_i X_i\n",
        "$$\n",
        "\n",
        "To implement the time evolution $e^{-iH\\tau}$, we divide the time interval $\\tau$ into $r$ steps and define $\\Delta\\tau=\\tau/r$. The second-order Trotter-Suzuki decomposition gives the following:\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",
        "For circuit construction, each Trotter step is implemented as a sequence of single-qubit $R_x$ rotations and two-qubit $R_{ZZ}$ gates. The circuit begins by preparing the Néel state $|0101\\cdots\\rangle$ using X gates on alternating qubits. Each subsequent Trotter step applies: (1) $R_x(h_i\\Delta\\tau)$ on every qubit, (2) $R_{ZZ}(2J_{ij}\\Delta\\tau)$ on each neighboring pair along the chain, and (3) $R_x(h_i\\Delta\\tau)$ again on every qubit. The total circuit depth grows linearly with the number of Trotter steps $r$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "020f72af",
      "metadata": {},
      "source": [
        "## Requirements\n",
        "\n",
        "Note that this tutorial requires macOS or Linux. Qiskit.jl is not currently supported on Windows (tracked in [this open issue](https://github.com/Qiskit/Qiskit.jl/issues/15)).\n",
        "\n",
        "To get started, install Julia, following the instructions on the [Julia download page](https://julialang.org/downloads/). This tutorial was developed with Julia 1.11; install it with `juliaup add 1.11`.\n",
        "\n",
        "Next, run the following command in a terminal to install the Julia package `IJulia` into the global environment, so that you can run Julia inside the Jupyter notebook.\n",
        "\n",
        "```\n",
        "julia -e 'using Pkg; Pkg.add(\"IJulia\")'\n",
        "```\n",
        "\n",
        "We use Julia's built-in package manager to set up the project environment. There are two ways to set up the environment.\n",
        "\n",
        "*Option 1*: temporary environment. You can run the following code cell to set up a temporary environment and install the required packages;\n",
        "\n",
        "*Option 2*: reproduce the exact tested environment. Set `download_toml_files = true` in the cell below. The cell will download `Project.toml` and `Manifest.toml` from the documentation [repository](https://github.com/Qiskit/documentation/tree/main/docs/tutorials/assets/time-evolution/julia) into an `env_tutorial/time-evolution/` folder next to this notebook, then activate that environment and install the exact package versions it records. The project file describes the environment at a high level, for example, the `[deps]` section lists all dependencies. The manifest file pins the precise version of every package (including indirect dependencies), which makes the environment reproducible. See the [Julia documentation](https://pkgdocs.julialang.org/v1/toml-files/) for more details.\n",
        "\n",
        "The following dependencies will be installed in the environment.\n",
        "\n",
        "For quantum circuit construction and execution:\n",
        "\n",
        "* `Qiskit.jl`\n",
        "* `QiskitIBMRuntime.jl`\n",
        "\n",
        "For classical simulation:\n",
        "\n",
        "* `OrdinaryDiffEq.jl`\n",
        "* `TensorNetworkQuantumSimulator.jl`\n",
        "\n",
        "For post-processing results and visualization:\n",
        "\n",
        "* `StatsBase.jl`\n",
        "* `Plots.jl`\n",
        "\n",
        "This tutorial is tested with `Qiskit.jl` version 0.6.0 and `QiskitIBMRuntime.jl` version 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": [
        "Up to this point, we have set up the Julia project environment to run the notebook. In order to run the workflow on an IBM quantum processing unit, you need an IBM Quantum account and API token to instantiate service from `qiskit-ibm-runtime`. Follow the \"Install and authenticate\" steps in the [Run your first circuit on hardware](/docs/guides/hello-world) topic to generate your API token and find your instance CRN.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "613853a6",
      "metadata": {},
      "source": [
        "## Setup\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": [
        "We also define the following utility function, which returns the value of the bit in a bitstring `v` at position `i`. For example, with `v = 6` (binary `110`),\n",
        "\n",
        "* `bit_at(6, 1)` returns `0`,\n",
        "* `bit_at(6, 2)` returns `1`,\n",
        "* `bit_at(6, 3)` returns `1`.\n",
        "\n",
        "This follows the [little-endian convention](/docs/guides/bit-ordering) used in Qiskit: the position `i` is indexed from the least-significant (the \"rightmost\") bit.\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": [
        "## Small-scale simulator example\n",
        "\n",
        "We consider a 1D chain of $N$ qubits, described by the transverse-field Ising model above. For the system of interest, we specify below the system size `N`, the Trotter step size `δt`, and the total number of Trotter steps `r_max`. The total evolution time is `δt * r_max`. Note that Julia supports Unicode identifiers such as `δt`; in the notebook or Julia REPL, type `\\delta` followed by Tab to enter `δ`. For a full reference, see the [Julia Unicode input documentation](https://docs.julialang.org/en/v1/manual/unicode-input/).\n",
        "\n",
        "### Exact solution\n",
        "\n",
        "To establish a baseline for comparing results from the quantum hardware, we first demonstrate the classical simulation workflow for a small-scale problem. We build the Ising Hamiltonian as a sparse matrix, then obtain the exact time evolution by numerically integrating the Schrödinger equation using `ODEProblem` from `OrdinaryDiffEq.jl`. This approach scales exponentially in the number of qubits $N$. It requires storing the full $2^N$-dimensional state vector. For $N=20$ the Hilbert space already has over one million dimensions, making it impractical for larger systems.\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": [
        "We define the right-hand side of the Schrödinger equation in the in-place form `schrodinger!(dψ, ψ, H, t)`, which computes $d\\psi/dt = -iH\\psi$ using a sparse matrix-vector multiplication. We then set up an `ODEProblem` with the Néel state as the initial condition and solve it over the time span $[0, r \\cdot \\delta t]$, saving the state at each time step $\\delta t$. The solver used is `Tsit5()`, a standard explicit fourth/fifth-order Runge-Kutta method suitable for non-stiff problems.\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": [
        "From the solution, which describes the state vector $\\psi(t)$, we can obtain the magnetization per site, expressed as the single-qubit $\\langle Z\\rangle$ expectation values as a function of time. We compare this with the results obtained from the Trotterized circuits.\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": [
        "### Small-scale simulation of the Trotterized circuits\n",
        "\n",
        "In the following, we show the classical simulation of the noiseless circuits using tensor network methods supported by `TensorNetworkQuantumSimulator.jl`, so we can validate our circuit construction. These methods provide a baseline to compare with results from the quantum hardware.\n",
        "\n",
        "We first define the lattice as a 1D chain graph using `named_grid((N,))`, where each vertex is a tuple `(i,)`. We then specify the circuit gates as a list of tuples `(gate_name, qubit_indices, gate_parameter)`, which serves as the input format for the tensor network simulator.\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": [
        "We use the belief propagation algorithm for tensor network contraction. This method is efficient for circuits with limited entanglement, but its accuracy degrades as entanglement grows with circuit depth. The `maxdim` and `cutoff` parameters control the trade-off between accuracy and computational cost. Similarly, we compute the magnetization at each site to compare later.\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": [
        "### Step 1: Map classical inputs to a quantum problem\n",
        "\n",
        "Now we construct the Trotterized time-evolution circuit using `Qiskit.jl`. The circuit mirrors the tensor network version: it initializes the Néel state, applies $r$ Trotter steps of $R_x$ and $R_{ZZ}$ gates, and finally measures all qubits in the Z basis.\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": [
        "We build a list of circuits for Trotter steps 0 through 10, corresponding to evolution times $\\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": [
        "### Step 2: Optimize problem for quantum hardware execution\n",
        "\n",
        "To run on quantum hardware, the circuits must first be transpiled. This includes the following steps: select a set of physical qubits to map the circuit onto, recompile the gates into the native instruction set of the backend, and optimize the resulting circuit depth. We use `least_busy()` to automatically select the least-busy available backend, `target_from_backend()` to retrieve its native gate set and qubit connectivity, and `transpile()` to perform the compilation.\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": [
        "After transpilation, we inspect two properties of the compiled circuits. `get_circuit_layout()` returns the set of physical qubit indices selected for the circuit. `two_qubit_depth()` computes the two-qubit gate depth — the length of the longest chain of the two-qubit operations in the circuit — which is a useful indicator of noise accumulation on the 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": [
        "Below we print the two-qubit gate count and circuit depth at each Trotter step; as expected, both grow linearly with the number of steps. Note that the fractional $R_\\mathrm{ZZ}$ gate is not yet available through the C API (see [qiskit-ibm-runtime-c#29](https://github.com/Qiskit/qiskit-ibm-runtime-c/issues/29)). As a result, each `RZZGate` is transpiled into two two-qubit gates instead of one, which inflates the two-qubit gate counts.\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": [
        "### Step 3: Execute using Qiskit primitives\n",
        "\n",
        "Now we can submit the transpiled circuits to the backend as `Sampler` jobs with `shots` specified.\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": [
        "As the jobs are completed, we can retrieve their results. Note that `get_sampler_job_results` function will block until the job is completed.\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": [
        "### Step 4: Post-process and return result in desired classical format\n",
        "\n",
        "From the bitstring samples obtained from the quantum hardware, we compute the magnetization per site (the single-qubit $\\langle Z\\rangle$ expectation values) by averaging $(-1)^{b_i}$ over all shots, where $b_i$ is the measured bit for qubit $i$. We then plot the magnetization as a heatmap over qubits and Trotter steps, comparing the three methods side by side: exact classical simulation, noiseless tensor network simulation, and hardware execution.\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": [
        "The three panels below show the site magnetization $\\langle Z_i \\rangle$ as a function of qubit index (x-axis) and Trotter step (y-axis). At $\\delta t = 0.05$ with $r_{\\max} = 10$, the total evolution time is $\\tau = r_{\\max} \\cdot \\delta t = 0.5$, which is short enough that the initial antiferromagnetic pattern has not yet decayed — all three methods show a strongly alternating pattern. The classical and tensor network results are now in close agreement, confirming that Trotter error is small at this step size. The hardware results broadly track the other two, though some qubits deviate from the simulations more than others, reflecting variation in qubit quality across the backend. These deviations grow at later Trotter steps as the circuit depth increases.\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": [
        "## Large-scale hardware example\n",
        "\n",
        "### Steps 1–4 in a single workflow\n",
        "\n",
        "We now combine all four steps above into a single workflow, at a scale beyond the reach of exact classical simulation. Instead of resolving the magnetization site by site, we track a single scalar measure of antiferromagnetic order, the staggered magnetization:\n",
        "\n",
        "$$\n",
        "    \\langle M_s \\rangle = \\frac{1}{N} \\sum_{i=1}^{N} (-1)^i \\langle Z_i \\rangle\n",
        "$$\n",
        "\n",
        "The alternating sign in the summation makes the signal visible. For the Néel initial state $|0101\\ldots01\\rangle$ every term contributes $+1$, so $\\langle M_s\\rangle = 1$, whereas the plain average $\\frac{1}{N}\\sum_i \\langle Z_i\\rangle$ vanishes identically for all $t$. As the transverse field scrambles the alternating pattern, $\\langle M_s\\rangle$ decays toward $0$, so the staggered magnetization tells us how much of the initial order survives during the time evolution.\n",
        "\n",
        "We also use this example to see how the Trotter step size affects accuracy. We fix the total evolution time $T = 1.5$ and vary the number of Trotter steps $r \\in \\{3, 6, 12\\}$, so that $\\delta t = T/r$. On hardware, two error sources compete: a smaller $\\delta t$ reduces Trotter error, but requires proportionally more two-qubit gates, which accumulate more hardware noise.\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": [
        "At $N=100$ the exact solution from the ODE solver used above is out of reach, because the state vector alone would need $2^{100} \\approx 10^{30}$ amplitudes. Instead we use a noiseless tensor network simulation of the same 1D chain with a much finer Trotter step ($r = 96$, $\\delta t \\approx 0.016$) as the reference, where the Trotter error is negligible compared with any $\\delta t$ we run on hardware. This reference is itself approximate: its dominant error is now the bond-dimension truncation discussed above, reported as a truncation fidelity for each run.\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": [
        "The plot below shows the staggered magnetization over time for the three Trotter step sizes, against the noiseless tensor network reference (dashed black).\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": [
        "Overall, the coarsest Trotter step $\\delta t = 0.5$ (orange dots) shows the largest deviation from the tensor network reference, presumably with a large contribution from Trotter error. At the finer step $\\delta t = 0.25$ (green dots), the hardware results agree more closely with the reference. At the finest step $\\delta t = 0.125$ (purple dots), the Trotter error is smallest, yet the agreement is worse than at $\\delta t = 0.25$. With half the step size, each time point requires twice as many two-qubit gates, and the additional noise outweighs the reduction in Trotter error. Choosing $\\delta t$ for a Trotterized circuit on hardware is therefore a trade-off between Trotter error and the noise accumulated from additional gates.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8defe5bb",
      "metadata": {},
      "source": [
        "## Next steps\n",
        "\n",
        "You might be interested in the following material:\n",
        "\n",
        "* [Multi-product formulas to reduce Trotter error](/docs/tutorials/multi-product-formula)\n",
        "\n",
        "* [Integrating quantum and high-performance computing course](/learning/courses/integrating-quantum-and-high-performance-computing)\n",
        "\n",
        "* [Qiskit.jl](https://github.com/Qiskit/Qiskit.jl) and [QiskitIBMRuntime.jl](https://github.com/Qiskit/QiskitIBMRuntime.jl) on 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
}