{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "aff344db",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Simule um campo inclinado de Ising 2D com a função QESEM\"\n",
        "description: \"Este tutorial mostra uma simulação de um modelo de Ising de campo transversal d 2d a com mitigação de erros QESEM combinada com o módulo de retropropagação do operador Qiskit.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore bppeps */}\n",
        "\n",
        "<span id=\"simulate-2d-tilted-field-ising-with-the-qesem-function\" />\n",
        "\n",
        "# Simule um campo inclinado de Ising 2D com a função QESEM\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "edb68a2a9b5ee911",
      "metadata": {},
      "source": [
        "<Admonition type=\"note\">\n",
        "  Qiskit Functions são um recurso experimental disponível apenas para usuários dos planos IBM Quantum® Premium Plan, Flex Plan e On-Prem (via IBM Quantum Platform API). Eles estão no status de versão prévia e estão sujeitos a alterações.\n",
        "</Admonition>\n",
        "\n",
        "*Estimativa de uso: 20 minutos em um processador Heron r2. (OBSERVAÇÃO: essa é apenas uma estimativa. Seu tempo de execução pode variar)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "88c617fe",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Segundo plano\n",
        "\n",
        "Este tutorial mostra como usar [o QESEM](/docs/guides/qedma-qesem), a função Qiskit da Qedma, para simular a dinâmica de um modelo de spin quântico canônico, o modelo 2D tilted-field Ising (TFI) com ângulos não-Clifford:\n",
        "\n",
        "$$\n",
        "H = J \\sum_{\\langle i,j \\rangle} Z_i Z_j + g_x \\sum_i X_i + g_z \\sum_i Z_i ,\n",
        "$$\n",
        "\n",
        "em que $\\langle i,j \\rangle$ denota os vizinhos mais próximos em uma rede. Simular a evolução temporal de sistemas quânticos de muitos corpos é uma tarefa computacionalmente difícil para computadores clássicos. Os computadores quânticos, por outro lado, são naturalmente projetados para realizar essa tarefa com eficiência. O modelo TFI, em particular, tornou-se uma referência popular em hardware quântico devido ao seu rico comportamento físico e implementação amigável ao hardware.\n",
        "\n",
        "Em vez de simular a dinâmica de tempo contínuo, adotamos o modelo de Ising com chute intimamente relacionado. A dinâmica pode ser expressa exatamente como um circuito quântico periódico, em que cada etapa de evolução consiste em três camadas de portas fracionárias de dois qubits $R_{ZZ} (\\alpha_{ZZ})$, intercaladas com camadas de portas de um único qubit $R_X (\\alpha_X)$ e $R_Z (\\alpha_Z)$.\n",
        "\n",
        "Usaremos ângulos genéricos que são desafiadores tanto para a simulação clássica quanto para a atenuação de erros. Especificamente, escolhemos $\\alpha_{ZZ} = 1.0$, $\\alpha_X = 0.53$ e $\\alpha_Z = 0.1$, colocando o modelo longe de qualquer ponto integrável.\n",
        "\n",
        "Neste tutorial, faremos o seguinte:\n",
        "\n",
        "* Estimar o tempo de execução esperado da QPU para mitigação total de erros usando os recursos de estimativa de tempo analítico e empírico do QESEM.\n",
        "* Construa e simule o circuito modelo de Ising de campo inclinado 2D usando layouts de qubit e camadas de porta inspirados em hardware.\n",
        "* Visualize a conectividade do qubit do dispositivo e os subgráficos selecionados para seu experimento.\n",
        "* Demonstre o uso da [retropropagação do operador (OBP)](https://qiskit.github.io/qiskit-addon-obp/) para reduzir a profundidade do circuito. Essa técnica reduz as operações da extremidade do circuito, mas exige mais medições por parte do operador.\n",
        "* Execute a mitigação de erros (EM) imparcial para vários observáveis simultaneamente usando o QESEM, comparando resultados ideais, ruidosos e mitigados.\n",
        "* Analise e trace o impacto da atenuação de erros na magnetização em diferentes profundidades de circuito.\n",
        "\n",
        "Observação: o OBP geralmente retorna um conjunto de observáveis possivelmente não comutáveis. O QESEM otimiza automaticamente as bases de medição quando os observáveis de destino contêm termos não comutáveis. Ele gera conjuntos de bases de medição candidatos usando vários algoritmos heurísticos e seleciona o conjunto que minimiza o número de bases distintas. Isso significa que o QESEM agrupa observáveis compatíveis em bases comuns para reduzir o número total de configurações de medição necessárias, melhorando a eficiência.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bb2bd156c9c6db20",
      "metadata": {},
      "source": [
        "<span id=\"about-qesem\" />\n",
        "\n",
        "## Sobre a QESEM\n",
        "\n",
        "O QESEM é um software confiável, de alta precisão e baseado em caracterização, que implementa uma mitigação de erros quase probabilística eficiente e imparcial.\n",
        "Ele foi projetado para atenuar erros em circuitos quânticos genéricos e é independente do aplicativo. Ele foi validado em diversas plataformas de hardware, incluindo experimentos em escala de utilidade em dispositivos IBM® Eagle e Heron. Os estágios do fluxo de trabalho do QESEM são os seguintes:\n",
        "\n",
        "1. Caracterização de dispositivos - mapeia fidelidades de porta e identifica erros coerentes, fornecendo dados de calibração em tempo real. Esse estágio garante que a mitigação aproveite as operações de maior fidelidade disponíveis.\n",
        "2. Transpilação com reconhecimento de ruído - gera e avalia mapeamentos alternativos de qubit, conjuntos de operações e bases de medição, selecionando a variante que minimiza o tempo de execução estimado da QPU, com paralelização opcional para acelerar a coleta de dados.\n",
        "3. Supressão de erros - redefine portas nativas, aplica o giro de Pauli e otimiza o controle de nível de pulso (em plataformas compatíveis) para melhorar a fidelidade.\n",
        "4. Caracterização de circuitos - cria um modelo de erro local personalizado e o ajusta às medições de QPU para quantificar o ruído residual.\n",
        "5. Atenuação de erros - constrói decomposições quase probabilísticas de vários tipos e coleta amostras delas em um processo adaptativo que minimiza o tempo de atenuação da QPU e a sensibilidade às flutuações do hardware, obtendo alta precisão em grandes volumes de circuitos.\n",
        "\n",
        "Para obter mais informações sobre o QESEM e sobre um experimento em escala real desse modelo em um subgrafo de alta conectividade com 103 qubits da geometria nativa heavy-hex de `ibm_marrakesh`, consulte o artigo “[Reliable high-accuracy error mitigation for utility-scale quantum circuits](https://arxiv.org/abs/2508.10997) ”.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d1ad1f56516888f2",
      "metadata": {},
      "source": [
        "![Fluxo de trabalho QESEM.](https://quantum.cloud.ibm.com/docs/images/tutorials/qedma-2d-ising-with-qesem/QESEM_workflow.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4a2ede52",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Requisitos\n",
        "\n",
        "Instale os seguintes pacotes Python antes de executar o notebook:\n",
        "\n",
        "* Qiskit SDK v2.0.0 ou posterior (`pip install qiskit`)\n",
        "* Qiskit Runtime v0.40.0 ou posterior (`pip install qiskit-ibm-runtime`)\n",
        "* Qiskit Functions Catalog v0.8.0 ou posterior ( `pip install qiskit-ibm-catalog` )\n",
        "* Complemento Operator Backpropagation Qiskit v0.3.0 ou posterior ( `pip install qiskit-addon-obp` )\n",
        "* Complemento do Qiskit Utils v0.1.1 ou posterior ( `pip install qiskit-addon-utils` )\n",
        "* Simulador Qiskit Aer v0.17.1 ou posterior ( `pip install qiskit-aer` )\n",
        "* Matplotlib v3.10.3 ou posterior ( `pip install matplotlib` )\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a675b3b1",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Instalação\n",
        "\n",
        "Primeiro, importe as bibliotecas relevantes:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "acea2e46",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T12:50:58.471653Z",
          "start_time": "2025-11-05T12:50:58.467381Z"
        }
      },
      "outputs": [],
      "source": [
        "%matplotlib inline\n",
        "\n",
        "from typing import Sequence\n",
        "\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "\n",
        "import qiskit\n",
        "from qiskit_ibm_runtime import EstimatorV2 as Estimator\n",
        "from qiskit_ibm_catalog import QiskitFunctionsCatalog\n",
        "from qiskit_aer import AerSimulator\n",
        "from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types\n",
        "from qiskit_addon_obp import backpropagate\n",
        "from qiskit_addon_obp.utils.simplify import OperatorBudget\n",
        "from qiskit.visualization import (\n",
        "    plot_gate_map,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "467f1569",
      "metadata": {},
      "source": [
        "Em seguida, faça a autenticação usando sua chave de API do painel do [IBM Quantum Platform](http://quantum.cloud.ibm.com/). Em seguida, selecione a função do Qiskit da seguinte maneira. (Observe que, por motivos de segurança, é recomendável [salvar as credenciais da sua conta](/docs/guides/functions-get-started#install-qiskit-functions-catalog-client) no seu ambiente local, caso esteja em um computador confiável, para que você não precise digitar sua chave de API toda vez que for se autenticar.)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "41a53d27",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Paste here your instance and token strings\n",
        "\n",
        "instance = \"YOUR_INSTANCE\"\n",
        "token = \"YOUR_TOKEN\"\n",
        "channel = \"ibm_quantum_platform\"\n",
        "\n",
        "catalog = QiskitFunctionsCatalog(\n",
        "    channel=channel, token=token, instance=instance\n",
        ")\n",
        "qesem_function = catalog.load(\"qedma/qesem\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "69eafdcc",
      "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"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ac9dcff0",
      "metadata": {},
      "source": [
        "Começamos definindo uma função que cria o circuito de Trotter:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "3842021c",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T12:51:12.446678Z",
          "start_time": "2025-11-05T12:51:12.442978Z"
        }
      },
      "outputs": [],
      "source": [
        "def trotter_circuit_from_layers(\n",
        "    steps: int,\n",
        "    theta_x: float,\n",
        "    theta_z: float,\n",
        "    theta_zz: float,\n",
        "    layers: Sequence[Sequence[tuple[int, int]]],\n",
        "    init_state: str | None = None,\n",
        ") -> qiskit.QuantumCircuit:\n",
        "    \"\"\"\n",
        "    Generates an ising trotter circuit\n",
        "    :param steps: trotter steps\n",
        "    :param theta_x: RX angle\n",
        "    :param theta_z: RZ angle\n",
        "    :param theta_zz: RZZ angle\n",
        "    :param layers: list of layers (can be list of layers in device)\n",
        "    :param init_state: Initial state to prepare.\n",
        "     If None, will not prepare any state. If \"+\", will\n",
        "     add Hadamard gates to all qubits.\n",
        "    :return: QuantumCircuit\n",
        "    \"\"\"\n",
        "    qubits = sorted({i for layer in layers for edge in layer for i in edge})\n",
        "    circ = qiskit.QuantumCircuit(max(qubits) + 1)\n",
        "\n",
        "    if init_state == \"+\":\n",
        "        print(\"init_state = +\")\n",
        "        for q in qubits:\n",
        "            circ.h(q)\n",
        "\n",
        "    for _ in range(steps):\n",
        "        for q in qubits:\n",
        "            circ.rx(theta_x, q)\n",
        "            circ.rz(theta_z, q)\n",
        "\n",
        "        for layer in layers:\n",
        "            for edge in layer:\n",
        "                circ.rzz(theta_zz, *edge)\n",
        "        circ.barrier(qubits)\n",
        "\n",
        "    return circ"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8b4b76968dc50bb1",
      "metadata": {},
      "source": [
        "Em seguida, criamos uma função para calcular os valores de expectativa ideal usando `AerSimulator`.\n",
        "\n",
        "Observe que, para circuitos de grande porte (30 ou mais qubits), recomendamos o uso de valores pré-calculados a partir de simulações PEPS com propagação de crenças (BP). Este código inclui valores pré-calculados para 35 qubits a título de exemplo, com base na abordagem BP para a evolução de uma rede tensorial PEPS apresentada [neste artigo](https://www.science.org/doi/10.1126/sciadv.adk4321) (à qual nos referimos como PEPS-BP), utilizando o pacote [quimb](https://joss.theoj.org/papers/10.21105/joss.00819) do Python para redes tensoriais.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "12f4f2e334e4268b",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-06T20:20:32.759885Z",
          "start_time": "2025-11-06T20:20:32.752872Z"
        }
      },
      "outputs": [],
      "source": [
        "def calculate_ideal_evs(circ, obs, num_qubits, step):\n",
        "    # Predefined results for large circuits - calculated using\n",
        "    # bppeps for 3, 5, 7, 9 trotter steps\n",
        "    predefined_35 = [\n",
        "        0.79537,\n",
        "        0.78653,\n",
        "        0.79699,\n",
        "    ]\n",
        "\n",
        "    if num_qubits == 35:\n",
        "        print(\n",
        "            \"Using precalculated ideal values for large circuits calculated \"\n",
        "            \"with belief propagation PEPS. Currently only for 35 qubits.\"\n",
        "        )\n",
        "        return predefined_35[step]\n",
        "\n",
        "    else:\n",
        "        simulator = AerSimulator()\n",
        "\n",
        "        # Use Estimator primitive to get expectation value\n",
        "        estimator = Estimator(simulator)\n",
        "        sim_result = estimator.run([(circ, [obs])], precision=0.0001).result()\n",
        "\n",
        "        # Extracting the result\n",
        "        ideal_values = sim_result[0].data.evs[0]\n",
        "        return ideal_values"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7d342cd7",
      "metadata": {},
      "source": [
        "Usamos um mapeamento de camada $R_{ZZ}$ baseado em hardware retirado do dispositivo Heron, do qual cortamos as camadas de acordo com o número de qubits que queremos simular. Definimos subgrafos para 10, 21, 28 e 35 qubits que mantêm uma estrutura 2D (sinta-se à vontade para mudar para seu subgrafo favorito):\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "27402210",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T12:51:22.025741Z",
          "start_time": "2025-11-05T12:51:22.018325Z"
        }
      },
      "outputs": [],
      "source": [
        "LAYERS_HERON_R2 = [  # the full set of hardware layers for Heron r2\n",
        "    [\n",
        "        (2, 3),\n",
        "        (6, 7),\n",
        "        (10, 11),\n",
        "        (14, 15),\n",
        "        (20, 21),\n",
        "        (16, 23),\n",
        "        (24, 25),\n",
        "        (17, 27),\n",
        "        (28, 29),\n",
        "        (18, 31),\n",
        "        (32, 33),\n",
        "        (19, 35),\n",
        "        (36, 41),\n",
        "        (42, 43),\n",
        "        (37, 45),\n",
        "        (46, 47),\n",
        "        (38, 49),\n",
        "        (50, 51),\n",
        "        (39, 53),\n",
        "        (60, 61),\n",
        "        (56, 63),\n",
        "        (64, 65),\n",
        "        (57, 67),\n",
        "        (68, 69),\n",
        "        (58, 71),\n",
        "        (72, 73),\n",
        "        (59, 75),\n",
        "        (76, 81),\n",
        "        (82, 83),\n",
        "        (77, 85),\n",
        "        (86, 87),\n",
        "        (78, 89),\n",
        "        (90, 91),\n",
        "        (79, 93),\n",
        "        (94, 95),\n",
        "        (100, 101),\n",
        "        (96, 103),\n",
        "        (104, 105),\n",
        "        (97, 107),\n",
        "        (108, 109),\n",
        "        (98, 111),\n",
        "        (112, 113),\n",
        "        (99, 115),\n",
        "        (116, 121),\n",
        "        (122, 123),\n",
        "        (117, 125),\n",
        "        (126, 127),\n",
        "        (118, 129),\n",
        "        (130, 131),\n",
        "        (119, 133),\n",
        "        (134, 135),\n",
        "        (140, 141),\n",
        "        (136, 143),\n",
        "        (144, 145),\n",
        "        (137, 147),\n",
        "        (148, 149),\n",
        "        (138, 151),\n",
        "        (152, 153),\n",
        "        (139, 155),\n",
        "    ],\n",
        "    [\n",
        "        (1, 2),\n",
        "        (3, 4),\n",
        "        (5, 6),\n",
        "        (7, 8),\n",
        "        (9, 10),\n",
        "        (11, 12),\n",
        "        (13, 14),\n",
        "        (21, 22),\n",
        "        (23, 24),\n",
        "        (25, 26),\n",
        "        (27, 28),\n",
        "        (29, 30),\n",
        "        (31, 32),\n",
        "        (33, 34),\n",
        "        (40, 41),\n",
        "        (43, 44),\n",
        "        (45, 46),\n",
        "        (47, 48),\n",
        "        (49, 50),\n",
        "        (51, 52),\n",
        "        (53, 54),\n",
        "        (55, 59),\n",
        "        (61, 62),\n",
        "        (63, 64),\n",
        "        (65, 66),\n",
        "        (67, 68),\n",
        "        (69, 70),\n",
        "        (71, 72),\n",
        "        (73, 74),\n",
        "        (80, 81),\n",
        "        (83, 84),\n",
        "        (85, 86),\n",
        "        (87, 88),\n",
        "        (89, 90),\n",
        "        (91, 92),\n",
        "        (93, 94),\n",
        "        (95, 99),\n",
        "        (101, 102),\n",
        "        (103, 104),\n",
        "        (105, 106),\n",
        "        (107, 108),\n",
        "        (109, 110),\n",
        "        (111, 112),\n",
        "        (113, 114),\n",
        "        (120, 121),\n",
        "        (123, 124),\n",
        "        (125, 126),\n",
        "        (127, 128),\n",
        "        (129, 130),\n",
        "        (131, 132),\n",
        "        (133, 134),\n",
        "        (135, 139),\n",
        "        (141, 142),\n",
        "        (143, 144),\n",
        "        (145, 146),\n",
        "        (147, 148),\n",
        "        (149, 150),\n",
        "        (151, 152),\n",
        "        (153, 154),\n",
        "    ],\n",
        "    [\n",
        "        (3, 16),\n",
        "        (7, 17),\n",
        "        (11, 18),\n",
        "        (22, 23),\n",
        "        (26, 27),\n",
        "        (30, 31),\n",
        "        (34, 35),\n",
        "        (21, 36),\n",
        "        (25, 37),\n",
        "        (29, 38),\n",
        "        (33, 39),\n",
        "        (41, 42),\n",
        "        (44, 45),\n",
        "        (48, 49),\n",
        "        (52, 53),\n",
        "        (43, 56),\n",
        "        (47, 57),\n",
        "        (51, 58),\n",
        "        (62, 63),\n",
        "        (66, 67),\n",
        "        (70, 71),\n",
        "        (74, 75),\n",
        "        (61, 76),\n",
        "        (65, 77),\n",
        "        (69, 78),\n",
        "        (73, 79),\n",
        "        (81, 82),\n",
        "        (84, 85),\n",
        "        (88, 89),\n",
        "        (92, 93),\n",
        "        (83, 96),\n",
        "        (87, 97),\n",
        "        (91, 98),\n",
        "        (102, 103),\n",
        "        (106, 107),\n",
        "        (110, 111),\n",
        "        (114, 115),\n",
        "        (101, 116),\n",
        "        (105, 117),\n",
        "        (109, 118),\n",
        "        (113, 119),\n",
        "        (121, 122),\n",
        "        (124, 125),\n",
        "        (128, 129),\n",
        "        (132, 133),\n",
        "        (123, 136),\n",
        "        (127, 137),\n",
        "        (131, 138),\n",
        "        (142, 143),\n",
        "        (146, 147),\n",
        "        (150, 151),\n",
        "        (154, 155),\n",
        "        (0, 1),\n",
        "        (4, 5),\n",
        "        (8, 9),\n",
        "        (12, 13),\n",
        "        (54, 55),\n",
        "        (15, 19),\n",
        "    ],\n",
        "]\n",
        "\n",
        "subgraphs = {  # the subgraphs for the different qubit counts such that it's 2D\n",
        "    10: list(range(22, 29)) + [16, 17, 37],\n",
        "    21: list(range(3, 12)) + list(range(23, 32)) + [16, 17, 18],\n",
        "    28: list(range(3, 12))\n",
        "    + list(range(23, 32))\n",
        "    + list(range(45, 50))\n",
        "    + [16, 17, 18, 37, 38],\n",
        "    35: list(range(3, 12))\n",
        "    + list(range(21, 32))\n",
        "    + list(range(41, 50))\n",
        "    + [16, 17, 18, 36, 37, 38],\n",
        "    42: list(range(3, 12))\n",
        "    + list(range(21, 32))\n",
        "    + list(range(41, 50))\n",
        "    + list(range(63, 68))\n",
        "    + [16, 17, 18, 36, 37, 38, 56, 57],\n",
        "}\n",
        "\n",
        "n_qubits = 35  # 21, 28, 35, 42"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "c03b7ca9951aba38",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T12:51:25.383040Z",
          "start_time": "2025-11-05T12:51:25.380298Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[(6, 7), (10, 11), (16, 23), (24, 25), (17, 27), (28, 29), (18, 31), (36, 41), (42, 43), (37, 45), (46, 47), (38, 49)], [(3, 4), (5, 6), (7, 8), (9, 10), (21, 22), (23, 24), (25, 26), (27, 28), (29, 30), (43, 44), (45, 46), (47, 48)], [(3, 16), (7, 17), (11, 18), (22, 23), (26, 27), (30, 31), (21, 36), (25, 37), (29, 38), (41, 42), (44, 45), (48, 49), (4, 5), (8, 9)]]\n"
          ]
        }
      ],
      "source": [
        "layers = [\n",
        "    [\n",
        "        edge\n",
        "        for edge in layer\n",
        "        if edge[0] in subgraphs[n_qubits] and edge[1] in subgraphs[n_qubits]\n",
        "    ]\n",
        "    for layer in LAYERS_HERON_R2\n",
        "]\n",
        "\n",
        "print(layers)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "46f93e695a2ae23e",
      "metadata": {},
      "source": [
        "Agora, visualizamos o layout do qubit no dispositivo Heron para o subgráfico selecionado:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "4f02995559bb437f",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T12:51:33.346900Z",
          "start_time": "2025-11-05T12:51:29.975427Z"
        }
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/qedma-2d-ising-with-qesem/extracted-outputs/4f02995559bb437f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "catalog = QiskitFunctionsCatalog(\n",
        "    channel=channel,\n",
        "    token=token,\n",
        "    instance=instance,\n",
        ")\n",
        "backend = catalog.backend(\"ibm_fez\")  # or any available device\n",
        "\n",
        "selected_qubits = subgraphs[n_qubits]\n",
        "num_qubits = backend.configuration().num_qubits\n",
        "qubit_color = [\n",
        "    \"#ff7f0e\" if i in selected_qubits else \"#d3d3d3\"\n",
        "    for i in range(num_qubits)\n",
        "]\n",
        "\n",
        "plot_gate_map(\n",
        "    backend=backend,\n",
        "    figsize=(15, 10),\n",
        "    qubit_color=qubit_color,\n",
        ")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7b6ecaa9",
      "metadata": {},
      "source": [
        "Observe que a conectividade do layout de qubit escolhido não é necessariamente linear e pode abranger grandes regiões do dispositivo Heron, dependendo do número selecionado de qubits.\n",
        "\n",
        "Agora, geramos o circuito de Trotter e a magnetização média observável para o número escolhido de qubits e parâmetros:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "1d34dd0d",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T12:56:50.447530Z",
          "start_time": "2025-11-05T12:56:47.922845Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Circuit 2q layers: 27\n",
            "\n",
            "Circuit structure:\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/qedma-2d-ising-with-qesem/extracted-outputs/1d34dd0d-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'ZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],\n",
            "              coeffs=[0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,\n",
            " 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,\n",
            " 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,\n",
            " 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,\n",
            " 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,\n",
            " 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,\n",
            " 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,\n",
            " 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,\n",
            " 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j])\n"
          ]
        }
      ],
      "source": [
        "# Chosen parameters:\n",
        "theta_x = 0.53\n",
        "theta_z = 0.1\n",
        "theta_zz = 1.0\n",
        "steps = 9\n",
        "\n",
        "circ = trotter_circuit_from_layers(steps, theta_x, theta_z, theta_zz, layers)\n",
        "print(\n",
        "    f\"Circuit 2q layers: \"\n",
        "    f\"{circ.depth(filter_function=lambda instr: len(instr.qubits) == 2)}\"\n",
        ")\n",
        "print(\"\\nCircuit structure:\")\n",
        "\n",
        "circ.draw(\"mpl\", scale=0.8, fold=-1, idle_wires=False)\n",
        "plt.show()\n",
        "\n",
        "observable = qiskit.quantum_info.SparsePauliOp.from_sparse_list(\n",
        "    [(\"Z\", [q], 1 / n_qubits) for q in subgraphs[n_qubits]],\n",
        "    np.max(subgraphs[n_qubits]) + 1,\n",
        ")  # Average magnetization observable\n",
        "\n",
        "print(observable)\n",
        "obs_list = [observable]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "76923674",
      "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"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7402ea001f0b9f1a",
      "metadata": {},
      "source": [
        "<span id=\"qpu-time-estimation-with-and-without-obp\" />\n",
        "\n",
        "### Estimativa do tempo de QPU com e sem OBP\n",
        "\n",
        "Os usuários geralmente querem saber quanto tempo de QPU é necessário para o seu experimento.\n",
        "No entanto, esse é considerado um problema difícil para os computadores clássicos.\n",
        "\n",
        "O QESEM oferece dois modos de estimativa de tempo para informar os usuários sobre a viabilidade de seus experimentos:\n",
        "\n",
        "1. Estimativa analítica de tempo - fornece uma estimativa muito aproximada e não requer tempo de QPU. Isso pode ser usado para testar se uma passagem de transpilação reduziria potencialmente o tempo de QPU.\n",
        "2. Estimativa empírica de tempo (demonstrada aqui) - fornece uma estimativa muito boa e usa alguns minutos de tempo de QPU.\n",
        "\n",
        "Em ambos os casos, o QESEM produz a estimativa de tempo para atingir a precisão necessária para **todos os** observáveis.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "478e18ff",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T14:39:16.112626Z",
          "start_time": "2025-11-05T14:39:12.416396Z"
        }
      },
      "outputs": [],
      "source": [
        "run_on_real_hardware = True\n",
        "\n",
        "precision = 0.05\n",
        "if run_on_real_hardware:\n",
        "    backend_name = \"ibm_fez\"\n",
        "else:\n",
        "    backend_name = \"fake_fez\"\n",
        "\n",
        "# Start a job for empirical time estimation\n",
        "estimation_job_wo_obp = qesem_function.run(\n",
        "    pubs=[(circ, obs_list)],\n",
        "    instance=instance,\n",
        "    backend_name=backend_name,  # E.g. \"ibm_brisbane\"\n",
        "    options={\n",
        "        # \"empirical\" - gets actual time estimates without running full mitigation\n",
        "        \"estimate_time_only\": \"empirical\",\n",
        "        \"max_execution_time\": 120,  # Limits the QPU time, specified in seconds.\n",
        "        \"default_precision\": precision,\n",
        "    },\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 31,
      "id": "bd0ff03fe6324ce5",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T15:04:27.695966Z",
          "start_time": "2025-11-05T15:04:25.885170Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "17d3828e-9fdb-482e-8e9b-392f3eefe313\n",
            "DONE\n"
          ]
        }
      ],
      "source": [
        "print(estimation_job_wo_obp.job_id)\n",
        "print(estimation_job_wo_obp.status())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "6357b2b5",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T15:09:13.194534Z",
          "start_time": "2025-11-05T15:09:05.286214Z"
        }
      },
      "outputs": [],
      "source": [
        "# Get the result object (blocking method).\n",
        "# Use job.status() in a loop for non-blocking.\n",
        "# This takes 1-3 minutes\n",
        "result = estimation_job_wo_obp.result()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 33,
      "id": "1e3eab49",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T15:04:47.335176Z",
          "start_time": "2025-11-05T15:04:47.332618Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Empirical time estimation (sec): 1200\n"
          ]
        }
      ],
      "source": [
        "print(\n",
        "    f\"Empirical time estimation (sec): {result[0].metadata['time_estimation_sec']}\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "75dbab74",
      "metadata": {},
      "source": [
        "Agora, utilizaremos a retropropagação do operador (OBP). (Consulte a documentação [do OBP](https://qiskit.github.io/qiskit-addon-obp/) para obter mais detalhes sobre o complemento OBP Qiskit.) Criaremos uma função que gera as fatias do circuito para retropropagação:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2ac55a76daddddcf",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T15:08:38.887654Z",
          "start_time": "2025-11-05T15:08:38.878679Z"
        }
      },
      "outputs": [],
      "source": [
        "def run_backpropagation(circ_vec, observable, steps_vec, max_qwc_groups=8):\n",
        "    \"\"\"\n",
        "    Runs backpropagation for a list of circuits and observables.\n",
        "    Returns lists of backpropagated circuits and observables.\n",
        "    \"\"\"\n",
        "    op_budget = OperatorBudget(max_qwc_groups=max_qwc_groups)\n",
        "    bp_circuit_vec = []\n",
        "    bp_observable_vec = []\n",
        "\n",
        "    for i, circ in enumerate(circ_vec):\n",
        "        slices = slice_by_gate_types(circ)\n",
        "        bp_observable, remaining_slices, metadata = backpropagate(\n",
        "            observable,\n",
        "            slices,\n",
        "            operator_budget=op_budget,\n",
        "        )\n",
        "        bp_circuit = combine_slices(remaining_slices, include_barriers=True)\n",
        "        bp_circuit_vec.append(bp_circuit)\n",
        "        bp_observable_vec.append(bp_observable)\n",
        "        print(f\"n.o. steps: {steps_vec[i]}\")\n",
        "        print(f\"Backpropagated {metadata.num_backpropagated_slices} slices.\")\n",
        "        print(\n",
        "            f\"New observable has {len(bp_observable.paulis)} terms, \"\n",
        "            f\"which can be combined into \"\n",
        "            f\"{len(bp_observable.group_commuting(qubit_wise=True))} groups.\\n\"\n",
        "            f\"After truncation, the error in our observable is bounded by \"\n",
        "            f\"{metadata.accumulated_error(0):.3e}\"\n",
        "        )\n",
        "        print(\"-----------------\")\n",
        "    return bp_circuit_vec, bp_observable_vec"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "84135dc1fb687d2d",
      "metadata": {},
      "source": [
        "Chamamos a função:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 39,
      "id": "1bc2f43bd93b3a92",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T15:15:00.391655Z",
          "start_time": "2025-11-05T15:14:59.993228Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "n.o. steps: 9\n",
            "Backpropagated 11 slices.\n",
            "New observable has 363 terms, which can be combined into 4 groups.\n",
            "After truncation, the error in our observable is bounded by 0.000e+00\n",
            "-----------------\n"
          ]
        }
      ],
      "source": [
        "bp_circ_vec, bp_obs_vec = run_backpropagation([circ], observable, [steps])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 40,
      "id": "cedb7fa1",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T15:15:53.031008Z",
          "start_time": "2025-11-05T15:15:49.863350Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The remaining circuit after backpropagation looks as follows:\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/qedma-2d-ising-with-qesem/extracted-outputs/cedb7fa1-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "print(\"The remaining circuit after backpropagation looks as follows:\")\n",
        "bp_circ_vec[-1].draw(\"mpl\", scale=0.8, fold=-1, idle_wires=False)\n",
        "None"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fc1e532a",
      "metadata": {},
      "source": [
        "Podemos ver que a retropropagação reduziu duas camadas do circuito.\n",
        "Agora que temos nosso circuito reduzido e observáveis expandidos, vamos fazer a estimativa de tempo para o circuito retropropagado:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 42,
      "id": "88160fbc",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T15:27:07.496690Z",
          "start_time": "2025-11-05T15:27:01.891318Z"
        }
      },
      "outputs": [],
      "source": [
        "# Start a job for empirical time estimation\n",
        "estimation_job_obp = qesem_function.run(\n",
        "    pubs=[(bp_circ_vec[-1], [bp_obs_vec[-1]])],\n",
        "    instance=instance,\n",
        "    backend_name=backend_name,\n",
        "    options={\n",
        "        \"estimate_time_only\": \"empirical\",\n",
        "        \"max_execution_time\": 120,\n",
        "        \"default_precision\": precision,\n",
        "    },\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 48,
      "id": "c9ec8a5dcf0bfb21",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T17:15:10.776085Z",
          "start_time": "2025-11-05T17:15:08.740353Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "8bae699d-a16b-4d39-bbd9-d123fbcce55d\n",
            "DONE\n"
          ]
        }
      ],
      "source": [
        "print(estimation_job_obp.job_id)\n",
        "print(estimation_job_obp.status())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 49,
      "id": "19cd4cc2",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T17:15:19.426157Z",
          "start_time": "2025-11-05T17:15:13.048448Z"
        }
      },
      "outputs": [],
      "source": [
        "result_obp = estimation_job_obp.result()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 50,
      "id": "feca3059",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T17:15:21.205216Z",
          "start_time": "2025-11-05T17:15:21.203160Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Empirical time estimation (sec): 900\n"
          ]
        }
      ],
      "source": [
        "print(\n",
        "    f\"Empirical time estimation (sec): {result_obp[0].metadata['time_estimation_sec']}\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "504669f5",
      "metadata": {},
      "source": [
        "Vemos que o OBP reduz o custo de tempo para a atenuação do circuito.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4d1d5092",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## Passo 3: Execute usando Qiskit primitives\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3da535e9",
      "metadata": {},
      "source": [
        "<span id=\"run-with-real-backend\" />\n",
        "\n",
        "### Execute com backend real\n",
        "\n",
        "Agora, executamos o experimento completo em algumas etapas de Trotter. O número de qubits, a precisão necessária e o tempo máximo da QPU podem ser modificados de acordo com os recursos disponíveis da QPU. Observe que restringir o tempo máximo de QPU afetará a precisão final, como você verá no gráfico final abaixo.\n",
        "\n",
        "Analisamos quatro circuitos com 5, 7 e 9 etapas de Trotter em uma precisão de 0.05, comparando seus valores de expectativa ideais, ruidosos e mitigados por erro:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 84,
      "id": "7cfb4dbc",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-06T13:47:04.428822Z",
          "start_time": "2025-11-06T13:47:04.415793Z"
        }
      },
      "outputs": [],
      "source": [
        "steps_vec = [5, 7, 9]\n",
        "\n",
        "circ_vec = []\n",
        "for steps in steps_vec:\n",
        "    circ = trotter_circuit_from_layers(\n",
        "        steps, theta_x, theta_z, theta_zz, layers\n",
        "    )\n",
        "    circ_vec.append(circ)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d63c1de0",
      "metadata": {},
      "source": [
        "Novamente, realizamos o OBP em cada circuito para reduzir o tempo de execução:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 85,
      "id": "267e030f",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-06T13:47:10.089306Z",
          "start_time": "2025-11-06T13:47:09.003172Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "n.o. steps: 5\n",
            "Backpropagated 11 slices.\n",
            "New observable has 363 terms, which can be combined into 4 groups.\n",
            "After truncation, the error in our observable is bounded by 0.000e+00\n",
            "-----------------\n",
            "n.o. steps: 7\n",
            "Backpropagated 11 slices.\n",
            "New observable has 363 terms, which can be combined into 4 groups.\n",
            "After truncation, the error in our observable is bounded by 0.000e+00\n",
            "-----------------\n",
            "n.o. steps: 9\n",
            "Backpropagated 11 slices.\n",
            "New observable has 363 terms, which can be combined into 4 groups.\n",
            "After truncation, the error in our observable is bounded by 0.000e+00\n",
            "-----------------\n"
          ]
        }
      ],
      "source": [
        "bp_circ_vec_35, bp_obs_vec_35 = run_backpropagation(\n",
        "    circ_vec, observable, steps_vec\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "68bb0915",
      "metadata": {},
      "source": [
        "Agora, executamos um lote de trabalhos completos do QESEM. Limitamos o tempo máximo de execução da QPU para cada um dos pontos para melhor controle do orçamento da QPU.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 106,
      "id": "c039197fca88d16a",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-06T16:41:01.087031Z",
          "start_time": "2025-11-06T16:41:01.073625Z"
        }
      },
      "outputs": [],
      "source": [
        "run_on_real_hardware = True\n",
        "\n",
        "precision = 0.05\n",
        "if run_on_real_hardware:\n",
        "    backend_name = \"ibm_marrakesh\"\n",
        "else:\n",
        "    backend_name = \"fake_fez\""
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 54,
      "id": "e60a2fc8",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T17:27:41.525432Z",
          "start_time": "2025-11-05T17:27:41.523337Z"
        }
      },
      "outputs": [],
      "source": [
        "# Running full jobs for:\n",
        "pubs_list = [\n",
        "    [(bp_circ_vec_35[i], bp_obs_vec_35[i])] for i in range(len(bp_obs_vec_35))\n",
        "]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 55,
      "id": "534ad1d013e4fb2",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T17:29:17.142120Z",
          "start_time": "2025-11-05T17:29:04.155649Z"
        }
      },
      "outputs": [],
      "source": [
        "# Initiating multiple jobs for different lengths\n",
        "job_list = []\n",
        "for pubs in pubs_list:\n",
        "    job_obp = qesem_function.run(\n",
        "        pubs=pubs,\n",
        "        instance=instance,\n",
        "        backend_name=backend_name,  # E.g. \"ibm_brisbane\"\n",
        "        options={\n",
        "            \"max_execution_time\": 300,  # Limits the QPU time, specified in seconds.\n",
        "            \"default_precision\": 0.05,\n",
        "        },\n",
        "    )\n",
        "    job_list.append(job_obp)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "05c75ada",
      "metadata": {},
      "source": [
        "Aqui verificamos o status de cada trabalho:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 61,
      "id": "b869fd4f",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T19:44:14.257551Z",
          "start_time": "2025-11-05T19:44:08.130764Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "DONE\n",
            "DONE\n",
            "DONE\n",
            "DONE\n"
          ]
        }
      ],
      "source": [
        "for job in job_list:\n",
        "    print(job.status())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "68cabcc4",
      "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"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "72e511b12012254d",
      "metadata": {},
      "source": [
        "Quando todos os trabalhos terminarem de ser executados, poderemos comparar seus valores de expectativa ruidosos e atenuados.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 130,
      "id": "9bc7cce51e0d4b4c",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-06T20:49:40.252799Z",
          "start_time": "2025-11-06T20:49:14.517682Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Using precalculated ideal values for large circuits calculated with belief propagation PEPS. Currently only for 35 qubits.\n",
            "---------------------------------\n",
            "Ideal: 0.79537\n",
            "Noisy: 0.7039237951821501\n",
            "QESEM: 0.7828018244130982 ± 0.013257266977728376\n",
            "Using precalculated ideal values for large circuits calculated with belief propagation PEPS. Currently only for 35 qubits.\n",
            "---------------------------------\n",
            "Ideal: 0.78653\n",
            "Noisy: 0.6478583812958806\n",
            "QESEM: 0.7875259197423828 ± 0.02703045139248604\n",
            "Using precalculated ideal values for large circuits calculated with belief propagation PEPS. Currently only for 35 qubits.\n",
            "---------------------------------\n",
            "Ideal: 0.79699\n",
            "Noisy: 0.6171787879868142\n",
            "QESEM: 0.6918791909168913 ± 0.0740873782039517\n"
          ]
        }
      ],
      "source": [
        "ideal_values = []\n",
        "noisy_values = []\n",
        "error_mitigated_values = []\n",
        "error_mitigated_stds = []\n",
        "\n",
        "for i in range(len(job_list)):\n",
        "    job = job_list[i]\n",
        "    result = job.result()  # Blocking - takes 3-5 minutes\n",
        "    noisy_results = result[0].metadata[\"noisy_results\"]\n",
        "\n",
        "    ideal_val = calculate_ideal_evs(circ_vec[i], observable, n_qubits, i)\n",
        "    print(\"---------------------------------\")\n",
        "    print(f\"Ideal: {ideal_val}\")\n",
        "    print(f\"Noisy: {noisy_results.evs}\")\n",
        "    print(f\"QESEM: {result[0].data.evs} \\u00b1 {result[0].data.stds}\")\n",
        "\n",
        "    ideal_values.append(ideal_val)\n",
        "    noisy_values.append(noisy_results.evs)\n",
        "    error_mitigated_values.append(result[0].data.evs)\n",
        "    error_mitigated_stds.append(result[0].data.stds)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "52d3b66d2e42b464",
      "metadata": {},
      "source": [
        "Por fim, podemos traçar um gráfico da magnetização em relação ao número de etapas. Isso resume o benefício de usar a função Qiskit do QESEM para a atenuação de erros sem polarização em dispositivos quânticos com ruído.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 131,
      "id": "0f1a44d0",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-06T20:49:49.067165Z",
          "start_time": "2025-11-06T20:49:48.990714Z"
        }
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Text(0, 0.5, 'Magnetization')"
            ]
          },
          "execution_count": 131,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/qedma-2d-ising-with-qesem/extracted-outputs/0f1a44d0-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.plot(steps_vec, ideal_values, \"--\", label=\"ideal\")\n",
        "plt.scatter(steps_vec, noisy_values, label=\"noisy\")\n",
        "plt.errorbar(\n",
        "    steps_vec,\n",
        "    error_mitigated_values,\n",
        "    yerr=error_mitigated_stds,\n",
        "    fmt=\"o\",\n",
        "    capsize=5,\n",
        "    label=\"QESEM mitigation\",\n",
        ")\n",
        "plt.legend()\n",
        "plt.xlabel(\"n.o. steps\")\n",
        "plt.ylabel(\"Magnetization\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e14cb01633a8451a",
      "metadata": {},
      "source": [
        "<Admonition type=\"note\">\n",
        "  A nona etapa tem uma grande barra de erro estatístico porque limitamos o tempo da QPU a 5 minutos. Se você executar essa etapa por 15 minutos (como sugere a estimativa empírica de tempo), obterá uma barra de erro menor. Portanto, o valor atenuado ficará mais próximo do valor ideal.\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,
    "qpuSeconds": 1200
  },
  "nbformat": 4,
  "nbformat_minor": 5
}