{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Inicio rápido\"\n",
        "description: \"Guía de inicio rápido para la última versión de Shaded lightcones\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "38de8ef2",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Inicio rápido\"\n",
        "description: \"Guía de inicio rápido para el complemento «Shaded lightcones» de Qiskit (qiskit-addon-slc)\"\n",
        "---\n",
        "\n",
        "<span id=\"quickstart\" />\n",
        "\n",
        "# Inicio rápido\n",
        "\n",
        "Esta guía muestra un ejemplo mínimo y funcional del paquete `qiskit-addon-slc` . Calculamos un cono de luz sombreado para reducir el coste de muestreo de la cancelación probabilística de errores (PEC).\n",
        "\n",
        "El PEC mitiga el ruido de puerta mediante el muestreo a partir de una descomposición en cuasiprobabilidades del canal de ruido inverso. Su coste de muestreo aumenta con cada término de error que debe mitigar; sin embargo, no todos los errores afectan por igual a la variable observable. Un error situado fuera del cono de luz causal del observable no puede influir en absoluto en el valor esperado medido, e incluso dentro del cono de luz hay errores que son más perjudiciales que otros. Un cono de luz sombreado cuantifica esto al delimitar el efecto que cada término de error de Pauli tiene sobre la observable. Al truncar los términos de error con menor efecto, se reduce el modelo de ruido que el PEC debe mitigar, lo que disminuye el coste de muestreo a cambio de un sesgo pequeño y acotado.\n",
        "\n",
        "Para ver cómo crear un flujo de trabajo realista y ejecutarlo en hardware cuántico, consulta el tutorial [«Cancelación probabilística de errores con conos de luz sombreados»](/docs/tutorials/pec-with-shaded-lightcones) en IBM Quantum Platform.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "71bc0134",
      "metadata": {},
      "source": [
        "<span id=\"1-prepare-the-inputs-for-slc\" />\n",
        "\n",
        "## 1. Preparar los datos de entrada para SLC\n",
        "\n",
        "Aquí construimos un circuito de Ising de campo transversal «trotterizado» de 6 qubits y elegimos un observable de « $Z$ » de un solo qubit para el qubit central.\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": [
        "SLC opera en las capas ruidosas de puertas de dos qubits del circuito. Aquí utilizamos `samplomatic` para agrupar las puertas en recuadros anotados y adjuntamos una anotación de inyección de ruido a cada capa de dos qubits. `generate_noise_model_paulis` A continuación, enumera los términos de error de Pauli de cada una de las capas con ruido.\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 el cono de luz sombreado\n",
        "\n",
        "El cono de luz sombreado se construye asignando una escala a cada término de error de Pauli del modelo de ruido, en función del efecto que este tenga sobre el valor esperado de la observable. Estas escalas se derivan de los límites de error hacia adelante y hacia atrás de cada término de error (que se describen a continuación), así como de su tasa de error:\n",
        "\n",
        "* `compute_forward_bounds` progresa cada término de error *hacia* el final del circuito para limitar su efecto sobre la magnitud observable medida en ese punto.\n",
        "* `compute_backward_bounds` evoluciona cada término de error *hacia atrás* hasta el inicio del circuito para limitar su efecto sobre el estado inicial.\n",
        "\n",
        "`merge_bounds` combina ambos en un único término de error. La fusión de escalas, cada una de ellas limitada por la tasa de error del término. Estas tasas suelen proceder de un experimento de aprendizaje con ruido (p. ej., `NoiseLearnerV3`); En este caso, utilizamos tasas aleatorias 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": [
        "En la visualización del cono de luz sombreado que se muestra a continuación, cada recuadro está sombreado en función de la intensidad con la que los errores en ese punto pueden afectar a la magnitud observable: los recuadros más brillantes corresponden a los límites más amplios, mientras que los recuadros que se desvanecen hacia el fondo contienen términos de error que tienen un efecto mínimo en el cálculo; estos errores son candidatos naturales para ser eliminados del modelo de ruido. Los valores que aparecen en la visualización siguiente representan la suma de los límites de error de todos los errores de Pauli en ese punto, razón por la cual algunos de los valores superan —el límite correspondiente a cualquier error de Pauli individual— `2.0` .\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. Reducir el coste del muestreo\n",
        "\n",
        "`compute_local_scales` convierte el cono de luz sombreado en una configuración PEC concreta. Prioriza los términos de error en función de su efecto acotado sobre la variable observable y elimina los que tienen menor impacto hasta `bias_tolerance` alcanzar el valor solicitado. Devuelve escalas para cada término de error: `0.0` para los términos que deben ignorarse durante la mitigación y `-1.0` para los términos que deben mitigarse. La función también devuelve la sobrecarga **del coste de muestreo** resultante ( $\\gamma^2$ ) y un límite del **sesgo** residual provocado por el truncamiento.\n",
        "\n",
        "Este ajuste mitiga `bias_tolerance=0.0` todos los términos de error dentro del cono de luz causal de la magnitud observable y proporciona un coste de muestreo de referencia. Al permitir un pequeño sesgo, SLC puede descartar los términos de bajo impacto y reducir aún más ese coste.\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
}