{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Simular a dispersão de nêutrons com um fluxo de trabalho sem servidor utilizando AQC + dinâmica de Trotter\"\n",
        "description: \"Calcule o fator de estrutura dinâmica S(q, w) do ímã quântico KCuF3 executando um fluxo de trabalho de Trotter com compressão AQC como um modelo de função implantado.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore Trotter Trotterization spinon spinons KCuF quimb DMRG magnon antiferromagnetic Suzuki fidelities isa COBYQA Gjjc qpoints viridis fontsize vmax vmin */}\n",
        "\n",
        "<span id=\"simulate-neutron-scattering-with-an-aqc-+-trotter-dynamics-serverless-workflow\" />\n",
        "\n",
        "# Simular a dispersão de nêutrons com um fluxo de trabalho sem servidor utilizando AQC + dinâmica de Trotter\n",
        "\n",
        "Estimativa de tempo de *execução: 18 minutos em um processador Heron r3 (NOTA: Trata-se apenas de uma estimativa. (O tempo de execução pode variar.)*\n",
        "\n",
        "<Admonition type=\"note\" title=\"Qual tutorial devo usar?\">\n",
        "  Use este tutorial para realizar o experimento de espalhamento de nêutrons por meio de uma função implementada no `Qiskit Serverless`, que agrupa a construção, a compactação e a execução do circuito em uma única chamada. A compactação utiliza os recursos de computação e memória do worker sem servidor e pode continuar mesmo depois que você fechar seu notebook; a preparação do estado fundamental e o pós-processamento ainda são executados localmente. Primeiro, você deve [implantar o modelo de função](/docs/guides/function-template-aqc-trotter). Para aprender a implementação passo a passo, consulte o [tutorial original](/docs/tutorials/simulate-neutron-scattering).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "learning-outcomes",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Resultados do aprendizado\n",
        "\n",
        "* Como um espectro de espalhamento inelástico de nêutrons se relaciona com o fator de estrutura dinâmico $S(q, \\omega)$ de um ímã quântico do tipo “ 1D ”.\n",
        "* Como preparar o estado fundamental KCuF$_3$ (Heisenberg isotrópico) utilizando o grupo de renormalização da matriz de densidade (DMRG) e a maximização da fidelidade do estado de produto matricial (MPS).\n",
        "* Como executar a evolução temporal do Trotter, a compactação de circuitos por compilação quântica aproximada (AQC) e a execução mitigada por meio de uma única chamada de função.\n",
        "* Como realizar o pós-processamento das séries temporais de “ $\\langle \\sigma_z \\rangle(t)$ ” por local, transformando-as em “ $S(q, \\omega)$ ” e identificando o continuum de dois spinons.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "prerequisites",
      "metadata": {},
      "source": [
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Pré-requisitos\n",
        "\n",
        "* Conhecimento dos [padrões do Qiskit](/docs/guides/intro-to-patterns) e [`SparsePauliOp`](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp) [da evolução temporal de Trotter](/learning/courses/utility-scale-quantum-computing/quantum-simulation).\n",
        "* É útil ter um conhecimento básico dos métodos de redes tensoriais (DMRG e MPS), mas não é obrigatório, assim como a familiaridade com a [`qiskit-addon-aqc-tensor`](https://github.com/Qiskit/qiskit-addon-aqc-tensor) biblioteca que a função utiliza para compactar circuitos de Trotter.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "background",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Segundo plano\n",
        "\n",
        "A dispersão inelástica de nêutrons mede o fator de estrutura dinâmico $S(q, \\omega)$, que é a transformada de Fourier no espaço e no tempo da função de correlação spin-spin; portanto, a reprodução de $S(q, \\omega)$ a partir de um modelo microscópico de spin constitui um teste direto e falsificável de uma simulação quântica. Este tutorial estuda KCuF$_3$, uma cadeia de Heisenberg antiferromagnética spin- $\\frac{1}{2}$, cujas excitações não são inversões de spin únicas, mas pares de spinons fracionados: em vez de uma dispersão nítida de magnons, $S(q, \\omega)$ mostra um amplo *continuum de dois spinons*, limitado inferiormente por $\\tfrac{\\pi}{2}|\\sin q|$ e superiormente por $\\pi|\\sin(q/2)|$. Essas são as curvas tracejadas nos gráficos a seguir. A física completa e a comparação com os dados medidos de nêutrons são abordadas no [tutorial](/docs/tutorials/simulate-neutron-scattering) original e em Lee et al., [arXiv:2603.15608](https://arxiv.org/abs/2603.15608).\n",
        "\n",
        "O fluxo de trabalho quântico reflete o experimento de espalhamento:\n",
        "\n",
        "1. Prepare o estado fundamental da cadeia $|\\psi_0\\rangle$.\n",
        "2. Comece com uma perturbação local no ponto central, uma rotação do tipo “ $\\pi/2$ ” $Z$, imitando a transferência de momento e energia do nêutron.\n",
        "3. Evoluir no tempo sob o hamiltoniano de Heisenberg, $e^{-iHt}$, utilizando a fórmula do produto de Trotter.\n",
        "4. Meça a magnetização por site $\\langle \\sigma_z^j \\rangle(t)$. Em função do site $j$ e do tempo $t$, essa é exatamente a função de Green retardada $G^R(j, j_c, t)$; portanto, não é necessária nenhuma conversão antes da transformada de Fourier na etapa 5.\n",
        "5. A transformada de Fourier de $G^R$ resulta em $S(q, \\omega)$.\n",
        "\n",
        "Podem surgir problemas na etapa 3, quando os circuitos de Trotter exatos para evoluções longas se tornam complexos demais para o hardware. O AQC com redes tensoriais resolve essa questão ao compactar um bloco de passos de Trotter em um ansatz parametrizado fixo e raso, cuja fidelidade do estado em relação à evolução exata é maximizada classicamente com um simulador MPS ( [arXiv:2301.08609](https://arxiv.org/abs/2301.08609) ). O [modelo AQC Dynamics](/docs/guides/function-template-aqc-trotter) agrupa todo esse núcleo quântico (síntese de Trotter, compactação AQC e execução mitigada) em uma única chamada:\n",
        "\n",
        "| PRE (este caderno)                                                                                                                         | FUNÇÃO (`aqc-dynamics-function`)                                                                                                                 | PUBLICAR (este caderno)                         |\n",
        "| ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------- |\n",
        "| Estado fundamental obtido por DMRG combinado com a maximização da fidelidade do MPS, com o “kick” do nêutron incorporado ao mesmo circuito | Síntese de Trotter → compressão AQC → execução em `statevector`, `fake`, ou `runtime`, retornando um $\\langle \\sigma_z^j \\rangle(t)$ e por local | $S(q, \\omega)$ o, o fator de estrutura dinâmica |\n",
        "\n",
        "O trabalho específico do experimento fica aqui no caderno: preparação do estado fundamental (PRE) e o pós-processamento “ $S(q, \\omega)$ ” (POST). As duas etapas que exigem muitos recursos quânticos, a compactação e a execução, são executadas dentro da função.\n",
        "\n",
        "Este tutorial é um complemento do [artigo](/docs/tutorials/simulate-neutron-scattering) “Simulação da dispersão de nêutrons em materiais quânticos com circuitos quânticos”, que desenvolve o mesmo experimento passo a passo: o mesmo modelo KCuF$_3$, a preparação do estado fundamental, o impulso de nêutrons e o pós-processamento, com a síntese de Trotter, a compactação AQC e a execução mitigada descritas passo a passo. Leia esse tutorial para saber como funciona a compactação AQC. Leia este artigo para realizar o mesmo experimento por meio de um modelo de função implantado: o núcleo quântico se torna uma única chamada de função, e a compactação AQC, que leva várias horas, é executada dentro do worker do Serverless, em vez de na sua máquina; assim, você não precisa de um sistema HPC nem de manter o kernel aberto enquanto ela é executada. Essa mesma chamada também aciona outros experimentos de dinâmica do 1D.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "requirements",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Requisitos\n",
        "\n",
        "Antes de iniciar este tutorial, certifique-se de ter o seguinte:\n",
        "\n",
        "* A função foi implantada na sua conta do Qiskit Serverless. Execute primeiro o modelo de função complementar: [implante e execute o modelo de função AQC + dinâmica de Trotter](/docs/guides/function-template-aqc-trotter). Esse guia explica passo a passo como obter os arquivos-fonte e enviar a função para a sua conta. Este tutorial apenas chama a função implantada.\n",
        "\n",
        "* IBM Quantum® credenciais salvas para `QiskitServerless` (consulte o modelo de função). Ambos os exemplos deste tutorial chamam a função implantada; portanto, ambos precisam dela.\n",
        "\n",
        "* Qiskit SDK v2.0 ou versão posterior (`pip install qiskit`).\n",
        "\n",
        "* O cliente do Catálogo do Qiskit IBM (`pip install qiskit-ibm-catalog`).\n",
        "\n",
        "* NumPy, SciPy, e Matplotlib (`pip install numpy scipy matplotlib`). É necessário o SciPy 1.14 ou uma versão posterior para o otimizador COBYQA utilizado na preparação do estado fundamental.\n",
        "\n",
        "* A pilha de redes tensoriais AQC, pois a preparação do estado fundamental na Etapa 1 é executada localmente neste notebook: `pip install 'qiskit-addon-aqc-tensor[quimb-jax]==0.3.1'`.\n",
        "\n",
        "A primeira chamada a uma função recém-implantada fica em espera enquanto o worker do Serverless instala suas dependências; portanto, é de se esperar uma latência adicional nessa execução.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "setup-md",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Instalação\n",
        "\n",
        "Importe as bibliotecas e defina os auxiliares específicos do experimento que serão utilizados posteriormente: `build_gs_ansatz` (o ansatz variacional do hamiltoniano, ou HVA, para a preparação do estado fundamental), `prepare_ground_state` (DMRG com maximização da fidelidade MPS) e `get_spectrum`, `plot_green`, e `plot_spectrum` (o pós-processamento “ $S(q, \\omega)$ ”). Essas informações foram adaptadas do [tutorial](/docs/tutorials/simulate-neutron-scattering) original sobre espalhamento de nêutrons.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "setup-imports",
      "metadata": {},
      "outputs": [],
      "source": [
        "from functools import partial\n",
        "\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "import scipy.optimize\n",
        "\n",
        "import quimb.tensor as qtn\n",
        "from qiskit import QuantumCircuit\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit_addon_aqc_tensor.simulation import tensornetwork_from_circuit\n",
        "from qiskit_addon_aqc_tensor.simulation.quimb import QuimbSimulator\n",
        "from qiskit_ibm_catalog import QiskitServerless"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "setup-helpers",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Setup complete - helpers defined.\n"
          ]
        }
      ],
      "source": [
        "#  Dynamical structure factor via discrete Fourier transform\n",
        "\n",
        "\n",
        "def get_spectrum(n, Gjjc, dt, time_steps, q_steps, w_steps):\n",
        "    \"\"\"Compute the dynamical structure factor from the retarded Green's function.\n",
        "\n",
        "    Uses the center-site approximation and a discrete Fourier transform.\n",
        "    \"\"\"\n",
        "    green = Gjjc / 4  # sigma -> S=1/2\n",
        "    omega_max = np.pi / dt\n",
        "    qpoints = np.arange(0, 2 * np.pi, 2 * np.pi / q_steps)\n",
        "    omegas = np.arange(0, omega_max, omega_max / w_steps)\n",
        "    green_map = np.zeros((omegas.shape[0], qpoints.shape[0]))\n",
        "    center = n // 2 - 1\n",
        "    for iw, w in enumerate(omegas):\n",
        "        exponent = np.exp(1j * w * dt * np.arange(1, time_steps + 1))\n",
        "        S_w = np.dot(green.T, exponent) * dt\n",
        "        for iq, q in enumerate(qpoints):\n",
        "            q_matrix = np.exp(-1j * q * np.arange(-center, center + 2, 1))\n",
        "            green_map[iw, iq] = np.imag(np.dot(S_w, q_matrix))\n",
        "    return green_map\n",
        "\n",
        "\n",
        "#  Plotting helpers\n",
        "\n",
        "\n",
        "def plot_spectrum(\n",
        "    dsf,\n",
        "    dt,\n",
        "    q_steps,\n",
        "    w_steps,\n",
        "    lower_bound=False,\n",
        "    upper_bound=False,\n",
        "    title=None,\n",
        "):\n",
        "    \"\"\"Heat-map of the dynamical structure factor.\"\"\"\n",
        "    omega_max = np.pi / dt\n",
        "    qpoints = np.arange(0, 2 * np.pi, 2 * np.pi / q_steps)\n",
        "    omegas = np.arange(0, omega_max, omega_max / w_steps)\n",
        "    x, y = np.meshgrid(qpoints, omegas)\n",
        "    fig, ax = plt.subplots(figsize=(8, 5))\n",
        "    c = ax.pcolormesh(x, y, dsf / np.max(dsf), cmap=\"viridis\", shading=\"auto\")\n",
        "    fig.colorbar(c, ax=ax, label=\"Normalized intensity\")\n",
        "    if lower_bound:\n",
        "        ax.plot(\n",
        "            qpoints,\n",
        "            np.pi * np.abs(np.sin(qpoints)) / 2,\n",
        "            \"--\",\n",
        "            color=\"white\",\n",
        "            lw=1.5,\n",
        "            label=\"Lower bound\",\n",
        "        )\n",
        "    if upper_bound:\n",
        "        ax.plot(\n",
        "            qpoints,\n",
        "            np.pi * np.abs(np.sin(qpoints / 2)),\n",
        "            \"--\",\n",
        "            color=\"red\",\n",
        "            lw=1.5,\n",
        "            label=\"Upper bound\",\n",
        "        )\n",
        "    ax.set_ylim(0, 3.6)\n",
        "    ax.set_xlim(0, 2 * np.pi - 2 * np.pi / q_steps)\n",
        "    ax.set_xlabel(r\"$q$\", fontsize=16)\n",
        "    ax.set_ylabel(r\"$\\tilde{\\omega} = \\omega / J$\", fontsize=16)\n",
        "    ax.set_xticks([0, np.pi / 2, np.pi, 3 * np.pi / 2, 2 * np.pi])\n",
        "    ax.set_xticklabels([\"0\", r\"$\\pi/2$\", r\"$\\pi$\", r\"$3\\pi/2$\", r\"$2\\pi$\"])\n",
        "    if lower_bound or upper_bound:\n",
        "        ax.legend(loc=\"upper right\", fontsize=11)\n",
        "    if title:\n",
        "        ax.set_title(title, fontsize=14)\n",
        "    plt.tight_layout()\n",
        "    plt.show()\n",
        "\n",
        "\n",
        "def plot_green(n, Gjjc, time_steps, dt, title=None):\n",
        "    \"\"\"Heat-map of the retarded Green's function in real space and time.\"\"\"\n",
        "    fig, ax = plt.subplots(figsize=(8, 6))\n",
        "    t_axis = np.arange(1, time_steps + 1) * dt\n",
        "    site_axis = np.arange(n)\n",
        "    x, y = np.meshgrid(t_axis, site_axis)\n",
        "    c = ax.pcolormesh(\n",
        "        x,\n",
        "        y,\n",
        "        np.real(Gjjc).T,\n",
        "        cmap=\"RdBu\",\n",
        "        vmax=0.5,\n",
        "        vmin=-0.5,\n",
        "        shading=\"auto\",\n",
        "    )\n",
        "    fig.colorbar(c, ax=ax, label=r\"Re $G^R(j, j_c, t)$\")\n",
        "    ax.set_xlabel(r\"Time  ($t / J^{-1}$)\", fontsize=16)\n",
        "    ax.set_ylabel(\"Site index $j$\", fontsize=16)\n",
        "    if title:\n",
        "        ax.set_title(title, fontsize=14)\n",
        "    plt.tight_layout()\n",
        "    plt.show()\n",
        "\n",
        "\n",
        "#  Variational ground-state ansatz (HVA)\n",
        "\n",
        "\n",
        "def _apply_xxz_pair_gate(qc, q0, q1, theta):\n",
        "    \"\"\"Apply the parameterized XXZ-type two-qubit gate used in the HVA.\"\"\"\n",
        "    qc.cx(q0, q1)\n",
        "    qc.rz(theta, q1)\n",
        "    qc.h(q0)\n",
        "    qc.rz(theta + np.pi / 2, q0)\n",
        "    qc.cx(q0, q1)\n",
        "    qc.rz(-theta, q1)\n",
        "    qc.h(q1)\n",
        "    qc.cx(q1, q0)\n",
        "    qc.rz(np.pi / 2, q1)\n",
        "    qc.rz(-np.pi / 2, q0)\n",
        "    qc.h(q1)\n",
        "    qc.h(q0)\n",
        "\n",
        "\n",
        "def build_gs_ansatz(n, params, layers):\n",
        "    \"\"\"Build the Hamiltonian variational ansatz (HVA) circuit for\n",
        "    ground-state preparation of the 1D Heisenberg model.\n",
        "\n",
        "    Starts from a product of singlet pairs and applies alternating\n",
        "    odd/even layers of parameterized XXZ gates. For layer r,\n",
        "    params[2 * r] is the odd-layer (inter-pair) angle and\n",
        "    params[2 * r + 1] is the even-layer (intra-pair) angle.\n",
        "    \"\"\"\n",
        "    qc = QuantumCircuit(n)\n",
        "    # Initial singlet product state\n",
        "    for i in range(n // 2):\n",
        "        qc.x(2 * i)\n",
        "        qc.x(2 * i + 1)\n",
        "        qc.h(2 * i + 1)\n",
        "        qc.cx(2 * i + 1, 2 * i)\n",
        "    # Variational layers\n",
        "    for r in range(layers):\n",
        "        for i in range(1, (n + 1) // 2):  # odd layer\n",
        "            _apply_xxz_pair_gate(qc, 2 * i - 1, 2 * i, params[2 * r])\n",
        "        for i in range(n // 2):  # even layer\n",
        "            _apply_xxz_pair_gate(qc, 2 * i, 2 * i + 1, params[2 * r + 1])\n",
        "    return qc\n",
        "\n",
        "\n",
        "def prepare_ground_state(n, gs_layers=5, max_bond=128, cutoff=1e-8):\n",
        "    \"\"\"Prepare the KCuF3 (isotropic Heisenberg) ground state as a QuantumCircuit.\n",
        "\n",
        "    Runs DMRG (quimb MPO + DMRG2) to get the chain's ground state, then optimizes\n",
        "    the HVA angles to maximize the MPS overlap |<psi_ansatz|psi_DMRG>|^2. No exact\n",
        "    diagonalization, so it scales to larger n.\n",
        "    \"\"\"\n",
        "    J = Jz = 1.0\n",
        "    builder = qtn.SpinHam1D(S=1 / 2)\n",
        "    builder += J * 0.5, \"+\", \"-\"\n",
        "    builder += J * 0.5, \"-\", \"+\"\n",
        "    builder += Jz, \"Z\", \"Z\"\n",
        "    H_mpo = builder.build_mpo(L=n)\n",
        "    dmrg = qtn.DMRG2(H_mpo)\n",
        "    dmrg.solve(tol=1e-8, verbosity=0)\n",
        "\n",
        "    gs_sim = QuimbSimulator(\n",
        "        quimb_circuit_factory=partial(\n",
        "            qtn.CircuitMPS, gate_opts=dict(cutoff=cutoff, max_bond=max_bond)\n",
        "        ),\n",
        "        autodiff_backend=\"jax\",\n",
        "    )\n",
        "\n",
        "    def gs_infidelity(params):\n",
        "        psi = tensornetwork_from_circuit(\n",
        "            build_gs_ansatz(n, params, gs_layers), gs_sim\n",
        "        ).psi\n",
        "        return 1 - abs(psi.H @ dmrg.state) ** 2\n",
        "\n",
        "    # Seed and optimizer match the original tutorial. Each layer starts at\n",
        "    # [0, pi/2]: an odd-layer angle of 0 makes the inter-pair gate the identity,\n",
        "    # and an even-layer angle of pi/2 makes the intra-pair gate a SWAP (since\n",
        "    # 0.5 * (XX + YY + ZZ) = SWAP - I/2). That puts the seed at the singlet-pair\n",
        "    # product limit, which is already a decent approximation to the Heisenberg\n",
        "    # ground state, so the optimizer only has to refine it. The small jitter\n",
        "    # (fixed RNG seed, so runs are reproducible) breaks the exact symmetry\n",
        "    # between layers; COBYQA then runs for up to 100 iterations.\n",
        "    rng = np.random.default_rng(12345)\n",
        "    x0 = np.tile([0.0, np.pi / 2], gs_layers) + rng.normal(\n",
        "        scale=0.1, size=2 * gs_layers\n",
        "    )\n",
        "    result_gs = scipy.optimize.minimize(\n",
        "        gs_infidelity, x0, method=\"COBYQA\", options={\"maxiter\": 100}\n",
        "    )\n",
        "    print(f\"DMRG ground-state energy: {dmrg.energy:.6f}\")\n",
        "    print(f\"GS fidelity: {1 - result_gs.fun:.4f}\")\n",
        "    return build_gs_ansatz(n, result_gs.x, gs_layers)\n",
        "\n",
        "\n",
        "print(\"Setup complete - helpers defined.\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "setup-load-md",
      "metadata": {},
      "source": [
        "<span id=\"load-the-function-template\" />\n",
        "\n",
        "### Carregar o modelo de função\n",
        "\n",
        "Acesse Qiskit Serverless e carregue o arquivo implantado `aqc-dynamics-function`. Ambos os exemplos deste tutorial chamam o mesmo `fn` handle, portanto, a função é carregada apenas uma vez, aqui.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "setup-connect",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Credentials are read from the account saved once via QiskitServerless.save_account(...)\n",
        "serverless = QiskitServerless()\n",
        "fn = serverless.load(\"aqc-dynamics-function\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "small-md",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulator-example\" />\n",
        "\n",
        "## Exemplo de simulador em pequena escala\n",
        "\n",
        "Primeiramente, executamos o fluxo de trabalho completo em uma pequena rede de 10 lojas, utilizando exatamente o mesmo `statevector` backend. Isso valida o fluxo PRE → FUNCTION → POST antes de consumir qualquer tempo da QPU.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "small-s1-md",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "### Etapa 1: Mapeie entradas clássicas para um problema quântico\n",
        "\n",
        "Construa o hamiltoniano KCuF$_3$ como um hamiltoniano de Heisenberg isotrópico ( $XX + YY + ZZ$`SparsePauliOp` com acoplamento $\\tfrac14$ em cada ligação entre vizinhos mais próximos; as cadeias são operadores de Pauli, de modo que $\\tfrac14$ fornece o acoplamento de spin $\\frac{1}{2}$ ). Prepare o estado fundamental com DMRG e maximização da fidelidade MPS e, em seguida, incorpore o “neutron kick”: uma rotação $\\pi/2$ $Z$ no site central. O circuito preparado é o que passamos para a função como `initial_state`. Deixamos esse valor como `observables` padrão ( $Z$ por local), que corresponde exatamente à leitura $\\langle \\sigma_z^j \\rangle(t)$ necessária para o fluxo de trabalho de nêutrons.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "small-s1-code",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "DMRG ground-state energy: -4.258035\n",
            "GS fidelity: 0.9841\n",
            "Prepared 10-qubit ground state with the neutron kick at site 4.\n"
          ]
        }
      ],
      "source": [
        "n = 10\n",
        "dt = 0.6  # physical time per Trotter step (also the omega-axis unit in POST)\n",
        "time_steps = 10\n",
        "center = n // 2 - 1\n",
        "\n",
        "# MPS-simulator settings, shared by the ground-state prep here and the AQC\n",
        "# compression inside the function (matches the original tutorial).\n",
        "mps_max_bond = 32\n",
        "mps_cutoff = 1e-8\n",
        "\n",
        "# 1D isotropic Heisenberg (KCuF3) Hamiltonian on n qubits\n",
        "H = SparsePauliOp.from_sparse_list(\n",
        "    [(p, [i, i + 1], 0.25) for i in range(n - 1) for p in (\"XX\", \"YY\", \"ZZ\")],\n",
        "    num_qubits=n,\n",
        ")\n",
        "\n",
        "# Ground state (DMRG + fidelity max) + neutron kick baked into the same circuit\n",
        "gs_circuit = prepare_ground_state(\n",
        "    n, gs_layers=3, max_bond=mps_max_bond, cutoff=mps_cutoff\n",
        ")\n",
        "gs_circuit.rz(\n",
        "    np.pi / 2, center\n",
        ")  # exp(-i (pi/2)/2 Z_center): the neutron perturbation\n",
        "print(\n",
        "    f\"Prepared {n}-qubit ground state with the neutron kick at site {center}.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "small-s3-md",
      "metadata": {},
      "source": [
        "<span id=\"steps-2-and-3-compress-and-execute-with-the-function-template\" />\n",
        "\n",
        "### Etapas 2 e 3: Compactar e executar com o modelo de função\n",
        "\n",
        "Em um fluxo de trabalho feito manualmente, essas são duas etapas distintas: otimizar os circuitos para o hardware (Etapa 2) e executá-los (Etapa 3). O modelo de função agrupa ambas em uma única chamada. Ele realiza a síntese de Trotter, a compactação AQC e a transpilagem para hardware e, em seguida, executa os circuitos (aqui no simulador exato; posteriormente, com mitigação de erros integrada no hardware). Os dois parâmetros de ajuste são `aqc_segments` (o plano de compactação) e `aqc_options` (as configurações do MPS e do otimizador). Cada segmento comprime `{\"n_steps\": k, \"ansatz_steps\": m}` etapas consecutivas `k` do método de Trotter em um ansatz construído a partir de um alvo de Trotter de `m`-etapas, e quaisquer etapas além disso são `sum(n_steps)` executadas como o método de Trotter padrão. Os primeiros passos, com baixo entrelaçamento, podem ser bem comprimidos em um ansatz raso (`ansatz_steps=1`), portanto, neste caso, comprimimos os três primeiros passos em um ansatz de camada única e os dois seguintes em um ansatz mais profundo de duas camadas; os cinco passos restantes dos 10 passos de Trotter são executados como o método de Trotter padrão. Pois `aqc_options` seguimos o tutorial original: dimensão de ligação MPS `max_bond=32`, `cutoff=1e-8`, e um otimizador L-BFGS-B limitado a 100 iterações.\n",
        "\n",
        "Chame a função carregada na função Setup. `backend=\"statevector\"` executa o caminho de referência exato: sem tempo de QPU, com os circuitos sendo executados em um simulador de vetor de estado exato dentro do worker sem servidor (ainda é necessária uma conta Qiskit Serverless salva para chamá-lo). O representa `initial_state` o estado fundamental preparado (incluindo o kick); `observables` é omitido para que a função meça o $Z$ padrão por site.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "small-s3-run",
      "metadata": {},
      "outputs": [],
      "source": [
        "job = fn.run(\n",
        "    t_steps=time_steps,\n",
        "    aqc_segments=[\n",
        "        {\n",
        "            \"n_steps\": 3,\n",
        "            \"ansatz_steps\": 1,\n",
        "        },  # early steps -> shallow 1-layer ansatz\n",
        "        {\n",
        "            \"n_steps\": 2,\n",
        "            \"ansatz_steps\": 2,\n",
        "        },  # later steps -> deeper 2-layer ansatz\n",
        "    ],\n",
        "    aqc_options={\n",
        "        \"max_bond\": mps_max_bond,  # MPS bond dimension for AQC compression\n",
        "        \"cutoff\": mps_cutoff,\n",
        "        \"optimizer_settings\": {\n",
        "            \"method\": \"L-BFGS-B\",\n",
        "            \"jac\": True,\n",
        "            \"options\": {\"maxiter\": 100},\n",
        "        },\n",
        "    },\n",
        "    dt=dt,\n",
        "    hamiltonian=H,\n",
        "    initial_state=gs_circuit,  # prepared ground state including the neutron kick\n",
        "    # observables omitted -> default per-site Z (the neutron sigma_z readout)\n",
        "    backend=\"statevector\",\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "small-status",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "DONE\n"
          ]
        }
      ],
      "source": [
        "print(job.status())  # rerun this cell until status says DONE"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "small-s3-result",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "AQC fidelities: {'1': 1.0, '2': 0.9999, '3': 0.9992, '4': 0.9998, '5': 0.9995}\n",
            "Green's function shape: (10, 10)\n"
          ]
        }
      ],
      "source": [
        "# The per-site <sigma_z>(t) the function returns is the retarded Green's function\n",
        "# G(j, j_c, t). The workflow samples t = 1..time_steps, so drop the t = 0 row (the\n",
        "# prepared+kicked state before any evolution) before post-processing.\n",
        "result = job.result()\n",
        "print(\n",
        "    \"AQC fidelities:\",\n",
        "    {k: round(v, 4) for k, v in result[\"metadata\"][\"aqc_fidelities\"].items()},\n",
        ")\n",
        "\n",
        "ev = np.array(result[\"expectation_values\"])\n",
        "Gjjc = ev[1:]  # shape (time_steps, n)\n",
        "print(\"Green's function shape:\", Gjjc.shape)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "small-s4-md",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "### Etapa 4: Realizar o pós-processamento e apresentar o resultado no formato clássico desejado\n",
        "\n",
        "Aplicar a transformada de Fourier à função de Green para obter um $S(q, \\omega)$ e, aplicar simetria espelhada e cortar os valores negativos: o pós-processamento padrão de nêutrons. O espelhamento é exato porque $S(q, \\omega) = S(-q, \\omega)$ para este modelo, e os valores negativos que permanecem são artefatos da transformada de Fourier de uma série temporal finita e amostrada discretamente; portanto, eles são cortados em zero. Nesta pequena simulação exata, o continuum de dois spinons é resolvido apenas de forma aproximada, mas o mecanismo é idêntico ao da simulação de hardware que se segue.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "small-s4-code",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/simulate-neutron-scattering-with-a-serverless-workflow/extracted-outputs/small-s4-code-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/simulate-neutron-scattering-with-a-serverless-workflow/extracted-outputs/small-s4-code-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "q_res, w_res = 100, 100\n",
        "spectrum = get_spectrum(n, Gjjc, dt, time_steps, q_res, w_res)\n",
        "spectrum = -(spectrum + spectrum[:, ::-1]) / 2  # mirror symmetry\n",
        "spectrum = np.clip(spectrum, a_min=0, a_max=None)  # clip negatives\n",
        "\n",
        "plot_green(\n",
        "    n,\n",
        "    Gjjc,\n",
        "    time_steps,\n",
        "    dt,\n",
        "    title=f\"Retarded Green's function - {n} qubits (AQC, statevector)\",\n",
        ")\n",
        "plot_spectrum(\n",
        "    spectrum,\n",
        "    dt,\n",
        "    q_res,\n",
        "    w_res,\n",
        "    lower_bound=True,\n",
        "    upper_bound=True,\n",
        "    title=f\"Dynamical structure factor - {n} qubits (AQC, statevector)\",\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "large-md",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## Exemplo de hardware em grande escala\n",
        "\n",
        "O mesmo fluxo de trabalho pode ser ampliado sem alterar nenhum código científico: uma cadeia de 30 nós, o dobro da profundidade de Trotter (20 passos), um plano de compressão que varia a profundidade do ansatz (um ansatz mais profundo para os passos posteriores, mais entrelaçados) e a execução em um processador IBM Quantum com a mitigação de erros integrada à função (desacoplamento dinâmico, giro de Pauli e extinção de erros por leitura girada (TREX)). Seguimos as mesmas quatro etapas do exemplo do simulador, reutilizando o `fn` handle da função Setup.\n",
        "\n",
        "|                                                  | Pequena escala | Em grande escala                 |\n",
        "| ------------------------------------------------ | -------------- | -------------------------------- |\n",
        "| Qubits                                           | 22             | 30                               |\n",
        "| Passos de trotador                               | 22             | 20                               |\n",
        "| Etapas com compressão AQC (1 camada + 2 camadas) | 3 + 2 = 5      | 6 + 4 = 10                       |\n",
        "| Camadas de ansatz do estado fundamental          | 3              | 5                                |\n",
        "| Dimensão máxima da ligação do MPS                | 32             | 128                              |\n",
        "| Back-end                                         | `statevector`  | QPU com DD, giro de Pauli e TREX |\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "large-s1-md",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "### Etapa 1: Mapeie entradas clássicas para um problema quântico\n",
        "\n",
        "Construa o mesmo KCuF$_3$ Heisenberg `SparsePauliOp` e prepare o estado fundamental, agora com um `gs_layers=5` ansatz mais profundo para a cadeia mais longa; em seguida, aplique o impulso de nêutrons $\\pi/2$ $Z$ no sítio central. Isso é idêntico ao mapeamento em pequena escala, mas em $n = 30$.\n",
        "\n",
        "É de se esperar uma fidelidade do estado fundamental menor do que na simulação de 10 nós: cerca de 0.82 neste caso, contra 0.98 para a cadeia menor, pois cinco camadas de HVA não conseguem capturar totalmente um estado fundamental de 30 nós. Isso é esperado, e não uma falha; além disso, o tutorial original aceita cerca de 0.65 em 50 sites pela mesma razão. Aumentar o limite de iterações do COBYQA melhora o desempenho, mas `gs_layers` acarreta um custo clássico adicional.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 34,
      "id": "large-code",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "DMRG ground-state energy: -13.111355\n",
            "GS fidelity: 0.8201\n",
            "Prepared 30-qubit ground state with the neutron kick at site 14.\n"
          ]
        }
      ],
      "source": [
        "n = 30\n",
        "dt = 0.6\n",
        "time_steps = 20\n",
        "center = n // 2 - 1\n",
        "\n",
        "# Same MPS settings as the original large-scale run: a larger bond for the\n",
        "# longer, more-entangled chain (shared by GS prep and AQC compression).\n",
        "mps_max_bond = 128\n",
        "mps_cutoff = 1e-8\n",
        "\n",
        "# Same KCuF3 Hamiltonian and ground-state prep, on a larger chain\n",
        "H = SparsePauliOp.from_sparse_list(\n",
        "    [(p, [i, i + 1], 0.25) for i in range(n - 1) for p in (\"XX\", \"YY\", \"ZZ\")],\n",
        "    num_qubits=n,\n",
        ")\n",
        "gs_circuit = prepare_ground_state(\n",
        "    n, gs_layers=5, max_bond=mps_max_bond, cutoff=mps_cutoff\n",
        ")\n",
        "gs_circuit.rz(np.pi / 2, center)  # neutron kick at the center site\n",
        "print(\n",
        "    f\"Prepared {n}-qubit ground state with the neutron kick at site {center}.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "large-s3-md",
      "metadata": {},
      "source": [
        "<span id=\"steps-2-and-3-compress-and-execute-with-the-function-template\" />\n",
        "\n",
        "### Etapas 2 e 3: Compactar e executar com o modelo de função\n",
        "\n",
        "A mesma chamada única do exemplo do simulador, agora com `backend_name` apontando para um processador IBM Quantum, de modo que a função seja compilada e executada nesse local. O plano de compressão varia a profundidade do ansatz: os seis primeiros passos de Trotter (de baixo entrelaçamento) são comprimidos em um ansatz raso de camada única; os quatro seguintes, em um ansatz mais profundo de duas camadas; e os 10 passos restantes, dos 20, são executados como o método de Trotter padrão. `aqc_options` aumenta a dimensão do grafo de ligações MPS para `max_bond=128` na cadeia mais longa e mais entrelaçada (igual à original), mantendo o mesmo otimizador L-BFGS-B limitado a 100 iterações. Ative `estimator_options` a mitigação de erros integrada: desacoplamento dinâmico ( XY4 ), “gate twirling” e mitigação de medições TREX. Os valores padrão da função já correspondem ao tutorial original para todos esses itens, exceto pelo orçamento de aprendizado do TREX (`measure_noise_learning`). O bloco inteiro ainda é exibido porque um valor fornecido pelo chamador substitui `estimator_options` os valores padrão da função em sua totalidade, em vez de se fundir com eles; portanto, omitir uma chave faria com que o sistema recorresse ao valor padrão de IBM Quantum Compute, em vez do valor padrão da função.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "large-s3-run",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "job ID (save this to reconnect later): 43ed8d07-6d7d-4f33-b70a-7f31b765b310\n"
          ]
        }
      ],
      "source": [
        "# Steps 2 + 3: the function compresses (varied ansatz) and executes on hardware.\n",
        "job = fn.run(\n",
        "    t_steps=time_steps,\n",
        "    aqc_segments=[\n",
        "        {\n",
        "            \"n_steps\": 6,\n",
        "            \"ansatz_steps\": 1,\n",
        "        },  # early steps -> shallow 1-layer ansatz\n",
        "        {\n",
        "            \"n_steps\": 4,\n",
        "            \"ansatz_steps\": 2,\n",
        "        },  # later steps -> deeper 2-layer ansatz\n",
        "    ],\n",
        "    aqc_options={\n",
        "        \"max_bond\": mps_max_bond,  # 128 for the longer chain\n",
        "        \"cutoff\": mps_cutoff,\n",
        "        \"optimizer_settings\": {\n",
        "            \"method\": \"L-BFGS-B\",\n",
        "            \"jac\": True,\n",
        "            \"options\": {\"maxiter\": 100},\n",
        "        },\n",
        "    },\n",
        "    dt=dt,\n",
        "    hamiltonian=H,\n",
        "    initial_state=gs_circuit,\n",
        "    backend_name=\"ibm_pittsburgh\",\n",
        "    # Mitigation settings from the original tutorial. Only the two\n",
        "    # measure_noise_learning values differ from the function's defaults; the rest\n",
        "    # restates them, because a caller-supplied estimator_options dict replaces the\n",
        "    # function's defaults wholesale rather than merging into them.\n",
        "    estimator_options={\n",
        "        \"environment\": {\"job_tags\": [\"TUT-SNS\"]},\n",
        "        \"dynamical_decoupling\": {\"enable\": True, \"sequence_type\": \"XY4\"},\n",
        "        \"twirling\": {\n",
        "            \"enable_gates\": True,\n",
        "            \"num_randomizations\": 1000,\n",
        "            \"shots_per_randomization\": 128,\n",
        "        },\n",
        "        \"resilience\": {\n",
        "            \"measure_mitigation\": True,\n",
        "            \"measure_noise_learning\": {\n",
        "                \"num_randomizations\": 32,\n",
        "                \"shots_per_randomization\": 100,\n",
        "            },\n",
        "        },\n",
        "    },\n",
        ")\n",
        "print(\"job ID (save this to reconnect later):\", job.job_id)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "large-reconnect-md",
      "metadata": {},
      "source": [
        "<Admonition type=\"note\" title=\"Reconectando-se a um trabalho em execução de longa duração\">\n",
        "  A execução em grande escala não é rápida, e na maior parte do tempo é feita de forma clássica, em vez de na QPU. A compactação AQC é executada dentro da função antes que qualquer dado chegue à QPU: em 30 locais, `max_bond=128` isso levou quase quatro horas em nossa execução, contra os cerca de 18 minutos de tempo de QPU indicados na *estimativa* de uso no início deste tutorial. Além disso, há o tempo de espera na fila. Você não precisa manter este notebook ou o kernel abertos enquanto ele estiver em execução.\n",
        "\n",
        "  Copie o ID do trabalho exibido na célula anterior e salve-o. As próximas três células permitem que você retome a corrida mais tarde:\n",
        "\n",
        "  1. Reconecte-se, o que só é necessário em uma nova sessão do kernel: execute novamente as células [de configuração](#setup) para recriá-las e `serverless`, em seguida, reconstrua o `job` identificador a partir do ID que você salvou. Ignore esta célula se você ainda estiver na sessão em que fez o envio, pois o identificador já está ativo.\n",
        "  2. Verificar status: execute novamente até que seja exibido o resultado `DONE`.\n",
        "  3. Obter o resultado: execute somente quando o status for `DONE`.\n",
        "\n",
        "  A célula de reconexão a seguir contém um marcador de posição. Substitua por sua própria versão `job_id`:\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "large-reconnect",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Reconnect to a previously submitted job by its ID. Only needed in a NEW kernel\n",
        "# session; if you are still in the session where you submitted, the `job` handle\n",
        "# from the preceding cell is already live, so skip this cell. Replace the ID that follows with your own.\n",
        "job = serverless.get_job_by_id(\"<your job ID>\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "id": "large-status",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "DONE\n"
          ]
        }
      ],
      "source": [
        "# Check where the job is. Re-run this until it reports DONE before fetching the\n",
        "# result in the following cell: QUEUED -> INITIALIZING -> RUNNING: OPTIMIZING_FOR_HARDWARE ->\n",
        "# RUNNING: WAITING_FOR_QPU -> RUNNING: EXECUTING_QPU -> RUNNING: POST_PROCESSING\n",
        "# -> DONE.\n",
        "print(job.status())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "large-s3-result",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "AQC fidelities: {'1': 1.0, '2': 0.9994, '3': 0.9944, '4': 0.9853, '5': 0.9747, '6': 0.959, '7': 0.9495, '8': 0.9542, '9': 0.9533, '10': 0.9451}\n"
          ]
        }
      ],
      "source": [
        "# Run this only once the preceding status cell reports DONE. result() blocks until\n",
        "# the job finishes, so calling it earlier just waits (possibly for hours).\n",
        "result = job.result()\n",
        "print(\n",
        "    \"AQC fidelities:\",\n",
        "    {k: round(v, 4) for k, v in result[\"metadata\"][\"aqc_fidelities\"].items()},\n",
        ")\n",
        "\n",
        "ev = np.array(result[\"expectation_values\"])\n",
        "Gjjc = ev[1:]  # drop the t = 0 row -> shape (time_steps, n)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "large-s4-md",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "### Etapa 4: Realizar o pós-processamento e apresentar o resultado no formato clássico desejado\n",
        "\n",
        "Pós-processamento idêntico ao da simulação: aplicar a transformada de Fourier à função de Green para obter um $S(q, \\omega)$ e, aplicar simetria espelhada e cortar os valores negativos. Com a cadeia mais longa e a evolução, o continuum de dois spinons é muito melhor resolvido. Deve preencher a faixa entre os limites tracejados, sendo mais brilhante próximo a $q = \\pi$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 33,
      "id": "large-result",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/simulate-neutron-scattering-with-a-serverless-workflow/extracted-outputs/large-result-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/simulate-neutron-scattering-with-a-serverless-workflow/extracted-outputs/large-result-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "n = result[\"metadata\"][\"n\"]\n",
        "q_res, w_res = 100, 100\n",
        "spectrum = get_spectrum(n, Gjjc, dt, time_steps, q_res, w_res)\n",
        "spectrum = -(spectrum + spectrum[:, ::-1]) / 2  # mirror symmetry\n",
        "spectrum = np.clip(spectrum, a_min=0, a_max=None)  # clip negatives\n",
        "\n",
        "plot_green(\n",
        "    n,\n",
        "    Gjjc,\n",
        "    time_steps,\n",
        "    dt,\n",
        "    title=f\"Retarded Green's function - {n} qubits (AQC, hardware)\",\n",
        ")\n",
        "plot_spectrum(\n",
        "    spectrum,\n",
        "    dt,\n",
        "    q_res,\n",
        "    w_res,\n",
        "    lower_bound=True,\n",
        "    upper_bound=True,\n",
        "    title=f\"Dynamical structure factor - {n} qubits (AQC, hardware)\",\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "appendix-md",
      "metadata": {},
      "source": [
        "<span id=\"appendix\" />\n",
        "\n",
        "## Apêndice\n",
        "\n",
        "O exemplo de hardware anterior executa um único comprimento de cadeia. Os três espectros a seguir provêm de execuções anteriores desse mesmo fluxo de trabalho `ibm_pittsburgh` em 10, 20 e 30 locais, com todas as outras variáveis de entrada mantidas fixas: 20 passos de Trotter em `dt = 0.6`, o plano de compressão de seis passos de uma camada mais quatro de duas camadas com compressão AQC, e `max_bond = 128`. Esses são resultados registrados, e não valores provenientes das células anteriores.\n",
        "\n",
        "As mesmas configurações são utilizadas nos três tamanhos, de modo que os espectros são diretamente comparáveis. Ajustá-los de acordo com o comprimento da cadeia, com mais camadas de ansatz do estado fundamental ou um valor maior de `max_bond`, por exemplo, pode proporcionar resultados melhores do que qualquer um dos apresentados aqui.\n",
        "\n",
        "![Fator de estrutura dinâmica em 10 locais: um único pico brilhante e acentuado em q = π próximo ao ](https://quantum.cloud.ibm.com/docs/images/tutorials/simulate-neutron-scattering-with-a-serverless-workflow/appendix-dsf-10.avif \"limite inferior de 10 qubits\")\n",
        "\n",
        "![Fator de estrutura dinâmica em 20 nós, com peso espectral preenchendo a faixa entre os dois limites](https://quantum.cloud.ibm.com/docs/images/tutorials/simulate-neutron-scattering-with-a-serverless-workflow/appendix-dsf-20.avif \" pontilhados de dois spinons em 20 qubits\")\n",
        "\n",
        "![Fator de estrutura dinâmica em 30 pontos; o contínuo foi resolvido com maior precisão, com contraste mais fraco e algum peso fora dos ](https://quantum.cloud.ibm.com/docs/images/tutorials/simulate-neutron-scattering-with-a-serverless-workflow/appendix-dsf-30.avif \"limites de 30 qubits\")\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "nextsteps",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Próximas etapas\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recomendações\">\n",
        "  * Adapte esse fluxo de trabalho ao seu próprio sistema: a função aceita qualquer 1D vizinho mais próximo `SparsePauliOp`, portanto, um hamiltoniano de cadeia, um estado inicial ou um conjunto de observáveis diferentes seguem o mesmo fluxo PRE → FUNÇÃO → POST. Consulte o contrato completo de entrada/saída no [modelo da AQC Dynamics em GitHub](https://github.com/qiskit-community/qiskit-function-templates/tree/main/physics/aqc_trotter).\n",
        "  * Leia o artigo do qual este benchmark foi extraído: Lee et al., [*Avaliação comparativa da simulação quântica com experimentos de espalhamento de nêutrons*](https://arxiv.org/abs/2603.15608) ( arXiv:2603.15608 ).\n",
        "  * Em comparação com o [tutorial original “Simular espalhamento de nêutrons”](/docs/tutorials/simulate-neutron-scattering), este tutorial adapta o fluxo de trabalho embutido para um modelo de função implantado.\n",
        "  * Aprofunde-se nas [técnicas de mitigação e supressão de](/docs/guides/error-mitigation-and-suppression-techniques) erros aplicadas na execução em hardware: desacoplamento dinâmico, “Pauli twirling” e TREX.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "hours": 4,
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    },
    "qpuSeconds": 1080
  },
  "nbformat": 4,
  "nbformat_minor": 5
}