{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Schnelleinstieg\"\n",
        "description: \"Schnellstart für die neueste Version von Operator Backpropagation (OBP)\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "intro",
      "metadata": {},
      "source": [
        "<span id=\"quick-start\" />\n",
        "\n",
        "# Schnelleinstieg\n",
        "\n",
        "Diese Anleitung zeigt ein minimales, funktionsfähiges Beispiel für das Paket `qiskit-addon-obp` . Wir nutzen die Operator-Backpropagation (OBP), um die Tiefe eines Quantenschaltkreises zu verringern, indem wir nachgelagerte Gatter in den Observablen integrieren.\n",
        "\n",
        "Ein Erwartungswert $\\langle O \\rangle = \\langle \\psi | U^\\dagger O U | \\psi \\rangle$ bleibt unverändert, wenn wir einen hinteren Block von Gatterelementen aus der Schaltung $U$ herauslösen und die Observable klassisch damit konjugieren. OBP wendet dies wiederholt an und wertet einen Teil der Schaltung klassisch aus, sodass auf der Hardware nur eine einfachere Schaltung ausgeführt wird. Der Preis dafür ist, dass jedes absorbierte Gate die Observable in weitere Pauli-Terme aufspalten kann; daher muss die eingesparte Tiefe gegen die Zunahme der Observable abgewogen werden.\n",
        "\n",
        "Beispiele dafür, wie man mit diesem Tool realistische Workflows erstellt und auf Quantenhardware ausführt, finden Sie in den Tutorials auf der „ IBM Quantum Platform “ ( [OBP-Tutorial](/docs/tutorials/operator-back-propagation) ).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "inputs-header",
      "metadata": {},
      "source": [
        "<span id=\"prepare-inputs-for-obp\" />\n",
        "\n",
        "## Vorbereitung der Eingaben für OBP\n",
        "\n",
        "OBP nimmt als Eingabe eine Liste von Schaltungsabschnitten und eine Beobachtungsgröße entgegen. Es führt eine Rückpropagierung der Schnitte nacheinander vom Ende des Schaltkreises in den Observablen durch, wodurch die Schaltkreistiefe auf Kosten zusätzlicher Pauli-Terme im Observablen verringert wird. Hier erzeugen wir eine Zeitentwicklungsschaltung für ein Heisenberg-Modell mit 10 Qubits und unterteilen sie nach Gattentypen.\n",
        "\n",
        "Im Folgenden stellen wir zunächst die ursprüngliche Schaltung dar, anschließend dieselbe Schaltung, ergänzt um Barrieren, die die Schnittgrenzen markieren – jeder Schnitt ist eine Einheit, die in einem einzigen Backpropagation-Schritt in den Observablen absorbiert werden kann.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "setup-inputs",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Original circuit depth: 18\n",
            "Number of slices: 18\n",
            "Observable terms: 1\n"
          ]
        }
      ],
      "source": [
        "import numpy as np\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit.synthesis import LieTrotter\n",
        "from qiskit.transpiler import CouplingMap\n",
        "from qiskit_addon_utils.problem_generators import (\n",
        "    generate_time_evolution_circuit,\n",
        "    generate_xyz_hamiltonian,\n",
        ")\n",
        "from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types\n",
        "\n",
        "# Generate a circuit to reduce\n",
        "coupling_map = CouplingMap.from_heavy_hex(3, bidirectional=False)\n",
        "reduced_coupling_map = coupling_map.reduce(\n",
        "    [0, 13, 1, 14, 10, 16, 5, 12, 8, 18]\n",
        ")\n",
        "\n",
        "hamiltonian = generate_xyz_hamiltonian(\n",
        "    reduced_coupling_map,\n",
        "    coupling_constants=(np.pi / 8, np.pi / 4, np.pi / 2),\n",
        "    ext_magnetic_field=(np.pi / 3, np.pi / 6, np.pi / 9),\n",
        ")\n",
        "\n",
        "circuit = generate_time_evolution_circuit(\n",
        "    hamiltonian,\n",
        "    time=0.2,\n",
        "    synthesis=LieTrotter(reps=2),\n",
        ")\n",
        "\n",
        "# Slice the circuit and define an observable\n",
        "slices = slice_by_gate_types(circuit)\n",
        "observable = SparsePauliOp(\"IIIIIIIIIZ\")\n",
        "\n",
        "print(f\"Original circuit depth: {circuit.depth()}\")\n",
        "print(f\"Number of slices: {len(slices)}\")\n",
        "print(f\"Observable terms: {len(observable)}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "46efdcfc",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Original circuit:\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/quickstart/extracted-outputs/46efdcfc-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Sliced circuit (recombined with barriers for visualization)\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/quickstart/extracted-outputs/46efdcfc-3.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Recombine the slices with barriers to make the slice boundaries visible\n",
        "sliced_circuit = combine_slices(slices, include_barriers=True)\n",
        "\n",
        "print(\"Original circuit:\")\n",
        "display(circuit.draw(\"mpl\", scale=0.6, fold=-1))\n",
        "print(\"Sliced circuit (recombined with barriers for visualization)\")\n",
        "sliced_circuit.draw(\"mpl\", scale=0.6, fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "obp-header",
      "metadata": {},
      "source": [
        "<span id=\"reduce-circuit-depth-with-obp\" />\n",
        "\n",
        "## Reduzierung der Schaltungstiefe mit OBP\n",
        "\n",
        "Wir bezeichnen dies als „Absorbieren `backpropagate` von Schnitten in den Observablen“. Die Funktion gibt das erweiterte Observable, die nicht durchlaufenen Schaltkreisschnitte sowie Metadaten zur Prozedur zurück.\n",
        "\n",
        "Wird dies nicht kontrolliert, kann die Observable in Richtung der Pauli-Terme „ $2^n$ “ wachsen. Dies begrenzt `operator_budget` das Wachstum – hier lassen wir höchstens 8 qubitweise kommutierende Gruppen zu, was in etwa die Anzahl der Versuche festlegt, die erforderlich sind, um die Observable auf der QPU auszuwerten. Die Rückpropagierung wird beendet, sobald die Aufnahme des nächsten Schnitts das Budget überschreiten würde – genau das geschieht im folgenden Beispiel: Nur 7 der 18 Schnitte werden aufgenommen, bevor die Observable alle 8 Kommutationsgruppen füllt und der Vorgang beendet wird.\n",
        "\n",
        "Um tiefer in die Schaltung einzudringen, `backpropagate` kann das `truncation_error_budget` Schlüsselwort-Argument von verwendet werden, um Pauli-Terme mit kleinen Koeffizienten aus der Observablen zu entfernen, wenn diese zunimmt. Dies dämpft das Wachstum der Observable, was mit einem Fehler einhergeht, der proportional zur Größe der abgeschnittenen Pauli-Terme ist. Die beiden Budgets ergänzen sich und können gemeinsam verwendet werden: `operator_budget` steuert die *Größe* der beobachtbaren Größe, während den *Fehler* `truncation_error_budget` steuert, der durch das Weglassen von Termen entsteht.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "run-obp",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 7 of 18 slices.\n",
            "Reduced circuit depth: 11 (was 18)\n",
            "Observable grew from 1 to 18 Pauli terms.\n",
            "Filled 8 of 8 commuting groups, exhausting the budget.\n"
          ]
        }
      ],
      "source": [
        "from qiskit_addon_obp import backpropagate\n",
        "from qiskit_addon_obp.utils.simplify import OperatorBudget\n",
        "\n",
        "max_qwc_groups = 8\n",
        "bp_obs, remaining_slices, metadata = backpropagate(\n",
        "    observable,\n",
        "    slices,\n",
        "    operator_budget=OperatorBudget(max_qwc_groups=max_qwc_groups),\n",
        ")\n",
        "\n",
        "reduced_circuit = combine_slices(remaining_slices)\n",
        "num_groups = len(bp_obs.group_commuting(qubit_wise=True))\n",
        "\n",
        "print(\n",
        "    f\"Backpropagated {metadata.num_backpropagated_slices} of {len(slices)} slices.\"\n",
        ")\n",
        "print(\n",
        "    f\"Reduced circuit depth: {reduced_circuit.depth()} (was {circuit.depth()})\"\n",
        ")\n",
        "print(f\"Observable grew from {len(observable)} to {len(bp_obs)} Pauli terms.\")\n",
        "print(\n",
        "    f\"Filled {num_groups} of {max_qwc_groups} commuting groups, exhausting the budget.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "b006625e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/quickstart/extracted-outputs/b006625e-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "reduced_circuit.draw(\"mpl\", scale=0.6, fold=-1)"
      ]
    },
    {
      "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
}