{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "eb419bc5-908c-4f0d-a4ff-c4d13f04e332",
      "metadata": {
        "tags": []
      },
      "source": [
        "---\n",
        "title: \"Estimativa da energia do estado fundamental da cadeia de Heisenberg com VQE\"\n",
        "description: \"Crie, implemente e execute um padrão Qiskit para simular uma cadeia de Heisenberg e estimar sua energia no estado fundamental.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore hyperparameters, forall, nabla, nparams */}\n",
        "\n",
        "<span id=\"ground-state-energy-estimation-of-the-heisenberg-chain-with-vqe\" />\n",
        "\n",
        "# Estimativa da energia do estado fundamental da cadeia de Heisenberg com VQE\n",
        "\n",
        "*Estimativa de tempo de execução: 37 minutos em um processador Heron (NOTA: Trata-se apenas de uma estimativa. (O tempo de execução pode variar.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e28ad544",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Resultados do aprendizado\n",
        "\n",
        "Ao concluir este tutorial, você deverá compreender as seguintes informações:\n",
        "\n",
        "* Como modelar uma cadeia de espín de Heisenberg como um hamiltoniano quântico usando o Qiskit\n",
        "* Como usar o otimizador SPSA para estimar a energia do estado fundamental de um sistema quântico\n",
        "* Como executar fluxos de trabalho variacionais em um hardware quântico d IBM®, utilizando primitivas e sessões d Qiskit Runtime\n",
        "\n",
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Pré-requisitos\n",
        "\n",
        "Recomenda-se que você se familiarize com estes tópicos:\n",
        "\n",
        "* [Noções básicas de informação quântica](/learning/courses/basics-of-quantum-information)\n",
        "* [Introdução aos padrões do Qiskit](/docs/guides/intro-to-patterns)\n",
        "* [Design de algoritmos variacionais](/learning/courses/variational-algorithm-design)\n",
        "\n",
        "<span id=\"background\" />\n",
        "\n",
        "## Segundo plano\n",
        "\n",
        "A cadeia de espín de Heisenberg é um dos modelos mais estudados na física da matéria condensada e no magnetismo quântico. Descreve uma rede unidimensional de spins quânticos em interação, na qual os spins vizinhos mais próximos estão acoplados por meio de interações de troca. O hamiltoniano do modelo de Heisenberg isotrópico com um campo magnético externo é dado por:\n",
        "\n",
        "$H = \\sum_{\\langle i,j \\rangle} \\left( J_x X_i X_j + J_y Y_i Y_j + J_z Z_i Z_j \\right) + \\sum_{i} h_i Z_i,$\n",
        "\n",
        "onde $X_i$, $Y_i$ e $Z_i$ são os operadores de Pauli que atuam no sítio $i$, a soma $\\langle i,j \\rangle$ abrange os pares de vizinhos mais próximos, $J_x = J_y = J_z = 0.5$ são as constantes de acoplamento de troca (isotrópicas neste tutorial) e $h_i$ representa um campo magnético externo dependente do sítio. Neste tutorial, os valores do campo magnético são amostrados aleatoriamente no intervalo $[-1, 1]$. Observe que, na implementação abaixo, o conjunto de pares de “vizinhos mais próximos” é determinado pelo acoplamento nativo do backend de hardware entre os primeiros qubits $N$, o que pode não formar uma cadeia linear estrita, dependendo da topologia do dispositivo.\n",
        "\n",
        "Compreender a energia do estado fundamental desse hamiltoniano é de importância fundamental na física. O estado fundamental contém informações sobre transições de fase quânticas, estrutura de entrelaçamento e ordenação magnética. Tradicionalmente, o cálculo da energia exata do estado fundamental torna-se impraticável à medida que o número de spins aumenta, uma vez que a dimensão do espaço de Hilbert varia exponencialmente como $2^N$ para $N$ spins. Isso o torna um candidato natural para a simulação quântica.\n",
        "\n",
        "O Variational Quantum Eigensolver (VQE) é um algoritmo híbrido quântico-clássico projetado para estimar a energia do estado fundamental de um hamiltoniano. O método funciona preparando-se um estado quântico parametrizado $|\\psi(\\theta)\\rangle$ (denominado ansatz) em um computador quântico e medindo-se o valor esperado $\\langle \\psi(\\theta) | H | \\psi(\\theta) \\rangle$. Em seguida, um otimizador clássico ajusta iterativamente os parâmetros $\\theta$ para minimizar essa energia, aproveitando o princípio variacional, que garante que a energia medida seja sempre um limite superior da verdadeira energia do estado fundamental.\n",
        "\n",
        "Neste tutorial, utilizamos o `efficient_su2` ansatz da biblioteca de circuitos do Qiskit, que constrói camadas de rotações de qubits únicos e portas de entrelaçamento. A otimização é realizada utilizando o algoritmo de Aproximação Estocástica por Perturbação Simultânea (SPSA), que é adequado para hardware quântico sujeito a ruídos, pois estima gradientes utilizando apenas duas avaliações da função por iteração, independentemente do número de parâmetros.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bc52f763",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Requisitos\n",
        "\n",
        "Antes de iniciar este tutorial, verifique se você tem os seguintes itens instalados:\n",
        "\n",
        "* Qiskit SDK v2.0 ou posterior, com suporte [à visualização](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime v0.44 ou posterior (`pip install qiskit-ibm-runtime`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a46e9e3e",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Instalação\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "e7754922",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "from typing import Sequence\n",
        "\n",
        "from qiskit import QuantumCircuit\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit.primitives import BaseEstimatorV2\n",
        "from qiskit.circuit.library import XGate\n",
        "from qiskit.circuit.library import efficient_su2\n",
        "from qiskit.transpiler import PassManager\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "from qiskit.transpiler.passes.scheduling import (\n",
        "    ALAPScheduleAnalysis,\n",
        "    PadDynamicalDecoupling,\n",
        ")\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService, Session, EstimatorV2\n",
        "\n",
        "\n",
        "def visualize_results(results):\n",
        "    plt.plot(results[\"cost_history\"], lw=2)\n",
        "    plt.xlabel(\"Number of function evaluations\")\n",
        "    plt.ylabel(\"Energy\")\n",
        "    plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "890d9c81",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-example\" />\n",
        "\n",
        "## Exemplo em pequena escala\n",
        "\n",
        "Nesta seção, vamos percorrer cada etapa do padrão Qiskit em pequena escala, explicando os principais componentes à medida que construímos o fluxo de trabalho.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "132fb15f-10b4-4d7e-83d8-f512a6f675d1",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "### Passo 1: Mapear entradas clássicas para um problema quântico\n",
        "\n",
        "* Entrada: Número de giros\n",
        "* Resultado: Ansatz e Hamiltoniano modelando a cadeia de Heisenberg\n",
        "\n",
        "Construa um ansatz e um hamiltoniano que modelem uma cadeia de Heisenberg de 10 spins. Nesta etapa, construiremos um hamiltoniano de Heisenberg de 10 spins sobre o mapa de acoplamento do backend menos ocupado e prepararemos o `efficient_su2` ansatz.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "7e8d2f10-f1d6-4ec2-bac9-9db23499c9e1",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/spin-chain-vqe/extracted-outputs/7e8d2f10-f1d6-4ec2-bac9-9db23499c9e1-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "num_spins = 10\n",
        "ansatz = efficient_su2(num_qubits=num_spins, reps=2)\n",
        "\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, min_num_qubits=num_spins, simulator=False\n",
        ")\n",
        "\n",
        "coupling = backend.target.build_coupling_map()\n",
        "reduced_coupling = coupling.reduce(list(range(num_spins)))\n",
        "\n",
        "edge_list = reduced_coupling.graph.edge_list()\n",
        "ham_list = []\n",
        "\n",
        "for edge in edge_list:\n",
        "    ham_list.append((\"ZZ\", edge, 0.5))\n",
        "    ham_list.append((\"YY\", edge, 0.5))\n",
        "    ham_list.append((\"XX\", edge, 0.5))\n",
        "\n",
        "for qubit in reduced_coupling.physical_qubits:\n",
        "    ham_list.append((\"Z\", [qubit], np.random.random() * 2 - 1))\n",
        "\n",
        "hamiltonian = SparsePauliOp.from_sparse_list(ham_list, num_qubits=num_spins)\n",
        "\n",
        "ansatz.draw(\"mpl\", style=\"iqp\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ab79119b-5e56-49d8-a20e-1c8e665baec0",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "### Etapa 2: Otimizar o problema para execução em hardware quântico\n",
        "\n",
        "* Entrada: Circuito abstrato, observável\n",
        "* Saída: Circuito-alvo e observável, otimizado para a QPU selecionada\n",
        "\n",
        "Use a função `generate_preset_pass_manager` do Qiskit para gerar automaticamente uma rotina de otimização para o nosso circuito com relação à QPU selecionada. Escolhemos `optimization_level=3`, que oferece o mais alto nível de otimização dos gerenciadores de passagem predefinidos. Incluímos também `ALAPScheduleAnalysis` e `PadDynamicalDecoupling` passes de agendamento para suprimir erros de decoerência.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "a0a5f1c8-5c31-4d9f-ae81-37bd67271d44",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/spin-chain-vqe/extracted-outputs/a0a5f1c8-5c31-4d9f-ae81-37bd67271d44-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "target = backend.target\n",
        "pm = generate_preset_pass_manager(optimization_level=3, target=target)\n",
        "pm.scheduling = PassManager(\n",
        "    [\n",
        "        ALAPScheduleAnalysis(durations=target.durations()),\n",
        "        PadDynamicalDecoupling(\n",
        "            durations=target.durations(),\n",
        "            dd_sequence=[XGate(), XGate()],\n",
        "            pulse_alignment=target.pulse_alignment,\n",
        "        ),\n",
        "    ]\n",
        ")\n",
        "isa_ansatz = pm.run(ansatz)\n",
        "isa_observable = hamiltonian.apply_layout(isa_ansatz.layout)\n",
        "isa_ansatz.draw(\"mpl\", scale=0.6, style=\"iqp\", fold=-1, idle_wires=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9e889d0b-30b5-4e6b-84c9-d1f096abf132",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Passo 3: Execute usando Qiskit primitives\n",
        "\n",
        "* Entrada: Circuito alvo e observável\n",
        "* Saída: Resultados da otimização\n",
        "\n",
        "Minimize a energia estimada do estado fundamental do sistema otimizando os parâmetros do circuito. Use a `Estimator` primitiva disponível em Qiskit Runtime para calcular a função de custo durante a otimização.\n",
        "\n",
        "Como otimizamos o circuito para o backend na Etapa 2, podemos evitar a transpilagem no servidor de tempo de execução definindo `skip_transpilation=True` e passando o circuito otimizado. Para esta demonstração, vamos executar em uma QPU usando `qiskit-ibm-runtime` primitivas. Para executar com primitivas `qiskit` baseadas em vetores de estado, substitua o bloco de código que utiliza primitivas do tipo `Qiskit Runtime` pelo bloco comentado.\n",
        "\n",
        "Neste tutorial, utilizamos a Aproximação Estocástica por Perturbação Simultânea (SPSA), que é um otimizador baseado no gradiente. A seguir, apresentamos uma breve introdução ao assunto e fornecemos o código para implementar o SPSA usando o Qiskit v2.0.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "64f97e6f",
      "metadata": {},
      "source": [
        "<span id=\"introducing-spsa\" />\n",
        "\n",
        "### Apresentando a SPSA\n",
        "\n",
        "A Aproximação Estocástica por Perturbação Simultânea (SPSA) [\\[1\\]](#references) é um algoritmo de otimização que aproxima todo o vetor gradiente utilizando apenas duas chamadas de função em cada iteração. Seja $f:\\mathbb{R}^p\\rightarrow \\mathbb{R}$ a função de custo com $p$ parâmetros a serem otimizados, e $x_i\\in \\mathbb{R}^p$ o vetor de parâmetros no $i^{th}$ passo da iteração. Para calcular o gradiente, é criado um vetor aleatório $\\Delta_i$ de dimensão $p$, em que cada elemento $\\Delta_{ij}$, $\\forall$ $j\\in \\{1,2,...,p\\}$ é amostrado uniformemente a partir de $\\{-1, 1\\}$. Em seguida, cada elemento do vetor aleatório $\\Delta_i$ é multiplicado por um pequeno valor $c_i$ para criar uma perturbação aleatória. O gradiente é então estimado como\n",
        "\n",
        "$[\\nabla f(x_i)]_j \\approx \\frac{f(x_i + c_i \\Delta_i) - f(x_i - c_i \\Delta_i)}{2c_i\\Delta_{ij}}.$\n",
        "\n",
        "Intuitivamente, uma vez que uma perturbação aleatória é aplicada durante a estimativa do gradiente, espera-se que pequenos desvios nos valores exatos de um $f$, decorrentes do ruído, possam ser tolerados e levados em conta. Na verdade, o SPSA é especialmente conhecido por sua robustez contra ruídos e requer apenas duas chamadas de hardware por iteração. É, portanto, um dos otimizadores mais utilizados para a implementação de algoritmos variacionais.\n",
        "\n",
        "Neste tutorial, os hiperparâmetros para a iteração do algoritmo de otimização por gradiente descendente ( $i^{th}$ ), $a_i$ e $c_i$, são calculados da seguinte forma:\n",
        "\n",
        "$a_i = \\frac{a}{(A + i + 1)^\\alpha} \\quad \\text{and} \\quad c_i = \\frac{c}{(i+1)^\\gamma},$\n",
        "\n",
        "onde os valores constantes são tomados como $A = 30$, $\\alpha = 0.9$, $a = 0.3$, $c = 0.1$ e $\\gamma = 0.4$. Esses valores foram selecionados a partir de [\\[2\\]](#references). É necessário ajustar adequadamente os hiperparâmetros para obter um bom desempenho do SPSA.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "73a9352c",
      "metadata": {},
      "outputs": [],
      "source": [
        "def spsa(\n",
        "    fun, x0, args=(), A=30, alpha=0.9, a=0.3, c=0.1, gamma=0.4, maxiter=100\n",
        "):\n",
        "    nparams = len(x0)\n",
        "    x = np.copy(x0)\n",
        "\n",
        "    for i in range(maxiter):\n",
        "        a_i = a / (A + i + 1) ** alpha\n",
        "        c_i = c / (i + 1) ** gamma\n",
        "        delta_i = np.random.choice([-1, 1], nparams)\n",
        "\n",
        "        # two hardware calls\n",
        "        eval_1 = fun(x + c_i * delta_i, *args)\n",
        "        eval_2 = fun(x - c_i * delta_i, *args)\n",
        "\n",
        "        # compute the gradient and update the parameters\n",
        "        grad = (eval_1 - eval_2) / (2 * c_i) * np.reciprocal(delta_i)\n",
        "        x = x - a_i * grad\n",
        "\n",
        "    return x"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "32ca3b6a",
      "metadata": {},
      "outputs": [],
      "source": [
        "def cost_func(\n",
        "    params: Sequence,\n",
        "    ansatz: QuantumCircuit,\n",
        "    hamiltonian: SparsePauliOp,\n",
        "    estimator: BaseEstimatorV2,\n",
        "    cost_history_dict: dict,\n",
        ") -> float:\n",
        "    \"\"\"Ground state energy evaluation.\"\"\"\n",
        "    energy = (\n",
        "        estimator.run([(ansatz, hamiltonian, [params])]).result()[0].data.evs\n",
        "    )\n",
        "\n",
        "    cost_history_dict[\"iters\"] += 1\n",
        "    cost_history_dict[\"prev_vector\"] = list(params)\n",
        "    cost_history_dict[\"cost_history\"].append(float(energy[0]))\n",
        "\n",
        "    print(\n",
        "        f\"Fx Iters. done: {cost_history_dict['iters']} [Current cost: {round(energy[0], 5)}]\",\n",
        "        end=\"\\r\",\n",
        "    )\n",
        "\n",
        "    return energy\n",
        "\n",
        "\n",
        "def solve(x0, isa_ansatz, isa_observable, maxiter=150):\n",
        "    cost_history_dict = {\n",
        "        \"prev_vector\": None,\n",
        "        \"iters\": 0,\n",
        "        \"cost_history\": [],\n",
        "        \"y_min\": None,\n",
        "    }\n",
        "\n",
        "    # Evaluate the problem using a QPU via Qiskit IBM Runtime\n",
        "    with Session(backend=backend) as session:\n",
        "        estimator = EstimatorV2(mode=session)\n",
        "        estimator.skip_transpilation = True\n",
        "        estimator.options.environment.job_tags = [\"TUT_HSVQE\"]\n",
        "        x_opt = spsa(\n",
        "            cost_func,\n",
        "            x0=x0,\n",
        "            args=(isa_ansatz, isa_observable, estimator, cost_history_dict),\n",
        "            maxiter=maxiter,\n",
        "        )\n",
        "\n",
        "        y_min = cost_func(\n",
        "            x_opt, isa_ansatz, isa_observable, estimator, cost_history_dict\n",
        "        )\n",
        "\n",
        "    return y_min, cost_history_dict"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "f418b372",
      "metadata": {},
      "outputs": [],
      "source": [
        "np.random.seed(42)\n",
        "num_params = ansatz.num_parameters\n",
        "params = 2 * np.pi * np.random.random(num_params)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3bf42923",
      "metadata": {},
      "source": [
        "Aqui definimos o `maxiter = 50`. Observe que, como cada iteração requer duas chamadas à função para calcular o gradiente, o número total de chamadas à função será de $2 \\times \\text{maxiter}$. O valor de `maxiter` pode ser aumentado para qualquer valor maior, a fim de obter uma estimativa de energia mais precisa.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "1732ce37",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Fx Iters. done: 101 [Current cost: -3.03843]"
          ]
        }
      ],
      "source": [
        "maxiter = 50\n",
        "spsa_min, spsa_history = solve(\n",
        "    params, isa_ansatz, isa_observable, maxiter=maxiter\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "33abbb3f-6245-4610-a05d-e2bc4cc551f0",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "### Etapa 4: Pós-processamento e retorno do resultado no formato clássico desejado\n",
        "\n",
        "* Entrada: Estimativas da energia do estado fundamental durante a otimização\n",
        "* Resultado: Energia estimada do estado fundamental\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "e5b58771-d543-4e75-9746-fbc7b28e4360",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Estimated ground state energy: [-3.03842968]\n"
          ]
        }
      ],
      "source": [
        "print(f\"Estimated ground state energy: {spsa_min}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "ecd7762a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/spin-chain-vqe/extracted-outputs/ecd7762a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "results = {\n",
        "    \"spsa\": spsa_history,\n",
        "}\n",
        "\n",
        "visualize_results(spsa_history)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a3f4171f",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## Exemplo de hardware em grande escala\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "746e6133",
      "metadata": {},
      "source": [
        "Este tutorial não inclui um exemplo de hardware em grande escala. À medida que o número de qubits aumenta, a VQE enfrenta desafios significativos devido ao fenômeno [do platô estéril](/learning/courses/variational-algorithm-design/optimization-loops#barren-plateaus) : o gradiente da função de custo desaparece exponencialmente com o aumento do tamanho do sistema, tornando a otimização praticamente inviável para circuitos de grande porte. Quando combinado com o ruído do hardware, isso significa que a aplicação do VQE a cadeias de spin maiores não produz resultados confiáveis e reproduzíveis. Para conhecer abordagens que superam essas limitações, consulte a seção “Próximos passos” abaixo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8f14492c",
      "metadata": {},
      "source": [
        "<span id=\"challenge\" />\n",
        "\n",
        "## Desafio\n",
        "\n",
        "Agora que você tem uma implementação funcional do VQE para a cadeia de Heisenberg, tente o seguinte:\n",
        "\n",
        "1. **Experimente alterar a profundidade do ansatz:** modifique o `reps` parâmetro em `efficient_su2` (por exemplo, tente `reps=1` e `reps=3`). Como a profundidade do ansatz afeta a estimativa da energia do estado fundamental e a velocidade de convergência? Em que momento você percebe uma diminuição do rendimento ou instabilidade?\n",
        "2. **Ajuste os hiperparâmetros do SPSA:** ajuste os parâmetros da programação da taxa de aprendizagem (`a`, `c`, `alpha`, `gamma`, `A`) e observe como eles afetam a convergência. Você consegue encontrar uma configuração que converja mais rapidamente do que as predefinições usadas aqui?\n",
        "3. **Compare topologias de acoplamento:** em vez de usar o mapa de acoplamento nativo do backend, tente construir uma cadeia linear simples de vizinhos mais próximos e compare os resultados. De que forma a conectividade do hardware físico afeta a profundidade do circuito transpilado e a estimativa final de energia?\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "217a9379",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Referências\n",
        "\n",
        "\\[1] Spall, J. C. (2002). Implementação do algoritmo de perturbação simultânea para otimização estocástica.\n",
        "IEEE Transactions on Aerospace and Electronic Systems, 34(3), 817-823.\n",
        "\n",
        "\\[2] Sahin, M. Emre, et al. (2025). Machine Learning do Qiskit: uma biblioteca de código aberto para tarefas de aprendizado de máquina quântico em grande escala em hardware quântico e simuladores clássicos. arXiv:2505.17756.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8aefe54c",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Próximas etapas\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recomendações\">\n",
        "  Se você achou este trabalho interessante, talvez se interesse pelo seguinte material:\n",
        "\n",
        "  * **Experimente a diagonalização quântica baseada em amostras (SQD):** Conforme demonstrado neste tutorial, o VQE enfrenta desafios em grande escala devido a platôs estéreis e ao alto custo de medição. IBM desenvolveu [a Diagonalização Quântica Baseada em Amostras (SQD)](/docs/addons/qiskit-addon-sqd) como uma alternativa mais escalável. Ao contrário do VQE, o SQD evita totalmente a otimização variacional; em vez disso, um computador quântico gera amostras e um computador clássico projeta o hamiltoniano em um subespaço gerado por essas amostras e o diagonaliza. Isso fornece um limite superior para a energia do estado fundamental com um número significativamente menor de medições e sem suscetibilidade a platôs estéreis. Siga o [tutorial do SQD](/docs/tutorials/sample-based-quantum-diagonalization) para ver essa abordagem em ação.\n",
        "  * **Explore o curso “Algoritmos de diagonalização quântica”:** aprofunde seus conhecimentos sobre VQE e SQD, incluindo suas vantagens e desvantagens, no curso “[Algoritmos de diagonalização quântica](/learning/courses/quantum-diagonalization-algorithms) ” em IBM Quantum Learning.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    },
    "hours": 1.5,
    "qpuSeconds": 2220
  },
  "nbformat": 4,
  "nbformat_minor": 5
}