{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "f69d5853-e815-4754-894d-833017217572",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Avaliação comparativa em tempo real para seleção de qubits\"\n",
        "description: \"Execute experimentos de caracterização em tempo real e atualize as propriedades de back-end para melhorar a seleção de qubits ao mapear um circuito para os qubits físicos em uma QPU.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore sharex */}\n",
        "\n",
        "<span id=\"real-time-benchmarking-for-qubit-selection\" />\n",
        "\n",
        "# Avaliação comparativa em tempo real para seleção de qubits\n",
        "\n",
        "*Estimativa de uso: 4 minutos em um processador Eagle r2 (OBSERVAÇÃO: essa é apenas uma estimativa. Seu tempo de execução pode variar)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "500dc8c9-a5d8-4ef1-932f-30e400d6bdde",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Segundo plano\n",
        "\n",
        "Este tutorial mostra como executar experimentos de caracterização em tempo real e atualizar as propriedades de back-end para melhorar a seleção de qubit ao mapear um circuito para os qubits físicos em uma QPU. Você aprenderá os experimentos básicos de caracterização que são usados para determinar as propriedades da QPU, como fazê-los no Qiskit e como atualizar as propriedades salvas no objeto backend que representa a QPU com base nesses experimentos.\n",
        "\n",
        "As propriedades relatadas pelo QPU são atualizadas uma vez por dia, mas o sistema pode se desviar mais rapidamente do que o tempo entre as atualizações. Isso pode afetar a confiabilidade das rotinas de seleção de qubit no estágio `Layout` do gerenciador de passagem, pois elas estariam usando propriedades relatadas que não representam o estado atual da QPU. Por esse motivo, pode valer a pena dedicar algum tempo da QPU a experimentos de caracterização, que podem ser usados para atualizar as propriedades da QPU usadas pela rotina `Layout` .\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0babd413-d91f-4fd7-a0f5-bb46ae0bbf5b",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Requisitos\n",
        "\n",
        "Antes de iniciar este tutorial, verifique se você tem os seguintes itens instalados:\n",
        "\n",
        "* Qiskit SDK v2.0 ou posterior, com suporte [para visualização](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime v0.40 ou posterior ( `pip install qiskit-ibm-runtime` )\n",
        "* Qiskit Experiments v0.12 ou posterior ( `pip install qiskit-experiments` )\n",
        "* Biblioteca de gráficos Rustworkx (`pip install rustworkx`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3df52d5f-806a-4846-849e-633706a96d0b",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Instalação\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "4766c18a-ba45-456b-8b78-6b6f1d214586",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_ibm_runtime import SamplerV2\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "from qiskit.quantum_info import hellinger_fidelity\n",
        "from qiskit.transpiler import InstructionProperties\n",
        "\n",
        "\n",
        "from qiskit_experiments.library import (\n",
        "    T1,\n",
        "    T2Hahn,\n",
        "    LocalReadoutError,\n",
        "    StandardRB,\n",
        ")\n",
        "from qiskit_experiments.framework import BatchExperiment, ParallelExperiment\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import Session\n",
        "\n",
        "from datetime import datetime\n",
        "from collections import defaultdict\n",
        "import numpy as np\n",
        "import rustworkx\n",
        "import matplotlib.pyplot as plt\n",
        "import copy"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "65d49ed2-0581-486e-9031-a08fa9bace92",
      "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",
        "Para avaliar a diferença de desempenho, consideramos um circuito que prepara um estado Bell em uma cadeia linear de comprimento variável. A fidelidade do estado de Bell nas extremidades da cadeia é medida.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "64c25da9-a728-4ae4-a377-3078a1dc618d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/real-time-benchmarking-for-qubit-selection/extracted-outputs/64c25da9-a728-4ae4-a377-3078a1dc618d-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/real-time-benchmarking-for-qubit-selection/extracted-outputs/64c25da9-a728-4ae4-a377-3078a1dc618d-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "\n",
        "ideal_dist = {\"00\": 0.5, \"11\": 0.5}\n",
        "\n",
        "num_qubits_list = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 127]\n",
        "circuits = []\n",
        "for num_qubits in num_qubits_list:\n",
        "    circuit = QuantumCircuit(num_qubits, 2)\n",
        "    circuit.h(0)\n",
        "    for i in range(num_qubits - 1):\n",
        "        circuit.cx(i, i + 1)\n",
        "    circuit.barrier()\n",
        "    circuit.measure(0, 0)\n",
        "    circuit.measure(num_qubits - 1, 1)\n",
        "    circuits.append(circuit)\n",
        "\n",
        "circuits[-1].draw(output=\"mpl\", style=\"clifford\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "16948f21-a39b-4444-bf02-5f81331825c4",
      "metadata": {},
      "source": [
        "<span id=\"set-up-backend-and-coupling-map\" />\n",
        "\n",
        "### Configurar backend e mapa de acoplamento\n",
        "\n",
        "Primeiro, selecione um backend\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "f968acca-9131-4f5d-aa74-70befcdda4f5",
      "metadata": {},
      "outputs": [],
      "source": [
        "# To run on hardware, select the backend with the fewest number of jobs in the queue\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=127\n",
        ")\n",
        "\n",
        "qubits = list(range(backend.num_qubits))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "90b0ddab-0d35-4784-8575-fe0505699197",
      "metadata": {},
      "source": [
        "Em seguida, obtenha seu mapa de acoplamento\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "86d84baa-bd5e-4faf-9582-4568ab4c4a4e",
      "metadata": {},
      "outputs": [],
      "source": [
        "coupling_graph = backend.coupling_map.graph.to_undirected(multigraph=False)\n",
        "\n",
        "# Get unidirectional coupling map\n",
        "one_dir_coupling_map = coupling_graph.edge_list()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d48b5ba2-74ec-4484-9ba6-b0681ae32919",
      "metadata": {},
      "source": [
        "Para avaliar o maior número possível de portas de dois qubits simultaneamente, separamos o mapa de acoplamento em um `layered_coupling_map`. Esse objeto contém uma lista de camadas em que cada camada é uma lista de bordas nas quais portas de dois qubits podem ser executadas ao mesmo tempo. Isso também é chamado de coloração de borda do mapa de acoplamento.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "62b36ded-ab4e-414e-b146-ff522786a871",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Get layered coupling map\n",
        "edge_coloring = rustworkx.graph_bipartite_edge_color(coupling_graph)\n",
        "layered_coupling_map = defaultdict(list)\n",
        "for edge_idx, color in edge_coloring.items():\n",
        "    layered_coupling_map[color].append(\n",
        "        coupling_graph.get_edge_endpoints_by_index(edge_idx)\n",
        "    )\n",
        "layered_coupling_map = [\n",
        "    sorted(layered_coupling_map[i])\n",
        "    for i in sorted(layered_coupling_map.keys())\n",
        "]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "875117af-8a2c-4aea-92d9-ffeee7ff37d5",
      "metadata": {},
      "source": [
        "<span id=\"characterization-experiments\" />\n",
        "\n",
        "### Experimentos de caracterização\n",
        "\n",
        "Uma série de experimentos é usada para caracterizar as principais propriedades dos qubits em uma QPU. São eles: $T_1$, $T_2$, erro de leitura e erro de porta de um e dois qubits. Resumiremos brevemente quais são essas propriedades e faremos referência aos experimentos no pacote [`qiskit-experiments`](https://qiskit-community.github.io/qiskit-experiments/index.html) que são usados para caracterizá-las.\n",
        "\n",
        "<span id=\"t1\" />\n",
        "\n",
        "#### T1\n",
        "\n",
        "$T_1$ é o tempo característico que leva para um qubit excitado cair para o estado fundamental devido aos processos de decoerência de amortecimento de amplitude. Em um [experimento $T_1$](https://qiskit-community.github.io/qiskit-experiments/manuals/characterization/t1.html), medimos um qubit excitado após um atraso. Quanto maior for o tempo de atraso, maior será a probabilidade de o qubit cair para o estado fundamental. O objetivo do experimento experimento é caracterizar a taxa de decaimento do qubit em direção ao estado fundamental.\n",
        "\n",
        "<span id=\"t2\" />\n",
        "\n",
        "#### T2\n",
        "\n",
        "$T_2$ representa o tempo necessário para que a projeção do vetor Bloch de um único qubit de um único qubit no plano XY caia para aproximadamente 37% ( $\\frac{1}{e}$ ) de de sua amplitude inicial devido aos processos de decoerência de defasagem. Em um [experimento $T_2$ Hahn Echo](https://qiskit-community.github.io/qiskit-experiments/manuals/characterization/t2hahn.html), podemos estimar a taxa desse decaimento.\n",
        "\n",
        "<span id=\"state-preparation-and-measurement-spam-error-characterization\" />\n",
        "\n",
        "#### Caracterização do erro de preparação e medição do estado (SPAM)\n",
        "\n",
        "Em um [experimento de caracterização de erro SPAM](https://qiskit-community.github.io/qiskit-experiments/manuals/measurement/readout_mitigation.html), os qubits são preparados em um determinado estado ( $\\vert 0 \\rangle$ ou $\\vert 1 \\rangle$ ) e medidos. A probabilidade de medir um estado diferente do preparado fornece a probabilidade do erro.\n",
        "\n",
        "<span id=\"single-qubit-and-two-qubit-randomized-benchmarking\" />\n",
        "\n",
        "#### Benchmarking aleatório de um qubit e dois qubits\n",
        "\n",
        "[O benchmarking aleatório (RB)](https://qiskit-community.github.io/qiskit-experiments/manuals/verification/randomized_benchmarking.html) é um protocolo popular para caracterizar a taxa de erro dos processadores quânticos. Um experimento RB consiste na geração de circuitos aleatórios de Clifford aleatórios nos qubits fornecidos, de modo que a unitária computada pelos circuitos seja a identidade. Depois de executar os circuitos, o número de disparos que resultam em um erro (ou seja, uma saída diferente do estado fundamental) é contado e, a partir desses dados, é possível inferir estimativas de erro para o dispositivo quântico, calculando o erro por Clifford.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "9d57c42d-7a91-4e79-bc6c-6e579da929f8",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Create T1 experiments on all qubit in parallel\n",
        "t1_exp = ParallelExperiment(\n",
        "    [\n",
        "        T1(\n",
        "            physical_qubits=[qubit],\n",
        "            delays=[1e-6, 20e-6, 40e-6, 80e-6, 200e-6, 400e-6],\n",
        "        )\n",
        "        for qubit in qubits\n",
        "    ],\n",
        "    backend,\n",
        "    analysis=None,\n",
        ")\n",
        "\n",
        "# Create T2-Hahn experiments on all qubit in parallel\n",
        "t2_exp = ParallelExperiment(\n",
        "    [\n",
        "        T2Hahn(\n",
        "            physical_qubits=[qubit],\n",
        "            delays=[1e-6, 20e-6, 40e-6, 80e-6, 200e-6, 400e-6],\n",
        "        )\n",
        "        for qubit in qubits\n",
        "    ],\n",
        "    backend,\n",
        "    analysis=None,\n",
        ")\n",
        "\n",
        "# Create readout experiments on all qubit in parallel\n",
        "readout_exp = LocalReadoutError(qubits)\n",
        "\n",
        "# Create single-qubit RB experiments on all qubit in parallel\n",
        "singleq_rb_exp = ParallelExperiment(\n",
        "    [\n",
        "        StandardRB(\n",
        "            physical_qubits=[qubit], lengths=[10, 100, 500], num_samples=10\n",
        "        )\n",
        "        for qubit in qubits\n",
        "    ],\n",
        "    backend,\n",
        "    analysis=None,\n",
        ")\n",
        "\n",
        "# Create two-qubit RB experiments on the three layers of disjoint edges of the heavy-hex\n",
        "twoq_rb_exp_batched = BatchExperiment(\n",
        "    [\n",
        "        ParallelExperiment(\n",
        "            [\n",
        "                StandardRB(\n",
        "                    physical_qubits=pair,\n",
        "                    lengths=[10, 50, 100],\n",
        "                    num_samples=10,\n",
        "                )\n",
        "                for pair in layer\n",
        "            ],\n",
        "            backend,\n",
        "            analysis=None,\n",
        "        )\n",
        "        for layer in layered_coupling_map\n",
        "    ],\n",
        "    backend,\n",
        "    flatten_results=True,\n",
        "    analysis=None,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cad4f8d3-c2d5-4bb5-92be-432a4573e14a",
      "metadata": {},
      "source": [
        "<span id=\"qpu-properties-over-time\" />\n",
        "\n",
        "### Propriedades da QPU ao longo do tempo\n",
        "\n",
        "Observando as propriedades de QPU relatadas ao longo do tempo (consideraremos uma única semana abaixo), vemos como elas podem flutuar em uma escala de um único dia. Pequenas flutuações podem ocorrer até mesmo em um dia. Nesse cenário, as propriedades informadas (atualizadas uma vez por dia) não capturarão com precisão o status atual da QPU. Além disso, se um trabalho for transpilado localmente (usando as propriedades atuais informadas) e enviado, mas executado somente em um momento posterior (minutos ou dias), ele poderá correr o risco de ter usado propriedades desatualizadas para a seleção de qubit na etapa de transpilação. Isso destaca a importância de ter informações atualizadas sobre a QPU no momento da execução. Primeiro, vamos recuperar as propriedades em um determinado intervalo de tempo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "af1b6722-e77b-436a-bbcd-f272b95bb28c",
      "metadata": {},
      "outputs": [],
      "source": [
        "instruction_2q_name = \"cz\"  # set the name of the default 2q of the device\n",
        "errors_list = []\n",
        "for day_idx in range(10, 17):\n",
        "    calibrations_time = datetime(\n",
        "        year=2025, month=8, day=day_idx, hour=0, minute=0, second=0\n",
        "    )\n",
        "    targer_hist = backend.target_history(datetime=calibrations_time)\n",
        "\n",
        "    t1_dict, t2_dict = {}, {}\n",
        "    for qubit in range(targer_hist.num_qubits):\n",
        "        t1_dict[qubit] = targer_hist.qubit_properties[qubit].t1\n",
        "        t2_dict[qubit] = targer_hist.qubit_properties[qubit].t2\n",
        "\n",
        "    errors_dict = {\n",
        "        \"1q\": targer_hist[\"sx\"],\n",
        "        \"2q\": targer_hist[f\"{instruction_2q_name}\"],\n",
        "        \"spam\": targer_hist[\"measure\"],\n",
        "        \"t1\": t1_dict,\n",
        "        \"t2\": t2_dict,\n",
        "    }\n",
        "\n",
        "    errors_list.append(errors_dict)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ac75df1b-f689-475c-94a2-d70d85b1f8ca",
      "metadata": {},
      "source": [
        "Em seguida, vamos plotar os valores\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "e0ba509d-e0e0-438b-aedf-5e01919c7d4f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/real-time-benchmarking-for-qubit-selection/extracted-outputs/e0ba509d-e0e0-438b-aedf-5e01919c7d4f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, axs = plt.subplots(5, 1, figsize=(10, 20), sharex=False)\n",
        "\n",
        "\n",
        "# Plot for T1 values\n",
        "for qubit in range(targer_hist.num_qubits):\n",
        "    t1s = []\n",
        "    for errors_dict in errors_list:\n",
        "        t1_dict = errors_dict[\"t1\"]\n",
        "        try:\n",
        "            t1s.append(t1_dict[qubit] / 1e-6)\n",
        "        except:\n",
        "            print(f\"missing t1 data for qubit {qubit}\")\n",
        "\n",
        "    axs[0].plot(t1s)\n",
        "\n",
        "axs[0].set_title(\"T1\")\n",
        "axs[0].set_ylabel(r\"Time ($\\mu s$)\")\n",
        "axs[0].set_xlabel(\"Days\")\n",
        "\n",
        "# Plot for T2 values\n",
        "for qubit in range(targer_hist.num_qubits):\n",
        "    t2s = []\n",
        "    for errors_dict in errors_list:\n",
        "        t2_dict = errors_dict[\"t2\"]\n",
        "        try:\n",
        "            t2s.append(t2_dict[qubit] / 1e-6)\n",
        "        except:\n",
        "            print(f\"missing t2 data for qubit {qubit}\")\n",
        "\n",
        "    axs[1].plot(t2s)\n",
        "\n",
        "axs[1].set_title(\"T2\")\n",
        "axs[1].set_ylabel(r\"Time ($\\mu s$)\")\n",
        "axs[1].set_xlabel(\"Days\")\n",
        "\n",
        "# Plot SPAM values\n",
        "for qubit in range(targer_hist.num_qubits):\n",
        "    spams = []\n",
        "    for errors_dict in errors_list:\n",
        "        spam_dict = errors_dict[\"spam\"]\n",
        "        spams.append(spam_dict[tuple([qubit])].error)\n",
        "\n",
        "    axs[2].plot(spams)\n",
        "\n",
        "axs[2].set_title(\"SPAM Errors\")\n",
        "axs[2].set_ylabel(\"Error Rate\")\n",
        "axs[2].set_xlabel(\"Days\")\n",
        "\n",
        "# Plot 1Q Gate Errors\n",
        "for qubit in range(targer_hist.num_qubits):\n",
        "    oneq_gates = []\n",
        "    for errors_dict in errors_list:\n",
        "        oneq_gate_dict = errors_dict[\"1q\"]\n",
        "        oneq_gates.append(oneq_gate_dict[tuple([qubit])].error)\n",
        "\n",
        "    axs[3].plot(oneq_gates)\n",
        "\n",
        "axs[3].set_title(\"1Q Gate Errors\")\n",
        "axs[3].set_ylabel(\"Error Rate\")\n",
        "axs[3].set_xlabel(\"Days\")\n",
        "\n",
        "# Plot 2Q Gate Errors\n",
        "for pair in one_dir_coupling_map:\n",
        "    twoq_gates = []\n",
        "    for errors_dict in errors_list:\n",
        "        twoq_gate_dict = errors_dict[\"2q\"]\n",
        "        twoq_gates.append(twoq_gate_dict[pair].error)\n",
        "\n",
        "    axs[4].plot(twoq_gates)\n",
        "\n",
        "axs[4].set_title(\"2Q Gate Errors\")\n",
        "axs[4].set_ylabel(\"Error Rate\")\n",
        "axs[4].set_xlabel(\"Days\")\n",
        "\n",
        "plt.subplots_adjust(hspace=0.5)\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a3343934-9273-457c-900b-2004b3aa9d0c",
      "metadata": {},
      "source": [
        "Você pode ver que, ao longo de vários dias, algumas das propriedades do qubit podem mudar consideravelmente. Isso destaca a importância de ter informações atualizadas sobre o status da QPU para poder selecionar os qubits de melhor desempenho para um experimento.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0e53f0a5-7713-4915-ae47-c1aa0a4f17cd",
      "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",
        "Neste tutorial, não é feita nenhuma otimização dos circuitos ou operadores.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "862789c2-d17e-43ed-8e1b-2f37d7628ed2",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## Passo 3: Execute usando Qiskit primitives\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d88f925f-3ffd-43de-ac1f-2c5ba3b8cd96",
      "metadata": {},
      "source": [
        "<span id=\"execute-a-quantum-circuit-with-default-qubit-selection\" />\n",
        "\n",
        "### Executar um circuito quântico com seleção padrão de qubits\n",
        "\n",
        "Como resultado de referência de desempenho, executaremos um circuito quântico em uma QPU usando os qubits padrão, que são os qubits selecionados com as propriedades de backend solicitadas. Usaremos o site `optimization_level = 3`.  Essa configuração inclui a otimização de transpilação mais avançada e usa propriedades de destino (como erros de operação) para selecionar os qubits de melhor desempenho para execução.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "5c0d09ad-2e6a-4067-8034-8df92a475ff1",
      "metadata": {},
      "outputs": [],
      "source": [
        "pm = generate_preset_pass_manager(target=backend.target, optimization_level=3)\n",
        "isa_circuits = pm.run(circuits)\n",
        "initial_qubits = [\n",
        "    [\n",
        "        idx\n",
        "        for idx, qb in circuit.layout.initial_layout.get_physical_bits().items()\n",
        "        if qb._register.name != \"ancilla\"\n",
        "    ]\n",
        "    for circuit in isa_circuits\n",
        "]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6d4fb04c-a5cf-48e8-a759-dde64fd751ac",
      "metadata": {},
      "source": [
        "<span id=\"execute-a-quantum-circuit-with-real-time-qubit-selection\" />\n",
        "\n",
        "### Execute um circuito quântico com seleção de qubits em tempo real\n",
        "\n",
        "Nesta seção, investigaremos a importância de ter informações atualizadas sobre as propriedades de qubit da QPU para obter resultados ideais. Primeiro, realizaremos um conjunto completo de experimentos de caracterização de QPU ( $T_1$, $T_2$, SPAM, RB de um e dois qubits), que poderemos usar para atualizar as propriedades de backend. Isso permite que o gerenciador de passagens selecione qubits para a execução com base em novas informações sobre a QPU, possivelmente melhorando o desempenho da execução. Em segundo lugar, executamos o circuito do par de Bell e comparamos a fidelidade obtida após a seleção dos qubits com a atualização das propriedades da QPU com a fidelidade obtida anteriormente quando usamos as propriedades relatadas padrão para a seleção de qubits.\n",
        "\n",
        "<Admonition type=\"caution\">\n",
        "  Observe que alguns dos experimentos de caracterização podem falhar quando a rotina de ajuste não consegue ajustar uma curva aos dados medidos. Se você vir avisos provenientes desses experimentos, inspecione-os para entender qual caracterização falhou em quais qubits e tente ajustar os parâmetros do experimento (como os tempos para $T_1$, $T_2$, ou o número de comprimentos dos experimentos RB).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "a8266467-9d60-411f-89dd-8cad6558f588",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Prepare characterization experiments\n",
        "batches = [t1_exp, t2_exp, readout_exp, singleq_rb_exp, twoq_rb_exp_batched]\n",
        "batches_exp = BatchExperiment(batches, backend)  # , analysis=None)\n",
        "run_options = {\"shots\": 1e3, \"dynamic\": False}\n",
        "\n",
        "with Session(backend=backend) as session:\n",
        "    sampler = SamplerV2(mode=session)\n",
        "\n",
        "    # Run characterization experiments\n",
        "    batches_exp_data = batches_exp.run(\n",
        "        sampler=sampler, **run_options\n",
        "    ).block_for_results()\n",
        "\n",
        "    EPG_sx_result_list = batches_exp_data.analysis_results(\"EPG_sx\")\n",
        "    EPG_sx_result_q_indices = [\n",
        "        result.device_components.index for result in EPG_sx_result_list\n",
        "    ]\n",
        "    EPG_x_result_list = batches_exp_data.analysis_results(\"EPG_x\")\n",
        "    EPG_x_result_q_indices = [\n",
        "        result.device_components.index for result in EPG_x_result_list\n",
        "    ]\n",
        "    T1_result_list = batches_exp_data.analysis_results(\"T1\")\n",
        "    T1_result_q_indices = [\n",
        "        result.device_components.index for result in T1_result_list\n",
        "    ]\n",
        "\n",
        "    T2_result_list = batches_exp_data.analysis_results(\"T2\")\n",
        "    T2_result_q_indices = [\n",
        "        result.device_components.index for result in T2_result_list\n",
        "    ]\n",
        "\n",
        "    Readout_result_list = batches_exp_data.analysis_results(\n",
        "        \"Local Readout Mitigator\"\n",
        "    )\n",
        "\n",
        "    EPG_2q_result_list = batches_exp_data.analysis_results(\n",
        "        f\"EPG_{instruction_2q_name}\"\n",
        "    )\n",
        "\n",
        "    # Update target properties\n",
        "    target = copy.deepcopy(backend.target)\n",
        "    for i in range(target.num_qubits - 1):\n",
        "        qarg = (i,)\n",
        "\n",
        "        if qarg in EPG_sx_result_q_indices:\n",
        "            target.update_instruction_properties(\n",
        "                instruction=\"sx\",\n",
        "                qargs=qarg,\n",
        "                properties=InstructionProperties(\n",
        "                    error=EPG_sx_result_list[i].value.nominal_value\n",
        "                ),\n",
        "            )\n",
        "        if qarg in EPG_x_result_q_indices:\n",
        "            target.update_instruction_properties(\n",
        "                instruction=\"x\",\n",
        "                qargs=qarg,\n",
        "                properties=InstructionProperties(\n",
        "                    error=EPG_x_result_list[i].value.nominal_value\n",
        "                ),\n",
        "            )\n",
        "\n",
        "        err_mat = Readout_result_list.value.assignment_matrix(i)\n",
        "        readout_assignment_error = (\n",
        "            err_mat[0, 1] + err_mat[1, 0]\n",
        "        ) / 2  # average readout error\n",
        "        target.update_instruction_properties(\n",
        "            instruction=\"measure\",\n",
        "            qargs=qarg,\n",
        "            properties=InstructionProperties(error=readout_assignment_error),\n",
        "        )\n",
        "\n",
        "        if qarg in T1_result_q_indices:\n",
        "            target.qubit_properties[i].t1 = T1_result_list[\n",
        "                i\n",
        "            ].value.nominal_value\n",
        "        if qarg in T2_result_q_indices:\n",
        "            target.qubit_properties[i].t2 = T2_result_list[\n",
        "                i\n",
        "            ].value.nominal_value\n",
        "\n",
        "    for pair_idx, pair in enumerate(one_dir_coupling_map):\n",
        "        qarg = tuple(pair)\n",
        "        try:\n",
        "            target.update_instruction_properties(\n",
        "                instruction=instruction_2q_name,\n",
        "                qargs=qarg,\n",
        "                properties=InstructionProperties(\n",
        "                    error=EPG_2q_result_list[pair_idx].value.nominal_value\n",
        "                ),\n",
        "            )\n",
        "        except:\n",
        "            target.update_instruction_properties(\n",
        "                instruction=instruction_2q_name,\n",
        "                qargs=qarg[::-1],\n",
        "                properties=InstructionProperties(\n",
        "                    error=EPG_2q_result_list[pair_idx].value.nominal_value\n",
        "                ),\n",
        "            )\n",
        "\n",
        "    # transpile circuits to updated target\n",
        "    pm = generate_preset_pass_manager(target=target, optimization_level=3)\n",
        "    isa_circuit_updated = pm.run(circuits)\n",
        "    updated_qubits = [\n",
        "        [\n",
        "            idx\n",
        "            for idx, qb in circuit.layout.initial_layout.get_physical_bits().items()\n",
        "            if qb._register.name != \"ancilla\"\n",
        "        ]\n",
        "        for circuit in isa_circuit_updated\n",
        "    ]\n",
        "\n",
        "    n_trials = 3  # run multiple trials to see variations\n",
        "\n",
        "    # interleave circuits\n",
        "    interleaved_circuits = []\n",
        "    for original_circuit, updated_circuit in zip(\n",
        "        isa_circuits, isa_circuit_updated\n",
        "    ):\n",
        "        interleaved_circuits.append(original_circuit)\n",
        "        interleaved_circuits.append(updated_circuit)\n",
        "\n",
        "    # Run circuits\n",
        "    # Set simple error suppression/mitigation options\n",
        "    sampler.options.dynamical_decoupling.enable = True\n",
        "    sampler.options.dynamical_decoupling.sequence_type = \"XY4\"\n",
        "\n",
        "    job_interleaved = sampler.run(interleaved_circuits * n_trials)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d72e021a-3ea6-4ed2-829f-e59ce8017ef4",
      "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, vamos comparar a fidelidade do estado de Bell obtido nas duas configurações diferentes:\n",
        "\n",
        "* `original`ou seja, com os qubits padrão escolhidos pelo transpilador com base nas propriedades relatadas do backend.\n",
        "* `updated`ou seja, com os qubits escolhidos com base nas propriedades atualizadas do backend após a execução dos experimentos de caracterização.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "5a19a5d1-5daf-4d65-9785-8f8724853821",
      "metadata": {},
      "outputs": [],
      "source": [
        "results = job_interleaved.result()\n",
        "all_fidelity_list, all_fidelity_updated_list = [], []\n",
        "for exp_idx in range(n_trials):\n",
        "    fidelity_list, fidelity_updated_list = [], []\n",
        "\n",
        "    for idx, num_qubits in enumerate(num_qubits_list):\n",
        "        pub_result_original = results[\n",
        "            2 * exp_idx * len(num_qubits_list) + 2 * idx\n",
        "        ]\n",
        "        pub_result_updated = results[\n",
        "            2 * exp_idx * len(num_qubits_list) + 2 * idx + 1\n",
        "        ]\n",
        "\n",
        "        fid = hellinger_fidelity(\n",
        "            ideal_dist, pub_result_original.data.c.get_counts()\n",
        "        )\n",
        "        fidelity_list.append(fid)\n",
        "\n",
        "        fid_up = hellinger_fidelity(\n",
        "            ideal_dist, pub_result_updated.data.c.get_counts()\n",
        "        )\n",
        "        fidelity_updated_list.append(fid_up)\n",
        "    all_fidelity_list.append(fidelity_list)\n",
        "    all_fidelity_updated_list.append(fidelity_updated_list)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "656ec97a-3fd9-4635-9a98-1c5589761689",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/real-time-benchmarking-for-qubit-selection/extracted-outputs/656ec97a-3fd9-4635-9a98-1c5589761689-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.figure(figsize=(8, 6))\n",
        "plt.errorbar(\n",
        "    num_qubits_list,\n",
        "    np.mean(all_fidelity_list, axis=0),\n",
        "    yerr=np.std(all_fidelity_list, axis=0),\n",
        "    fmt=\"o-.\",\n",
        "    label=\"original\",\n",
        "    color=\"b\",\n",
        ")\n",
        "# plt.plot(num_qubits_list, fidelity_list, '-.')\n",
        "plt.errorbar(\n",
        "    num_qubits_list,\n",
        "    np.mean(all_fidelity_updated_list, axis=0),\n",
        "    yerr=np.std(all_fidelity_updated_list, axis=0),\n",
        "    fmt=\"o-.\",\n",
        "    label=\"updated\",\n",
        "    color=\"r\",\n",
        ")\n",
        "# plt.plot(num_qubits_list, fidelity_updated_list, '-.')\n",
        "plt.xlabel(\"Chain length\")\n",
        "plt.xticks(num_qubits_list)\n",
        "plt.ylabel(\"Fidelity\")\n",
        "plt.title(\"Bell pair fidelity at the edge of N-qubits chain\")\n",
        "plt.legend()\n",
        "plt.grid(\n",
        "    alpha=0.2,\n",
        "    linestyle=\"-.\",\n",
        ")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3705d85c-e11d-4a8f-bfae-20bd501124d8",
      "metadata": {},
      "source": [
        "Nem todas as execuções mostrarão melhorias no desempenho devido à caracterização em tempo real e, com o aumento do comprimento da cadeia e, portanto, menos liberdade para escolher qubits físicos, a importância das informações atualizadas do dispositivo se torna menos substancial. No entanto, é uma boa prática coletar novos dados sobre as propriedades do dispositivo para entender seu desempenho. Ocasionalmente, sistemas transitórios de dois níveis podem afetar o desempenho de alguns dos qubits. Os dados em tempo real podem nos informar quando esses eventos estão ocorrendo e nos ajudar a evitar falhas experimentais nesses casos.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cd81b208-b13b-4988-854e-1741408f36f3",
      "metadata": {},
      "source": [
        "<Admonition type=\"note\" title=\"Ligue para obter ação\">\n",
        "  Tente aplicar esse método em suas execuções e determine o quanto você se beneficia! Você também pode experimentar e ver quantas melhorias obtém com diferentes backends.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b94755c1-1c19-434a-96b9-b83922b5d63c",
      "metadata": {},
      "source": [
        "<span id=\"tutorial-survey\" />\n",
        "\n",
        "## Pesquisa tutorial\n",
        "\n",
        "Responda a esta breve pesquisa para fornecer feedback 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_0w6FZ9QrWkKfTQq)\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"
    },
    "vscode": {
      "interpreter": {
        "hash": "72460b19b35c51a2148375679c8e282bc3cd3c5550c209f0a38b7d09cded82d9"
      }
    },
    "hours": 2.5,
    "qpuSeconds": 240
  },
  "nbformat": 4,
  "nbformat_minor": 4
}