{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "b6d1e3ec",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Modelo Ising de campo transversal com gerenciamento de desempenho da Q-CTRL\"\n",
        "description: \"Construa e resolva o modelo Ising de campo transversal usando Fire Opal e Qiskit\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore fontsize sharey rmse RMSE boxstyle */}\n",
        "\n",
        "<span id=\"transverse-field-ising-model-with-q-ctrls-performance-management\" />\n",
        "\n",
        "# Modelo Ising de campo transversal com gerenciamento de desempenho da Q-CTRL\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "a6f69b77",
      "metadata": {},
      "source": [
        "*Estimativa de uso: 2 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": "8bf80006",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Segundo plano\n",
        "\n",
        "O Modelo de Ising de Campo Transversal (TFIM) é importante para o estudo do magnetismo quântico e das transições de fase. Ele descreve um conjunto de spins dispostos em uma rede, em que cada spin interage com seus vizinhos e, ao mesmo tempo, é influenciado por um campo magnético externo que gera flutuações quânticas.\n",
        "\n",
        "Uma abordagem comum para simular esse modelo é usar a decomposição de Trotter para aproximar o operador de evolução temporal, construindo circuitos que alternam entre rotações de um único qubit e interações de dois qubits emaranhados. No entanto, essa simulação em hardware real é desafiadora devido ao ruído e à decoerência, o que leva a desvios da dinâmica real. Para superar isso, usamos as ferramentas de gerenciamento de desempenho e supressão de erros Fire Opal da Q-CTRL, oferecidas como uma função Qiskit (consulte a [documentação do Fire Opal](/docs/guides/q-ctrl-performance-management) ). O Fire Opal otimiza automaticamente a execução do circuito aplicando o desacoplamento dinâmico, o layout avançado, o roteamento e outras técnicas de supressão de erros, todas com o objetivo de reduzir o ruído. Com esses aprimoramentos, os resultados do hardware se alinham mais estreitamente com as simulações sem ruído e, portanto, podemos estudar a dinâmica de magnetização do TFIM com maior fidelidade.\n",
        "\n",
        "Neste tutorial, veremos:\n",
        "\n",
        "* Construa o Hamiltoniano TFIM em um gráfico de triângulos de spin conectados\n",
        "* Simular a evolução do tempo com circuitos Trotterizados em diferentes profundidades\n",
        "* Calcule e visualize magnetizações de um único qubit $\\langle Z_i \\rangle$ ao longo do tempo\n",
        "* Compare simulações de linha de base com resultados de execuções de hardware usando o gerenciamento de desempenho Fire Opal da Q-CTRL\n",
        "\n",
        "<span id=\"overview\" />\n",
        "\n",
        "## Visão geral\n",
        "\n",
        "O Modelo de Ising de Campo Transversal (TFIM) é um modelo de spin quântico que captura os recursos essenciais das transições de fase quântica. O Hamiltoniano é definido como:\n",
        "\n",
        "$$\n",
        "H = -J \\sum_{i} Z_i Z_{i+1} - h \\sum_{i} X_i\n",
        "$$\n",
        "\n",
        "em que $Z_i$ e $X_i$ são operadores Pauli que atuam no qubit $i$, $J$ é a força de acoplamento entre os spins vizinhos e $h$ é a força do campo magnético transversal. O primeiro termo representa interações ferromagnéticas clássicas, enquanto o segundo introduz flutuações quânticas por meio do campo transversal. Para simular a dinâmica do TFIM, você usa uma decomposição de Trotter do operador de evolução unitária $e^{-iHt}$, implementado por meio de camadas de portas RX e RZZ com base em um gráfico personalizado de triângulos de spin conectados. A simulação explora como a magnetização $\\langle Z \\rangle$ evolui com o aumento das etapas de Trotter.\n",
        "\n",
        "O desempenho da implementação proposta do TFIM é avaliado comparando simulações sem ruído com backends com ruído. Os recursos aprimorados de execução e supressão de erros do Fire Opal são usados para atenuar o efeito do ruído no hardware real, produzindo estimativas mais confiáveis de observáveis de spin, como $\\langle Z_i \\rangle$ e correlacionadores $\\langle Z_i Z_j \\rangle$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "55b94021",
      "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 v1.4 ou posterior, com suporte [para visualização](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime v0.40 ou posterior (`pip install qiskit-ibm-runtime`)\n",
        "* Qiskit Functions Catalog v0.9.0 (`pip install qiskit-ibm-catalog`)\n",
        "* Fire Opal SDK v9.0.2 ou posterior (`pip install fire-opal`)\n",
        "* Q-CTRL Visualizer v8.0.2 ou posterior (`pip install qctrl-visualizer`)\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "7db2e559",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Instalação\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ecaa518f",
      "metadata": {},
      "source": [
        "Primeiro, autentique-se usando sua [chave de API IBM Quantum](http://quantum.cloud.ibm.com/). Em seguida, selecione a função do Qiskit da seguinte maneira. (Este código pressupõe que você já tenha [salvo sua conta](/docs/guides/functions-get-started#install-qiskit-functions-catalog-client) no seu ambiente local.)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "bc380c46",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "from qiskit import QuantumCircuit\n",
        "from qiskit_ibm_catalog import QiskitFunctionsCatalog\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import SamplerV2 as Sampler\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit_aer import AerSimulator\n",
        "\n",
        "import numpy as np\n",
        "import networkx as nx\n",
        "import matplotlib.pyplot as plt\n",
        "import qctrlvisualizer as qv"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "e9d916b6",
      "metadata": {},
      "outputs": [],
      "source": [
        "catalog = QiskitFunctionsCatalog(channel=\"ibm_quantum_platform\")\n",
        "\n",
        "# Access Function\n",
        "perf_mgmt = catalog.load(\"q-ctrl/performance-management\")"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "988ee237",
      "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",
        "<span id=\"generate-tfim-graph\" />\n",
        "\n",
        "### Gerar gráfico TFIM\n",
        "\n",
        "Começamos definindo a rede de spins e os acoplamentos entre eles. Neste tutorial, a estrutura é construída a partir de triângulos conectados dispostos em uma cadeia linear. Cada triângulo consiste em três nós conectados em um loop fechado, e a cadeia é formada pela ligação de um nó de cada triângulo ao triângulo anterior.\n",
        "\n",
        "A função auxiliar `connected_triangles_adj_matrix` cria a matriz de adjacência para essa estrutura. Para uma cadeia de $n$ triângulos, o gráfico resultante contém $2n+1$ nós.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "829f1b6d",
      "metadata": {},
      "outputs": [],
      "source": [
        "def connected_triangles_adj_matrix(n):\n",
        "    \"\"\"\n",
        "    Generate the adjacency matrix for 'n' connected triangles in a chain.\n",
        "    \"\"\"\n",
        "    num_nodes = 2 * n + 1\n",
        "    adj_matrix = np.zeros((num_nodes, num_nodes), dtype=int)\n",
        "\n",
        "    for i in range(n):\n",
        "        a, b, c = i * 2, i * 2 + 1, i * 2 + 2  # Nodes of the current triangle\n",
        "\n",
        "        # Connect the three nodes in a triangle\n",
        "        adj_matrix[a, b] = adj_matrix[b, a] = 1\n",
        "        adj_matrix[b, c] = adj_matrix[c, b] = 1\n",
        "        adj_matrix[a, c] = adj_matrix[c, a] = 1\n",
        "\n",
        "        # If not the first triangle, connect to the previous triangle\n",
        "        if i > 0:\n",
        "            adj_matrix[a, a - 1] = adj_matrix[a - 1, a] = 1\n",
        "\n",
        "    return adj_matrix"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "53869b13",
      "metadata": {},
      "source": [
        "Para visualizar a estrutura que acabamos de definir, podemos desenhar a cadeia de triângulos conectados e rotular cada nó. A função abaixo cria o gráfico para um número escolhido de triângulos e o exibe.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "2fc293aa",
      "metadata": {},
      "outputs": [],
      "source": [
        "def plot_triangle_chain(n, side=1.0):\n",
        "    \"\"\"\n",
        "    Plot a horizontal chain of n equilateral triangles.\n",
        "    Baseline: even nodes (0,2,4,...,2n) on y=0\n",
        "    Apexes: odd nodes (1,3,5,...,2n-1) above the midpoint.\n",
        "    \"\"\"\n",
        "    # Build graph\n",
        "    A = connected_triangles_adj_matrix(n)\n",
        "    G = nx.from_numpy_array(A)\n",
        "\n",
        "    h = np.sqrt(3) / 2 * side\n",
        "    pos = {}\n",
        "\n",
        "    # Place baseline nodes\n",
        "    for k in range(n + 1):\n",
        "        pos[2 * k] = (k * side, 0.0)\n",
        "\n",
        "    # Place apex nodes\n",
        "    for k in range(n):\n",
        "        x_left = pos[2 * k][0]\n",
        "        x_right = pos[2 * k + 2][0]\n",
        "        pos[2 * k + 1] = ((x_left + x_right) / 2, h)\n",
        "\n",
        "    # Draw\n",
        "    fig, ax = plt.subplots(figsize=(1.5 * n, 2.5))\n",
        "    nx.draw(\n",
        "        G,\n",
        "        pos,\n",
        "        ax=ax,\n",
        "        with_labels=True,\n",
        "        font_size=10,\n",
        "        font_color=\"white\",\n",
        "        node_size=600,\n",
        "        node_color=qv.QCTRL_STYLE_COLORS[0],\n",
        "        edge_color=\"black\",\n",
        "        width=2,\n",
        "    )\n",
        "    ax.set_aspect(\"equal\")\n",
        "    ax.margins(0.2)\n",
        "    plt.show()\n",
        "\n",
        "    return G, pos"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8abc0622",
      "metadata": {},
      "source": [
        "Para este tutorial, usaremos uma corrente de 20 triângulos.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "861ab6e3",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/transverse-field-ising-model/extracted-outputs/861ab6e3-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "n_triangles = 20\n",
        "n_qubits = 2 * n_triangles + 1\n",
        "plot_triangle_chain(n_triangles, side=1.0)\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ac6f36e3",
      "metadata": {},
      "source": [
        "<span id=\"coloring-graph-edges\" />\n",
        "\n",
        "### Colorir arestas de grafos\n",
        "\n",
        "Para implementar o acoplamento spin-spin, é útil agrupar as bordas que não se sobrepõem. Isso nos permite aplicar portas de dois qubits em paralelo. Podemos fazer isso com um procedimento simples de coloração de bordas [\\[1\\]](#references), que atribui uma cor a cada borda para que as bordas que se encontram no mesmo nó sejam colocadas em grupos diferentes.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "c893b88f",
      "metadata": {},
      "outputs": [],
      "source": [
        "def edge_coloring(graph):\n",
        "    \"\"\"\n",
        "    Takes a NetworkX graph and returns a list of lists\n",
        "    where each inner list contains\n",
        "    the edges assigned the same color.\n",
        "    \"\"\"\n",
        "    line_graph = nx.line_graph(graph)\n",
        "    edge_colors = nx.coloring.greedy_color(line_graph)\n",
        "\n",
        "    color_groups = {}\n",
        "    for edge, color in edge_colors.items():\n",
        "        if color not in color_groups:\n",
        "            color_groups[color] = []\n",
        "        color_groups[color].append(edge)\n",
        "\n",
        "    return list(color_groups.values())"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "b4d480b3",
      "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",
        "<span id=\"generate-trotterized-circuits-on-spin-graphs\" />\n",
        "\n",
        "### Gerar circuitos trotterizados em gráficos de spin\n",
        "\n",
        "Para simular a dinâmica do TFIM, construímos circuitos que aproximam o operador de evolução temporal.\n",
        "\n",
        "$$\n",
        "U(t) = e^{-i H t}, \\quad \\text{where} \\quad H = -J \\sum_{\\langle i,j \\rangle} Z_i Z_j - h \\sum_i X_i .\n",
        "$$\n",
        "\n",
        "Usamos uma decomposição de Trotter de segunda ordem:\n",
        "\n",
        "$$\n",
        "e^{-i H \\Delta t} \\approx e^{-i H_X \\Delta t / 2}\\, e^{-i H_Z \\Delta t}\\, e^{-i H_X \\Delta t / 2},\n",
        "$$\n",
        "\n",
        "onde $H_X = -h \\sum_i X_i$ e $H_Z = -J \\sum_{\\langle i,j \\rangle} Z_i Z_j$.\n",
        "\n",
        "* O termo $H_X$ é implementado com camadas de rotações `RX` .\n",
        "* O termo $H_Z$ é implementado com camadas de portas `RZZ` ao longo das bordas do gráfico de interação.\n",
        "\n",
        "Os ângulos dessas portas são determinados pelo campo transversal $h$, pela constante de acoplamento $J$ e pela etapa de tempo $\\Delta t$. Ao empilhar várias etapas de Trotter, geramos circuitos de profundidade crescente que se aproximam da dinâmica do sistema. As funções `generate_tfim_circ_custom_graph` e `trotter_circuits` constroem um circuito quântico Trotterizado a partir de um gráfico de interação de spin arbitrário.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ebe5c712",
      "metadata": {},
      "outputs": [],
      "source": [
        "def generate_tfim_circ_custom_graph(\n",
        "    steps, h, J, dt, psi0, graph: nx.graph.Graph, meas_basis=\"Z\", mirror=False\n",
        "):\n",
        "    \"\"\"\n",
        "    Generate a second order trotter of the form e^(a+b) ~ e^(b/2) e^a e^(b/2)\n",
        "    for simulating a transverse field ising model:\n",
        "    e^{-i H t} where the Hamiltonian H = -J \\\\sum_i Z_i Z_{i+1} + h \\\\sum_i X_i.\n",
        "\n",
        "    steps: Number of trotter steps\n",
        "    theta_x: Angle for layer of X rotations\n",
        "    theta_zz: Angle for layer of ZZ rotations\n",
        "    theta_x: Angle for second layer of X rotations\n",
        "    J: Coupling between nearest neighbor spins\n",
        "    h: The transverse magnetic field strength\n",
        "    dt: t/total_steps\n",
        "    psi0: initial state (assumed to be prepared in the computational basis).\n",
        "    meas_basis: basis to measure all correlators in\n",
        "\n",
        "    This is a second order trotter of the form e^(a+b) ~ e^(b/2) e^a e^(b/2)\n",
        "    \"\"\"\n",
        "    theta_x = h * dt\n",
        "    theta_zz = -2 * J * dt\n",
        "    nq = graph.number_of_nodes()\n",
        "    color_edges = edge_coloring(graph)\n",
        "    circ = QuantumCircuit(nq, nq)\n",
        "    # Initial state, for typical cases in the computational basis\n",
        "    for i, b in enumerate(psi0):\n",
        "        if b == \"1\":\n",
        "            circ.x(i)\n",
        "    # Trotter steps\n",
        "    for step in range(steps):\n",
        "        for i in range(nq):\n",
        "            circ.rx(theta_x, i)\n",
        "        if mirror:\n",
        "            color_edges = [sublist[::-1] for sublist in color_edges[::-1]]\n",
        "        for edge_list in color_edges:\n",
        "            for edge in edge_list:\n",
        "                circ.rzz(theta_zz, edge[0], edge[1])\n",
        "        for i in range(nq):\n",
        "            circ.rx(theta_x, i)\n",
        "\n",
        "    # some typically used basis rotations\n",
        "    if meas_basis == \"X\":\n",
        "        for b in range(nq):\n",
        "            circ.h(b)\n",
        "    elif meas_basis == \"Y\":\n",
        "        for b in range(nq):\n",
        "            circ.sdg(b)\n",
        "            circ.h(b)\n",
        "\n",
        "    for i in range(nq):\n",
        "        circ.measure(i, i)\n",
        "\n",
        "    return circ\n",
        "\n",
        "\n",
        "def trotter_circuits(G, d_ind_tot, J, h, dt, meas_basis, mirror=True):\n",
        "    \"\"\"\n",
        "    Generates a sequence of Trotterized circuits, each with increasing depth.\n",
        "    Given a spin interaction graph and Hamiltonian parameters, it constructs\n",
        "    a list of circuits with 1 to d_ind_tot Trotter steps\n",
        "\n",
        "    G: Graph defining spin interactions (edges = ZZ couplings)\n",
        "    d_ind_tot: Number of Trotter steps (maximum depth)\n",
        "    J: Coupling between nearest neighboring spins\n",
        "    h: Transverse magnetic field strength\n",
        "    dt: (t / total_steps\n",
        "    meas_basis: Basis to measure all correlators in\n",
        "    mirror: If True, mirror the Trotter layers\n",
        "    \"\"\"\n",
        "    qubit_count = len(G)\n",
        "    circuits = []\n",
        "    psi0 = \"0\" * qubit_count\n",
        "\n",
        "    for steps in range(1, d_ind_tot + 1):\n",
        "        circuits.append(\n",
        "            generate_tfim_circ_custom_graph(\n",
        "                steps, h, J, dt, psi0, G, meas_basis, mirror\n",
        "            )\n",
        "        )\n",
        "    return circuits"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "50b94af2",
      "metadata": {},
      "source": [
        "<span id=\"estimate-single-qubit-magnetizations-$langle-z_i-rangle$\" />\n",
        "\n",
        "### Estimar magnetizações de um único qubit $\\langle Z_i \\rangle$\n",
        "\n",
        "Para estudar a dinâmica do modelo, queremos medir a magnetização de cada qubit, definida pelo valor de expectativa $\\langle Z_i \\rangle = \\langle \\psi | Z_i | \\psi \\rangle$.\n",
        "\n",
        "Nas simulações, podemos calcular isso diretamente a partir dos resultados das medições. A função `z_expectation` processa as contagens de bitstring e retorna o valor de $\\langle Z_i \\rangle$ para um índice de qubit escolhido. No hardware real, avaliamos a mesma quantidade especificando o operador Pauli usando a função `generate_z_observables` e, em seguida, o backend calcula o valor da expectativa.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "3fc929e0",
      "metadata": {},
      "outputs": [],
      "source": [
        "def z_expectation(counts, index):\n",
        "    \"\"\"\n",
        "    counts: Dict of mitigated bitstrings.\n",
        "    index: Index i in the single operator expectation value < II...Z_i...I >\n",
        "        to be calculated.\n",
        "    return:  < Z_i >\n",
        "    \"\"\"\n",
        "    z_exp = 0\n",
        "    tot = 0\n",
        "    for bitstring, value in counts.items():\n",
        "        bit = int(bitstring[index])\n",
        "        sign = 1\n",
        "        if bit % 2 == 1:\n",
        "            sign = -1\n",
        "        z_exp += sign * value\n",
        "        tot += value\n",
        "\n",
        "    return z_exp / tot"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "c9e79716",
      "metadata": {},
      "outputs": [],
      "source": [
        "def generate_z_observables(nq):\n",
        "    observables = []\n",
        "    for i in range(nq):\n",
        "        pauli_string = \"\".join([\"Z\" if j == i else \"I\" for j in range(nq)])\n",
        "        observables.append(SparsePauliOp(pauli_string))\n",
        "    return observables"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "598d11cf",
      "metadata": {},
      "outputs": [],
      "source": [
        "observables = generate_z_observables(n_qubits)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "397d9b05",
      "metadata": {},
      "source": [
        "Agora, definimos os parâmetros para gerar os circuitos Trotterizados. Neste tutorial, a rede é uma cadeia de 20 triângulos conectados, o que corresponde a um sistema de 41 qubits.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "2853d21b",
      "metadata": {},
      "outputs": [],
      "source": [
        "all_circs_mirror = []\n",
        "for num_triangles in [n_triangles]:\n",
        "    for meas_basis in [\"Z\"]:\n",
        "        A = connected_triangles_adj_matrix(num_triangles)\n",
        "        G = nx.from_numpy_array(A)\n",
        "        nq = len(G)\n",
        "        d_ind_tot = 22\n",
        "        dt = 2 * np.pi * 1 / 30 * 0.25\n",
        "        J = 1\n",
        "        h = -7\n",
        "        all_circs_mirror.extend(\n",
        "            trotter_circuits(G, d_ind_tot, J, h, dt, meas_basis, True)\n",
        "        )\n",
        "circs = all_circs_mirror"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a4b0476d",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## Passo 3: Execute usando Qiskit primitives\n",
        "\n",
        "<span id=\"run-mps-simulation\" />\n",
        "\n",
        "### Executar simulação MPS\n",
        "\n",
        "A lista de circuitos Trotterizados é executada usando o simulador `matrix_product_state` com uma escolha arbitrária de $4096$ tiros. O método MPS fornece uma aproximação eficiente da dinâmica do circuito, com precisão determinada pela dimensão da ligação escolhida. Para os tamanhos de sistema considerados aqui, a dimensão padrão da ligação é suficiente para capturar a dinâmica da magnetização com alta fidelidade. As contagens brutas são normalizadas e, a partir delas, calculamos os valores de expectativa de um único qubit $\\langle Z_i \\rangle$ em cada etapa de Trotter. Por fim, calculamos a média de todos os qubits para obter uma única curva que mostra como a magnetização muda com o tempo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "3709531f",
      "metadata": {},
      "outputs": [],
      "source": [
        "backend_sim = AerSimulator(method=\"matrix_product_state\")\n",
        "\n",
        "\n",
        "def normalize_counts(counts_list, shots):\n",
        "    new_counts_list = []\n",
        "    for counts in counts_list:\n",
        "        a = {k: v / shots for k, v in counts.items()}\n",
        "        new_counts_list.append(a)\n",
        "    return new_counts_list\n",
        "\n",
        "\n",
        "def run_sim(circ_list):\n",
        "    shots = 4096\n",
        "    res = backend_sim.run(circ_list, shots=shots)\n",
        "    normed = normalize_counts(res.result().get_counts(), shots)\n",
        "    return normed\n",
        "\n",
        "\n",
        "sim_counts = run_sim(circs)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5895aa46",
      "metadata": {},
      "source": [
        "<span id=\"run-on-hardware\" />\n",
        "\n",
        "### Executar no hardware\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "4b5a2f87",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.backend(\"ibm_marrakesh\")\n",
        "\n",
        "\n",
        "def run_qiskit(circ_list):\n",
        "    shots = 4096\n",
        "    pm = generate_preset_pass_manager(backend=backend)\n",
        "    isa_circuits = [pm.run(qc) for qc in circ_list]\n",
        "    sampler = Sampler(mode=backend)\n",
        "    res = sampler.run(isa_circuits, shots=shots)\n",
        "    res = [r.data.c.get_counts() for r in res.result()]\n",
        "    normed = normalize_counts(res, shots)\n",
        "    return normed\n",
        "\n",
        "\n",
        "qiskit_counts = run_qiskit(circs)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d30269ee",
      "metadata": {},
      "source": [
        "<span id=\"run-on-hardware-with-fire-opal\" />\n",
        "\n",
        "### Execute em hardware com Fire Opal\n",
        "\n",
        "Avaliamos a dinâmica da magnetização em hardware quântico real. O Fire Opal fornece uma função Qiskit que amplia a primitiva padrão do Qiskit Runtime Estimator com supressão automatizada de erros e gerenciamento de desempenho. Enviamos os circuitos Trotterizados diretamente para um backend IBM®, enquanto o Fire Opal cuida da execução com reconhecimento de ruído.\n",
        "\n",
        "Preparamos uma lista de `pubs`, em que cada item contém um circuito e os observáveis Pauli-Z correspondentes. Eles são passados para a função estimadora do Fire Opal, que retorna os valores de expectativa $\\langle Z_i \\rangle$ para cada qubit em cada etapa do Trotter. Os resultados podem, então, ser calculados pela média dos qubits para obter a curva de magnetização do hardware.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ca8348c9",
      "metadata": {},
      "outputs": [],
      "source": [
        "backend_name = \"ibm_marrakesh\"\n",
        "estimator_pubs = [(qc, observables) for qc in all_circs_mirror[:]]\n",
        "\n",
        "# Run the circuit using the estimator\n",
        "qctrl_estimator_job = perf_mgmt.run(\n",
        "    primitive=\"estimator\",\n",
        "    pubs=estimator_pubs,\n",
        "    backend_name=backend_name,\n",
        "    options={\"default_shots\": 4096},\n",
        ")\n",
        "\n",
        "result_qctrl = qctrl_estimator_job.result()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "aa081308",
      "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",
        "Por fim, comparamos a curva de magnetização do simulador com os resultados obtidos no hardware real. A plotagem de ambos lado a lado mostra a proximidade da execução do hardware com o Fire Opal com a linha de base sem ruído nas etapas do Trotter.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 102,
      "id": "91dd23a5",
      "metadata": {},
      "outputs": [],
      "source": [
        "def make_correlators(test_counts, nq, d_ind_tot):\n",
        "    mz = np.empty((nq, d_ind_tot))\n",
        "    for d_ind in range(d_ind_tot):\n",
        "        counts = test_counts[d_ind]\n",
        "        for i in range(nq):\n",
        "            mz[i, d_ind] = z_expectation(counts, i)\n",
        "    average_z = np.mean(mz, axis=0)\n",
        "    return np.concatenate((np.array([1]), average_z), axis=0)\n",
        "\n",
        "\n",
        "sim_exp = make_correlators(sim_counts[0:22], nq=nq, d_ind_tot=22)\n",
        "qiskit_exp = make_correlators(qiskit_counts[0:22], nq=nq, d_ind_tot=22)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 103,
      "id": "1581f9f0",
      "metadata": {},
      "outputs": [],
      "source": [
        "qctrl_exp = [ev.data.evs for ev in result_qctrl[:]]\n",
        "qctrl_exp_mean = np.concatenate(\n",
        "    (np.array([1]), np.mean(qctrl_exp, axis=1)), axis=0\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "id": "d1f90811",
      "metadata": {},
      "outputs": [],
      "source": [
        "def make_expectations_plot(\n",
        "    sim_z,\n",
        "    depths,\n",
        "    exp_qctrl=None,\n",
        "    exp_qctrl_error=None,\n",
        "    exp_qiskit=None,\n",
        "    exp_qiskit_error=None,\n",
        "    plot_from=0,\n",
        "    plot_upto=23,\n",
        "):\n",
        "    import numpy as np\n",
        "    import matplotlib.pyplot as plt\n",
        "\n",
        "    depth_ticks = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22]\n",
        "\n",
        "    d = np.asarray(depths)[plot_from:plot_upto]\n",
        "    sim = np.asarray(sim_z)[plot_from:plot_upto]\n",
        "\n",
        "    qk = (\n",
        "        None\n",
        "        if exp_qiskit is None\n",
        "        else np.asarray(exp_qiskit)[plot_from:plot_upto]\n",
        "    )\n",
        "    qc = (\n",
        "        None\n",
        "        if exp_qctrl is None\n",
        "        else np.asarray(exp_qctrl)[plot_from:plot_upto]\n",
        "    )\n",
        "\n",
        "    qk_err = (\n",
        "        None\n",
        "        if exp_qiskit_error is None\n",
        "        else np.asarray(exp_qiskit_error)[plot_from:plot_upto]\n",
        "    )\n",
        "    qc_err = (\n",
        "        None\n",
        "        if exp_qctrl_error is None\n",
        "        else np.asarray(exp_qctrl_error)[plot_from:plot_upto]\n",
        "    )\n",
        "\n",
        "    # ---- helper(s) ----\n",
        "    def rmse(a, b):\n",
        "        if a is None or b is None:\n",
        "            return None\n",
        "        a = np.asarray(a, dtype=float)\n",
        "        b = np.asarray(b, dtype=float)\n",
        "        mask = np.isfinite(a) & np.isfinite(b)\n",
        "        if not np.any(mask):\n",
        "            return None\n",
        "        diff = a[mask] - b[mask]\n",
        "        return float(np.sqrt(np.mean(diff**2)))\n",
        "\n",
        "    def plot_panel(ax, method_y, method_err, color, label, band_color=None):\n",
        "        # Noiseless reference\n",
        "        ax.plot(d, sim, color=\"grey\", label=\"Noiseless simulation\")\n",
        "\n",
        "        # Method line + band\n",
        "        if method_y is not None:\n",
        "            ax.plot(d, method_y, color=color, label=label)\n",
        "            if method_err is not None:\n",
        "                lo = np.clip(method_y - method_err, -1.05, 1.05)\n",
        "                hi = np.clip(method_y + method_err, -1.05, 1.05)\n",
        "                ax.fill_between(\n",
        "                    d,\n",
        "                    lo,\n",
        "                    hi,\n",
        "                    alpha=0.18,\n",
        "                    color=band_color if band_color else color,\n",
        "                    label=f\"{label} ± error\",\n",
        "                )\n",
        "        else:\n",
        "            ax.text(\n",
        "                0.5,\n",
        "                0.5,\n",
        "                \"No data\",\n",
        "                transform=ax.transAxes,\n",
        "                ha=\"center\",\n",
        "                va=\"center\",\n",
        "                fontsize=10,\n",
        "                color=\"0.4\",\n",
        "            )\n",
        "\n",
        "        # RMSE box (vs sim)\n",
        "        r = rmse(method_y, sim)\n",
        "        if r is not None:\n",
        "            ax.text(\n",
        "                0.98,\n",
        "                0.02,\n",
        "                f\"RMSE: {r:.4f}\",\n",
        "                transform=ax.transAxes,\n",
        "                va=\"bottom\",\n",
        "                ha=\"right\",\n",
        "                fontsize=8,\n",
        "                bbox=dict(\n",
        "                    boxstyle=\"round,pad=0.35\", fc=\"white\", ec=\"0.7\", alpha=0.9\n",
        "                ),\n",
        "            )\n",
        "        # Axes\n",
        "        ax.set_xticks(depth_ticks)\n",
        "        ax.set_ylim(-1.05, 1.05)\n",
        "        ax.grid(True, which=\"both\", linewidth=0.4, alpha=0.4)\n",
        "        ax.set_axisbelow(True)\n",
        "        ax.legend(prop={\"size\": 8}, loc=\"best\")\n",
        "\n",
        "    fig, axes = plt.subplots(1, 2, figsize=(10, 4), dpi=300, sharey=True)\n",
        "\n",
        "    axes[0].set_title(\"Fire Opal (Q-CTRL)\", fontsize=10)\n",
        "    plot_panel(\n",
        "        axes[0],\n",
        "        qc,\n",
        "        qc_err,\n",
        "        color=\"#680CE9\",\n",
        "        label=\"Fire Opal\",\n",
        "        band_color=\"#680CE9\",\n",
        "    )\n",
        "    axes[0].set_xlabel(\"Trotter step\")\n",
        "    axes[0].set_ylabel(r\"$\\langle Z \\rangle$\")\n",
        "    axes[1].set_title(\"Qiskit\", fontsize=10)\n",
        "    plot_panel(\n",
        "        axes[1], qk, qk_err, color=\"blue\", label=\"Qiskit\", band_color=\"blue\"\n",
        "    )\n",
        "    axes[1].set_xlabel(\"Trotter step\")\n",
        "\n",
        "    plt.tight_layout()\n",
        "    plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "id": "415760ad",
      "metadata": {},
      "outputs": [],
      "source": [
        "depths = list(range(d_ind_tot + 1))\n",
        "errors = np.abs(np.array(qctrl_exp_mean) - np.array(sim_exp))\n",
        "\n",
        "errors_qiskit = np.abs(np.array(qiskit_exp) - np.array(sim_exp))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "id": "d4902d14",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/transverse-field-ising-model/extracted-outputs/d4902d14-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "make_expectations_plot(\n",
        "    sim_exp,\n",
        "    depths,\n",
        "    exp_qctrl=qctrl_exp_mean,\n",
        "    exp_qctrl_error=errors,\n",
        "    exp_qiskit=qiskit_exp,\n",
        "    exp_qiskit_error=errors_qiskit,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ee41a301",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Referências\n",
        "\n",
        "\\[1] Coloração de gráficos. Wikipedia. Recuperado em 15 de setembro de 2025, de [https://en.wikipedia.org/wiki/Graph \\_coloring](https://en.wikipedia.org/wiki/Graph_coloring)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2cb5785c",
      "metadata": {},
      "source": [
        "<span id=\"tutorial-survey\" />\n",
        "\n",
        "## Pesquisa tutorial\n",
        "\n",
        "Reserve um minuto para fazer comentários sobre este tutorial. Suas percepções nos ajudarão a melhorar nossas ofertas de conteúdo e a experiência do usuário.\n",
        "\n",
        "[Link para a pesquisa](https://your.feedback.ibm.com/jfe/form/SV_3BLFkNVEuh0QBWm)\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": 120
  },
  "nbformat": 4,
  "nbformat_minor": 5
}