{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "eb419bc5-908c-4f0d-a4ff-c4d13f04e332",
      "metadata": {
        "tags": []
      },
      "source": [
        "---\n",
        "title: \"Grundzustandsenergie-Schätzung der Heisenberg-Kette mit VQE\"\n",
        "description: \"Erstellen, implementieren und führen Sie ein Qiskit-Muster aus, um eine Heisenberg-Kette zu simulieren und deren Grundzustandsenergie zu schätzen.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore hyperparameters, forall, nabla, nparams */}\n",
        "\n",
        "<span id=\"ground-state-energy-estimation-of-the-heisenberg-chain-with-vqe\" />\n",
        "\n",
        "# Grundzustandsenergie-Schätzung der Heisenberg-Kette mit VQE\n",
        "\n",
        "*Geschätzte Laufzeit: 37 Minuten auf einem Heron-Prozessor (HINWEIS: Dies ist lediglich eine Schätzung. (Die Laufzeit kann variieren.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e28ad544",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Lernziele\n",
        "\n",
        "* Wie man eine Heisenberg-Spinkette mithilfe von Qiskit als Quantenhamiltonian modelliert\n",
        "* Wie man den SPSA-Optimierer zur Abschätzung der Grundzustandsenergie eines Quantensystems einsetzt\n",
        "* So führen Sie Variations-Workflows auf der „ IBM® “-Quantenhardware mithilfe von „ IBM Quantum “-Primitiven und -Sitzungen aus\n",
        "\n",
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Voraussetzungen\n",
        "\n",
        "* [Grundlagen der Quanteninformation](/learning/courses/basics-of-quantum-information)\n",
        "* [Einführung in Qiskit-Muster](/docs/guides/intro-to-patterns)\n",
        "* [Entwurf eines Variationsalgorithmus](/learning/courses/variational-algorithm-design)\n",
        "\n",
        "<span id=\"background\" />\n",
        "\n",
        "## Hintergrund\n",
        "\n",
        "Die Heisenberg-Spinkette ist eines der am intensivsten untersuchten Modelle in der Festkörperphysik und im Bereich des Quantenmagnetismus. Es beschreibt ein eindimensionales Gitter aus wechselwirkenden Quantenspins, in dem benachbarte Spins durch Austauschwechselwirkungen gekoppelt sind. Der Hamilton-Operator für das isotrope Heisenberg-Modell mit einem externen Magnetfeld lautet:\n",
        "\n",
        "$H = \\sum_{\\langle i,j \\rangle} \\left( J_x X_i X_j + J_y Y_i Y_j + J_z Z_i Z_j \\right) + \\sum_{i} h_i Z_i,$\n",
        "\n",
        "wobei $X_i$, $Y_i$ und $Z_i$ die auf die Position $i$ wirkenden Pauli-Operatoren sind, die Summe $\\langle i,j \\rangle$ über die Paare der nächsten Nachbarn läuft, $J_x = J_y = J_z = 0.5$ die Austauschkopplungskonstanten sind (in diesem Tutorial isotrop) und $h_i$ ein ortsabhängiges externes Magnetfeld darstellt. In diesem Tutorial werden die Magnetfeldwerte zufällig aus dem Intervall $[-1, 1]$ entnommen. Beachten Sie, dass in der folgenden Implementierung die Menge der „Nearest-Neighbor“-Paare durch die native Kopplung des Hardware-Backends zwischen den ersten $N$ Qubits bestimmt wird, die je nach Gerätetopologie möglicherweise keine streng lineare Kette bilden.\n",
        "\n",
        "Das Verständnis der Grundzustandsenergie dieses Hamilton-Operators ist in der Physik von grundlegender Bedeutung. Der Grundzustand enthält Informationen über Quantenphasenübergänge, die Verschränkungsstruktur und die magnetische Ordnung. Im klassischen Sinne wird die Berechnung der exakten Grundzustandsenergie mit steigender Anzahl von Spins unlösbar, da die Dimension des Hilbertraums exponentiell mit $2^N$ für $N$ Spins wächst. Damit ist es ein naheliegender Kandidat für die Quantensimulation.\n",
        "\n",
        "Der Variational Quantum Eigensolver (VQE) ist ein hybrid-quantentheoretisch-klassischer Algorithmus, der zur Schätzung der Grundzustandsenergie eines Hamilton-Operators entwickelt wurde. Dabei wird auf einem Quantencomputer ein parametrisierter Quantenzustand $|\\psi(\\theta)\\rangle$ (ein sogenannter Ansatz) erzeugt und der Erwartungswert $\\langle \\psi(\\theta) | H | \\psi(\\theta) \\rangle$ gemessen. Ein klassischer Optimierer passt dann die Parameter $\\theta$ iterativ an, um diese Energie zu minimieren, wobei er sich das Variationsprinzip zunutze macht, das garantiert, dass die gemessene Energie stets eine Obergrenze für die tatsächliche Grundzustandsenergie darstellt.\n",
        "\n",
        "In diesem Tutorial verwenden wir den `efficient_su2` Ansatz aus der Schaltkreisbibliothek von Qiskit, der Schichten aus Rotationen einzelner Quantenbits und Verschränkungsgates aufbaut. Die Optimierung erfolgt mithilfe des SPSA-Algorithmus (Simultaneous Perturbation Stochastic Approximation), der sich gut für verrauschte Quantenhardware eignet, da er Gradienten mit nur zwei Funktionsauswertungen pro Iteration schätzt, unabhängig von der Anzahl der Parameter.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bc52f763",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Anforderungen\n",
        "\n",
        "Vergewissern Sie sich vor Beginn dieses Tutorials, dass Sie die folgenden Komponenten installiert haben:\n",
        "\n",
        "* Qiskit SDK v2.0 oder höher, mit [Visual](/docs/api/qiskit/visualization) isierungsunterstützung\n",
        "* Qiskit Runtime v0.44 oder später (`pip install qiskit-ibm-runtime`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a46e9e3e",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Einrichtung\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "e7754922",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "from typing import Sequence\n",
        "\n",
        "from qiskit import QuantumCircuit\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit.primitives import BaseEstimatorV2\n",
        "from qiskit.circuit.library import XGate\n",
        "from qiskit.circuit.library import efficient_su2\n",
        "from qiskit.transpiler import PassManager\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "from qiskit.transpiler.passes.scheduling import (\n",
        "    ALAPScheduleAnalysis,\n",
        "    PadDynamicalDecoupling,\n",
        ")\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService, Session, EstimatorV2\n",
        "\n",
        "\n",
        "def visualize_results(results):\n",
        "    plt.plot(results[\"cost_history\"], lw=2)\n",
        "    plt.xlabel(\"Number of function evaluations\")\n",
        "    plt.ylabel(\"Energy\")\n",
        "    plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "890d9c81",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-example\" />\n",
        "\n",
        "## Ein kleines Beispiel\n",
        "\n",
        "In diesem Abschnitt gehen wir jeden Schritt des Qiskit-Musters im Kleinen durch und erläutern dabei die wichtigsten Komponenten, während wir den Arbeitsablauf aufbauen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "132fb15f-10b4-4d7e-83d8-f512a6f675d1",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "### Schritt 1: Klassische Eingaben einem Quantenproblem zuordnen\n",
        "\n",
        "* Eingabe: Anzahl der Spins\n",
        "* Ausgabe: Ansatz und Hamiltonian zur Modellierung der Heisenberg-Kette\n",
        "\n",
        "Entwickeln Sie einen Ansatz und einen Hamilton-Operator, die eine Heisenberg-Kette mit 10 Spins modellieren. In diesem Schritt werden wir einen Heisenberg-Hamilton-Operator mit 10 Spins über der Kopplungskarte des am wenigsten ausgelasteten Backends aufbauen und den `efficient_su2` Ansatz vorbereiten.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "7e8d2f10-f1d6-4ec2-bac9-9db23499c9e1",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/spin-chain-vqe/extracted-outputs/7e8d2f10-f1d6-4ec2-bac9-9db23499c9e1-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "num_spins = 10\n",
        "ansatz = efficient_su2(num_qubits=num_spins, reps=2)\n",
        "\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, min_num_qubits=num_spins, simulator=False\n",
        ")\n",
        "\n",
        "coupling = backend.target.build_coupling_map()\n",
        "reduced_coupling = coupling.reduce(list(range(num_spins)))\n",
        "\n",
        "edge_list = reduced_coupling.graph.edge_list()\n",
        "ham_list = []\n",
        "\n",
        "for edge in edge_list:\n",
        "    ham_list.append((\"ZZ\", edge, 0.5))\n",
        "    ham_list.append((\"YY\", edge, 0.5))\n",
        "    ham_list.append((\"XX\", edge, 0.5))\n",
        "\n",
        "for qubit in reduced_coupling.physical_qubits:\n",
        "    ham_list.append((\"Z\", [qubit], np.random.random() * 2 - 1))\n",
        "\n",
        "hamiltonian = SparsePauliOp.from_sparse_list(ham_list, num_qubits=num_spins)\n",
        "\n",
        "ansatz.draw(\"mpl\", style=\"iqp\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ab79119b-5e56-49d8-a20e-1c8e665baec0",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "### Schritt 2: Optimierung des Problems für die Ausführung auf Quantenhardware\n",
        "\n",
        "* Eingabe: Abstrakter Schaltkreis, beobachtbar\n",
        "* Ausgabe: Zielschaltung und Observable, optimiert für die ausgewählte QPU\n",
        "\n",
        "Verwenden Sie die Funktion `generate_preset_pass_manager` von Qiskit, um automatisch eine Optimierungsroutine für unsere Schaltung in Bezug auf die ausgewählte QPU zu erstellen. Wir haben uns für `optimization_level=3` entschieden, das den höchsten Optimierungsgrad der voreingestellten Passmanager bietet. Zur Unterdrückung von Dekohärenzfehlern werden auch `ALAPScheduleAnalysis` und `PadDynamicalDecoupling` in die Planung einbezogen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "a0a5f1c8-5c31-4d9f-ae81-37bd67271d44",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/spin-chain-vqe/extracted-outputs/a0a5f1c8-5c31-4d9f-ae81-37bd67271d44-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "target = backend.target\n",
        "pm = generate_preset_pass_manager(optimization_level=3, target=target)\n",
        "pm.scheduling = PassManager(\n",
        "    [\n",
        "        ALAPScheduleAnalysis(durations=target.durations()),\n",
        "        PadDynamicalDecoupling(\n",
        "            durations=target.durations(),\n",
        "            dd_sequence=[XGate(), XGate()],\n",
        "            pulse_alignment=target.pulse_alignment,\n",
        "        ),\n",
        "    ]\n",
        ")\n",
        "isa_ansatz = pm.run(ansatz)\n",
        "isa_observable = hamiltonian.apply_layout(isa_ansatz.layout)\n",
        "isa_ansatz.draw(\"mpl\", scale=0.6, style=\"iqp\", fold=-1, idle_wires=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9e889d0b-30b5-4e6b-84c9-d1f096abf132",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Schritt 3: Mit „ Qiskit primitives “ ausführen\n",
        "\n",
        "* Eingabe: Zielschaltung und Beobachtungsobjekt\n",
        "* Ausgabe: Ergebnisse der Optimierung\n",
        "\n",
        "Minimieren Sie die geschätzte Grundzustandsenergie des Systems durch Optimierung der Schaltungsparameter. Verwenden Sie die Primitive `Estimator` „ IBM Quantum “, um die Kostenfunktion während der Optimierung zu berechnen.\n",
        "\n",
        "Da wir den Schaltkreis in Schritt 2 für das Backend optimiert haben, können wir die Transpilation auf dem Quantum Compute-Server vermeiden, indem wir festlegen `skip_transpilation=True` und den optimierten Schaltkreis übergeben. Für diese Demo werden wir die Berechnung auf einer QPU unter Verwendung von „ IBM Quantum “-Primitiven durchführen. Um mit auf Zustandsvektoren basierenden `qiskit` Primitiven zu arbeiten, ersetzen Sie den Codeabschnitt, der „ IBM Quantum “-Primitiven verwendet, durch den auskommentierten Abschnitt.\n",
        "\n",
        "In diesem Tutorial verwenden wir die Simultaneous Perturbation Stochastic Approximation (SPSA), einen gradientenbasierten Optimierer. Als Nächstes geben wir eine kurze Einführung in das Thema und stellen den Code zur Implementierung von SPSA mit Qiskit v2.0 zur Verfügung.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "64f97e6f",
      "metadata": {},
      "source": [
        "<span id=\"introducing-spsa\" />\n",
        "\n",
        "### Wir stellen SPSA vor\n",
        "\n",
        "Die simultane stochastische Approximation durch Störung (SPSA) [\\[1\\]](#references) ist ein Optimierungsalgorithmus, der den gesamten Gradientenvektor mit nur zwei Funktionsaufrufen pro Iteration approximiert. Sei $f:\\mathbb{R}^p\\rightarrow \\mathbb{R}$ die Kostenfunktion mit den zu optimierenden Parametern $p$ und sei $x_i\\in \\mathbb{R}^p$ der Parametervektor im Schritt $i^{th}$ der Iteration. Zur Berechnung des Gradienten wird ein Zufallsvektor $\\Delta_i$ der Größe $p$ erzeugt, wobei jedes Element $\\Delta_{ij}$, $\\forall$ $j\\in \\{1,2,...,p\\}$ gleichverteilten Werten aus $\\{-1, 1\\}$ entnommen wird. Anschließend wird jedes Element des Zufallsvektors $\\Delta_i$ mit einem kleinen Wert $c_i$ multipliziert, um eine zufällige Störung zu erzeugen. Der Gradient wird dann wie folgt geschätzt:\n",
        "\n",
        "$[\\nabla f(x_i)]_j \\approx \\frac{f(x_i + c_i \\Delta_i) - f(x_i - c_i \\Delta_i)}{2c_i\\Delta_{ij}}.$\n",
        "\n",
        "Da bei der Gradientenschätzung eine zufällige Störung einwirkt, ist intuitiv zu erwarten, dass kleine Abweichungen der exakten Werte von $f$, die auf Rauschen zurückzuführen sind, toleriert und berücksichtigt werden können. Tatsächlich ist SPSA dafür bekannt, besonders unempfindlich gegenüber Störsignalen zu sein, und benötigt für jede Iteration nur zwei Hardware-Aufrufe. Daher ist er einer der am meisten bevorzugten Optimierer für die Implementierung von Variationsalgorithmen.\n",
        "\n",
        "In diesem Tutorial werden die Hyperparameter für die Iteration „ $i^{th}$ “, „ $a_i$ “ und „ $c_i$ “ wie folgt berechnet:\n",
        "\n",
        "$a_i = \\frac{a}{(A + i + 1)^\\alpha} \\quad \\text{and} \\quad c_i = \\frac{c}{(i+1)^\\gamma},$\n",
        "\n",
        "wobei die konstanten Werte wie folgt angenommen werden: $A = 30$, $\\alpha = 0.9$, $a = 0.3$, $c = 0.1$ und $\\gamma = 0.4$. Diese Werte wurden aus [\\[2\\]](#references) entnommen. Eine angemessene Abstimmung der Hyperparameter ist erforderlich, um eine gute Leistung aus SPSA herauszuholen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "73a9352c",
      "metadata": {},
      "outputs": [],
      "source": [
        "def spsa(\n",
        "    fun, x0, args=(), A=30, alpha=0.9, a=0.3, c=0.1, gamma=0.4, maxiter=100\n",
        "):\n",
        "    nparams = len(x0)\n",
        "    x = np.copy(x0)\n",
        "\n",
        "    for i in range(maxiter):\n",
        "        a_i = a / (A + i + 1) ** alpha\n",
        "        c_i = c / (i + 1) ** gamma\n",
        "        delta_i = np.random.choice([-1, 1], nparams)\n",
        "\n",
        "        # two hardware calls\n",
        "        eval_1 = fun(x + c_i * delta_i, *args)\n",
        "        eval_2 = fun(x - c_i * delta_i, *args)\n",
        "\n",
        "        # compute the gradient and update the parameters\n",
        "        grad = (eval_1 - eval_2) / (2 * c_i) * np.reciprocal(delta_i)\n",
        "        x = x - a_i * grad\n",
        "\n",
        "    return x"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "32ca3b6a",
      "metadata": {},
      "outputs": [],
      "source": [
        "def cost_func(\n",
        "    params: Sequence,\n",
        "    ansatz: QuantumCircuit,\n",
        "    hamiltonian: SparsePauliOp,\n",
        "    estimator: BaseEstimatorV2,\n",
        "    cost_history_dict: dict,\n",
        ") -> float:\n",
        "    \"\"\"Ground state energy evaluation.\"\"\"\n",
        "    energy = (\n",
        "        estimator.run([(ansatz, hamiltonian, [params])]).result()[0].data.evs\n",
        "    )\n",
        "\n",
        "    cost_history_dict[\"iters\"] += 1\n",
        "    cost_history_dict[\"prev_vector\"] = list(params)\n",
        "    cost_history_dict[\"cost_history\"].append(float(energy[0]))\n",
        "\n",
        "    print(\n",
        "        f\"Fx Iters. done: {cost_history_dict['iters']} [Current cost: {round(energy[0], 5)}]\",\n",
        "        end=\"\\r\",\n",
        "    )\n",
        "\n",
        "    return energy\n",
        "\n",
        "\n",
        "def solve(x0, isa_ansatz, isa_observable, maxiter=150):\n",
        "    cost_history_dict = {\n",
        "        \"prev_vector\": None,\n",
        "        \"iters\": 0,\n",
        "        \"cost_history\": [],\n",
        "        \"y_min\": None,\n",
        "    }\n",
        "\n",
        "    # Evaluate the problem using a QPU via `qiskit-ibm-runtime`\n",
        "    with Session(backend=backend) as session:\n",
        "        estimator = EstimatorV2(mode=session)\n",
        "        estimator.skip_transpilation = True\n",
        "        estimator.options.environment.job_tags = [\"TUT_HSVQE\"]\n",
        "        x_opt = spsa(\n",
        "            cost_func,\n",
        "            x0=x0,\n",
        "            args=(isa_ansatz, isa_observable, estimator, cost_history_dict),\n",
        "            maxiter=maxiter,\n",
        "        )\n",
        "\n",
        "        y_min = cost_func(\n",
        "            x_opt, isa_ansatz, isa_observable, estimator, cost_history_dict\n",
        "        )\n",
        "\n",
        "    return y_min, cost_history_dict"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "f418b372",
      "metadata": {},
      "outputs": [],
      "source": [
        "np.random.seed(42)\n",
        "num_params = ansatz.num_parameters\n",
        "params = 2 * np.pi * np.random.random(num_params)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3bf42923",
      "metadata": {},
      "source": [
        "Hier legen wir die `maxiter = 50`. Beachten Sie, dass jede Iteration zwei Funktionsaufrufe zur Berechnung des Gradienten erfordert, sodass die Gesamtzahl der Funktionsaufrufe $2 \\times \\text{maxiter}$ beträgt. Der Wert `maxiter` kann für eine genauere Energieschätzung auf einen beliebig höheren Wert erhöht werden.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "1732ce37",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Fx Iters. done: 101 [Current cost: -3.03843]"
          ]
        }
      ],
      "source": [
        "maxiter = 50\n",
        "spsa_min, spsa_history = solve(\n",
        "    params, isa_ansatz, isa_observable, maxiter=maxiter\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "33abbb3f-6245-4610-a05d-e2bc4cc551f0",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "### Schritt 4: Nachbearbeitung und Rückgabe des Ergebnisses im gewünschten klassischen Format\n",
        "\n",
        "* Eingabe: Schätzungen der Grundzustandsenergie während der Optimierung\n",
        "* Ergebnis: Geschätzte Grundzustandsenergie\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "e5b58771-d543-4e75-9746-fbc7b28e4360",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Estimated ground state energy: [-3.03842968]\n"
          ]
        }
      ],
      "source": [
        "print(f\"Estimated ground state energy: {spsa_min}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "ecd7762a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/spin-chain-vqe/extracted-outputs/ecd7762a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "results = {\n",
        "    \"spsa\": spsa_history,\n",
        "}\n",
        "\n",
        "visualize_results(spsa_history)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a3f4171f",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## Beispiel für groß angelegte Hardware\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "746e6133",
      "metadata": {},
      "source": [
        "Ein Beispiel für eine groß angelegte Hardware-Lösung ist in diesem Tutorial nicht enthalten. Mit steigender Anzahl von Qubits steht die VQE aufgrund des [karge Hochebene](/learning/courses/variational-algorithm-design/optimization-loops#barren-plateaus) folgenden Phänomens vor erheblichen Herausforderungen: Der Gradient der Kostenfunktion nimmt mit zunehmender Systemgröße exponentiell ab, was die Optimierung bei großen Schaltkreisen praktisch unmöglich macht. In Verbindung mit Hardware-Rauschen bedeutet dies, dass die Skalierung von VQE auf längere Spinketten keine zuverlässig reproduzierbaren Ergebnisse liefert. Ansätze zur Überwindung dieser Einschränkungen finden Sie im Abschnitt „Nächste Schritte“ weiter unten.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8f14492c",
      "metadata": {},
      "source": [
        "<span id=\"challenge\" />\n",
        "\n",
        "## Herausforderung\n",
        "\n",
        "Nachdem Sie nun eine funktionierende VQE-Implementierung für die Heisenberg-Kette haben, versuchen Sie Folgendes:\n",
        "\n",
        "1. `reps=3`**Experimentieren Sie mit der Ansatz-Tiefe:** Ändern Sie den `reps` Parameter in `efficient_su2` (probieren `reps=1` Sie beispielsweise und aus). Wie wirkt sich die Ansatztiefe auf die geschätzte Grundzustandsenergie und die Konvergenzgeschwindigkeit aus? Ab wann stellen Sie abnehmende Erträge oder Instabilität fest?\n",
        "2. **SPSA-Hyperparameter optimieren:** Passen Sie die Parameter des Lernratenverlaufs (`a`, `c`, `alpha`, `gamma`, `A`) an und beobachten Sie, wie sich dies auf die Konvergenz auswirkt. Findest du eine Konfiguration, die schneller konvergiert als die hier verwendeten Standardeinstellungen?\n",
        "3. **Vergleichen Sie Kopplungstopologien:** Anstatt die native Kopplungskarte des Backends zu verwenden, versuchen Sie, eine einfache lineare Kette nach dem Prinzip der nächsten Nachbarn zu erstellen, und vergleichen Sie die Ergebnisse. Inwiefern wirkt sich die Konnektivität der physikalischen Hardware auf die Tiefe der transpilierten Schaltung und die endgültige Energieschätzung aus?\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "217a9379",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Referenzen\n",
        "\n",
        "\\[1] Spall, J. C. (2002). Implementierung des Algorithmus der simultanen Störung für die stochastische Optimierung.\n",
        "IEEE Transactions on Aerospace and Electronic Systems, 34(3), 817–823.\n",
        "\n",
        "\\[2] Sahin, M. Emre u. a. (2025). Qiskit- Machine Learning : eine Open-Source-Bibliothek für groß angelegte Aufgaben im Bereich des quantenbasierten maschinellen Lernens auf Quantenhardware und klassischen Simulatoren. arXiv:2505.17756.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8aefe54c",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Nächste Schritte\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Empfehlungen\">\n",
        "  Wenn Sie diese Arbeit interessant fanden, könnten Sie auch an folgenden Materialien interessiert sein:\n",
        "\n",
        "  * **Probieren Sie die samplebasierte Quantendiagonalisierung (SQD) aus:** Wie in diesem Tutorial gezeigt wurde, stößt VQE bei großem Maßstab aufgrund von „barren plateaus“ und hohem Messaufwand auf Herausforderungen. IBM hat [die „Sample-based Quantum Diagonalization“ (SQD)](/docs/addons/qiskit-addon-sqd) als skalierbarere Alternative entwickelt. Im Gegensatz zu VQE verzichtet SQD gänzlich auf die variationale Optimierung; stattdessen generiert ein Quantencomputer Stichproben, und ein klassischer Computer projiziert den Hamiltonoperator auf einen von diesen Stichproben aufgespannten Unterraum und diagonalisiert ihn. Dies liefert eine Obergrenze für die Grundzustandsenergie bei deutlich weniger Messungen und ohne Anfälligkeit für „barren plateaus“. Folgen Sie der [SQD-Anleitung,](/docs/tutorials/sample-based-quantum-diagonalization) um diesen Ansatz in der Praxis zu sehen.\n",
        "  * **Entdecken Sie den Kurs „Quantum Diagonalization Algorithms“:** Vertiefen Sie Ihr Verständnis von VQE und SQD, einschließlich ihrer Vor- und Nachteile, im Kurs [„Quantum Diagonalization Algorithms“](/learning/courses/quantum-diagonalization-algorithms) auf IBM Quantum Learning.\n",
        "</Admonition>\n",
        "\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"
    },
    "hours": 1.5,
    "qpuSeconds": 2220
  },
  "nbformat": 4,
  "nbformat_minor": 5
}