{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "1379da5d-023f-42e7-8a3f-ea8c1e3a1d46",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Beispielbasierte Krylov-Quanten-Diagonalisierung\"\n",
        "description: \"Die Themen der vorherigen Lektionen werden kombiniert, um die Konvergenzgarantien von QKD und die Geschwindigkeit von SQD in SKQD zu nutzen.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore arxiv Neel probs eigenvals Hatano */}\n",
        "\n",
        "<span id=\"sample-based-krylov-quantum-diagonalization-skqd\" />\n",
        "\n",
        "# Sample-basierte Krylov-Quanten-Diagonalisierung (SKQD)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "97cfd899-4196-4d24-bf16-60bdd3899600",
      "metadata": {},
      "source": [
        "Diese Lektion über Sample-basierte Krylov-Quanten-Diagonalisierung (SKQD) kombiniert Methoden, die in früheren Methoden erklärt wurden. Es besteht aus einem einzigen Beispiel, das das Qiskit-Muster-Framework nutzt:\n",
        "\n",
        "* Schritt 1: Abbildung des Problems auf Quantenschaltungen und -operatoren\n",
        "* Schritt 2: Optimieren für die Zielhardware\n",
        "* Schritt 3: Ausführung mithilfe von „ IBM Quantum “-Primitiven\n",
        "* Schritt 4: Nachbearbeitung\n",
        "\n",
        "Ein wichtiger Schritt bei der stichprobenbasierten Quantendiagonalisierungsmethode ist die Erzeugung von Qualitätsvektoren für den Unterraum. In der vorigen Lektion haben wir den LUCJ-Ansatz verwendet, um Unterraumvektoren für einen Chemie-Hamiltonian zu erzeugen. In dieser Lektion werden wir Quanten-Krylov-Zustände [\\[1\\]](#references) verwenden, wie sie in Lektion 2 besprochen wurden. Zunächst werden wir uns ansehen, wie man den Krylov-Raum auf einem Quantencomputer mit Hilfe von Zeitentwicklungsoperationen erzeugt. Daraus werden wir dann Proben entnehmen. Wir projizieren den System-Hamiltonianer auf den abgetasteten Unterraum und diagonalisieren ihn, um die Grundzustandsenergie zu schätzen. Der Algorithmus konvergiert nachweislich und effizient zum Grundzustand, unter den in Lektion 2 beschriebenen Annahmen.\n",
        "\n",
        "<span id=\"0-the-krylov-space\" />\n",
        "\n",
        "## 0. Der Krylov-Raum\n",
        "\n",
        "Ein Krylov-Raum $\\mathcal{K}^r$ der Ordnung $r$ ist der Raum, der von Vektoren aufgespannt wird, die durch Multiplikation höherer Potenzen einer Matrix $A$ bis zu $r-1$ mit einem Referenzvektor $\\vert v \\rangle$ erhalten werden.\n",
        "\n",
        "$$\n",
        "\\mathcal{K}^r = \\left\\{ \\vert v \\rangle, A \\vert v \\rangle, A^2 \\vert v \\rangle, ..., A^{r-1} \\vert v \\rangle \\right\\}\n",
        "$$\n",
        "\n",
        "Wenn die Matrix $A$ der Hamiltonian $H$ ist, wird der entsprechende Raum als *Potenz-Krylov-Raum* $\\mathcal{K}_P$ bezeichnet. Ist $A$ der Zeitentwicklungsoperator, der durch den Hamiltonianer $U=e^{-iH(dt)}$ erzeugt wird, so wird der Raum als unitärer Krylov-Raum $\\mathcal{K}_U$ bezeichnet. Der Potenz-Krylov-Unterraum kann nicht direkt auf einem Quantencomputer erzeugt werden, da $H$ kein unitärer Operator ist. Stattdessen können wir den Zeit-Evolutions-Operator $U = e^{-iH(dt)}$ verwenden, der nachweislich ähnliche [Konvergenzgarantien](https://arxiv.org/abs/2110.07492) bietet wie der Krylov-Powerraum. Potenzen von $U$ werden dann zu verschiedenen Zeitschritten $U^k = e^{-iH(k dt)}$, wobei $k = 0, 1, 2, ..., (r-1)$.\n",
        "\n",
        "$$\n",
        "\\mathcal{K}_U^r = \\left\\{ \\vert \\psi \\rangle, U \\vert \\psi \\rangle, U^2 \\vert \\psi \\rangle, ..., U^{r-1} \\vert \\psi \\rangle \\right\\}\n",
        "$$\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "864a0ce7-d094-48ea-9cf0-2a7e38791815",
      "metadata": {},
      "source": [
        "<span id=\"1-map-problem-to-quantum-circuits-and-operators\" />\n",
        "\n",
        "## 1. Problem auf Quantenschaltungen und Operatoren abbilden\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5888e3e8-51a1-426a-9023-c137aa9c6c83",
      "metadata": {},
      "source": [
        "In dieser Lektion betrachten wir den Hamiltonian für die antiferromagnetische XX-Z spin-1/2 Kette mit $L = 22$ Stellen mit der periodischen Randbedingung:\n",
        "\n",
        "$$\n",
        "    H = \\sum_{i, j}^{N} J_{xy} (X_{i} X_{j} + Y_{i} Y_{j}) + Z_{i} Z_{j}\n",
        "$$\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "f0d3e153-17da-4457-b1d2-2a35d31aea3a",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.transpiler import CouplingMap\n",
        "from qiskit_addon_utils.problem_generators import generate_xyz_hamiltonian\n",
        "\n",
        "num_spins = 22\n",
        "coupling_map = CouplingMap.from_ring(num_spins)\n",
        "H_op = generate_xyz_hamiltonian(coupling_map, coupling_constants=(0.3, 0.3, 1.0))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b0660e0d-5521-4095-a810-949729421b57",
      "metadata": {},
      "source": [
        "Um den Krylov-Raum zu konstruieren, benötigen wir drei Hauptbestandteile:\n",
        "\n",
        "1. Eine Wahl der Krylov-Dimension ( $r$ ) und des Zeitschritts ( $dt$ ).\n",
        "2. Ein Anfangszustand (Referenzzustand) (Vektor $\\vert v \\rangle$ oben) mit polynomialer Überlappung mit dem Zielzustand (Grundzustand), wobei der Zielzustand spärlich ist. Diese Anforderung an die polynomiale Überlappung ist dieselbe wie beim Algorithmus zur Quantenphasenschätzung.\n",
        "3. Operatoren der zeitlichen Entwicklung $U^{k}=e^{-iH(k * dt)}$ ( $k = 0, 1, 2, ..., r-1$ ).\n",
        "\n",
        "Für einen gewählten Wert von $r$ (und $dt$ ) werden wir $r$ separate Quantenschaltungen erstellen und daraus eine Stichprobe ziehen. Jeder Quantenkreis wird durch die Verknüpfung der Quantenkreisdarstellung des Referenzzustands und des Zeitentwicklungsoperators für einen $k$ Wert erstellt.\n",
        "\n",
        "Eine größere Krylov-Dimension verbessert die Konvergenz der geschätzten Energie. In dieser Lektion setzen wir die Dimension auf $5$, um den Konvergenztrend zu veranschaulichen.\n",
        "\n",
        "In [\\[2\\]](#references) wurde gezeigt, dass ein hinreichend kleiner Zeitschritt für KQD $\\pi / \\vert \\vert H \\vert \\vert$ ist und dass es besser ist, diesen Wert zu unterschätzen, als ihn zu überschätzen. Andererseits führt eine zu kleine Wahl von $dt$ zu einer schlechteren Konditionierung des Krylov-Unterraums, da sich die Krylov-Basisvektoren von Zeitschritt zu Zeitschritt weniger unterscheiden. Darüber hinaus ist diese Wahl von $dt$ zwar nachweislich angemessen für die Konvergenz von SKQD, aber in diesem auf Stichproben basierenden Kontext ist die optimale Wahl von $dt$ in der Praxis ein Thema, das derzeit untersucht wird. In dieser Lektion stellen wir $dt = 0.15$ ein.\n",
        "\n",
        "Neben der Krylov-Dimension und dem Zeitschritt müssen wir auch die Anzahl der Trotter-Schritte für die Zeitentwicklung festlegen. Die Verwendung von zu wenigen Schritten führt zu größeren Trotterisierungsfehlern, während zu viele Schritte zu tieferen Schaltungen führen. In dieser Lektion setzen wir die Anzahl der Trotterschritte auf $6$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "52eb15e3-e06c-4820-bad1-80fb7c05feee",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Set parameters for quantum Krylov algorithm\n",
        "krylov_dim = 5  # size of krylov subspace\n",
        "dt = 0.15\n",
        "num_trotter_steps = 6"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a716ea3e-b3d6-42de-8ed5-331e6416e586",
      "metadata": {},
      "source": [
        "Als Nächstes müssen wir einen Referenzzustand $\\vert \\psi \\rangle$ auswählen, der eine gewisse Überschneidung mit dem Grundzustand aufweist. Für diesen Hamiltonian verwenden wir den *Neel-Zustand* mit abwechselnden 1s und 0s $\\vert ...101...010...101 \\rangle$ als Referenzzustand.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "53347df7-b43c-4859-a9ea-a3dea2a44184",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Prep `Neel` state as the reference state for evolution\n",
        "from qiskit import QuantumCircuit\n",
        "\n",
        "qc_state_prep = QuantumCircuit(num_spins)\n",
        "for i in range(num_spins):\n",
        "    if i % 2 == 0:\n",
        "        qc_state_prep.x(i)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6952aaf2-c598-4ce9-8dc5-e63f0725de10",
      "metadata": {},
      "source": [
        "Schließlich müssen wir den Zeitentwicklungsoperator auf eine Quantenschaltung übertragen. Dies wurde in Lektion 2 getan, aber hier werden wir die Methoden in Qiskit nutzen, insbesondere eine Methode namens *Synthese*. Es gibt verschiedene Methoden zur Synthese von mathematischen Operatoren zu Quantenschaltungen mit Quantengattern. Viele solcher Techniken sind im [Qiskit-Synthesemodul](/docs/api/qiskit/synthesis) verfügbar. Wir verwenden den [`LieTrotter`](/docs/api/qiskit/qiskit.synthesis.LieTrotter) ansatz für die Synthese [\\[3\\]](#references) [\\[4\\]](#references).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "c82afe58-a90a-49a9-9dfe-c24b2b5d0a8c",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.circuit import QuantumRegister\n",
        "from qiskit.circuit.library import PauliEvolutionGate\n",
        "from qiskit.synthesis import LieTrotter\n",
        "\n",
        "evol_gate = PauliEvolutionGate(\n",
        "    H_op, time=(dt / num_trotter_steps), synthesis=LieTrotter(reps=num_trotter_steps)\n",
        ")  # `U` operator\n",
        "\n",
        "qr = QuantumRegister(num_spins)\n",
        "qc_evol = QuantumCircuit(qr)\n",
        "qc_evol.append(evol_gate, qargs=qr)\n",
        "\n",
        "circuits = []\n",
        "for rep in range(krylov_dim):\n",
        "    circ = qc_state_prep.copy()\n",
        "\n",
        "    # Repeating the `U` operator to implement U^0, U^1, U^2, and so on, for power Krylov space\n",
        "    for _ in range(rep):\n",
        "        circ.compose(other=qc_evol, inplace=True)\n",
        "\n",
        "    circ.measure_all()\n",
        "    circuits.append(circ)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "7ffc3eaa-9563-4c89-9d7b-808a3a37b275",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/quantum-diagonalization-algorithms/qda-5-skqd/extracted-outputs/7ffc3eaa-9563-4c89-9d7b-808a3a37b275-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circuits[1].decompose().draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "bdbc0881-06b0-49f2-8919-a376aa7f08e3",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/quantum-diagonalization-algorithms/qda-5-skqd/extracted-outputs/bdbc0881-06b0-49f2-8919-a376aa7f08e3-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 9,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circuits[2].decompose().draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9b43f693-b6d0-43cb-b982-3f8a3b6e185d",
      "metadata": {},
      "source": [
        "<span id=\"2-optimize-for-target-hardware\" />\n",
        "\n",
        "## 2. Für die Zielhardware optimieren\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6ed5a22f-4bdc-4a8d-ad39-5307308d0cc4",
      "metadata": {},
      "source": [
        "Nachdem wir nun die Schaltungen erstellt haben, können wir sie für eine Zielhardware optimieren. Wir entscheiden uns für eine QPU im Versorgungsmaßstab.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "8944305a-0f29-4f97-a04f-43249cdc02ea",
      "metadata": {},
      "outputs": [],
      "source": [
        "import warnings\n",
        "\n",
        "from qiskit import generate_preset_pass_manager\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "\n",
        "warnings.filterwarnings(\"ignore\")\n",
        "\n",
        "service = QiskitRuntimeService()\n",
        "# Use the least-busy backend or specify a quantum computer using the syntax commented out below.\n",
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "# backend = service.backend(\"ibm_brisbane\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3ed422ad-04f5-4f1f-a142-f08a0ac0a34b",
      "metadata": {},
      "source": [
        "Nun werden die Schaltkreise mithilfe eines voreingestellten Pass-Managers in das Ziel-Backend übertragen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "8a72b655-ccee-4927-8bf2-b99d0b92723a",
      "metadata": {},
      "outputs": [],
      "source": [
        "pm = generate_preset_pass_manager(backend=backend, optimization_level=3)\n",
        "isa_circuits = pm.run(circuits=circuits)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6405ab01-5cec-4a02-8846-f9ba626d9cdc",
      "metadata": {},
      "source": [
        "<span id=\"3-execute-on-target-hardware\" />\n",
        "\n",
        "## 3. Auf der Zielhardware ausführen\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6aa67e8c-30db-4e11-9bf2-150c157ec49c",
      "metadata": {},
      "source": [
        "Nach der Optimierung der Schaltungen für die Hardware-Ausführung können wir sie auf der Zielhardware ausführen und Proben für die Abschätzung der Grundzustandsenergie sammeln.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "0613c95b-e696-4ddb-8f51-dc1714d21a0e",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_ibm_runtime import SamplerV2 as Sampler\n",
        "\n",
        "sampler = Sampler(mode=backend)\n",
        "job = sampler.run(isa_circuits, shots=100_000)  # Takes approximately 2m 58s of QPU time"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "1c60e828-9359-46c6-84ef-88aecdb85b81",
      "metadata": {},
      "outputs": [],
      "source": [
        "counts_all = [job.result()[k].data.meas.get_counts() for k in range(krylov_dim)]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fa2fcfe0-66ab-4230-9262-9a46ecda034b",
      "metadata": {},
      "source": [
        "<span id=\"4-post-process-results\" />\n",
        "\n",
        "## 4. Nachbearbeitungsergebnisse\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "75321a43-0740-4d13-bdc4-350b44727b62",
      "metadata": {},
      "source": [
        "Als Nächstes werden die Zählungen für zunehmende Krylov-Dimensionen kumulativ zusammengefasst. Unter Verwendung der kumulativen Zählungen werden wir Unterräume für zunehmende Krylov-Dimensionen aufspannen und das Konvergenzverhalten analysieren.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "9954125c-5ad8-40e8-affa-4a644354c51f",
      "metadata": {},
      "outputs": [],
      "source": [
        "from collections import Counter\n",
        "\n",
        "counts_cumulative = []\n",
        "for i in range(krylov_dim):\n",
        "    counter = Counter()\n",
        "    for d in counts_all[: i + 1]:\n",
        "        counter.update(d)\n",
        "\n",
        "    counts = dict(counter)\n",
        "    counts_cumulative.append(counts)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "14fcdfc2-b37e-4a9c-9794-7c1fa9a6ab1b",
      "metadata": {},
      "source": [
        "Für die Projektion und Diagonalisierung der Hamiltonfunktion nutzen wir die Fähigkeiten von [`qiskit-addon-sqd`](https://qiskit.github.io/qiskit-addon-sqd/). Das Addon bietet Funktionen, um Pauli-String-basierte Hamiltonianer auf einen Unterraum zu projizieren und Eigenwerte mit `SciPy` zu lösen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "5ace841d-59c6-4ea1-9ceb-6f6436546093",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_addon_sqd.counts import counts_to_arrays\n",
        "from qiskit_addon_sqd.qubit import solve_qubit"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "104cc0fd-4fa0-469b-bfb8-4089cea5145f",
      "metadata": {},
      "source": [
        "Im Prinzip können wir Bitstrings mit falschen Mustern herausfiltern, bevor wir den Unterraum aufspannen. Der Grundzustand für den antiferromagnetischen Hamiltonian in dieser Lektion hat typischerweise eine gleiche Anzahl von \"Up\"- und \"Down\"-Spins, d. h. die Anzahl der \"1 \"s in der Bitkette sollte genau die Hälfte der Gesamtzahl der Bits (Spins) im System betragen. Die folgende Funktion filtert Bitstrings mit einer falschen Anzahl von \"1 \"s aus den Zählungen heraus.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 37,
      "id": "81d728a1-6ab4-4664-a7ca-2017dae10e1a",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Filters out bitstrings that do not have specified number (`num_ones`) of `1` bits.\n",
        "def postselect_counts(counts, num_ones):\n",
        "    filtered_counts = {}\n",
        "    for bitstring, freq in counts.items():\n",
        "        if bitstring.count(\"1\") == num_ones:\n",
        "            filtered_counts[bitstring] = freq\n",
        "\n",
        "    return filtered_counts"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d77772f5-fe1c-4a6c-abcb-c2c3c7d7293a",
      "metadata": {},
      "source": [
        "Unter Verwendung von Bitstrings mit der korrekten Anzahl von Up/Down-Elektronen werden Unterräume aufgespannt und Eigenwerte für zunehmende Krylov-Dimensionen berechnet. Abhängig von der Größe des Problems und den verfügbaren klassischen Ressourcen müssen wir möglicherweise eine Unterabtastung vornehmen (ähnlich wie in der [Lektion über SQD](/learning/courses/quantum-diagonalization-algorithms/sqd-implementation) ), um die Dimension des Unterraums in Schach zu halten. Außerdem können wir den Begriff der Konfigurationswiederherstellung ähnlich wie in Lektion 4 anwenden. Aus den rekonstruierten Eigenzuständen können wir die Elektronenbelegung pro Stelle berechnen und die Informationen nutzen, um Bitstrings mit einer falschen Anzahl von Up/Down-Elektronen zu korrigieren. Wir überlassen dies den interessierten Lesern als Übung.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 38,
      "id": "f0e0f56f-ec56-40b6-b8be-059be35d2ce5",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "\n",
        "num_batches = 10\n",
        "rand_seed = 0\n",
        "scipy_kwargs = {\"k\": 2, \"which\": \"SA\"}\n",
        "\n",
        "ground_state_energies = []\n",
        "for idx, counts in enumerate(counts_cumulative):\n",
        "    counts = postselect_counts(counts, num_ones=num_spins // 2)\n",
        "    bitstring_matrix, probs = counts_to_arrays(counts=counts)\n",
        "\n",
        "    eigenvals, eigenstates = solve_qubit(\n",
        "        bitstring_matrix, H_op, verbose=False, **scipy_kwargs\n",
        "    )\n",
        "    gs_en = np.min(eigenvals)\n",
        "    ground_state_energies.append(gs_en)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "67b2a04b-4e70-45dc-bd21-2c4340c18377",
      "metadata": {},
      "source": [
        "Anschließend wird die berechnete Energie als Funktion der Krylov-Dimension dargestellt und mit der exakten Energie verglichen. Die genaue Energie wird separat mit einer klassischen Brute-Force-Methode berechnet. Es ist zu erkennen, dass die geschätzte Grundzustandsenergie mit zunehmender Krylov-Raumdimension konvergiert. Obwohl die Krylov-Dimension von $5$ begrenzt ist, zeigen die Ergebnisse dennoch eine beeindruckende Konvergenz, die sich mit einer größeren Krylov-Dimension verbessern dürfte [\\[1\\]](#references).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 39,
      "id": "debca96a-e0e3-4342-a755-8204dba50d74",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/quantum-diagonalization-algorithms/qda-5-skqd/extracted-outputs/debca96a-e0e3-4342-a755-8204dba50d74-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "\n",
        "exact_gs_en = -23.934184\n",
        "plt.plot(\n",
        "    range(1, krylov_dim + 1),\n",
        "    ground_state_energies,\n",
        "    color=\"blue\",\n",
        "    linestyle=\"-.\",\n",
        "    label=\"estimate\",\n",
        ")\n",
        "plt.plot(\n",
        "    range(1, krylov_dim + 1),\n",
        "    [exact_gs_en] * krylov_dim,\n",
        "    color=\"red\",\n",
        "    linestyle=\"-\",\n",
        "    label=\"exact\",\n",
        ")\n",
        "plt.xticks(range(1, krylov_dim + 1), range(1, krylov_dim + 1))\n",
        "plt.legend()\n",
        "plt.xlabel(\"Krylov space dimension\")\n",
        "plt.ylabel(\"Energy\")\n",
        "plt.ylim([-24, -22.50])\n",
        "plt.title(\n",
        "    \"Estimating Ground state energy with Sample-based Krylov Quantum Diagonalization\"\n",
        ")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5865567b-cc32-48dd-b7f9-32ce64d7dd7c",
      "metadata": {},
      "source": [
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Überprüfen Sie Ihr Verständnis\n",
        "\n",
        "Lies die folgenden Fragen, denke über deine Antworten nach und klicke dann auf die Dreiecke, um die Lösungen zu finden.\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Was könnte getan werden, um die Konvergenz in der obigen Grafik zu verbessern?\">\n",
        "    **Antwort:**\n",
        "\n",
        "    Erhöhen Sie die Krylov-Dimension. Im Allgemeinen könnte man auch die Anzahl der Schüsse erhöhen, aber diese ist in der obigen Berechnung bereits recht hoch.\n",
        "  </AccordionItem>\n",
        "\n",
        "  <AccordionItem title=\"Was sind die Hauptvorteile von SKQD gegenüber (a) SQD und (b) KQD?\">\n",
        "    **Antwort:**\n",
        "\n",
        "    Es kann auch andere gültige Antworten geben, aber vollständige Antworten sollten Folgendes beinhalten:\n",
        "\n",
        "    (a) SKQD verfügt über Konvergenzgarantien, die SQD nicht hat. Bei der SQD müssen Sie entweder eine sehr gute Schätzung für Ihren Ansatz vornehmen, die eine hervorragende Überlappung mit der Grundzustandsunterstützung in der Berechnungsbasis aufweist, oder Sie müssen eine Variationskomponente in die Berechnung einführen, um eine Familie von Ansätzen zu wählen.\n",
        "\n",
        "    (b) SKQD benötigt viel weniger QPU-Zeit, da es die kostspielige Berechnung von Matrixelementen über den Hadamard-Test vermeidet.\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a4cf119d-65cf-456c-be0d-731bb7a6799a",
      "metadata": {},
      "source": [
        "<span id=\"5-summary\" />\n",
        "\n",
        "## 5. Zusammenfassung\n",
        "\n",
        "* Die Abschätzung der Grundzustandsenergie durch Abtasten von Krylov-Basiszuständen eignet sich sehr gut für Gittermodelle, einschließlich Spinsysteme, Probleme mit kondensierter Materie und Gittereichtheorien. Dieser Ansatz lässt sich viel besser skalieren als VQE, da er keine Optimierung über viele Parameter in einem Variationsansatz wie in VQE oder in heuristischer, auf Ansätzen basierender SQD (z. B. das Chemieproblem in der vorherigen Lektion) erfordert.\n",
        "  * Um die Schaltungstiefe gering zu halten, ist es ratsam, sich mit Gitterproblemen zu befassen, die sich für eine vorfehlertolerante Hardware eignen.\n",
        "* Bei SKQD gibt es kein Quantenmessungsproblem wie bei VQE. Es gibt keine Gruppen von pendelnden Pauli-Operatoren, die geschätzt werden müssen.\n",
        "* SKQD ist robust gegenüber verrauschten Stichproben, da man entweder eine aufgabenspezifische Nachselektionsroutine verwenden kann (beispielsweise um Bitfolgen herauszufiltern, die nicht den aufgabenspezifischen Mustern entsprechen) oder den klassischen Diagonalisierungsaufwand in Kauf nehmen kann (d. h. die Diagonalisierung in einem größeren Unterraum durchführen), um den Einfluss des Rauschens wirksam zu beseitigen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c52bada7-1912-41f2-a770-3145a947de63",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Referenzen\n",
        "\n",
        "\\[1] Jeffery Yu et al, \"Quantenzentrierter Algorithmus für stichprobenbasierte Krylov-Diagonalisierung\" (2025). [arxiv:quant-ph/2501.09702](https://arxiv.org/abs/2501.09702).\n",
        "\n",
        "\\[2] Ethan N. Epperly, Lin Lin, und Yuji Nakatsukasa. \"Eine Theorie der Quantenunterraumdiagonalisierung\". SIAM Journal on Matrix Analysis and Applications 43, 1263-1290 (2022).\n",
        "\n",
        "\\[2] N. Hatano und M. Suzuki, \"Finden von Exponentialproduktformeln höherer Ordnungen\" (2005). [arXiv:math-ph/0506007](https://arxiv.org/abs/math-ph/0506007).\n",
        "\n",
        "\\[4] D. Berry, G. Ahokas, R. Cleve und B. Sanders, \"Effiziente Quantenalgorithmen für die Simulation von spärlichen Hamiltonianern\" (2006). [arXiv:quant-ph/0508139](https://arxiv.org/abs/quant-ph/0508139).\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"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 4
}