{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Iniciação rápida\"\n",
        "description: \"Guia de início rápido para a versão mais recente do Shaded lightcones\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "38de8ef2",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Iniciação rápida\"\n",
        "description: \"Um guia de início rápido para o complemento Shaded Lightcones do Qiskit (qiskit-addon-slc)\"\n",
        "---\n",
        "\n",
        "<span id=\"quickstart\" />\n",
        "\n",
        "# Iniciação rápida\n",
        "\n",
        "Este guia apresenta um exemplo mínimo funcional do `qiskit-addon-slc` pacote. Calculamos um cone de luz sombreado para reduzir o custo de amostragem do cancelamento probabilístico de erros (PEC).\n",
        "\n",
        "O PEC atenua o ruído de porta por meio da amostragem a partir de uma decomposição em quase-probabilidades do canal de ruído inverso. Seu custo de amostragem aumenta a cada termo de erro que precisa ser mitigado; no entanto, nem todo erro afeta o observável da mesma forma. Um erro fora do cone de luz causal do observável não pode influenciar de forma alguma o valor esperado medido; e, mesmo dentro do cone de luz, alguns erros são mais prejudiciais do que outros. Um cone de luz sombreado quantifica isso ao delimitar o efeito que cada termo de erro de Pauli exerce sobre o observável. O truncamento dos termos de erro com menor efeito reduz o modelo de ruído que o PEC precisa mitigar, diminuindo o custo de amostragem em troca de um viés pequeno e limitado.\n",
        "\n",
        "Para saber como criar um fluxo de trabalho realista e executá-lo em hardware quântico, confira o tutorial [“Cancelamento probabilístico de erros com cones de luz sombreados”](/docs/tutorials/pec-with-shaded-lightcones) no site IBM Quantum Platform.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "71bc0134",
      "metadata": {},
      "source": [
        "<span id=\"1-prepare-the-inputs-for-slc\" />\n",
        "\n",
        "## 1. Preparar os dados de entrada para o SLC\n",
        "\n",
        "Aqui, construímos um circuito de Ising de campo transversal com 6 qubits, submetido ao processo de Trotter, e escolhemos um observável de tipo “ $Z$ ” de um único qubit para o qubit do meio.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "0fff4e37",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Observable: IIZIII\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-slc/guides/quickstart/extracted-outputs/0fff4e37-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "import numpy as np\n",
        "from qiskit import QuantumCircuit\n",
        "from qiskit.quantum_info import Pauli\n",
        "\n",
        "\n",
        "def trotter_ising_circuit(num_qubits, num_steps, rx_angle, rzz_angle):\n",
        "    \"\"\"Trotterized transverse-field Ising evolution on a 1D chain.\"\"\"\n",
        "    circuit = QuantumCircuit(num_qubits)\n",
        "    for _ in range(num_steps):\n",
        "        circuit.rx(rx_angle, range(num_qubits))\n",
        "        circuit.barrier()\n",
        "        for start in (0, 1):  # even then odd bonds\n",
        "            for i in range(start, num_qubits - 1, 2):\n",
        "                circuit.rzz(rzz_angle, i, i + 1)\n",
        "        circuit.barrier()\n",
        "    return circuit\n",
        "\n",
        "\n",
        "num_qubits = 6\n",
        "circuit = trotter_ising_circuit(\n",
        "    num_qubits, num_steps=2, rx_angle=np.pi / 16, rzz_angle=-np.pi / 2\n",
        ")\n",
        "\n",
        "# Measure <Z> on the middle qubit\n",
        "observable = Pauli(\"I\" * num_qubits).compose(\"Z\", [num_qubits // 2])\n",
        "\n",
        "print(f\"Observable: {observable}\")\n",
        "circuit.draw(\"mpl\", fold=-1, scale=0.7)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9a732427",
      "metadata": {},
      "source": [
        "O SLC opera nas camadas ruidosas de portas de dois qubits do circuito. Aqui, usamos `samplomatic` para agrupar as portas em caixas anotadas e atribuímos uma anotação de injeção de ruído a cada camada de dois qubits. `generate_noise_model_paulis` em seguida, enumera os termos de erro de Pauli de cada camada ruidosa específica.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "3f4301ac",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Noisy layers: 2\n",
            "Pauli error terms across all layers: 102\n"
          ]
        }
      ],
      "source": [
        "from qiskit_addon_slc.utils import generate_noise_model_paulis\n",
        "from samplomatic.transpiler import generate_boxing_pass_manager\n",
        "from samplomatic.utils import find_unique_box_instructions\n",
        "\n",
        "# Group gates into boxes and annotate each two-qubit layer with a noise-injection point\n",
        "boxing_pass = generate_boxing_pass_manager(\n",
        "    inject_noise_targets=\"all\",\n",
        "    inject_noise_strategy=\"individual_modification\",\n",
        "    inject_noise_site=\"after\",\n",
        "    twirling_strategy=\"active\",\n",
        "    remove_barriers=\"never\",\n",
        ")\n",
        "boxed_circuit = boxing_pass.run(circuit)\n",
        "\n",
        "# Enumerate the 1- and 2-weight Pauli error terms of each unique noisy layer\n",
        "noise_model_paulis = generate_noise_model_paulis(\n",
        "    find_unique_box_instructions(boxed_circuit)\n",
        ")\n",
        "\n",
        "num_terms = sum(len(paulis) for paulis in noise_model_paulis.values())\n",
        "print(f\"Noisy layers: {len(noise_model_paulis)}\")\n",
        "print(f\"Pauli error terms across all layers: {num_terms}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "97101eed",
      "metadata": {},
      "source": [
        "<span id=\"2-compute-the-shaded-lightcone\" />\n",
        "\n",
        "## 2. Calcular o cone de luz sombreado\n",
        "\n",
        "O cone de luz sombreado é construído atribuindo-se uma escala a cada termo de erro de Pauli no modelo de ruído, com base no grau de influência que ele exerce sobre o valor esperado da observável. Essas escalas são derivadas dos limites de erro para a direção direta e para a direção reversa de cada termo de erro (descritos abaixo), bem como de sua taxa de erro:\n",
        "\n",
        "* `compute_forward_bounds` avança cada termo de erro *até* o final do circuito para limitar seu efeito sobre a grandeza observável medida nesse ponto.\n",
        "* `compute_backward_bounds` evolui cada termo de erro *retroativamente* até o início do circuito para limitar seu efeito sobre o estado inicial.\n",
        "\n",
        "`merge_bounds` combina os dois em um único termo de erro. A fusão das escalas é limitada pela taxa de erro de cada termo. Essas taxas geralmente são obtidas a partir de um experimento de aprendizado com ruído (por exemplo, `NoiseLearnerV3`); Aqui, utilizamos taxas aleatórias para simplificar.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "82205ea3",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.quantum_info import PauliLindbladMap, QubitSparsePauliList\n",
        "from qiskit_addon_slc.bounds import (\n",
        "    compute_backward_bounds,\n",
        "    compute_forward_bounds,\n",
        "    merge_bounds,\n",
        ")\n",
        "\n",
        "forward_bounds = compute_forward_bounds(\n",
        "    boxed_circuit, noise_model_paulis, observable\n",
        ")\n",
        "backward_bounds = compute_backward_bounds(boxed_circuit, noise_model_paulis)\n",
        "\n",
        "# Stand-in for rates that would be measured by a noise-learning experiment on hardware\n",
        "rng = np.random.default_rng(42)\n",
        "noise_rates = {\n",
        "    layer_id: PauliLindbladMap.from_components(\n",
        "        rng.random(len(paulis)) * 5e-3,\n",
        "        QubitSparsePauliList.from_sparse_list(\n",
        "            paulis.to_sparse_list(), paulis.num_qubits\n",
        "        ),\n",
        "    )\n",
        "    for layer_id, paulis in noise_model_paulis.items()\n",
        "}\n",
        "\n",
        "merged_bounds = merge_bounds(\n",
        "    boxed_circuit, forward_bounds, backward_bounds, noise_rates\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c324ebd6",
      "metadata": {},
      "source": [
        "Na visualização do cone de luz sombreado abaixo, cada caixa é sombreada de acordo com a intensidade com que os erros naquele ponto podem afetar a grandeza observável: as caixas mais claras representam os limites mais amplos, enquanto as caixas que se desvanecem no fundo contêm termos de erro que têm pouco efeito no cálculo — esses erros são candidatos naturais a serem excluídos do modelo de ruído. Os valores na visualização abaixo representam as somas dos limites de erro de todos os erros de Pauli naquele local; é por isso que alguns dos valores se tornam maiores do que `2.0` — o limite para qualquer erro de Pauli isolado.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "a2201cec",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-slc/guides/quickstart/extracted-outputs/a2201cec-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit_addon_slc.visualization import draw_shaded_lightcone\n",
        "\n",
        "draw_shaded_lightcone(boxed_circuit, merged_bounds, noise_model_paulis)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "42483e78",
      "metadata": {},
      "source": [
        "<span id=\"3-reduce-the-sampling-cost\" />\n",
        "\n",
        "## 3. Reduzir o custo da amostragem\n",
        "\n",
        "`compute_local_scales` transforma o cone de luz sombreado em uma configuração PEC concreta. Ele prioriza os termos de erro de acordo com seu efeito limitado sobre a variável observável e elimina os de menor impacto até que o valor solicitado `bias_tolerance` seja alcançado. Ele retorna escalas para cada termo de erro — `0.0` para os termos a serem ignorados durante a mitigação e `-1.0` para os termos que devem ser mitigados. A função também retorna a sobrecarga **de custo de amostragem** resultante ( $\\gamma^2$ ) e um limite para o **viés residual** causado pelo truncamento.\n",
        "\n",
        "Essa configuração `bias_tolerance=0.0` atenua todos os termos de erro dentro do cone de luz causal do observável e fornece um custo de amostragem de referência. Permitir um pequeno viés permite que o SLC descarte os termos de baixo impacto e reduza ainda mais esse custo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "31fe87d9",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Full PEC (bias_tolerance=0.0):  sampling cost 1.923, residual bias 0.000\n",
            "Shaded    (bias_tolerance=0.05): sampling cost 1.441, residual bias 0.044\n",
            "\n",
            "Sampling-cost reduction: 25% for <= 0.05 bias\n"
          ]
        }
      ],
      "source": [
        "from qiskit_addon_slc.bounds import compute_local_scales\n",
        "\n",
        "_, full_cost, full_bias = compute_local_scales(\n",
        "    boxed_circuit, merged_bounds, noise_rates, bias_tolerance=0.0\n",
        ")\n",
        "local_scales, reduced_cost, reduced_bias = compute_local_scales(\n",
        "    boxed_circuit, merged_bounds, noise_rates, bias_tolerance=0.05\n",
        ")\n",
        "\n",
        "print(\n",
        "    f\"Full PEC (bias_tolerance=0.0):  sampling cost {full_cost:.3f}, residual bias {full_bias:.3f}\"\n",
        ")\n",
        "print(\n",
        "    f\"Shaded    (bias_tolerance=0.05): sampling cost {reduced_cost:.3f}, residual bias {reduced_bias:.3f}\"\n",
        ")\n",
        "print(\n",
        "    f\"\\nSampling-cost reduction: {(1 - reduced_cost / full_cost):.0%} for <= 0.05 bias\"\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"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}