{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Schnellstart\"\n",
        "description: \"Schnellstart für die neueste Version der Sample-based Quantum Diagonalization (SQD)\"\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-addon-sqd` Paket. Wir verwenden die sample-basierte Quantendiagonalisierung (SQD), um verrauschte Stichproben zu verfeinern, die aus einem Quantenschaltkreis stammen, der den Grundzustand eines chemischen Hamilton-Operators approximiert. Die Grundzustandsenergien werden durch Diagonalisierung des Hamilton-Operators in Unterräumen approximiert, die durch die verfeinerten Stichproben gebildet werden.\n",
        "\n",
        "Weitere anschauliche End-to-End-Workflows, die auf Quantenhardware ausgeführt werden, finden Sie in den Tutorials:\n",
        "\n",
        "* [Sample-basierte Quantendiagonalisierung](/docs/tutorials/sample-based-quantum-diagonalization) : Arbeitsablauf für den chemischen Hamilton-Operator\n",
        "* [Sample-basierte Krylov-Quanten-Diagonalisierung](/docs/tutorials/sample-based-krylov-quantum-diagonalization) : Arbeitsablauf für ein fermionisches Gittermodell\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "prepare-header",
      "metadata": {},
      "source": [
        "<span id=\"1-prepare-the-inputs-for-sqd\" />\n",
        "\n",
        "## 1. Bereiten Sie die Eingaben für SQD vor\n",
        "\n",
        "Zunächst definieren wir den chemischen Hamilton-Operator und den Quantenschaltkreis, der dessen Grundzustand approximiert. Das in diesem Beispiel untersuchte System ist N₂ in Gleichgewichtsgeometrie auf der „ STO-3G “-Basis mit zwei eingefrorenen Kernorbitalen. Dieses System verfügt über `8` räumliche Orbitalen und `10` Elektronen, was bedeutet, dass der Quantenschaltkreis auf `16` Qubits definiert ist, wobei die Qubits `0-7` die Orbitalen mit Spin-up und die Qubits `8-15` die Orbitalen mit Spin-down darstellen. `5``10`Da es sich um ein System mit geschlossener Schale und `10` Elektronen handelt, wissen wir, dass jede physikalisch gültige Probe ein Hamming-Gewicht von haben sollte, wobei das Hamming-Gewicht sowohl der linken als auch der rechten Hälfte der Bitfolge beträgt.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "load-molecule",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Spatial orbitals: 8\n",
            "Qubits: 16\n",
            "Electrons (alpha, beta): (5, 5)\n"
          ]
        }
      ],
      "source": [
        "from pyscf import ao2mo, gto, mcscf, scf\n",
        "\n",
        "# Construct the Hamiltonian, pull out integrals and other information about the system\n",
        "mol = gto.M(\n",
        "    atom=\"N 0 0 0; N 0 0 1.09768\", basis=\"sto-3g\", spin=0, charge=0, verbose=0\n",
        ")\n",
        "mf = scf.RHF(mol).run()\n",
        "cas = mcscf.CASCI(mf, ncas=8, nelecas=10)\n",
        "hcore, nuclear_repulsion_energy = cas.get_h1eff()\n",
        "eri = cas.get_h2eff()\n",
        "num_orbitals = cas.ncas\n",
        "eri = ao2mo.restore(1, eri, num_orbitals)\n",
        "n_alpha, n_beta = cas.nelecas\n",
        "nelec = (n_alpha, n_beta)\n",
        "\n",
        "print(\n",
        "    f\"Spatial orbitals: {num_orbitals}\\nQubits: {num_orbitals * 2}\\nElectrons (alpha, beta): {nelec}\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "samples-explanation",
      "metadata": {},
      "source": [
        "<span id=\"2-generate-noisy-samples\" />\n",
        "\n",
        "## 2. Erzeugen Sie mit Rauschen versehene Stichproben\n",
        "\n",
        "In einem realen Arbeitsablauf würde man eine Ansatzschaltung entwerfen, diese für die Hardware optimieren und sie auf der QPU abtasten. Hier lassen wir das weg und generieren `10,000` stattdessen gleichverteilte Zufallsbitstrings. Obwohl unsere Stichproben keine Informationen über den tatsächlichen Grundzustand enthalten, werden wir sehen, dass das SQD-Protokoll den wahren Grundzustand dennoch rekonstruieren kann, indem es diese Stichproben iterativ verfeinert, diagonalisiert und die Energien jeder Iteration nutzt, um die nächste Runde der Bitstring-Verfeinerung zu steuern. Auf diese Weise können wir letztendlich den exakten Grundzustand wiederherstellen, da der Unterraum, der den Grundzustand enthält, klein genug ist, um exakt berechnet zu werden. `5`Da wir insbesondere wissen, dass jede Hälfte jeder Bitfolge (`8` bits) das Hamming-Gewicht haben sollte, wissen wir, dass der gesamte Hilbert-Unterraum des Systems die Größe $\\binom{8}{5}^2=3136$ hat.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "generate-samples",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Generated 10000 uniformly-random, 16-qubit samples.\n"
          ]
        }
      ],
      "source": [
        "import numpy as np\n",
        "from qiskit_addon_sqd.counts import generate_bit_array_uniform\n",
        "\n",
        "# Generate some uniformly-random bitstrings to simulate QPU samples\n",
        "rng = np.random.default_rng(24)\n",
        "bit_array = generate_bit_array_uniform(\n",
        "    10_000, num_orbitals * 2, rand_seed=rng\n",
        ")\n",
        "print(\n",
        "    f\"Generated {bit_array.num_shots} uniformly-random, {bit_array.num_bits}-qubit samples.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "postprocess-header",
      "metadata": {},
      "source": [
        "<span id=\"3-post-process-with-sqd\" />\n",
        "\n",
        "## 3. Nachbearbeitung mit SQD\n",
        "\n",
        "Das Ziel des SQD-Algorithmus besteht darin, verrauschte Stichproben aus einer QPU zu verfeinern und schließlich den Hamilton-Operator in einem Unterraum zu diagonalisieren, der durch diese verfeinerten Stichproben aufgespannt wird. Dies erfolgt iterativ wie folgt:\n",
        "\n",
        "* Verfeinern Sie die Stichproben, indem Sie im Anschluss nur diejenigen auswählen, die sowohl für das Spin-up- als auch für das Spin-down-Teilsystem die korrekte Partikelanzahl aufweisen\n",
        "* Führen Sie eine Diagonalisierung in kleinen Unterräumen durch, die durch die verfeinerten Stichproben aufgespannt werden, und sammeln Sie Informationen über die Unterräume, die die besten (niedrigsten) Energien liefern.\n",
        "* Bei jeder Iteration:\n",
        "  * Verfeinern Sie die verrauschten Messwerte, indem Sie Bits in Messwerten mit falscher Partikelanzahl probabilistisch umkehren. Die Wahrscheinlichkeiten werden anhand der Unterrauminformationen aus der letzten Diagonalisierung berechnet.\n",
        "  * Diagonalisierung in kleinen Unterräumen, die durch die verfeinerten Stichproben aufgespannt werden\n",
        "  * Sammeln Sie Informationen über die Unterräume, die die besten (niedrigsten) Energien liefern\n",
        "\n",
        "Aus der Ausgabe geht hervor, dass der Algorithmus nach `6` Iterationen zur exakten Grundzustandsenergie konvergiert und dass die Diagonalisierung über den gesamten Hilbert-Unterraum der Dimension $\\binom{8}{5}^2=3136$ durchgeführt wird. Natürlich ist eine Diagonalisierung über den gesamten Unterraum in der Regel nicht durchführbar, weshalb Systeme mit Grundzuständen, die einen spärlichen Trägerbereich aufweisen, am ehesten von Unterraum-Diagonalisierungstechniken wie SQD profitieren.\n",
        "\n",
        "Weitere Informationen zur Funktion „diagonalize\\_fermionic\\_hamiltonian“ finden Sie in der [API-Dokumentation](/docs/api/qiskit-addon-sqd/fermion#diagonalize_fermionic_hamiltonian).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "run-sqd",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Exact (FCI) reference energy: -107.652521 Ha\n",
            "\n",
            "Iteration 1\n",
            "  Subsample 0\n",
            "    Energy: -106.908481\n",
            "    Subspace dimension: 784\n",
            "    Error vs exact: 0.744040 Ha\n",
            "Iteration 2\n",
            "  Subsample 0\n",
            "    Energy: -106.934921\n",
            "    Subspace dimension: 1764\n",
            "    Error vs exact: 0.717600 Ha\n",
            "Iteration 3\n",
            "  Subsample 0\n",
            "    Energy: -106.953430\n",
            "    Subspace dimension: 2209\n",
            "    Error vs exact: 0.699091 Ha\n",
            "Iteration 4\n",
            "  Subsample 0\n",
            "    Energy: -107.651946\n",
            "    Subspace dimension: 2916\n",
            "    Error vs exact: 0.000575 Ha\n",
            "Iteration 5\n",
            "  Subsample 0\n",
            "    Energy: -107.652003\n",
            "    Subspace dimension: 3025\n",
            "    Error vs exact: 0.000518 Ha\n",
            "Iteration 6\n",
            "  Subsample 0\n",
            "    Energy: -107.652521\n",
            "    Subspace dimension: 3136\n",
            "    Error vs exact: 0.000000 Ha\n",
            "Iteration 7\n",
            "  Subsample 0\n",
            "    Energy: -107.652521\n",
            "    Subspace dimension: 3136\n",
            "    Error vs exact: 0.000000 Ha\n"
          ]
        }
      ],
      "source": [
        "from functools import partial\n",
        "\n",
        "from pyscf import fci\n",
        "from qiskit_addon_sqd.fermion import (\n",
        "    SCIResult,\n",
        "    diagonalize_fermionic_hamiltonian,\n",
        "    solve_sci_batch,\n",
        ")\n",
        "\n",
        "exact_energy, _ = fci.direct_spin1.kernel(\n",
        "    hcore, eri, int(num_orbitals), nelec\n",
        ")\n",
        "exact_energy += nuclear_repulsion_energy\n",
        "print(f\"Exact (FCI) reference energy: {exact_energy:.6f} Ha\\n\")\n",
        "\n",
        "sci_solver = partial(solve_sci_batch, spin_sq=0.0, max_cycle=200)\n",
        "\n",
        "result_history = []\n",
        "\n",
        "\n",
        "def callback(results: list[SCIResult]):\n",
        "    result_history.append(results)\n",
        "    iteration = len(result_history)\n",
        "    print(f\"Iteration {iteration}\")\n",
        "    for i, result in enumerate(results):\n",
        "        error = result.energy + nuclear_repulsion_energy - exact_energy\n",
        "        print(f\"  Subsample {i}\")\n",
        "        print(f\"    Energy: {result.energy + nuclear_repulsion_energy:.6f}\")\n",
        "        print(\n",
        "            f\"    Subspace dimension: {np.prod(result.sci_state.amplitudes.shape)}\"\n",
        "        )\n",
        "        print(f\"    Error vs exact: {error:.6f} Ha\")\n",
        "\n",
        "\n",
        "samples_per_batch = 20\n",
        "result = diagonalize_fermionic_hamiltonian(\n",
        "    hcore,\n",
        "    eri,\n",
        "    bit_array,\n",
        "    samples_per_batch=samples_per_batch,\n",
        "    norb=num_orbitals,\n",
        "    nelec=nelec,\n",
        "    occupancies_tol=1e-7,\n",
        "    max_iterations=30,\n",
        "    sci_solver=sci_solver,\n",
        "    symmetrize_spin=True,\n",
        "    callback=callback,\n",
        "    seed=np.random.default_rng(32),\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
}