{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Schnellstart\"\n",
        "description: \"Schnellstart für die neueste Version von Qiskit Paulice\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "intro",
      "metadata": {},
      "source": [
        "<span id=\"quickstart\" />\n",
        "\n",
        "# Schnellstart\n",
        "\n",
        "Diese Anleitung zeigt ein minimales, funktionsfähiges Beispiel für das `qiskit-paulice` Paket. Wir nutzen [Raumzeit-Pauli-Prüfungen](https://arxiv.org/abs/2504.15725), um Fehler während der Ausführung eines Clifford-Schaltkreises zu erkennen, und wählen anschließend nur diejenigen Stichproben aus, bei denen kein Fehler festgestellt wurde. Einen ausführlicheren End-to-End-Workflow, der die Verbesserung der Wiedergabetreue quantifiziert, finden Sie im [ausführlichen Tutorial](https://github.com/Qiskit/qiskit-paulice/blob/main/docs/guides/low_overhead_error_detection_using_spacetime_codes.ipynb).\n",
        "\n",
        "**Workflowschritte**\n",
        "\n",
        "1. Bereiten Sie die Eingaben vor: eine Clifford-Payload-Schaltung, die Ziel- und Ancilla-Qubits für die Checks sowie ein Rauschmodell.\n",
        "2. Finde geeignete Pauli-Prüfungen für die Raumzeit und füge sie dem Schaltkreis hinzu.\n",
        "3. Messen Sie die geprüfte Schaltung.\n",
        "4. Wählen Sie anschließend nur die Proben aus, bei denen kein Fehler festgestellt wurde.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "build-circuit",
      "metadata": {},
      "source": [
        "<span id=\"1-prepare-the-inputs\" />\n",
        "\n",
        "## 1. Bereiten Sie die Eingaben vor\n",
        "\n",
        "`add_pauli_checks` als Eingabe eine **Cliff** ord-Schaltung mit mindestens einer Endpunktmessung, die **Ziel-Qubits**, die zur Implementierung der Prüfungen verwendet werden sollen, sowie ein **Rauschmodell,** das zur Bewertung der in Frage kommenden Prüfungen dient. `ibm_boston`Hier bauen wir eine flache zufällige Clifford-Schaltung auf, ordnen sie auf einer „ 1D “-Kette physikalischer Qubits an, ordnen jedem Payload-Qubit ein benachbartes Ancilla-Qubit zu (sodass für eine Überprüfung keine SWAP-Gatter benötigt werden) und leiten aus Backend-Benchmark-Daten ein grobes Modell für depolarisierendes Rauschen ab.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "make-circuit",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-paulice/guides/quickstart/extracted-outputs/make-circuit-0.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_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_paulice.layout import get_check_qubits\n",
        "from qiskit_paulice.noise_models import NoiseModel\n",
        "\n",
        "# Backend and a 1D chain of physical qubits to run on\n",
        "backend = QiskitRuntimeService().backend(\"ibm_boston\")\n",
        "layout = [68, 69, 78, 89, 90, 91, 98, 111, 112, 113, 119, 133]\n",
        "\n",
        "# A shallow brickwork random Clifford payload circuit\n",
        "rng = np.random.default_rng(1764)\n",
        "circuit = QuantumCircuit(len(layout))\n",
        "circuit.h(range(circuit.num_qubits))\n",
        "for d in range(4):\n",
        "    for i in range(d % 2, circuit.num_qubits - 1, 2):\n",
        "        circuit.cz(i, i + 1)\n",
        "    for q in range(circuit.num_qubits):\n",
        "        if rng.integers(0, 2):\n",
        "            circuit.sx(q)\n",
        "        if rng.integers(0, 2):\n",
        "            circuit.s(q)\n",
        "circuit.measure_all()\n",
        "\n",
        "# Pair each payload qubit with a neighboring ancilla to host a check (no SWAPs needed)\n",
        "target_qubits, ancilla_qubits = get_check_qubits(backend.coupling_map, layout)\n",
        "\n",
        "# A rough depolarizing noise model from backend benchmark data, used to score checks\n",
        "noise_model = NoiseModel.from_backend(\n",
        "    backend, layout, uniform_gate_noise=True\n",
        ")\n",
        "\n",
        "circuit.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "add-checks-md",
      "metadata": {},
      "source": [
        "<span id=\"2-find-and-add-spacetime-pauli-checks\" />\n",
        "\n",
        "## 2. Raumzeit-Pauli-Prüfungen suchen und hinzufügen\n",
        "\n",
        "`add_pauli_checks` sucht nach effektiven, gewichtsarmen Prüfungen für jedes Ziel-Qubit, fügt diese dem Schaltkreis hinzu und gibt eine Folge von `CheckedCircuit` Instanzen zurück. Die `CheckedCircuit` Instanzen enthalten eine steigende Anzahl von Prüfungen, von `0` Prüfungen bis hin zu einer Prüfung pro Ziel-Qubit. Jede Überprüfung verbessert die Fehlererkennungsfähigkeit, was jedoch mit einer geringfügig höheren Schaltungstiefe einhergeht. In diesem Beispiel verwenden wir die Schaltung, bei der jedes Ziel-Qubit überprüft wird.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "add-checks",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-paulice/guides/quickstart/extracted-outputs/add-checks-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit_paulice import add_pauli_checks\n",
        "\n",
        "# The circuit has virtual qubits, so we specify our target qubits with virtual indices\n",
        "target_qubits_v = [layout.index(q) for q in target_qubits]\n",
        "\n",
        "# Add spacetime Pauli checks\n",
        "checked_circuit = add_pauli_checks(circuit, target_qubits_v, noise_model)[-1]\n",
        "checked_circuit.circuit.draw(\"mpl\", fold=-1, scale=0.4, idle_wires=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "postselect-md",
      "metadata": {},
      "source": [
        "<span id=\"3-sample-the-checked-circuit\" />\n",
        "\n",
        "## 3. Die geprüfte Schaltung abtasten\n",
        "\n",
        "`ibm_boston`Bevor wir eine Abtastung durchführen, transpilieren wir die geprüfte Schaltung auf physikalische Qubits (`initial_layout = layout + ancilla_qubits`) und in den nativen Basis-Gatensatz von. Um eine verrauschte QPU zu emulieren, führen wir eine Stichprobenauswertung mit einem verrauschten Aer-Stabilisator-Backend durch, dessen Fehlerraten mit denen `noise_model` übereinstimmen, die zur Auswahl der Prüfzahlen verwendet wurden.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "sample",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "sampled 1000 shots\n"
          ]
        }
      ],
      "source": [
        "from qiskit import transpile\n",
        "from qiskit_aer import AerSimulator\n",
        "from qiskit_aer.noise import NoiseModel as AerNoiseModel\n",
        "from qiskit_aer.noise import ReadoutError, depolarizing_error\n",
        "\n",
        "# Transpile once: lay the checked circuit out on our qubits and into the native basis\n",
        "isa_circuit = transpile(\n",
        "    checked_circuit.circuit,\n",
        "    backend,\n",
        "    initial_layout=layout + ancilla_qubits,\n",
        "    optimization_level=0,\n",
        ")\n",
        "\n",
        "# Build an Aer noise model matching the depolarizing model used to pick checks\n",
        "aer_noise = AerNoiseModel()\n",
        "aer_noise.add_all_qubit_quantum_error(\n",
        "    depolarizing_error(noise_model.gate_noise, 2), [\"cz\"]\n",
        ")\n",
        "p = noise_model.readout_noise\n",
        "aer_noise.add_all_qubit_readout_error(ReadoutError([[1 - p, p], [p, 1 - p]]))\n",
        "simulator = AerSimulator(method=\"stabilizer\", noise_model=aer_noise)\n",
        "\n",
        "counts = (\n",
        "    simulator.run(isa_circuit, shots=1000, seed_simulator=1764)\n",
        "    .result()\n",
        "    .get_counts()\n",
        ")\n",
        "print(f\"sampled {sum(counts.values())} shots\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "postselect-step-md",
      "metadata": {},
      "source": [
        "<span id=\"4-postselect-samples-with-no-detected-error\" />\n",
        "\n",
        "## 4. Proben, bei denen nach der Auswahl kein Fehler festgestellt wurde\n",
        "\n",
        "`get_postselection_method` ordnet jeden Schuss seinem Syndromvektor zu; wir behalten nur die Schüsse mit einem Syndrom, das ausschließlich aus Nullen besteht – also diejenigen, bei denen bei keiner Prüfung ein Fehler festgestellt wurde. Durch das Verwerfen der markierten Aufnahmen werden erkannte Fehler aus der verbleibenden Verteilung entfernt, wodurch deren Genauigkeit verbessert wird – allerdings auf Kosten einer geringeren Abtastrate. Einen quantitativen Vergleich der Genauigkeit finden Sie im [Tutorial](https://github.com/Qiskit/qiskit-paulice/blob/main/docs/guides/low_overhead_error_detection_using_spacetime_codes.ipynb).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "postselect",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "kept 927 of 1000 shots (93%)\n"
          ]
        }
      ],
      "source": [
        "# Keep only the shots in which no check reported an error\n",
        "ps_fn = checked_circuit.get_postselection_method()\n",
        "counts_postselected = {\n",
        "    bs: n for bs, n in counts.items() if not ps_fn(bs).any()\n",
        "}\n",
        "\n",
        "kept, total = sum(counts_postselected.values()), sum(counts.values())\n",
        "print(f\"kept {kept} of {total} shots ({kept / total:.0%})\")"
      ]
    },
    {
      "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
}