{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Schnellstart\"\n",
        "description: \"Schnellstart für die neueste Version von „Shaded Lightcones“\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "38de8ef2",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Schnellstart\"\n",
        "description: \"Eine Schnellstartanleitung für das Qiskit-Add-on „Shaded Lightcones“ (qiskit-addon-slc)\"\n",
        "---\n",
        "\n",
        "<span id=\"quickstart\" />\n",
        "\n",
        "# Schnellstart\n",
        "\n",
        "Diese Anleitung zeigt ein minimales, funktionsfähiges Beispiel für das Paket `qiskit-addon-slc` . Wir berechnen einen abgeschatteten Lichtkegel, um den Rechenaufwand bei der probabilistischen Fehlerkompensation (PEC) zu reduzieren.\n",
        "\n",
        "PEC reduziert das Gate-Rauschen durch Abtastung aus einer Quasiprobabilitätszerlegung des inversen Rauschkanals. Die Kosten für die Stichprobenziehung steigen mit jedem Fehlerterm, den sie ausgleichen muss; allerdings wirkt sich nicht jeder Fehler gleichermaßen auf die beobachtbare Größe aus. Ein Fehler außerhalb des kausalen Lichtkegels des Beobachters kann den gemessenen Erwartungswert überhaupt nicht beeinflussen, und selbst innerhalb des Lichtkegels sind manche Fehler schwerwiegender als andere. Ein schattierter Lichtkegel quantifiziert dies, indem er den Einfluss jedes Pauli-Fehlerterms auf die Observable begrenzt. Durch das Abschneiden der Fehlerterme mit dem geringsten Einfluss wird das Rauschmodell, das PEC kompensieren muss, verkleinert, wodurch die Abtastkosten im Gegenzug für eine kleine, begrenzte Verzerrung reduziert werden.\n",
        "\n",
        "Um zu erfahren, wie man einen realistischen Workflow aufbaut und auf Quantenhardware ausführt, sehen Sie sich das Tutorial [„Probabilistic error cancellation with shaded lightcones“](/docs/tutorials/pec-with-shaded-lightcones) auf der „ IBM Quantum Platform “ an.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "71bc0134",
      "metadata": {},
      "source": [
        "<span id=\"1-prepare-the-inputs-for-slc\" />\n",
        "\n",
        "## 1. Bereiten Sie die Eingaben für SLC vor\n",
        "\n",
        "Hier bauen wir einen 6-Qubit-Trotter-Ising-Schaltkreis mit transversalem Feld auf und wählen für das mittlere Qubit eine Einzelqubit- $Z$ -Beobachtungsgröße aus.\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 arbeitet auf den rauschbehafteten Zwei-Qubit-Gatter-Schichten der Schaltung. Hier verwenden wir, `samplomatic` um die Gatter in kommentierte Kästchen zu gruppieren, und fügen jeder Zwei-Qubit-Schicht eine Anmerkung zur Rauscheinjektion hinzu. `generate_noise_model_paulis` anschließend werden die Pauli-Fehlerterme jeder einzelnen verrauschten Schicht aufgezählt.\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. Berechne den schattierten Lichtkegel\n",
        "\n",
        "Der schattierte Lichtkegel wird gebildet, indem jedem Pauli-Fehlerterm im Rauschmodell ein Skalierungsfaktor zugewiesen wird, der davon abhängt, wie stark er den Erwartungswert der Observable beeinflusst. Diese Skalen leiten sich aus den Vorwärts- und Rückwärts-Fehlergrenzen (siehe unten) sowie aus der Fehlerrate des jeweiligen Fehlerterms ab:\n",
        "\n",
        "* `compute_forward_bounds` führt jeden Fehlerterm bis zum Ende der Schaltung *weiter,* um dessen Auswirkung auf die dort gemessene Größe zu begrenzen.\n",
        "* `compute_backward_bounds` führt jeden Fehlerterm *rückwärts* bis zum Anfang der Schaltung zurück, um dessen Auswirkung auf den Anfangszustand zu begrenzen.\n",
        "\n",
        "`merge_bounds` kombiniert die beiden zu einem einzigen Bindungs-pro-Fehler-Term. Zusammenführung von Skalen, die jeweils durch die Fehlerquote des Terms begrenzt sind. Diese Raten stammen in der Regel aus einem Experiment zum Lernen mit Rauschen (z. B. `NoiseLearnerV3`); Der Einfachheit halber verwenden wir hier Zufallsraten.\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": [
        "In der untenstehenden Visualisierung des schattierten Lichtkegels wird jedes Feld entsprechend der Stärke der Auswirkungen der Fehler an diesem Ort auf die beobachtbare Größe schattiert: Helle Felder weisen die größten Grenzen auf, während Felder, die in den Hintergrund verblassen, Fehlerterme enthalten, die nur einen geringen Einfluss auf die Berechnung haben – diese Fehler kommen als Kandidaten für den Ausschluss aus dem Rauschmodell in Frage. Die Werte in der folgenden Visualisierung stellen die Summen der Fehlergrenzen aller Pauli-Fehler an dieser Stelle dar, weshalb einige der Werte größer werden als `2.0` – die Grenze für einen einzelnen Pauli-Fehler.\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. Senkung der Kosten für die Probenahme\n",
        "\n",
        "`compute_local_scales` wandelt den schattierten Lichtkegel in eine konkrete PEC-Konfiguration um. Es ordnet die Fehlerterme nach ihrem begrenzten Einfluss auf die Observable und schneidet diejenigen mit dem geringsten Einfluss ab, bis der gewünschte Wert erreicht `bias_tolerance` ist. Es gibt für jeden Fehlerterm Skalen zurück – `0.0` für Terme, die bei der Minderung ignoriert werden sollen, und `-1.0` für Terme, die gemindert werden sollen. Die Funktion gibt außerdem den daraus resultierenden Overhead **bei den Stichprobenkosten** ( $\\gamma^2$ ) sowie eine Schranke für die durch die Trunkierung verursachte **Restverzerrung** zurück.\n",
        "\n",
        "Durch die Einstellung werden alle Fehlerterme innerhalb des kausalen Lichtkegels der Observable `bias_tolerance=0.0` gemildert und es ergibt sich ein Basiswert für die Abtastkosten. Durch die Zulassung einer geringen Verzerrung kann SLC die Terme mit geringem Einfluss vernachlässigen und diese Kosten weiter senken.\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
}