{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "e0cf0747",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Variationale Quantenalgorithmen\"\n",
        "description: \"Dieses Tutorial bietet einen Überblick über einen hybriden quantenklassischen Algorithmus, VQE und QAOA\"\n",
        "---\n",
        "\n",
        "<span id=\"quantum-algorithms-variational-quantum-algorithms\" />\n",
        "\n",
        "# Quantenalgorithmen: Variationale Quantenalgorithmen\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  Takashi Imamichi (24. Mai 2024)\n",
        "\n",
        "  [Laden Sie die PDF-Datei](https://ibm.ent.box.com/s/blnffu0pd7yzxarq3zc3w0jv90365ny2) der Originalvorlesung herunter. Beachten Sie, dass einige Codeschnipsel veraltet sein könnten, da es sich um statische Bilder handelt.\n",
        "\n",
        "  *Die ungefähre QPU-Zeit für die Durchführung dieses Experiments beträgt 9 Minuten (getestet auf einem Eagle-Prozessor).*\n",
        "\n",
        "  (Dieses Notebook kann möglicherweise nicht in der für den offenen Plan vorgesehenen Zeit bewertet werden. Bitte verwenden Sie Quantencomputer-Ressourcen mit Bedacht)\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "59fbaadc",
      "metadata": {},
      "source": [
        "<span id=\"1-introduction\" />\n",
        "\n",
        "## 1. Einführung\n",
        "\n",
        "Dieses Tutorial gibt einen Überblick über einen hybriden quantenklassischen Algorithmus, wobei der Schwerpunkt auf dem Variations-Quanten-Eigensolver (VQE) und dem Quanten-Approximationsalgorithmus (QAOA) liegt. Das Hauptziel dieser Algorithmen ist die Lösung von Optimierungsproblemen durch den Einsatz von Quantenschaltungen mit parametrisierten Quantengattern.\n",
        "\n",
        "Trotz der Fortschritte in der Quanteninformatik ist es aufgrund des Rauschens in aktuellen Quantengeräten schwierig, aussagekräftige Ergebnisse aus tiefen Quantenschaltungen zu gewinnen. Um diese Herausforderung zu meistern, verfolgen VQE und QAOA einen hybriden quantenklassischen Ansatz, bei dem relativ kurze Quantenschaltungen mit Hilfe von Quantenberechnungen iterativ ausgeführt und die Parameter der parametrisierten Zielquantenschaltungen mit Hilfe klassischer Berechnungen optimiert werden.\n",
        "\n",
        "QAOA hat das Potenzial, optimale Lösungen für die Zielprobleme auf der Ebene der Versorgungsunternehmen zu liefern, dank der Anwendung verschiedener Techniken zur Fehlerbegrenzung und -unterdrückung. VQE hat viele Anwendungen (wie die Quantenchemie), bei denen es weniger skalierbar ist. Es gibt jedoch eine Reihe von eigenwertbezogenen Ansätzen, die die VQE ergänzen und erweitern, darunter die Krylov-Unterraumdiagonalisierung und die Sampling-basierte Quantendiagonalisierung (SQD). Das Verständnis der VQE ist ein wichtiger erster Schritt, um die breite Palette der entstandenen klassisch-quantischen Hybridalgorithmen zu verstehen.\n",
        "\n",
        "Dieses Modul beschreibt die grundlegenden Konzepte und die Implementierung von VQE und QAOA. In weiteren Tutorien werden fortgeschrittene Themen und Techniken zur Skalierung dieser Algorithmen behandelt.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d3f4500f",
      "metadata": {},
      "source": [
        "Sie benötigen die folgende Bibliothek in Ihrer Umgebung, um dieses Notizbuch auszuführen.\n",
        "Wenn Sie es noch nicht installiert haben, können Sie es installieren, indem Sie den Kommentar entfernen und die folgende Zelle ausführen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2a8e650f",
      "metadata": {},
      "outputs": [],
      "source": [
        "# % pip install 'qiskit[visualization]' qiskit-ibm-runtime"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "be601762",
      "metadata": {},
      "source": [
        "<span id=\"2-computing-the-minimum-eigenvalue-of-a-simple-hamiltonian\" />\n",
        "\n",
        "## 2. Berechnung des minimalen Eigenwerts eines einfachen Hamilton-Operators\n",
        "\n",
        "Wir beginnen mit der Anwendung von VQE auf einen sehr einfachen Fall, um zu sehen, wie es funktioniert. Wir werden den minimalen Eigenwert der Pauli-Matrix $Z$ mit VQE berechnen. Wir beginnen mit dem Import einiger allgemeiner Pakete.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "e8f398b2",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "from qiskit.circuit import ParameterVector, QuantumCircuit\n",
        "from qiskit.primitives import StatevectorEstimator, StatevectorSampler\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from scipy.optimize import minimize"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d4395012",
      "metadata": {},
      "source": [
        "Wir definieren nun den interessierenden Operator und betrachten ihn in Matrixform.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "a4f374a2",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[ 1.+0.j,  0.+0.j],\n",
              "       [ 0.+0.j, -1.+0.j]])"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "op = SparsePauliOp(\"Z\")\n",
        "op.to_matrix()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0a001e71",
      "metadata": {},
      "source": [
        "Es ist einfach, die Eigenwerte klassisch zu ermitteln, so dass wir unsere Arbeit überprüfen können. Dies könnte sich als schwierig erweisen, wenn wir uns in Richtung Nutzwert entwickeln. Hier verwenden wir numpy.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "c9188662",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Eigenvalues: [-1.  1.]\n"
          ]
        }
      ],
      "source": [
        "# compute eigenvalues with numpy\n",
        "result = np.linalg.eigh(op.to_matrix())\n",
        "print(\"Eigenvalues:\", result.eigenvalues)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8f9af345",
      "metadata": {},
      "source": [
        "Um Eigenwerte mit Hilfe eines Quantenvariationsalgorithmus zu erhalten, konstruieren wir einen Schaltkreis mit Gattern, die Variationsparameter annehmen:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "99d5d36b",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/99d5d36b-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# define a variational form\n",
        "param = ParameterVector(\"a\", 3)\n",
        "qc = QuantumCircuit(1, 1)\n",
        "qc.u(param[0], param[1], param[2], 0)\n",
        "qc_estimator = qc.copy()\n",
        "qc.measure(0, 0)\n",
        "qc.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9607ab51",
      "metadata": {},
      "source": [
        "Wenn wir den Erwartungswert eines Operators (wie $Z$ ) schätzen wollen, sollten wir Estimator verwenden. Wenn wir uns die Zustände des Systems ansehen wollen, verwenden wir Sampler.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "df8ed8d3",
      "metadata": {},
      "outputs": [],
      "source": [
        "sampler = StatevectorSampler()\n",
        "estimator = StatevectorEstimator()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "880bccfb",
      "metadata": {},
      "source": [
        "Wir können die Anzahl der Bitstrings 0 und 1 mit zufälligen Parameterwerten `[1, 2, 3]` mit Sampler berechnen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "5301ee71",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'0': 783, '1': 241}"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# compute counts of bitstrings with random parameter values by Sampler\n",
        "result = sampler.run([(qc, [1, 2, 3])]).result()\n",
        "counts = result[0].data.c.get_counts()\n",
        "counts"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "30ef9b8f",
      "metadata": {},
      "source": [
        "Wir wissen, dass wir den Erwartungswert von Z durch $\\langle Z \\rangle = p_0 - p_1$ mit Wahrscheinlichkeiten $\\{0: p_0, 1: p_1\\}$ berechnen können.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "0f220c8d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "0.529296875"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# compute the expectation value of Z based on the counts\n",
        "(counts.get(\"0\", 0) - counts.get(\"1\", 0)) / sum(counts.values())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "695ee4bd",
      "metadata": {},
      "source": [
        "Diese Schaltung funktionierte, aber die gewählten Parameterwerte entsprachen nicht einem Zustand mit sehr niedriger Energie (oder niedrigem Eigenwert). Der erhaltene Eigenwert ist um einiges höher als das Minimum. Das Ergebnis ist ähnlich, wenn der Schätzer verwendet wird.\n",
        "\n",
        "Beachten Sie, dass Estimator Quantenschaltungen ohne Messungen verwendet.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "c9e05530",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array(0.54030231)"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "result = estimator.run([(qc_estimator, op, [1, 2, 3])]).result()\n",
        "result[0].data.evs"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b538e8b5",
      "metadata": {},
      "source": [
        "Wir müssen die Parameter durchsuchen und diejenigen finden, die den niedrigsten Eigenwert ergeben.\n",
        "Wir erstellen eine Funktion, die die Parameterwerte der Variationsform aufnimmt und den Erwartungswert $\\langle Z \\rangle$ zurückgibt.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "97fead46",
      "metadata": {},
      "outputs": [],
      "source": [
        "# define a cost function to look for the minimum eigenvalue of Z\n",
        "def cost(x):\n",
        "    result = sampler.run([(qc, x)]).result()\n",
        "    counts = result[0].data.c.get_counts()\n",
        "    expval = (counts.get(\"0\", 0) - counts.get(\"1\", 0)) / sum(counts.values())\n",
        "    # the following line shows the trajectory of the optimization\n",
        "    print(expval, counts)\n",
        "    return expval"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a97eaeba",
      "metadata": {},
      "source": [
        "Wenden wir die Funktion SciPy's `minimize` an, um den minimalen Eigenwert von Z zu finden.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "44f56300",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "1.0 {'0': 1024}\n",
            "0.494140625 {'0': 765, '1': 259}\n",
            "0.466796875 {'0': 751, '1': 273}\n",
            "0.564453125 {'0': 801, '1': 223}\n",
            "-0.4296875 {'1': 732, '0': 292}\n",
            "-0.984375 {'1': 1016, '0': 8}\n",
            "-0.8984375 {'1': 972, '0': 52}\n",
            "-0.990234375 {'1': 1019, '0': 5}\n",
            "-0.892578125 {'1': 969, '0': 55}\n",
            "-0.986328125 {'1': 1017, '0': 7}\n",
            "-0.861328125 {'1': 953, '0': 71}\n",
            "-1.0 {'1': 1024}\n",
            "-0.982421875 {'1': 1015, '0': 9}\n",
            "-0.99609375 {'1': 1022, '0': 2}\n",
            "-0.986328125 {'1': 1017, '0': 7}\n",
            "-1.0 {'1': 1024}\n",
            "-0.990234375 {'1': 1019, '0': 5}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.99609375 {'1': 1022, '0': 2}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-1.0 {'1': 1024}\n",
            "-0.99609375 {'1': 1022, '0': 2}\n",
            "-1.0 {'1': 1024}\n",
            "-0.99609375 {'1': 1022, '0': 2}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.99609375 {'1': 1022, '0': 2}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.99609375 {'1': 1022, '0': 2}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.994140625 {'1': 1021, '0': 3}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              " message: Optimization terminated successfully.\n",
              " success: True\n",
              "  status: 1\n",
              "     fun: -1.0\n",
              "       x: [ 3.182e+00  1.338e+00  1.664e-01]\n",
              "    nfev: 63\n",
              "   maxcv: 0.0"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# minimize the cost function with scipy's minimize\n",
        "min_result = minimize(cost, [0, 0, 0], method=\"COBYLA\", tol=1e-8)\n",
        "min_result"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "4198224e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'0': 1, '1': 1023}"
            ]
          },
          "execution_count": 11,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# check counts of bitstrings with the optimal parameters\n",
        "result = sampler.run([(qc, min_result.x)]).result()\n",
        "result[0].data.c.get_counts()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d4e77e29",
      "metadata": {},
      "source": [
        "<span id=\"21-exercise\" />\n",
        "\n",
        "### 2.1 Übung\n",
        "\n",
        "Berechnen Sie den minimalen Eigenwert von $Z \\otimes Z$ mit VQE.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "86a2d76d",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "SparsePauliOp(['ZZ'],\n",
            "              coeffs=[1.+0.j])\n",
            "[[ 1.+0.j  0.+0.j  0.+0.j  0.+0.j]\n",
            " [ 0.+0.j -1.+0.j  0.+0.j  0.+0.j]\n",
            " [ 0.+0.j  0.+0.j -1.+0.j  0.+0.j]\n",
            " [ 0.+0.j  0.+0.j  0.+0.j  1.+0.j]]\n"
          ]
        }
      ],
      "source": [
        "z2 = SparsePauliOp(\"ZZ\")\n",
        "print(z2)\n",
        "print(z2.to_matrix())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "76092648-3b28-442e-8319-8a8416e0c9d5",
      "metadata": {},
      "outputs": [],
      "source": [
        "# compute eigenvalues with numpy"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "f92a23b5",
      "metadata": {},
      "outputs": [],
      "source": [
        "# define a variational form\n",
        "# qc = ..."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "496572fb",
      "metadata": {
        "scrolled": true
      },
      "outputs": [],
      "source": [
        "# compute counts of bitstrings with a random parameter values by Sampler\n",
        "# result = sampler.run(...)\n",
        "# result"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "b4cad5d0",
      "metadata": {},
      "outputs": [],
      "source": [
        "# compute the expectation value of ZZ based on the counts"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "5ce555fe",
      "metadata": {},
      "outputs": [],
      "source": [
        "# verify the expectation value of ZZ with Estimator"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "c210c435",
      "metadata": {},
      "outputs": [],
      "source": [
        "# define a cost function to look for the minimum eigenvalue of ZZ\n",
        "# def cost(x):\n",
        "#    expval = ...\n",
        "#    return expval"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "a75f3303",
      "metadata": {},
      "outputs": [],
      "source": [
        "# minimize the cost function with scipy's minimize\n",
        "# min_result = minimize(cost, [...], method=\"COBYLA\", tol=1e-8)\n",
        "# min_result"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "8c476943",
      "metadata": {},
      "outputs": [],
      "source": [
        "# check counts of bitstrings with the optimal parameter values\n",
        "# result = sampler.run(qc, min_result.x).result()\n",
        "# result"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b82ba33a",
      "metadata": {},
      "source": [
        "<span id=\"solutions-of-the-exercise\" />\n",
        "\n",
        "#### Lösungen zur Übung\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7f2e0a4e",
      "metadata": {},
      "source": [
        "Wir definieren den interessierenden Operator und betrachten ihn in Matrixform.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "a1f9d371",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "SparsePauliOp(['ZZ'],\n",
            "              coeffs=[1.+0.j])\n",
            "[[ 1.+0.j  0.+0.j  0.+0.j  0.+0.j]\n",
            " [ 0.+0.j -1.+0.j  0.+0.j  0.+0.j]\n",
            " [ 0.+0.j  0.+0.j -1.+0.j  0.+0.j]\n",
            " [ 0.+0.j  0.+0.j  0.+0.j  1.+0.j]]\n"
          ]
        }
      ],
      "source": [
        "z2 = SparsePauliOp(\"ZZ\")\n",
        "print(z2)\n",
        "print(z2.to_matrix())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b123e9d9",
      "metadata": {},
      "source": [
        "Um Eigenwerte mit Hilfe eines Quantenvariationsalgorithmus zu erhalten, konstruieren wir einen Schaltkreis mit Gattern, die Variationsparameter annehmen:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "7d5e894a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/7d5e894a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# define a variational form\n",
        "param = ParameterVector(\"a\", 6)\n",
        "qc = QuantumCircuit(2, 2)\n",
        "qc.u(param[0], param[1], param[2], 0)\n",
        "qc.u(param[3], param[4], param[5], 1)\n",
        "qc_estimator = qc.copy()\n",
        "qc.measure([0, 1], [0, 1])\n",
        "qc.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f1d86563",
      "metadata": {},
      "source": [
        "Wenn wir den Erwartungswert eines Operators (z. B. $Z \\otimes Z$ ) schätzen wollen, würden wir Estimator verwenden. Wenn wir uns die Zustände des Systems ansehen wollen, verwenden wir Sampler.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "e3bda5ff",
      "metadata": {},
      "outputs": [],
      "source": [
        "sampler = StatevectorSampler()\n",
        "estimator = StatevectorEstimator()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "aeac09f7",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'10': 661, '11': 203, '01': 47, '00': 113}"
            ]
          },
          "execution_count": 16,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# compute counts of bitstrings with random parameter values by Sampler\n",
        "result = sampler.run([(qc, [1, 2, 3, 4, 5, 6])]).result()\n",
        "counts = result[0].data.c.get_counts()\n",
        "counts"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "c96acbdf",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "-0.3828125"
            ]
          },
          "execution_count": 17,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# compute the expectation value of ZZ based on the counts\n",
        "(\n",
        "    counts.get(\"00\", 0)\n",
        "    - counts.get(\"01\", 0)\n",
        "    - counts.get(\"10\", 0)\n",
        "    + counts.get(\"11\", 0)\n",
        ") / sum(counts.values())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d2cc2ed8",
      "metadata": {},
      "source": [
        "Diese Schaltung funktionierte, aber die gewählten Parameterwerte entsprachen nicht einem Zustand mit sehr niedriger Energie (oder niedrigem Eigenwert). Der erhaltene Eigenwert ist um einiges höher als das Minimum. Das Ergebnis ist ähnlich, wenn der Schätzer verwendet wird.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "474b84c5",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array(-0.35316516)"
            ]
          },
          "execution_count": 18,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# verify the expectation value of ZZ with Estimator\n",
        "result = estimator.run([(qc_estimator, z2, [1, 2, 3, 4, 5, 6])]).result()\n",
        "result[0].data.evs"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "25f4a655",
      "metadata": {},
      "source": [
        "Wir müssen die Parameter durchsuchen und diejenigen finden, die den niedrigsten Eigenwert ergeben.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "f29322f3",
      "metadata": {},
      "outputs": [],
      "source": [
        "# define a cost function to look for the minimum eigenvalue of ZZ\n",
        "def cost(x):\n",
        "    result = sampler.run([(qc, x)]).result()\n",
        "    counts = result[0].data.c.get_counts()\n",
        "    expval = (\n",
        "        counts.get(\"00\", 0)\n",
        "        - counts.get(\"01\", 0)\n",
        "        - counts.get(\"10\", 0)\n",
        "        + counts.get(\"11\", 0)\n",
        "    ) / sum(counts.values())\n",
        "    print(expval, counts)\n",
        "    return expval"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "12d8ba03",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "1.0 {'00': 1024}\n",
            "0.578125 {'00': 808, '01': 216}\n",
            "0.5234375 {'00': 780, '01': 244}\n",
            "0.548828125 {'00': 793, '01': 231}\n",
            "0.3515625 {'00': 637, '10': 164, '11': 55, '01': 168}\n",
            "0.3359375 {'00': 638, '11': 46, '10': 174, '01': 166}\n",
            "0.283203125 {'00': 602, '10': 181, '01': 186, '11': 55}\n",
            "-0.087890625 {'01': 414, '00': 184, '10': 143, '11': 283}\n",
            "0.236328125 {'10': 27, '11': 623, '01': 364, '00': 10}\n",
            "-0.0625 {'11': 261, '01': 403, '00': 219, '10': 141}\n",
            "0.248046875 {'01': 366, '11': 628, '00': 11, '10': 19}\n",
            "-0.0625 {'10': 145, '11': 254, '01': 399, '00': 226}\n",
            "0.228515625 {'01': 373, '11': 609, '00': 20, '10': 22}\n",
            "0.0546875 {'11': 376, '10': 273, '01': 211, '00': 164}\n",
            "-0.447265625 {'01': 731, '10': 10, '11': 267, '00': 16}\n",
            "-0.71484375 {'01': 871, '11': 99, '00': 47, '10': 7}\n",
            "-0.46484375 {'01': 741, '00': 253, '10': 9, '11': 21}\n",
            "-0.87890625 {'01': 962, '00': 39, '11': 23}\n",
            "-0.640625 {'00': 176, '01': 837, '11': 8, '10': 3}\n",
            "-0.88671875 {'01': 966, '00': 41, '11': 17}\n",
            "-0.994140625 {'01': 1021, '11': 3}\n",
            "-0.91796875 {'01': 982, '11': 35, '00': 7}\n",
            "-0.994140625 {'01': 1021, '11': 2, '00': 1}\n",
            "-0.939453125 {'01': 993, '00': 31}\n",
            "-0.990234375 {'01': 1019, '11': 5}\n",
            "-0.90234375 {'01': 974, '00': 21, '11': 29}\n",
            "-0.98046875 {'01': 1014, '11': 10}\n",
            "-0.994140625 {'01': 1021, '00': 3}\n",
            "-0.990234375 {'01': 1019, '11': 4, '00': 1}\n",
            "-0.98828125 {'01': 1018, '11': 6}\n",
            "-0.990234375 {'01': 1019, '11': 4, '00': 1}\n",
            "-0.994140625 {'01': 1021, '11': 2, '00': 1}\n",
            "-0.99609375 {'01': 1022, '11': 2}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-0.99609375 {'01': 1022, '00': 2}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-0.99609375 {'01': 1022, '00': 1, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-0.99609375 {'01': 1022, '11': 1, '00': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-0.994140625 {'01': 1021, '00': 3}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-0.99609375 {'01': 1022, '11': 2}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.99609375 {'01': 1022, '11': 2}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              " message: Optimization terminated successfully.\n",
              " success: True\n",
              "  status: 1\n",
              "     fun: -0.998046875\n",
              "       x: [ 3.167e+00  6.940e-01  1.033e+00 -2.894e-02  8.933e-01\n",
              "            1.885e+00]\n",
              "    nfev: 128\n",
              "   maxcv: 0.0"
            ]
          },
          "execution_count": 20,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              " message: Optimization terminated successfully.\n",
              " success: True\n",
              "  status: 1\n",
              "     fun: -0.99609375\n",
              "       x: [ 3.098e+00 -5.402e-01  1.091e+00 -1.004e-02  3.615e-01\n",
              "            6.913e-01]\n",
              "    nfev: 115\n",
              "   maxcv: 0.0"
            ]
          },
          "execution_count": 30,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# minimize the cost function with scipy's minimize\n",
        "min_result = minimize(cost, [0, 0, 0, 0, 0, 0], method=\"COBYLA\", tol=1e-8)\n",
        "min_result"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d3e2de8d",
      "metadata": {},
      "source": [
        "Wir haben einen Eigenwert erhalten, der sehr nahe am Minimum liegt, das uns von numpy vorgegeben wurde.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "d34a8544",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'01': 1024}"
            ]
          },
          "execution_count": 21,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# check counts of bitstrings with the optimal parameters\n",
        "result = sampler.run([(qc, min_result.x)]).result()\n",
        "result[0].data.c.get_counts()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bc51e7bf-e582-49ba-93f8-035624d56ccf",
      "metadata": {},
      "source": [
        "<span id=\"3-quantum-optimization-with-qiskit-patterns\" />\n",
        "\n",
        "## 3. Quantenoptimierung mit Qiskit-Mustern\n",
        "\n",
        "In dieser Anleitung lernen wir etwas über Qiskit-Muster und Quantenapproximation. Ein Qiskit-Muster ist eine intuitive, wiederholbare Reihe von Schritten zur Implementierung eines Quantencomputer-Workflows:\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "636ed1de-fc34-4cdd-9398-6fbd7c7fc9c6",
      "metadata": {},
      "source": [
        "![\"Qiskit-Funktion\"](https://quantum.cloud.ibm.com/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/qiskit-function.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8d3218ca-ce9e-40b4-a041-1b1d09bac8f5",
      "metadata": {},
      "source": [
        "Wir werden die Muster auf den Kontext der **kombinatorischen Optimierung** anwenden und zeigen, wie sich das **Max-Cut-** Problem mithilfe des **Quantum Approximate Optimization Algorithm (QAOA)**, einer hybriden (quanten-klassischen) iterativen Methode, lösen lässt.\n",
        "\n",
        "Beachten Sie, dass dieser QAOA-Teil auf \"Teil 1: QAOA im kleinen Maßstab\" des Tutorials über [den Algorithmus für die Quantenapproximation](/docs/tutorials/quantum-approximate-optimization-algorithm) basiert. In der Anleitung erfahren Sie, wie Sie es vergrößern können.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1e943b1a-218a-468c-bb63-4269896ebebe",
      "metadata": {},
      "source": [
        "<span id=\"31-small-scale-qiskit-pattern-for-optimization\" />\n",
        "\n",
        "### 3.1 (Kleines) Qiskit-Muster zur Optimierung\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "68fd0b4f-baa4-45dc-9f4c-d9cdff01a651",
      "metadata": {},
      "source": [
        "In diesem Abschnitt werden anhand eines kleinen Max-Cut-Problems die Schritte erläutert, die zur Lösung eines Optimierungsproblems mit einem Quantencomputer erforderlich sind.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "74b92ba5-c48a-405c-9c4b-04e985a7afbc",
      "metadata": {},
      "source": [
        "Das Max-Cut-Problem ist ein schwer zu lösendes Optimierungsproblem (genauer gesagt handelt es sich um ein NP-schweres Problem), das in den Bereichen Clusteranalyse, Netzwerkwissenschaft und statistische Physik vielfältige Anwendungsmöglichkeiten findet. In diesem Tutorial wird ein Graph betrachtet, der aus durch Kanten verbundenen Knoten besteht, und es geht darum, die Knoten durch das „Durchtrennen“ von Kanten in zwei Mengen zu unterteilen, sodass die Anzahl der durchtrennten Kanten maximiert wird.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "887eb6ea-f58e-482f-8965-953a08fceecf",
      "metadata": {},
      "source": [
        "![\"Maxcut\"](https://quantum.cloud.ibm.com/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/maxcut.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "893a25f2",
      "metadata": {},
      "source": [
        "Um vor der Umsetzung dieses Problems in einen Quantenalgorithmus etwas Hintergrundwissen zu vermitteln, lässt sich besser nachvollziehen, wie das Max-Cut-Problem zu einem klassischen kombinatorischen Optimierungsproblem wird, wenn man zunächst die Minimierung einer Funktion betrachtet: $f(x)$\n",
        "\n",
        "$$\n",
        "\\min_{x\\in \\{0, 1\\}^n}f(x),\n",
        "$$\n",
        "\n",
        "wobei die Eingabe $x$ ein Vektor ist, dessen Komponenten den einzelnen Knoten eines Graphen entsprechen.  Dann beschränken Sie jede dieser Komponenten entweder auf $0$ oder $1$ (was bedeutet, dass sie in den Schnitt einbezogen oder nicht einbezogen werden). In diesem kleinen Beispielfall wird ein Graph mit $n=5$ Knoten verwendet.\n",
        "\n",
        "Sie könnten eine Funktion für ein Knotenpaar $i,j$ schreiben, die anzeigt, ob die entsprechende Kante $(i,j)$ im Schnitt liegt. Zum Beispiel ist die Funktion $x_i + x_j - 2 x_i x_j$ nur dann 1, wenn entweder $x_i$ oder $x_j$ den Wert 1 haben (was bedeutet, dass die Kante im Schnitt liegt), und ansonsten Null. Das Problem der Maximierung der Kanten im Schnitt kann wie folgt formuliert werden\n",
        "\n",
        "$$\n",
        "\\max_{x\\in \\{0, 1\\}^n} \\sum_{(i,j)} x_i + x_j - 2 x_i x_j,\n",
        "$$\n",
        "\n",
        "die als Minimierung der folgenden Form umgeschrieben werden kann\n",
        "\n",
        "$$\n",
        "\\min_{x\\in \\{0, 1\\}^n} \\sum_{(i,j)}  2 x_i x_j - x_i - x_j.\n",
        "$$\n",
        "\n",
        "Das Minimum von $f(x)$ ist in diesem Fall, wenn die Anzahl der vom Schnitt durchquerten Kanten maximal ist. Wie Sie sehen können, gibt es noch nichts, was mit Quantencomputing zu tun hat. Sie müssen dieses Problem in etwas umformulieren, das ein Quantencomputer verstehen kann.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e2105a90-027f-44d7-97d1-c2c99373d488",
      "metadata": {},
      "source": [
        "Initialisieren Sie Ihr Problem, indem Sie einen Graphen mit $n=5$ Knoten erstellen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "d3c0dfa7",
      "metadata": {},
      "outputs": [],
      "source": [
        "import matplotlib\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "import rustworkx as rx\n",
        "from rustworkx.visualization import mpl_draw"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "99e763fe",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/99e763fe-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "n = 5\n",
        "\n",
        "graph = rx.PyGraph()\n",
        "graph.add_nodes_from(range(1, n + 1))\n",
        "edge_list = [\n",
        "    (0, 1, 1.0),\n",
        "    (0, 2, 1.0),\n",
        "    (1, 2, 1.0),\n",
        "    (1, 3, 1.0),\n",
        "    (2, 4, 1.0),\n",
        "    (3, 4, 1.0),\n",
        "]\n",
        "graph.add_edges_from(edge_list)\n",
        "pos = rx.spring_layout(graph, seed=2)\n",
        "mpl_draw(graph, node_size=600, pos=pos, with_labels=True, labels=str)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a06e4386-d7bd-4914-9baa-36a5cc60e3ab",
      "metadata": {},
      "source": [
        "<span id=\"32-step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "### 3.2 Schritt 1. Klassische Eingaben einem Quantenproblem zuordnen\n",
        "\n",
        "Der erste Schritt des Musters besteht darin, das klassische Problem (Graph) in **Quantenschaltungen** und **-operatoren** abzubilden. Dazu sind im Wesentlichen drei Schritte erforderlich:\n",
        "\n",
        "1. Verwenden Sie eine Reihe von mathematischen Umformulierungen, um dieses Problem unter Verwendung der Notation für quadratische, uneingeschränkte binäre Optimierungsprobleme (QUBO) darzustellen.\n",
        "2. Schreiben Sie das Optimierungsproblem in einen Hamiltonian um, bei dem der Grundzustand der Lösung entspricht, die die Kostenfunktion minimiert.\n",
        "3. Erstellen Sie einen Quantenschaltkreis, der den Grundzustand dieses Hamiltonianers durch einen Prozess ähnlich dem Quantenglühen vorbereitet.\n",
        "\n",
        "**Hinweis:** Bei der QAOA-Methode benötigen Sie letztendlich einen Operator (**Hamiltonian** ), der die **Kostenfunktion** unseres hybriden Algorithmus darstellt, sowie einen parametrisierten Schaltkreis (**Ansatz** ), der Quantenzustände mit Lösungsansätzen für das Problem darstellt. Sie können aus diesen Kandidatenzuständen eine Stichprobe ziehen und sie dann anhand der Kostenfunktion bewerten.\n",
        "\n",
        "<span id=\"graph-→-optimization-problem\" />\n",
        "\n",
        "#### Grafik → Optimierungsproblem\n",
        "\n",
        "Der erste Schritt der Zuordnung ist eine Änderung der Notation. Im Folgenden wird das Problem in QUBO-Notation ausgedrückt:\n",
        "\n",
        "$$\n",
        "\\min_{x\\in \\{0, 1\\}^n}x^T Q x,\n",
        "$$\n",
        "\n",
        "dabei ist $Q$ eine $n\\times n$ Matrix aus reellen Zahlen, $n$ entspricht der Anzahl der Knoten in Ihrem Graphen, $x$ ist der oben eingeführte Vektor der binären Variablen und $x^T$ gibt die Transponierung des Vektors $x$ an.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c00a3493-abab-46d8-86a9-5b853979a575",
      "metadata": {},
      "source": [
        "```\n",
        "Problem name: maxcut\n",
        "\n",
        "Minimize\n",
        "  2*x_1*x_2 + 2*x_1*x_3 + 2*x_2*x_3 + 2*x_2*x_4 + 2*x_3*x_5 + 2*x_4*x_5 - 2*x_1\n",
        "  - 3*x_2 - 3*x_3 - 2*x_4 - 2*x_5\n",
        "\n",
        "Subject to\n",
        "  No constraints\n",
        "\n",
        "  Binary variables (5)\n",
        "    x_1 x_2 x_3 x_4 x_5\n",
        "```\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a5b9e551-38a1-4543-b9f1-caaefb0ef3a9",
      "metadata": {},
      "source": [
        "<span id=\"optimization-problem-→-hamiltonian\" />\n",
        "\n",
        "#### Optimierungsproblem → Hamilton-Funktion\n",
        "\n",
        "Sie können dann das QUBO-Problem als **Hamiltonian** umformulieren (hier eine Matrix, die die Energie eines Systems darstellt):\n",
        "\n",
        "$$\n",
        "H_C=\\sum_{ij}Q_{ij}Z_iZ_j + \\sum_i b_iZ_i.\n",
        "$$\n",
        "\n",
        "**Reformulierungsschritte vom QAOA-Problem zum Hamiltonian**\n",
        "\n",
        "Um zu demonstrieren, wie das QAOA-Problem auf diese Weise umgeschrieben werden kann, ersetzen Sie zunächst die binären Variablen $x_i$ durch einen neuen Satz von Variablen $z_i\\in\\{-1, 1\\}$ über\n",
        "\n",
        "$$\n",
        "x_i = \\frac{1-z_i}{2}.\n",
        "$$\n",
        "\n",
        "Hier sieht man, dass, wenn $x_i$ gleich $0$ ist, $z_i$ gleich $1$ sein muss. Wenn die $x_i$ 's für die $z_i$ 's in der Optimierung Problem ( $x^TQx$ ) ersetzt werden, kann eine äquivalente Formulierung erhalten werden.\n",
        "\n",
        "$$\n",
        "x^TQx=\\sum_{ij}Q_{ij}x_ix_j \\\\ =\\frac{1}{4}\\sum_{ij}Q_{ij}(1-z_i)(1-z_j) \\\\=\\frac{1}{4}\\sum_{ij}Q_{ij}z_iz_j-\\frac{1}{4}\\sum_{ij}(Q_{ij}+Q_{ji})z_i + \\frac{n^2}{4}.\n",
        "$$\n",
        "\n",
        "Wenn wir nun $b_i=-\\sum_{j}(Q_{ij}+Q_{ji})$ definieren, den Vorfaktor und den konstanten Term $n^2$ entfernen, erhalten wir die beiden äquivalenten Formulierungen desselben Optimierungsproblems.\n",
        "\n",
        "$$\n",
        "min_{x\\in\\{0,1\\}^n} x^TQx\\Longleftrightarrow \\min_{z\\in\\{-1,1\\}^n}z^TQz + b^Tz\n",
        "$$\n",
        "\n",
        "Hier hängt $b$ von $Q$ ab. Um $z^TQz + b^Tz$ zu erhalten, haben wir den Faktor 1/4 und einen konstanten Offset von $n^2$ weggelassen, die bei der Optimierung keine Rolle spielen.\n",
        "\n",
        "Um nun eine Quantenformulierung des Problems zu erhalten, muss man die $z_i$ -Variablen zu einer Pauli-Matrix $Z$ befördern, beispielsweise zu einer $2\\times 2$ -Matrix der Form\n",
        "\n",
        "$$\n",
        "Z_i = \\begin{pmatrix}1 & 0 \\\\ 0 & -1\\end{pmatrix}.\n",
        "$$\n",
        "\n",
        "Wenn Sie diese Matrizen in das obige Optimierungsproblem einsetzen, erhalten Sie die folgende Hamiltonsche Funktion\n",
        "\n",
        "$$\n",
        "H_C=\\sum_{ij}Q_{ij}Z_iZ_j + \\sum_i b_iZ_i.\n",
        "$$\n",
        "\n",
        "*Erinnern Sie sich auch daran, dass die $Z$ Matrizen in den Rechenraum des Quantencomputers eingebettet sind, d.h. in einen Hilbert-Raum der Größe $2^n\\times 2^n$. Daher sollten Sie Begriffe wie $Z_iZ_j$ als das Tensorprodukt $Z_i\\otimes Z_j$ verstehen, das in den Hilbert-Raum $2^n\\times 2^n$ eingebettet ist. Bei einem Problem mit fünf Entscheidungsvariablen ist beispielsweise der Begriff $Z_1Z_3$ gleichbedeutend mit $I\\otimes Z_3\\otimes I\\otimes Z_1\\otimes I$, wobei $I$ die Identitätsmatrix $2\\times 2$ ist.*\n",
        "\n",
        "Dieser Hamiltonian wird <b>kostenfunktion Hamiltonian</b> genannt. Er hat die Eigenschaft, dass sein Grundzustand der Lösung von <b>minimiert die Kostenfunktion $f(x)$</b> entspricht. Um Ihr Optimierungsproblem zu lösen, müssen Sie nun den Grundzustand von $H_C$ (oder einen Zustand mit einer hohen Überlappung) auf dem Quantencomputer vorbereiten. Dann wird eine Stichprobe aus diesem Zustand mit hoher Wahrscheinlichkeit die Lösung von $\\min~f(x)$ ergeben.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "47256f53-8e02-494e-864a-a6f45b10442a",
      "metadata": {},
      "outputs": [],
      "source": [
        "def build_max_cut_operator(graph: rx.PyGraph) -> tuple[SparsePauliOp, float]:\n",
        "    sp_list = []\n",
        "    constant = 0\n",
        "    for s, t in graph.edge_list():\n",
        "        w = graph.get_edge_data(s, t)\n",
        "        sp_list.append((\"ZZ\", [s, t], w / 2))\n",
        "        constant -= 1 / 2\n",
        "    return SparsePauliOp.from_sparse_list(\n",
        "        sp_list, num_qubits=graph.num_nodes()\n",
        "    ), constant"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "id": "01a2d8eb-b63b-40bc-93c5-0b547c06b194",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Cost Function Hamiltonian: SparsePauliOp(['IIIZZ', 'IIZIZ', 'IIZZI', 'IZIZI', 'ZIZII', 'ZZIII'],\n",
            "              coeffs=[0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j])\n",
            "Constant: -3.0\n"
          ]
        }
      ],
      "source": [
        "cost_hamiltonian, constant = build_max_cut_operator(graph)\n",
        "print(\"Cost Function Hamiltonian:\", cost_hamiltonian)\n",
        "print(\"Constant:\", constant)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "33f71b0d-4a2a-4082-8c1a-ce9d2b769048",
      "metadata": {},
      "source": [
        "<span id=\"hamiltonian-→-quantum-circuit\" />\n",
        "\n",
        "#### Hamiltonian → Quantenschaltung\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "00431c46-30c2-40f9-99df-40baf8da98f6",
      "metadata": {},
      "source": [
        "Der Hamiltonian $H_C$ enthält die Quantendefinition Ihres Problems. Jetzt können Sie einen Quantenschaltkreis erstellen, mit dessen Hilfe Sie gute Lösungen vom Quantencomputer *abfragen* können. Die QAOA ist von der Quantenglühung inspiriert und wendet alternierende Schichten von Operatoren in der Quantenschaltung an.\n",
        "\n",
        "Die allgemeine Idee ist, im Grundzustand eines bekannten Systems zu beginnen ( $H^{\\otimes n}|0\\rangle$ ) und dann das System in den Grundzustand des Kostenoperators zu lenken, an dem man interessiert ist. Dies geschieht durch die Anwendung der Operatoren $\\exp\\{-i\\gamma_k H_C\\}$ und $\\exp\\{-i\\beta_k H_m\\}$ mit den Winkeln $\\gamma_1,...,\\gamma_p$ und $\\beta_1,...,\\beta_p~$.\n",
        "\n",
        "Der von Ihnen erzeugte Quantenschaltkreis wird durch $\\gamma_i$ und $\\beta_i$\\*\\* parametrisiert\\*\\*, so dass Sie verschiedene Werte von $\\gamma_i$ und $\\beta_i$ ausprobieren und aus dem resultierenden Zustand eine Probe entnehmen können.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "ca09d6bf-e421-4ada-9515-1f69687bb511",
      "metadata": {},
      "source": [
        "![\"QAOA-Schaltplan\"](https://quantum.cloud.ibm.com/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/circuit-diagram.svg)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0d12bc53-7805-43e4-b4cb-754fd234b519",
      "metadata": {},
      "source": [
        "In diesem Fall werden wir ein Beispiel mit einer QAOA-Ebene ausprobieren, die zwei Parameter enthält: $\\gamma_1$ und $\\beta_1$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "id": "1f6215c0",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.circuit.library import QAOAAnsatz"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "id": "7bd8c6d4-f40f-4a11-a440-0b26d9021b53",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/7bd8c6d4-f40f-4a11-a440-0b26d9021b53-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 27,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circuit = QAOAAnsatz(cost_operator=cost_hamiltonian, reps=1)\n",
        "circuit.measure_all()\n",
        "circuit.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "id": "148d2d62",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/148d2d62-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 28,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circuit.decompose(reps=3).draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "id": "315c495a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "ParameterView([ParameterVectorElement(β[0]), ParameterVectorElement(γ[0])])"
            ]
          },
          "execution_count": 29,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circuit.parameters"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "82f70daa-ff68-447a-8064-8b7df7a646cf",
      "metadata": {},
      "source": [
        "<span id=\"33-step-2-optimize-circuits-for-quantum-hardware-execution\" />\n",
        "\n",
        "### 3.3 Schritt 2. Optimierung von Schaltkreisen für die Ausführung auf Quantenhardware\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c08be444-e3ed-4178-a10b-414069b1b411",
      "metadata": {},
      "source": [
        "Die obige Schaltung enthält eine Reihe von Abstraktionen, die nützlich sind, um über Quantenalgorithmen nachzudenken, aber nicht auf der Hardware ausgeführt werden können. Um auf einer QPU ausgeführt werden zu können, muss die Schaltung eine Reihe von Operationen durchlaufen, die den Schritt **der Transpilierung** oder der **Schaltungsoptimierung** des Musters bilden.\n",
        "\n",
        "Die Qiskit-Bibliothek bietet eine Reihe von **Transpilationspässen**, die für eine Vielzahl von Schaltkreistransformationen geeignet sind. Sie müssen sicherstellen, dass Ihre Schaltung für Ihren Zweck **optimiert** ist.\n",
        "\n",
        "Die Umsetzung kann mehrere Schritte umfassen, wie zum Beispiel:\n",
        "\n",
        "* **Anfängliche Zuordnung** der Qubits im Schaltkreis (z. B. Entscheidungsvariablen) zu den physischen Qubits auf dem Gerät.\n",
        "* **Abrollen** der Befehle im Quantenschaltkreis zu den hardware-nativen Befehlen, die das Backend versteht.\n",
        "* **Weiterleitung** aller Qubits im Schaltkreis, die miteinander interagieren, an benachbarte physikalische Qubits.\n",
        "* **Fehlerunterdrückung** durch Hinzufügen von Ein-Qubit-Gattern zur Rauschunterdrückung mit dynamischer Entkopplung.\n",
        "\n",
        "Weitere Informationen zur Transpilierung finden Sie in unserer [Dokumentation](/docs/guides/transpile).\n",
        "\n",
        "Der folgende Code wandelt und optimiert die abstrakte Schaltung in ein Format, das für die Ausführung auf einem der Geräte bereit ist, die über die Cloud mit dem **Qiskit IBM® Runtime Service** zugänglich sind.\n",
        "\n",
        "Beachten Sie, dass Sie Ihre Programme im „lokalen Testmodus“ lokal testen können, bevor Sie sie an echte Quantencomputer senden.\n",
        "Weitere Informationen zum lokalen Testmodus finden Sie in der [Dokumentation](/docs/guides/local-testing-mode).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "95cd3eed-0348-4373-b664-16a65d42f1e7",
      "metadata": {},
      "outputs": [
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "  service = QiskitRuntimeService(channel=\"ibm_quantum_platform\")\n"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "<IBMBackend('ibm_strasbourg')>\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/95cd3eed-0348-4373-b664-16a65d42f1e7-2.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 31,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "# Use a quantum device\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(min_num_qubits=127)\n",
        "# backend = service.backend(\"ibm_kingston\")\n",
        "\n",
        "# You can test your programs locally with a fake backend (local testing mode)\n",
        "# backend = FakeBrisbane()\n",
        "\n",
        "print(backend)\n",
        "\n",
        "# Create pass manager for transpilation\n",
        "pm = generate_preset_pass_manager(optimization_level=3, backend=backend)\n",
        "\n",
        "candidate_circuit = pm.run(circuit)\n",
        "candidate_circuit.draw(\"mpl\", fold=False, idle_wires=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4e75cad7-f599-4937-b5fe-f4d01f53423c",
      "metadata": {},
      "source": [
        "<span id=\"34-step-3-execute-using-ibm-quantum-primitives\" />\n",
        "\n",
        "### 3.4 Schritt 3. Mit den Primitiven von „ IBM Quantum “ ausführen\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9b99ce67-f121-4244-b62a-536be38fea86",
      "metadata": {},
      "source": [
        "Im QAOA-Arbeitsablauf werden die optimalen QAOA-Parameter in einer iterativen Optimierungsschleife gefunden, die eine Reihe von Schaltkreisbewertungen durchführt und einen klassischen Optimierer verwendet, um die optimalen $\\beta_k$ und $\\gamma_k$ Parameter zu finden. Diese Ausführungsschleife wird in den folgenden Schritten ausgeführt:\n",
        "\n",
        "1. Definieren Sie die Ausgangsparameter\n",
        "2. Instanziieren Sie eine neue `Session` , die die Optimierungsschleife und das Primitiv enthält, das für die Abtastung der Schaltung verwendet wird\n",
        "3. Sobald ein optimaler Parametersatz gefunden wurde, führen Sie den Schaltkreis ein letztes Mal aus, um eine endgültige Verteilung zu erhalten, die im Nachbearbeitungsschritt verwendet wird.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "00b2b0f1-9bad-4ad3-b93e-5cbf40395dbf",
      "metadata": {},
      "source": [
        "<span id=\"define-circuit-with-initial-parameters\" />\n",
        "\n",
        "#### Schaltung mit Anfangsparametern definieren\n",
        "\n",
        "Wir beginnen mit willkürlich gewählten Parametern.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "id": "afa5747f-44dc-4e41-a875-7b6f896f13e2",
      "metadata": {},
      "outputs": [],
      "source": [
        "initial_gamma = np.pi\n",
        "initial_beta = np.pi / 2\n",
        "init_params = [initial_gamma, initial_beta]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b867f1b0-7196-4d34-9b28-e3fb1de8221c",
      "metadata": {},
      "source": [
        "<span id=\"define-backend-and-execution-primitive\" />\n",
        "\n",
        "#### Backend und Ausführungsprimitive definieren\n",
        "\n",
        "Verwenden Sie die „ **IBM Quantum “-Primitive,** um mit den „ IBM® “-Backends zu interagieren. Die beiden Primitive heißen „Sampler“ und „Estimator“, und die Wahl des Primitives hängt davon ab, welche Art von Messung Sie auf dem Quantencomputer durchführen möchten. Zur Minimierung von $H_C$ verwende den Schätzer, da die Messgröße der Kostenfunktion einfach der Erwartungswert von $\\langle H_C \\rangle$ ist.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "36c789f2",
      "metadata": {},
      "source": [
        "<span id=\"run\" />\n",
        "\n",
        "#### Ausführen\n",
        "\n",
        "Die Primitive bieten eine Vielzahl von [Ausführungsmodi](/docs/guides/execution-modes), um Arbeitslasten auf Quantengeräten zu planen, und ein QAOA-Workflow wird iterativ in einer Sitzung ausgeführt.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "122b7dc8-8d8a-45f2-813e-6199905d765b",
      "metadata": {},
      "source": [
        "![\"Ausführungsmodus\"](https://quantum.cloud.ibm.com/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/execution-mode.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fff58deb",
      "metadata": {},
      "source": [
        "Sie können die samplerbasierte Kostenfunktion in die Minimierungsroutine SciPy einfügen, um die optimalen Parameter zu finden.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 33,
      "id": "ff947109-cddc-4d3c-9119-2c729df73115",
      "metadata": {},
      "outputs": [],
      "source": [
        "def cost_func_estimator(params, ansatz, hamiltonian, estimator):\n",
        "    # transform the observable defined on virtual qubits to\n",
        "    # an observable defined on all physical qubits\n",
        "    isa_hamiltonian = hamiltonian.apply_layout(ansatz.layout)\n",
        "\n",
        "    pub = (ansatz, isa_hamiltonian, params)\n",
        "    job = estimator.run([pub])\n",
        "\n",
        "    results = job.result()[0]\n",
        "    cost = results.data.evs\n",
        "\n",
        "    objective_func_vals.append(cost)\n",
        "\n",
        "    return cost"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 34,
      "id": "f5f46775",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            " message: Optimization terminated successfully.\n",
            " success: True\n",
            "  status: 1\n",
            "     fun: -0.6557925874481715\n",
            "       x: [ 2.873e+00  9.414e-01]\n",
            "    nfev: 21\n",
            "   maxcv: 0.0\n"
          ]
        }
      ],
      "source": [
        "from qiskit_ibm_runtime import Session, EstimatorV2\n",
        "from scipy.optimize import minimize\n",
        "\n",
        "objective_func_vals = []  # Global variable\n",
        "with Session(backend=backend) as session:\n",
        "    # If using qiskit-ibm-runtime<0.24.0, change `mode=` to `session=`\n",
        "    estimator = EstimatorV2(mode=session)\n",
        "    estimator.options.default_shots = 1000\n",
        "\n",
        "    # Set simple error suppression/mitigation options\n",
        "    estimator.options.dynamical_decoupling.enable = True\n",
        "    estimator.options.dynamical_decoupling.sequence_type = \"XY4\"\n",
        "    estimator.options.twirling.enable_gates = True\n",
        "    estimator.options.twirling.num_randomizations = \"auto\"\n",
        "\n",
        "    result = minimize(\n",
        "        cost_func_estimator,\n",
        "        init_params,\n",
        "        args=(candidate_circuit, cost_hamiltonian, estimator),\n",
        "        method=\"COBYLA\",\n",
        "        tol=1e-2,\n",
        "    )\n",
        "    print(result)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ad878b62",
      "metadata": {},
      "source": [
        "Der Optimierer konnte die Kosten senken und bessere Parameter für die Schaltung finden.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 35,
      "id": "f923dd5d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/f923dd5d-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.figure(figsize=(12, 6))\n",
        "plt.plot(objective_func_vals)\n",
        "plt.xlabel(\"Iteration\")\n",
        "plt.ylabel(\"Cost\")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e2ea6359",
      "metadata": {},
      "source": [
        "Sobald Sie die optimalen Parameter für die Schaltung gefunden haben, können Sie diese Parameter zuweisen und die mit den optimierten Parametern erhaltene endgültige Verteilung stichprobenartig überprüfen. Hier sollte das *Sampler-Primitiv* verwendet werden, da es die Wahrscheinlichkeitsverteilung der Bitstring-Messungen ist, die dem optimalen Schnitt des Graphen entspricht.\n",
        "\n",
        "**Anmerkung:** Dies bedeutet, dass ein Quantenzustand $\\psi$ im Computer vorbereitet und dann gemessen wird. Durch eine Messung wird der Zustand in einen einzigen Berechnungsbasiszustand - zum Beispiel `010101110000...` - umgewandelt, der einer Kandidatenlösung $x$ für unser anfängliches Optimierungsproblem entspricht ( $\\max f(x)$ oder $\\min f(x)$, je nach Aufgabe).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 36,
      "id": "f8dddf5a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/f8dddf5a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 36,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "optimized_circuit = candidate_circuit.assign_parameters(result.x)\n",
        "optimized_circuit.draw(\"mpl\", fold=False, idle_wires=False)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 37,
      "id": "fd9669cf",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{12: 0.0652, 31: 0.0089, 4: 0.0085, 13: 0.0731, 26: 0.0256, 28: 0.0246, 17: 0.0405, 25: 0.0591, 20: 0.031, 15: 0.0221, 8: 0.017, 21: 0.0371, 14: 0.0461, 16: 0.0229, 19: 0.0723, 23: 0.0199, 22: 0.0478, 18: 0.0708, 24: 0.0165, 6: 0.0525, 7: 0.0155, 5: 0.0245, 3: 0.0231, 29: 0.0121, 30: 0.0062, 10: 0.0363, 1: 0.0097, 9: 0.042, 27: 0.0094, 11: 0.0349, 0: 0.0129, 2: 0.0119}\n"
          ]
        }
      ],
      "source": [
        "from qiskit_ibm_runtime import SamplerV2\n",
        "\n",
        "# If using qiskit-ibm-runtime<0.24.0, change `mode=` to `backend=`\n",
        "sampler = SamplerV2(mode=backend)\n",
        "\n",
        "# Set simple error suppression/mitigation options\n",
        "sampler.options.dynamical_decoupling.enable = True\n",
        "sampler.options.dynamical_decoupling.sequence_type = \"XY4\"\n",
        "sampler.options.twirling.enable_gates = True\n",
        "sampler.options.twirling.num_randomizations = \"auto\"\n",
        "\n",
        "pub = (optimized_circuit,)\n",
        "job = sampler.run([pub], shots=int(1e4))\n",
        "counts_int = job.result()[0].data.meas.get_int_counts()\n",
        "counts_bin = job.result()[0].data.meas.get_counts()\n",
        "shots = sum(counts_int.values())\n",
        "final_distribution_int = {key: val / shots for key, val in counts_int.items()}\n",
        "final_distribution_bin = {key: val / shots for key, val in counts_bin.items()}\n",
        "print(final_distribution_int)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2c89613f",
      "metadata": {},
      "source": [
        "<span id=\"35-step-4-post-process-return-result-in-classical-format\" />\n",
        "\n",
        "### 3.5 Schritt 4. Nachbearbeitung, Ergebnis im klassischen Format zurückgeben\n",
        "\n",
        "Der Nachbearbeitungsschritt interpretiert die Stichprobenergebnisse, um eine Lösung für Ihr ursprüngliches Problem zu finden. In diesem Fall sind Sie an der Bitfolge mit der höchsten Wahrscheinlichkeit interessiert, da diese den optimalen Schnitt bestimmt. Die Symmetrien in dem Problem lassen vier mögliche Lösungen zu, und der Stichprobenprozess wird eine davon mit einer etwas höheren Wahrscheinlichkeit zurückgeben, aber Sie können in der unten aufgezeichneten Verteilung sehen, dass vier der Bitstrings deutlich wahrscheinlicher sind als der Rest.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 38,
      "id": "d4f7fc70-883f-4b6b-8e92-2fc4afbbea46",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Result bitstring: [1, 0, 1, 1, 0]\n"
          ]
        }
      ],
      "source": [
        "# auxiliary functions to sample most likely bitstring\n",
        "def to_bitstring(integer, num_bits):\n",
        "    result = np.binary_repr(integer, width=num_bits)\n",
        "    return [int(digit) for digit in result]\n",
        "\n",
        "\n",
        "keys = list(final_distribution_int.keys())\n",
        "values = list(final_distribution_int.values())\n",
        "most_likely = keys[np.argmax(np.abs(values))]\n",
        "most_likely_bitstring = to_bitstring(most_likely, len(graph))\n",
        "most_likely_bitstring.reverse()\n",
        "\n",
        "print(\"Result bitstring:\", most_likely_bitstring)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 39,
      "id": "32a3020e-c1ea-4aff-988c-d7910a690fa8",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/32a3020e-c1ea-4aff-988c-d7910a690fa8-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "\n",
        "matplotlib.rcParams.update({\"font.size\": 10})\n",
        "final_bits = final_distribution_bin\n",
        "values = np.abs(list(final_bits.values()))\n",
        "top_4_values = sorted(values, reverse=True)[:4]\n",
        "positions = []\n",
        "for value in top_4_values:\n",
        "    positions.append(np.where(values == value)[0])\n",
        "fig = plt.figure(figsize=(11, 6))\n",
        "ax = fig.add_subplot(1, 1, 1)\n",
        "plt.xticks(rotation=45)\n",
        "plt.title(\"Result Distribution\")\n",
        "plt.xlabel(\"Bitstrings (reversed)\")\n",
        "plt.ylabel(\"Probability\")\n",
        "ax.bar(list(final_bits.keys()), list(final_bits.values()), color=\"tab:grey\")\n",
        "for p in positions:\n",
        "    ax.get_children()[p[0].item()].set_color(\"tab:purple\")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6cfcb278",
      "metadata": {},
      "source": [
        "<span id=\"visualize-best-cut\" />\n",
        "\n",
        "#### Den besten Schnitt visualisieren\n",
        "\n",
        "Ausgehend von der optimalen Bitfolge können Sie dann diesen Schnitt auf dem ursprünglichen Graphen visualisieren.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 40,
      "id": "22a48124-e6b4-4144-bee1-f01fa4c7ccbb",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/22a48124-e6b4-4144-bee1-f01fa4c7ccbb-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "colors = [\"tab:grey\" if i == 0 else \"tab:purple\" for i in most_likely_bitstring]\n",
        "mpl_draw(graph, node_size=600, pos=pos, with_labels=True, labels=str, node_color=colors)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4c4803ab",
      "metadata": {},
      "source": [
        "Und berechnen Sie den Wert des Schnitts. Die Lösung ist aufgrund von Rauschen nicht optimal (der Grenzwert der optimalen Lösung ist 5).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 41,
      "id": "7208ee7d",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The value of the cut is: 5\n"
          ]
        }
      ],
      "source": [
        "from typing import Sequence\n",
        "\n",
        "\n",
        "def evaluate_sample(x: Sequence[int], graph: rx.PyGraph) -> float:\n",
        "    assert len(x) == len(\n",
        "        list(graph.nodes())\n",
        "    ), \"The length of x must coincide with the number of nodes in the graph.\"\n",
        "    return sum(\n",
        "        x[u] * (1 - x[v]) + x[v] * (1 - x[u]) for u, v in list(graph.edge_list())\n",
        "    )\n",
        "\n",
        "\n",
        "cut_value = evaluate_sample(most_likely_bitstring, graph)\n",
        "print(\"The value of the cut is:\", cut_value)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1450be7e",
      "metadata": {},
      "source": [
        "Damit ist der kleine QAOA-Lehrgang abgeschlossen.\n",
        "In \"Teil 2: Skalieren!\" erfahren Sie, wie Sie QAOA an die Anforderungen von Versorgungsunternehmen anpassen können des Tutorials über den [Algorithmus für die Quantenapproximation](/docs/tutorials/quantum-approximate-optimization-algorithm).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 42,
      "id": "2a4f85ab",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'2.0.2'"
            ]
          },
          "execution_count": 42,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Check Qiskit version\n",
        "import qiskit\n",
        "\n",
        "qiskit.__version__"
      ]
    },
    {
      "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"
    },
    "widgets": {
      "application/vnd.jupyter.widget-state+json": {
        "state": {},
        "version_major": 2,
        "version_minor": 0
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}