{
  "cells": [
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "da8ce695-8435-4099-b0fd-ade7b298f540",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Kostenfunktionen\"\n",
        "description: \"In dieser Lektion wird erklärt, was eine Kostenfunktion ist, wie sie in Variationsalgorithmen verwendet wird und wie sie sich vom Hamilton-Operator unterscheiden kann.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore resil nbsp eigenbasis expvals IIZZ IZIZ IZZI ZIIZ ZZII */}\n",
        "\n",
        "<span id=\"cost-functions\" />\n",
        "\n",
        "# Kostenfunktionen\n",
        "\n",
        "In dieser Lektion werden wir lernen, wie man eine *Kostenfunktion* auswertet:\n",
        "\n",
        "* Zunächst werden wir uns mit [den Grundelementen von „ IBM Quantum “ befassen.](/docs/guides/primitives)\n",
        "* Definieren Sie eine *Kostenfunktion* $C(\\vec\\theta)$. Dies ist eine problemspezifische Funktion, die das Ziel des Problems definiert, das der Optimierer minimieren (oder maximieren) soll\n",
        "* Festlegung einer Messstrategie mit den „ IBM Quantum “-Primitiven zur Optimierung des Verhältnisses von Geschwindigkeit und Genauigkeit\n",
        "\n",
        " \n",
        "\n",
        "![Ein Diagramm, das die wichtigsten Komponenten einer Kostenfunktion zeigt, einschließlich der Verwendung von Primitiven wie Schätzer und Sampler.](https://quantum.cloud.ibm.com/learning/images/courses/variational-algorithm-design/cost-functions/cost-function-workflow.svg)\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "4a9b79a9-c040-43d3-a4cf-314277e30eea",
      "metadata": {
        "gloss": {
          "primitives": {
            "text": "Basic and fundamental operations or data type. Qiskit has the Sampler and Estimator primitives to serve as building blocks to easily construct complex workloads, like variational algorithms.",
            "title": "Primitives"
          }
        }
      },
      "source": [
        "<span id=\"primitives\" />\n",
        "\n",
        "## Basiselemente\n",
        "\n",
        "Alle physikalischen Systeme, ob klassisch oder quantenmechanisch, können in verschiedenen Zuständen existieren. So kann beispielsweise ein Auto auf einer Straße eine bestimmte Masse, Position, Geschwindigkeit oder Beschleunigung haben, die seinen Zustand charakterisieren. In ähnlicher Weise können auch Quantensysteme verschiedene Konfigurationen oder Zustände haben, aber sie unterscheiden sich von klassischen Systemen darin, wie wir mit Messungen und Zustandsentwicklung umgehen. Dies führt zu einzigartigen Eigenschaften wie *Überlagerung* und *Verschränkung*, die es nur in der Quantenmechanik gibt. So wie wir den Zustand eines Autos anhand physikalischer Eigenschaften wie Geschwindigkeit oder Beschleunigung beschreiben können, können wir auch den Zustand eines Quantensystems anhand von *Observablen* beschreiben, die mathematische Objekte sind.\n",
        "\n",
        "In der Quantenmechanik werden Zustände durch normalisierte komplexe Spaltenvektoren oder *kets* ( $|\\psi\\rangle$ ) dargestellt, und Beobachtungsgrößen sind lineare hermitesche Operatoren ( $\\hat{H}=\\hat{H}^{\\dagger}$ ), die auf die kets wirken. Ein Eigenvektor ( $|\\lambda\\rangle$ ) einer Beobachtungsgröße wird als *Eigenzustand* bezeichnet. Misst man eine Beobachtungsgröße für einen ihrer Eigenzustände ( $|\\lambda\\rangle$ ), so erhält man den entsprechenden Eigenwert ( $\\lambda$ ) als Messwert.\n",
        "\n",
        "Wenn Sie sich fragen, wie man ein Quantensystem misst und was man messen kann, bietet Qiskit zwei <DefinitionTooltip definition=\"Grundlegende und grundlegende Operationen oder Datentypen. Qiskit verfügt über die Primitive Sampler und Estimator, die als Bausteine für die einfache Konstruktion komplexer algorithmischer Workloads dienen.\">Basiselemente</DefinitionTooltip> an, die Ihnen helfen können:\n",
        "\n",
        "* `Sampler`: Ausgehend von einem Quantenzustand $|\\psi\\rangle$ ermittelt dieses Primitiv die Wahrscheinlichkeit jedes möglichen Berechnungsbasiszustands.\n",
        "* `Estimator`: Bei einer Quantenbeobachtung $\\hat{H}$ und einem Zustand $|\\psi\\rangle$ berechnet dieses Primitiv den Erwartungswert von $\\hat{H}$.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "00aa7dfa-34f1-440e-bbdb-19b6e6a1aff8",
      "metadata": {},
      "source": [
        "<span id=\"the-sampler-primitive\" />\n",
        "\n",
        "### Die Sampler-Primitive\n",
        "\n",
        "Das Primitiv `Sampler` berechnet die Wahrscheinlichkeit, jeden möglichen Zustand $|k\\rangle$ aus der Berechnungsgrundlage zu erhalten, wenn eine Quantenschaltung den Zustand $|\\psi\\rangle$ vorbereitet. Es errechnet\n",
        "\n",
        "$$\n",
        "p_k = |\\langle k | \\psi \\rangle|^2 \\quad \\forall k \\in \\mathbb{Z}_2^n \\equiv \\{0,1,\\cdots,2^n-1\\},\n",
        "$$\n",
        "\n",
        "Dabei ist $n$ die Anzahl der Qubits und $k$ die ganzzahlige Darstellung jeder möglichen binären Ausgabezeichenfolge $\\{0,1\\}^n$ (d. h. Ganzzahlen zur Basis $2$ ).\n",
        "\n",
        "Der „ IBM Quantum “ durchläuft [`Sampler`](/docs/api/qiskit-ibm-runtime/executor-sampler-sampler) den Schaltkreis mehrmals auf einem Quantenbauelement, führt bei jedem Durchlauf Messungen durch und rekonstruiert die Wahrscheinlichkeitsverteilung aus den wiederhergestellten Bitstrings. Je mehr Durchläufe (oder *Berechnungen* ) durchgeführt werden, desto genauer sind die Ergebnisse; dies erfordert jedoch mehr Zeit und Quantenressourcen.\n",
        "\n",
        "Da jedoch die Anzahl der möglichen Ausgaben exponentiell mit der Anzahl der Qubits $n$ (d. h. $2^n$ ) wächst, muss auch die Anzahl der Schüsse exponentiell wachsen, um eine *dichte* Wahrscheinlichkeitsverteilung zu erfassen. Daher ist `Sampler` nur für *spärliche* Wahrscheinlichkeitsverteilungen effizient, wobei der Zielzustand $|\\psi\\rangle$ als Linearkombination der Rechenbasiszustände ausgedrückt werden kann, wobei die Anzahl der Terme höchstens polynomiell mit der Anzahl der Qubits wächst:\n",
        "\n",
        "$$\n",
        "|\\psi\\rangle = \\sum^{\\text{Poly}(n)}_k w_k |k\\rangle.\n",
        "$$\n",
        "\n",
        "Die `Sampler` kann auch so konfiguriert werden, dass sie Wahrscheinlichkeiten aus einem Teilbereich der Schaltung abruft, der eine Teilmenge der insgesamt möglichen Zustände darstellt.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "96a74b2b-6aa0-4ef1-a8d1-448b591429e4",
      "metadata": {
        "gloss": {
          "pauli": {
            "text": "Set of matrices commonly used in quantum computing to represent and manipulate quantum states, consisting of the identity matrix and the three Pauli matrices (X, Y, and Z).",
            "title": "Pauli Operators"
          }
        }
      },
      "source": [
        "<span id=\"the-estimator-primitive\" />\n",
        "\n",
        "### Die Primitive „Estimator“\n",
        "\n",
        "Das Primitiv `Estimator` berechnet den Erwartungswert einer Observablen $\\hat{H}$ für einen Quantenzustand $|\\psi\\rangle$; wobei die Wahrscheinlichkeiten der Observablen als $p_\\lambda = |\\langle\\lambda|\\psi\\rangle|^2$ ausgedrückt werden können, wobei $|\\lambda\\rangle$ die Eigenzustände der Observablen $\\hat{H}$ sind. Der Erwartungswert ist dann definiert als der Durchschnitt aller möglichen Ergebnisse $\\lambda$ (d. h. der Eigenwerte der Observablen) einer Messung des Zustands $|\\psi\\rangle$, gewichtet mit den entsprechenden Wahrscheinlichkeiten:\n",
        "\n",
        "$$\n",
        "\\langle\\hat{H}\\rangle_\\psi := \\sum_\\lambda p_\\lambda \\lambda = \\langle \\psi | \\hat{H} | \\psi \\rangle\n",
        "$$\n",
        "\n",
        "Die Berechnung des Erwartungswerts einer Observablen ist jedoch nicht immer möglich, da wir ihre Eigenbasis oft nicht kennen. Bei der „ IBM Quantum “ [`Estimator`](/docs/api/qiskit-ibm-runtime/executor-estimator-estimator) wird ein komplexer algebraischer Prozess verwendet, um den Erwartungswert an einem realen Quantenbauelement zu schätzen, indem die Observable in eine Kombination anderer Observablen zerlegt wird, deren Eigenbasis uns bekannt ist.\n",
        "\n",
        "Einfacher ausgedrückt, zerlegt `Estimator` jede Beobachtung, die es nicht zu messen weiß, in einfachere, messbare Beobachtungen, die <DefinitionTooltip definition=\"Eine Gruppe von Matrizen, die in der Quanteninformatik häufig zur Darstellung und Manipulation von Quantenzuständen verwendet wird. Sie besteht aus der Identitätsmatrix und den drei Pauli-Matrizen (X, Y und Z).\">Pauli-Operatoren</DefinitionTooltip> genannt werden.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "6dcabf55-4462-4cf9-b6e4-e7860fd25b31",
      "metadata": {},
      "source": [
        "Jeder Operator kann als eine Kombination von $4^n$ Pauli-Operatoren ausgedrückt werden.\n",
        "\n",
        "$$\n",
        "\\hat{P}_k :=\n",
        "\\sigma_{k_{n-1}}\\otimes \\cdots \\otimes \\sigma_{k_0} \\quad\n",
        "\\forall k \\in \\mathbb{Z}_4^n \\equiv \\{0,1,\\cdots,4^n-1\\}, \\\\\n",
        "$$\n",
        "\n",
        "sodass\n",
        "\n",
        "$$\n",
        "\\hat{H} = \\sum^{4^n-1}_{k=0} w_k \\hat{P}_k\n",
        "$$\n",
        "\n",
        "dabei steht $n$ für die Anzahl der Qubits, $k \\equiv k_{n-1} \\cdots k_0$ für $k_l \\in \\mathbb{Z}_4 \\equiv \\{0, 1, 2, 3\\}$ (d.h. ganze Zahlen zur Basis $4$ ) und $(\\sigma_0, \\sigma_1, \\sigma_2, \\sigma_3) := (I, X, Y, Z)$.\n",
        "\n",
        "Nach dieser Zerlegung leitet `Estimator` eine neue Schaltung $V_k|\\psi\\rangle$ für jede Beobachtungsgröße $\\hat{P}_k$ (aus der ursprünglichen Schaltung) ab, um die Pauli-Beobachtungsgröße in der Berechnungsgrundlage effektiv *zu diagonalisieren* und zu messen. Wir können Pauli Observablen leicht messen, weil wir $V_k$ im Voraus kennen, was bei anderen Observablen im Allgemeinen nicht der Fall ist.\n",
        "\n",
        "Für jedes $\\hat{P}_{k}$ lässt `Estimator` den entsprechenden Schaltkreis auf einem Quantengerät mehrfach laufen, misst den Ausgangszustand in der Berechnungsbasis und berechnet die Wahrscheinlichkeit $p_{kj}$ für jeden möglichen Ausgang $j$. Dann sucht es den Eigenwert $\\lambda_{kj}$ von $P_k$, der jedem Ausgang $j$ entspricht, multipliziert mit $w_k$ und addiert alle Ergebnisse, um den Erwartungswert der Beobachtungsgröße $\\hat{H}$ für den gegebenen Zustand $|\\psi\\rangle$ zu erhalten.\n",
        "\n",
        "$$\n",
        "\\langle\\hat{H}\\rangle_\\psi =\n",
        "\\sum_{k=0}^{4^n-1} w_k \\sum_{j=0}^{2^n-1}p_{kj} \\lambda_{kj},\n",
        "$$\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "dcffb899-ef2d-4dca-b2a7-11c197d45ed0",
      "metadata": {
        "gloss": {
          "sampling": {
            "text": "The process of taking several measurements of one or several things.",
            "title": "Sampling"
          }
        }
      },
      "source": [
        "Da die Berechnung des Erwartungswerts von $4^n$ Paulis unpraktikabel ist (d. h. exponentiell anwächst), `Estimator` kann dies nur dann effizient sein, wenn eine große Anzahl von $w_k$ gleich Null ist (d. h. bei einer *spärlichen* statt einer *dichten* Pauli-Zerlegung). Formal ausgedrückt gilt: Damit diese Berechnung *effizient gelöst* werden kann, muss die Anzahl der von Null verschiedenen Terme höchstens polynominal mit der Anzahl der Qubits $n$ wachsen: $\\hat{H} = \\sum^{\\text{Poly}(n)}_k w_k \\hat{P}_k$.\n",
        "\n",
        "Der Leser mag die implizite Annahme bemerken, dass die Wahrscheinlichkeit <DefinitionTooltip definition=\"Der Vorgang, bei dem mehrere Messungen an einem oder mehreren Dingen vorgenommen werden.\">Stichprobenziehung</DefinitionTooltip> ebenfalls effizient sein muss, wie für `Sampler` erläutert, was bedeutet\n",
        "\n",
        "$$\n",
        "\\langle\\hat{H}\\rangle_\\psi =\n",
        "\\sum_{k}^{\\text{Poly}(n)} w_k \\sum_{j}^{\\text{Poly}(n)}p_{kj} \\lambda_{kj}.\n",
        "$$\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "c1547f3e-3c05-46d9-be5a-3e7e4141dc92",
      "metadata": {},
      "source": [
        "<span id=\"guided-example-to-calculate-expectation-values\" />\n",
        "\n",
        "### Geführtes Beispiel zur Berechnung von Erwartungswerten\n",
        "\n",
        "Nehmen wir den Ein-Qubit-Zustand $|+\\rangle := H|0\\rangle = \\frac{1}{\\sqrt{2}}(|0\\rangle + |1\\rangle)$ an, und die Beobachtungsgröße\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        "\\hat{H}\n",
        "& = \\begin{pmatrix}\n",
        "-1 & 2 \\\\\n",
        "2 & 1 \\\\\n",
        "\\end{pmatrix}\\\\[1mm]\n",
        "& = 2X - Z\n",
        "\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n",
        "mit dem folgenden theoretischen Erwartungswert $\\langle\\hat{H}\\rangle_+ = \\langle+|\\hat{H}|+\\rangle = 2$.\n",
        "\n",
        "Da wir nicht wissen, wie wir diese Beobachtungsgröße messen können, können wir ihren Erwartungswert nicht direkt berechnen, sondern müssen ihn als $\\langle\\hat{H}\\rangle_+ = 2\\langle X \\rangle_+ - \\langle Z \\rangle_+ $ neu ausdrücken. Es kann gezeigt werden, dass $\\langle+|X|+\\rangle = 1$ und $\\langle+|Z|+\\rangle = 0$ zum gleichen Ergebnis führen.\n",
        "\n",
        "Sehen wir uns an, wie man $\\langle X \\rangle_+$ und $\\langle Z \\rangle_+$ direkt berechnen kann. Da $X$ und $Z$ nicht kommutieren (d. h. nicht dieselbe Eigenbasis haben), können sie nicht gleichzeitig gemessen werden, weshalb wir die Hilfsschaltungen benötigen:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "7c32e970-f9cb-48ec-a60b-2dccb0744141",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/variational-algorithm-design/cost-functions/extracted-outputs/7c32e970-f9cb-48ec-a60b-2dccb0744141-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "\n",
        "# The following code will work for any other initial single-qubit state and observable\n",
        "original_circuit = QuantumCircuit(1)\n",
        "original_circuit.h(0)\n",
        "\n",
        "H = SparsePauliOp([\"X\", \"Z\"], [2, -1])\n",
        "\n",
        "aux_circuits = []\n",
        "for pauli in H.paulis:\n",
        "    aux_circ = original_circuit.copy()\n",
        "    aux_circ.barrier()\n",
        "    if str(pauli) == \"X\":\n",
        "        aux_circ.h(0)\n",
        "    elif str(pauli) == \"Y\":\n",
        "        aux_circ.sdg(0)\n",
        "        aux_circ.h(0)\n",
        "    else:\n",
        "        aux_circ.id(0)\n",
        "    aux_circ.measure_all()\n",
        "    aux_circuits.append(aux_circ)\n",
        "\n",
        "original_circuit.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "ec3cf7af-66b2-4ea9-94d7-6ec6ba2bfc5e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/variational-algorithm-design/cost-functions/extracted-outputs/ec3cf7af-66b2-4ea9-94d7-6ec6ba2bfc5e-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Auxiliary circuit for X\n",
        "aux_circuits[0].draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "678d3bd0-d8fd-4767-b0a6-77cae945810a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/variational-algorithm-design/cost-functions/extracted-outputs/678d3bd0-d8fd-4767-b0a6-77cae945810a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Auxiliary circuit for Z\n",
        "aux_circuits[1].draw(\"mpl\")"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "beef97f0-c968-4a3f-9b7b-fed39b88cbe3",
      "metadata": {},
      "source": [
        "Wir können die Berechnung nun manuell mit `Sampler` durchführen und die Ergebnisse auf `Estimator` überprüfen:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "d59dffe0-85cb-4849-8421-03bbcc9668df",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Sampler results:\n",
            "  >> Expected value of X: 1.00000\n",
            "  >> Expected value of Z: 0.00420\n",
            "  >> Total expected value: 1.99580\n",
            "Estimator results:\n",
            "  >> Expected value of X: 1.00000\n",
            "  >> Expected value of Z: 0.00000\n",
            "  >> Total expected value: 2.00000\n"
          ]
        }
      ],
      "source": [
        "from qiskit.primitives import StatevectorSampler, StatevectorEstimator\n",
        "from qiskit.result import QuasiDistribution\n",
        "import numpy as np\n",
        "\n",
        "\n",
        "## SAMPLER\n",
        "shots = 10000\n",
        "sampler = StatevectorSampler()\n",
        "job = sampler.run(aux_circuits, shots=shots)\n",
        "\n",
        "# Run the sampler job and step through results\n",
        "expvals = []\n",
        "for index, pauli in enumerate(H.paulis):\n",
        "    data_pub = job.result()[index].data\n",
        "    bitstrings = data_pub.meas.get_bitstrings()\n",
        "    counts = data_pub.meas.get_counts()\n",
        "    quasi_dist = QuasiDistribution(\n",
        "        {outcome: freq / shots for outcome, freq in counts.items()}\n",
        "    )\n",
        "\n",
        "    # Use the probabilities and known eigenvalues of Pauli operators to estimate\n",
        "    # the expectation value.\n",
        "    val = 0\n",
        "\n",
        "    if str(pauli) == \"X\":\n",
        "        val += -1 * quasi_dist.get(1, 0)\n",
        "        val += 1 * quasi_dist.get(0, 0)\n",
        "\n",
        "    if str(pauli) == \"Y\":\n",
        "        val += -1 * quasi_dist.get(1, 0)\n",
        "        val += 1 * quasi_dist.get(0, 0)\n",
        "\n",
        "    if str(pauli) == \"Z\":\n",
        "        val += 1 * quasi_dist.get(0, 0)\n",
        "        val += -1 * quasi_dist.get(1, 0)\n",
        "\n",
        "    expvals.append(val)\n",
        "\n",
        "# Print expectation values\n",
        "\n",
        "print(\"Sampler results:\")\n",
        "for pauli, expval in zip(H.paulis, expvals):\n",
        "    print(f\"  >> Expected value of {str(pauli)}: {expval:.5f}\")\n",
        "\n",
        "total_expval = np.sum(H.coeffs * expvals).real\n",
        "print(f\"  >> Total expected value: {total_expval:.5f}\")\n",
        "\n",
        "# Use estimator for comparison\n",
        "observables = [\n",
        "    *H.paulis,\n",
        "    H,\n",
        "]  # Note: run for individual Paulis as well as full observable H\n",
        "\n",
        "estimator = StatevectorEstimator()\n",
        "job = estimator.run([(original_circuit, observables)])\n",
        "estimator_expvals = job.result()[0].data.evs\n",
        "\n",
        "# Print results\n",
        "print(\"Estimator results:\")\n",
        "for obs, expval in zip(observables, estimator_expvals):\n",
        "    if obs is not H:\n",
        "        print(f\"  >> Expected value of {str(obs)}: {expval:.5f}\")\n",
        "    else:\n",
        "        print(f\"  >> Total expected value: {expval:.5f}\")"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "44f13c37-50e3-426c-90cb-f730a81e20d1",
      "metadata": {},
      "source": [
        "<span id=\"mathematical-rigor-optional\" />\n",
        "\n",
        "### Mathematische Strenge (optional)\n",
        "\n",
        "Wenn man $|\\psi\\rangle$ in Bezug auf die Basis der Eigenzustände von $\\hat{H}$, $|\\psi\\rangle = \\sum_\\lambda a_\\lambda |\\lambda\\rangle$, ausdrückt, ergibt sich folgendes:\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        "\\langle \\psi | \\hat{H} | \\psi \\rangle\n",
        "& = \\bigg(\\sum_{\\lambda'}a^*_{\\lambda'} \\langle \\lambda'|\\bigg) \\hat{H}\n",
        "  \\bigg(\\sum_{\\lambda} a_\\lambda | \\lambda\\rangle\\bigg)\\\\[1mm]\n",
        "\n",
        "& = \\sum_{\\lambda}\\sum_{\\lambda'} a^*_{\\lambda'}a_{\\lambda}\n",
        "  \\langle \\lambda'|\\hat{H}| \\lambda\\rangle\\\\[1mm]\n",
        "\n",
        "& = \\sum_{\\lambda}\\sum_{\\lambda'} a^*_{\\lambda'}a_{\\lambda} \\lambda\n",
        "\\langle \\lambda'| \\lambda\\rangle\\\\[1mm]\n",
        "\n",
        "& = \\sum_{\\lambda}\\sum_{\\lambda'} a^*_{\\lambda'}a_{\\lambda} \\lambda\n",
        "\\cdot \\delta_{\\lambda, \\lambda'}\\\\[1mm]\n",
        "\n",
        "& = \\sum_\\lambda |a_\\lambda|^2 \\lambda\\\\[1mm]\n",
        "\n",
        "& = \\sum_\\lambda p_\\lambda \\lambda\\\\[1mm]\n",
        "\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "6a3bea3d-a2f6-4033-85f9-9a10c5d5c64c",
      "metadata": {
        "gloss": {
          "hermitian": {
            "text": "A hermitian is a square matrix that is equal to its own conjugate transpose, or a linear operator that is self-adjoint.",
            "title": "Hermitian"
          }
        }
      },
      "source": [
        "Da wir weder die Eigenwerte noch die Eigenzustände der Zielgrößen $\\hat{H}$ kennen, müssen wir zunächst deren Diagonalisierung betrachten. Da $\\hat{H}$ gleich <DefinitionTooltip definition=\"Eine hermitsche Matrix ist eine quadratische Matrix, die gleich ihrer eigenen konjugierten Transponierung ist, oder ein linearer Operator, der selbstadjungiert ist.\">Hermitisch</DefinitionTooltip> ist, gibt es eine unitäre Transformation $V$, sodass $\\hat{H}=V^\\dagger \\Lambda V$ gilt, wobei $\\Lambda$ die Diagonalmatrix der Eigenwerte ist, sodass $\\langle j | \\Lambda | k \\rangle = 0$ gilt, wenn $j\\neq k$ ist, und $\\langle j | \\Lambda | j \\rangle = \\lambda_j$.\n",
        "\n",
        "Dies bedeutet, dass der Erwartungswert wie folgt umgeschrieben werden kann:\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        "\\langle\\psi|\\hat{H}|\\psi\\rangle\n",
        "& = \\langle\\psi|V^\\dagger \\Lambda V|\\psi\\rangle\\\\[1mm]\n",
        "\n",
        "& = \\langle\\psi|V^\\dagger \\bigg(\\sum_{j=0}^{2^n-1} |j\\rangle\n",
        "\\langle j|\\bigg) \\Lambda \\bigg(\\sum_{k=0}^{2^n-1} |k\\rangle \\langle k|\\bigg) V|\\psi\\rangle\\\\[1mm]\n",
        "\n",
        "& = \\sum_{j=0}^{2^n-1} \\sum_{k=0}^{2^n-1}\\langle\\psi|V^\\dagger |j\\rangle\n",
        "\\langle j| \\Lambda  |k\\rangle \\langle k| V|\\psi\\rangle\\\\[1mm]\n",
        "\n",
        "& = \\sum_{j=0}^{2^n-1}\\langle\\psi|V^\\dagger |j\\rangle\n",
        "\\langle j| \\Lambda  |j\\rangle \\langle j| V|\\psi\\rangle\\\\[1mm]\n",
        "\n",
        "& = \\sum_{j=0}^{2^n-1}|\\langle j| V|\\psi\\rangle|^2 \\lambda_j\\\\[1mm]\n",
        "\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n",
        "Wenn sich ein System im Zustand $|\\phi\\rangle = V |\\psi\\rangle$ befindet, ist die Wahrscheinlichkeit, dass $| j\\rangle$ gemessen wird, $p_j = |\\langle j|\\phi \\rangle|^2$. Der obige Erwartungswert kann wie folgt ausgedrückt werden:\n",
        "\n",
        "$$\n",
        "\\langle\\psi|\\hat{H}|\\psi\\rangle =\n",
        "\\sum_{j=0}^{2^n-1} p_j \\lambda_j.\n",
        "$$\n",
        "\n",
        "Es ist sehr wichtig zu beachten, dass die Wahrscheinlichkeiten aus dem Zustand $V |\\psi\\rangle$ anstelle von $|\\psi\\rangle$ entnommen werden. Deshalb ist die Matrix $V$ unbedingt erforderlich.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "c21a9a05-6e07-45dc-a090-4414dfdf2521",
      "metadata": {},
      "source": [
        "Sie fragen sich vielleicht, wie man die Matrix $V$ und die Eigenwerte $\\Lambda$ erhält. Hätten Sie die Eigenwerte bereits, bräuchten Sie keinen Quantencomputer, denn das Ziel von Variationsalgorithmen ist es, diese Eigenwerte von $\\hat{H}$ zu finden.\n",
        "\n",
        "Glücklicherweise gibt es einen Ausweg: Jede $2^n \\times 2^n$ -Matrix lässt sich als Linearkombination von $4^n$ Tensorprodukten aus $n$ Pauli-Matrizen und Identitäten darstellen, die alle sowohl hermitisch als auch unitär sind und deren $V$ und $\\Lambda$ bekannt sind. Genau das macht `Estimator` die IBM Quantum intern, indem sie jedes Objekt [`Operator`](/docs/api/qiskit/qiskit.quantum_info.Operator) in ein zerlegt [`SparsePauliOp`](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp).\n",
        "\n",
        "Hier sind die Operatoren, die verwendet werden können:\n",
        "\n",
        "$$\n",
        "\\begin{array}{c|c|c|c}\n",
        "  \\text{Operator} & \\sigma & V & \\Lambda \\\\[1mm]\n",
        "  \\hline\n",
        "  I & \\sigma_0 = \\begin{pmatrix} 1 & 0 \\\\ 0 & 1 \\end{pmatrix} & V_0 = I & \\Lambda_0 = I = \\begin{pmatrix} 1 & 0 \\\\ 0 & 1 \\end{pmatrix} \\\\[4mm]\n",
        "\n",
        "  X & \\sigma_1 = \\begin{pmatrix} 0 & 1 \\\\ 1 & 0 \\end{pmatrix} & V_1 = H =\\frac{1}{\\sqrt{2}} \\begin{pmatrix} 1 & 1 \\\\ 1 & -1 \\end{pmatrix} & \\Lambda_1 = \\sigma_3 = \\begin{pmatrix} 1 & 0 \\\\ 0 & -1 \\end{pmatrix} \\\\[4mm]\n",
        "\n",
        "  Y & \\sigma_2 = \\begin{pmatrix} 0 & -i \\\\ i & 0 \\end{pmatrix} & V_2 = HS^\\dagger  =\\frac{1}{\\sqrt{2}} \\begin{pmatrix} 1 & 1 \\\\ 1 & -1 \\end{pmatrix}\\cdot  \\begin{pmatrix} 1 & 0 \\\\ 0 & -i \\end{pmatrix} = \\frac{1}{\\sqrt{2}} \\begin{pmatrix} 1 & -i \\\\ 1 & i \\end{pmatrix}\\quad & \\Lambda_2 = \\sigma_3 = \\begin{pmatrix} 1 & 0 \\\\ 0 & -1 \\end{pmatrix} \\\\[4mm]\n",
        "\n",
        "  Z & \\sigma_3 = \\begin{pmatrix} 1 & 0 \\\\ 0 & -1 \\end{pmatrix} & V_3 = I & \\Lambda_3 = \\sigma_3 = \\begin{pmatrix} 1 & 0 \\\\ 0 & -1 \\end{pmatrix}\n",
        "\\end{array}\n",
        "$$\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "9e1c0933-5fbe-466d-9114-92d66d21785f",
      "metadata": {},
      "source": [
        "Schreiben wir also $\\hat{H}$ in Bezug auf die Paulis und Identitäten um:\n",
        "\n",
        "$$\n",
        "\\hat{H} =\n",
        "\\sum_{k_{n-1}=0}^3...\n",
        "\\sum_{k_0=0}^3 w_{k_{n-1}...k_0}\n",
        "\\sigma_{k_{n-1}}\\otimes ... \\otimes \\sigma_{k_0} = \\sum_{k=0}^{4^n-1} w_k \\hat{P}_k,\n",
        "$$\n",
        "\n",
        "wobei $k = \\sum_{l=0}^{n-1} 4^l k_l \\equiv k_{n-1}...k_0$ für $k_{n-1},...,k_0\\in \\{0,1,2,3\\}$ (d. h. die Basis $4$ ) und $\\hat{P}_{k} := \\sigma_{k_{n-1}}\\otimes ... \\otimes \\sigma_{k_0}$ steht:\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        "\\langle\\psi|\\hat{H}|\\psi\\rangle\n",
        "& = \\sum_{k=0}^{4^n-1} w_k\n",
        "\\sum_{j=0}^{2^n-1}|\\langle j| V_k|\\psi\\rangle|^2 \\langle j| \\Lambda_k |j\\rangle \\\\[1mm]\n",
        "\n",
        "& = \\sum_{k=0}^{4^n-1} w_k \\sum_{j=0}^{2^n-1}p_{kj} \\lambda_{kj}, \\\\[1mm]\n",
        "\n",
        "\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n",
        "wobei $V_k := V_{k_{n-1}}\\otimes ... \\otimes V_{k_0}$ und $\\Lambda_k := \\Lambda_{k_{n-1}}\\otimes ... \\otimes \\Lambda_{k_0}$, sodass gilt: $\\hat{P_k}=V_k^\\dagger \\Lambda_k V_k$.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "42f771a0-486d-4a5f-9df1-a1720d7bf252",
      "metadata": {
        "gloss": {
          "oracle": {
            "text": " A hypothetical device or software component that performs a specific function, but the internal workings of which are unknown. A user is only aware of the inputs and outputs of the black box, and has no knowledge of how the black box processes the inputs to produce the outputs.",
            "title": "Black-box oracle"
          }
        }
      },
      "source": [
        "<span id=\"cost-functions\" />\n",
        "\n",
        "## Kostenfunktionen\n",
        "\n",
        "Im Allgemeinen werden Kostenfunktionen verwendet, um das Ziel eines Problems zu beschreiben und zu ermitteln, wie gut ein Versuchszustand im Hinblick auf dieses Ziel abschneidet. Diese Definition lässt sich auf verschiedene Beispiele in den Bereichen Chemie, maschinelles Lernen, Finanzen, Optimierung usw. anwenden.\n",
        "\n",
        "Betrachten wir ein einfaches Beispiel für die Bestimmung des Grundzustands eines Systems. Unser Ziel ist es, den Erwartungswert der Beobachtungsgröße zu minimieren, die die Energie darstellt (Hamiltonian $\\hat{\\mathcal{H}}$ ):\n",
        "\n",
        "$$\n",
        "\\min_{\\vec\\theta} \\langle\\psi(\\vec\\theta)|\\hat{\\mathcal{H}}|\\psi(\\vec\\theta)\\rangle\n",
        "$$\n",
        "\n",
        "Wir können die `Estimator` verwenden, um den Erwartungswert zu bewerten und diesen Wert zur Minimierung an einen Optimierer zu übergeben. Wenn die Optimierung erfolgreich ist, liefert sie einen Satz optimaler Parameterwerte $\\vec\\theta^*$, aus denen wir den vorgeschlagenen Lösungszustand $|\\psi(\\vec\\theta^*)\\rangle$ konstruieren und den beobachteten Erwartungswert als $C(\\vec\\theta^*)$ berechnen können.\n",
        "\n",
        "Beachten Sie, dass wir die Kostenfunktion nur für die begrenzte Anzahl von Zuständen minimieren können, die wir betrachten. Dies führt uns zu zwei verschiedenen Möglichkeiten:\n",
        "\n",
        "* **Unser Ansatz definiert den Lösungszustand nicht für den gesamten Suchraum** : Wenn dies der Fall ist, wird unser Optimierer niemals die Lösung finden, und wir müssen mit anderen Ansätzen experimentieren, die unseren Suchraum vielleicht genauer darstellen können.\n",
        "* **Unser Optimierer ist nicht in der Lage, diese gültige Lösung zu finden** : Die Optimierung kann global und lokal definiert werden. Was das bedeutet, werden wir in einem späteren Abschnitt erläutern.\n",
        "\n",
        "Alles in allem werden wir eine klassische Optimierungsschleife durchführen, aber die Auswertung der Kostenfunktion einem Quantencomputer überlassen. Aus dieser Perspektive könnte man sich die Optimierung als ein rein klassisches Unterfangen vorstellen, bei dem wir jedes Mal, wenn der Optimierer die Kostenfunktion auswerten muss, die Seite <DefinitionTooltip definition=\"Ein hypothetisches Gerät oder eine Softwarekomponente, das/die eine bestimmte Funktion ausführt, dessen interne Funktionsweise jedoch unbekannt ist. Ein Benutzer kennt nur die Eingaben und Ausgaben der Blackbox und weiß nicht, wie die Blackbox die Eingaben verarbeitet, um die Ausgaben zu erzeugen.\">black-Box-Quantenorakel</DefinitionTooltip> aufrufen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "650faaab-b964-4587-a39f-d6cc80b225b2",
      "metadata": {},
      "outputs": [],
      "source": [
        "def cost_func_vqe(params, circuit, hamiltonian, estimator):\n",
        "    \"\"\"Return estimate of energy from estimator\n",
        "\n",
        "    Parameters:\n",
        "        params (ndarray): Array of ansatz parameters\n",
        "        ansatz (QuantumCircuit): Parameterized ansatz circuit\n",
        "        hamiltonian (SparsePauliOp): Operator representation of Hamiltonian\n",
        "        estimator (Estimator): Estimator primitive instance\n",
        "\n",
        "    Returns:\n",
        "        float: Energy estimate\n",
        "    \"\"\"\n",
        "    pub = (circuit, hamiltonian, params)\n",
        "    cost = estimator.run([pub]).result()[0].data.evs\n",
        "    return cost"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "a01a54f2-da16-4008-b422-fa7ded531f67",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/variational-algorithm-design/cost-functions/extracted-outputs/a01a54f2-da16-4008-b422-fa7ded531f67-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit.circuit.library import TwoLocal\n",
        "\n",
        "observable = SparsePauliOp.from_list([(\"XX\", 1), (\"YY\", -3)])\n",
        "\n",
        "reference_circuit = QuantumCircuit(2)\n",
        "reference_circuit.x(0)\n",
        "\n",
        "variational_form = TwoLocal(\n",
        "    2,\n",
        "    rotation_blocks=[\"rz\", \"ry\"],\n",
        "    entanglement_blocks=\"cx\",\n",
        "    entanglement=\"linear\",\n",
        "    reps=1,\n",
        ")\n",
        "ansatz = reference_circuit.compose(variational_form)\n",
        "\n",
        "theta_list = (2 * np.pi * np.random.rand(1, 8)).tolist()\n",
        "ansatz.decompose().draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7357e05e-f68a-4cd5-bf0d-77eab963a86d",
      "metadata": {},
      "source": [
        "Wir werden dies zunächst mit einem Simulator durchführen: StatevectorEstimator. Dies ist normalerweise für die Fehlersuche ratsam, aber wir werden dem Fehlersuchlauf sofort eine Berechnung auf echter Quantenhardware folgen lassen. In zunehmendem Maße sind interessante Probleme nicht mehr klassisch simulierbar ohne modernste Supercomputing-Einrichtungen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "883bcb28-15d0-4046-a654-f43d9480a642",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[-0.58744589]\n"
          ]
        }
      ],
      "source": [
        "estimator = StatevectorEstimator()\n",
        "cost = cost_func_vqe(theta_list, ansatz, observable, estimator)\n",
        "print(cost)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1a94eba3-146d-4eee-970d-9a966d1e15f3",
      "metadata": {},
      "source": [
        "Wir werden nun mit der Ausführung auf einem echten Quantencomputer fortfahren. Beachten Sie die Änderungen in der Syntax. Die Schritte im Zusammenhang mit dem pass\\_manager werden im nächsten Beispiel näher erläutert. Ein besonders wichtiger Schritt bei Variationsalgorithmen ist die Verwendung einer Sitzung des „ IBM Quantum “-Compute-Service. Durch das Starten einer Sitzung können Sie mehrere Iterationen eines Variationsalgorithmus ausführen, ohne bei jeder Aktualisierung der Parameter erneut in einer neuen Warteschlange warten zu müssen. Dies ist wichtig, wenn die Wartezeiten lang sind und/oder viele Durchläufe erforderlich sind. Bitte beachten Sie, dass Nutzer des Open-Plan-Tarifs keine Quantum-Compute-Sitzungen nutzen können. Falls Sie keinen Zugriff auf die Sitzungen haben, können Sie die Anzahl der Iterationen, die Sie zu einem bestimmten Zeitpunkt übermitteln, reduzieren und die zuletzt verwendeten Parameter für zukünftige Durchläufe speichern. Wenn Sie zu viele Iterationen übermitteln oder auf zu lange Wartezeiten stoßen, wird möglicherweise der Fehlercode 1217 angezeigt, der auf lange Verzögerungen zwischen den Auftragsübermittlungen hinweist.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "e4ca4722-46ab-418f-8a1f-740a65e92eb9",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Estimated usage: < 1 min. Benchmarked at 7 seconds on an Eagle processor\n",
        "# Load necessary packages:\n",
        "\n",
        "from qiskit_ibm_runtime import (\n",
        "    QiskitRuntimeService,\n",
        "    Session,\n",
        "    EstimatorOptions,\n",
        "    EstimatorV2 as Estimator,\n",
        ")\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "# Select the least busy backend:\n",
        "\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, min_num_qubits=ansatz.num_qubits, simulator=False\n",
        ")\n",
        "# Or get a specific backend:\n",
        "# backend = service.backend(\"ibm_brisbane\")\n",
        "\n",
        "# Use a pass manager to transpile the circuit and observable for the specific backend being used:\n",
        "\n",
        "pm = generate_preset_pass_manager(backend=backend, optimization_level=1)\n",
        "isa_ansatz = pm.run(ansatz)\n",
        "isa_observable = observable.apply_layout(layout=isa_ansatz.layout)\n",
        "\n",
        "\n",
        "# Set estimator options\n",
        "estimator_options = EstimatorOptions(resilience_level=1, default_shots=10_000)\n",
        "\n",
        "# Open a Quantum Compute session:\n",
        "\n",
        "with Session(backend=backend) as session:\n",
        "    estimator = Estimator(mode=session, options=estimator_options)\n",
        "    cost = cost_func_vqe(theta_list, isa_ansatz, isa_observable, estimator)\n",
        "\n",
        "session.close()\n",
        "print(cost)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "95dc0e83-46ce-4839-91c3-af550aef9ddf",
      "metadata": {},
      "source": [
        "Beachten Sie, dass die aus den beiden obigen Berechnungen resultierenden Werte sehr ähnlich sind. Auf Techniken zur Verbesserung der Ergebnisse wird weiter unten eingegangen.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "7f3951a3-47ad-4ddb-90a0-2218b1e11d15",
      "metadata": {},
      "source": [
        "<span id=\"example-mapping-to-non-physical-systems\" />\n",
        "\n",
        "### Beispiel für die Zuordnung zu nicht-physikalischen Systemen\n",
        "\n",
        "Das Maximal-Schnitt-Problem (Max-Cut-Problem) ist ein kombinatorisches Optimierungsproblem, bei dem es darum geht, die Knoten eines Graphen in zwei disjunkte Mengen zu unterteilen, sodass die Anzahl der Kanten zwischen den beiden Mengen maximiert wird. Etwas formeller ausgedrückt: Bei einem ungerichteten Graphen $G=(V,E)$, wobei $V$ die Menge der Knoten und $E$ die Menge der Kanten ist, besteht das Max-Cut-Problem darin, die Knoten in zwei disjunkte Teilmengen $S$ und $T$ zu partitionieren, sodass die Anzahl der Kanten, bei denen ein Endpunkt in $S$ und der andere in $T$ liegt, maximiert wird.\n",
        "\n",
        "Wir können Max-Cut zur Lösung verschiedener Probleme einsetzen, darunter: Clusterbildung, Netzwerkdesign, Phasenübergänge und so weiter. Zunächst erstellen wir ein Problemdiagramm:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "739ca7a0-fae3-4389-a8de-7ae93c13b9e1",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/variational-algorithm-design/cost-functions/extracted-outputs/739ca7a0-fae3-4389-a8de-7ae93c13b9e1-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "import rustworkx as rx\n",
        "from rustworkx.visualization import mpl_draw\n",
        "\n",
        "n = 4\n",
        "G = rx.PyGraph()\n",
        "G.add_nodes_from(range(n))\n",
        "# The edge syntax is (start, end, weight)\n",
        "edges = [(0, 1, 1.0), (0, 2, 1.0), (0, 3, 1.0), (1, 2, 1.0), (2, 3, 1.0)]\n",
        "G.add_edges_from(edges)\n",
        "\n",
        "mpl_draw(\n",
        "    G, pos=rx.shell_layout(G), with_labels=True, edge_labels=str, node_color=\"#1192E8\"\n",
        ")"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "3246e173-b1a8-48c2-bce8-f80a1524bcfb",
      "metadata": {},
      "source": [
        "Dieses Problem kann als binäres Optimierungsproblem ausgedrückt werden. Für jeden Knoten $0 \\leq i < n$, wobei $n$ die Anzahl der Knoten des Graphen ist (in diesem Fall $n=4$ ), wird die binäre Variable $x_i$ betrachtet. Diese Variable hat den Wert $1$, wenn der Knoten $i$ zu einer der Gruppen gehört, die wir als $1$ bezeichnen, und $0$, wenn er zu der anderen Gruppe gehört, die wir als $0$ bezeichnen. Wir bezeichnen auch als $w_{ij}$ (Element $(i,j)$ der Adjazenzmatrix $w$ ) das Gewicht der Kante, die vom Knoten $i$ zum Knoten $j$ führt. Da der Graph ungerichtet ist, $w_{ij}=w_{ji}$. Dann können wir unser Problem als Maximierung der folgenden Kostenfunktion formulieren:\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        "C(\\vec{x})\n",
        "& =\\sum_{i,j=0}^n w_{ij} x_i(1-x_j)\\\\[1mm]\n",
        "\n",
        "& = \\sum_{i,j=0}^n w_{ij} x_i - \\sum_{i,j=0}^n w_{ij} x_ix_j\\\\[1mm]\n",
        "\n",
        "& = \\sum_{i,j=0}^n w_{ij} x_i - \\sum_{i=0}^n \\sum_{j=0}^i 2w_{ij} x_ix_j\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n",
        "Um dieses Problem mit einem Quantencomputer zu lösen, werden wir die Kostenfunktion als den Erwartungswert einer Beobachtungsgröße ausdrücken. Die Observablen, die Qiskit zulässt, bestehen jedoch von Natur aus aus Pauli-Operatoren, die Eigenwerte $1$ und $-1$ anstelle von $0$ und $1$ haben. Aus diesem Grund werden wir den folgenden Variablenwechsel vornehmen:\n",
        "\n",
        "Wobei $\\vec{x}=(x_0,x_1,\\cdots ,x_{n-1})$. Wir können die Adjazenzmatrix $w$ verwenden, um bequem auf die Gewichte aller Kanten zuzugreifen. Auf diese Weise erhalten wir unsere Kostenfunktion:\n",
        "\n",
        "$$\n",
        "z_i = 1-2x_i \\rightarrow x_i = \\frac{1-z_i}{2}\n",
        "$$\n",
        "\n",
        "Dies bedeutet, dass:\n",
        "\n",
        "$$\n",
        "\\begin{array}{lcl} x_i=0 & \\rightarrow & z_i=1 \\\\ x_i=1 & \\rightarrow & z_i=-1.\\end{array}\n",
        "$$\n",
        "\n",
        "Die neue Kostenfunktion, die wir maximieren wollen, lautet also:\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        "C(\\vec{z})\n",
        "& = \\sum_{i,j=0}^n w_{ij} \\bigg(\\frac{1-z_i}{2}\\bigg)\\bigg(1-\\frac{1-z_j}{2}\\bigg)\\\\[1mm]\n",
        "\n",
        "& = \\sum_{i,j=0}^n \\frac{w_{ij}}{4} - \\sum_{i,j=0}^n \\frac{w_{ij}}{4} z_iz_j\\\\[1mm]\n",
        "\n",
        "& = \\sum_{i=0}^n \\sum_{j=0}^i \\frac{w_{ij}}{2} -  \\sum_{i=0}^n \\sum_{j=0}^i \\frac{w_{ij}}{2} z_iz_j\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n",
        "Außerdem besteht die natürliche Tendenz eines Quantencomputers darin, Minima (in der Regel die niedrigste Energie) anstelle von Maxima zu finden, so dass wir anstelle der Maximierung von $C(\\vec{z})$ die Minimierung anstreben:\n",
        "\n",
        "$$\n",
        "-C(\\vec{z}) =  \\sum_{i=0}^n \\sum_{j=0}^i \\frac{w_{ij}}{2} z_iz_j -  \\sum_{i=0}^n \\sum_{j=0}^i \\frac{w_{ij}}{2}\n",
        "$$\n",
        "\n",
        "Da wir nun eine zu minimierende Kostenfunktion haben, deren Variablen die Werte $-1$ und $1$ annehmen können, können wir die folgende Analogie mit dem Pauli $Z$ herstellen:\n",
        "\n",
        "$$\n",
        "z_i \\equiv Z_i = \\overbrace{I}^{n-1}\\otimes ... \\otimes \\overbrace{Z}^{i} \\otimes ... \\otimes \\overbrace{I}^{0}\n",
        "$$\n",
        "\n",
        "Mit anderen Worten, die Variable $z_i$ ist äquivalent zu einem $Z$ Gate, das auf das Qubit $i$ wirkt. Außerdem:\n",
        "\n",
        "$$\n",
        "Z_i|x_{n-1}\\cdots x_0\\rangle = z_i|x_{n-1}\\cdots x_0\\rangle \\rightarrow \\langle x_{n-1}\\cdots x_0 |Z_i|x_{n-1}\\cdots x_0\\rangle = z_i\n",
        "$$\n",
        "\n",
        "Die Beobachtungsgröße, die wir betrachten wollen, ist also die folgende:\n",
        "\n",
        "$$\n",
        "\\hat{H} = \\sum_{i=0}^n \\sum_{j=0}^i \\frac{w_{ij}}{2} Z_iZ_j\n",
        "$$\n",
        "\n",
        "zu dem wir anschließend den unabhängigen Begriff hinzufügen müssen:\n",
        "\n",
        "$$\n",
        "\\texttt{offset} = - \\sum_{i=0}^n \\sum_{j=0}^i \\frac{w_{ij}}{2}\n",
        "$$\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "83c86ba1-8b9d-4222-bb7e-637e6f29e5cd",
      "metadata": {},
      "source": [
        "Der Operator ist eine Linearkombination von Termen mit Z-Operatoren auf Knoten, die durch eine Kante verbunden sind (das 0. Qubit ist am weitesten rechts): $IIZZ + IZIZ + IZZI + ZIIZ + ZZII$. Sobald der Operator konstruiert ist, kann der Ansatz für den QAOA-Algorithmus leicht mit Hilfe der `QAOAAnsatz` -Schaltung aus der Qiskit-Schaltungsbibliothek erstellt werden.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "29398315-3baf-4363-88fc-69b6438e4afa",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/variational-algorithm-design/cost-functions/extracted-outputs/29398315-3baf-4363-88fc-69b6438e4afa-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 17,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit.circuit.library import QAOAAnsatz\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "\n",
        "hamiltonian = SparsePauliOp.from_list(\n",
        "    [(\"IIZZ\", 1), (\"IZIZ\", 1), (\"IZZI\", 1), (\"ZIIZ\", 1), (\"ZZII\", 1)]\n",
        ")\n",
        "\n",
        "\n",
        "ansatz = QAOAAnsatz(hamiltonian, reps=2)\n",
        "# Draw\n",
        "ansatz.decompose(reps=3).draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "91d02379-9da6-426b-a5f0-d1fb75a891ee",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Offset: -2.5\n"
          ]
        }
      ],
      "source": [
        "# Sum the weights, and divide by 2\n",
        "\n",
        "offset = -sum(edge[2] for edge in edges) / 2\n",
        "print(f\"\"\"Offset: {offset}\"\"\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "84a98a08-00e4-4b05-a3f0-de29a7e73a60",
      "metadata": {},
      "source": [
        "Da der „ IBM Quantum “-Estimator direkt einen Hamilton- und einen parametrisierten Ansatz berücksichtigt und die erforderliche Energie zurückgibt, ist die Kostenfunktion für eine QAOA-Instanz recht einfach:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "73ac80dd-87cf-4a43-b19b-e89a4782a44a",
      "metadata": {},
      "outputs": [],
      "source": [
        "def cost_func(params, ansatz, hamiltonian, estimator):\n",
        "    \"\"\"Return estimate of energy from estimator\n",
        "\n",
        "    Parameters:\n",
        "        params (ndarray): Array of ansatz parameters\n",
        "        ansatz (QuantumCircuit): Parameterized ansatz circuit\n",
        "        hamiltonian (SparsePauliOp): Operator representation of Hamiltonian\n",
        "        estimator (Estimator): Estimator primitive instance\n",
        "\n",
        "    Returns:\n",
        "        float: Energy estimate\n",
        "    \"\"\"\n",
        "    pub = (ansatz, hamiltonian, params)\n",
        "    cost = estimator.run([pub]).result()[0].data.evs\n",
        "    #    cost = estimator.run(ansatz, hamiltonian, parameter_values=params).result().values[0]\n",
        "    return cost"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "fc6af87b-a838-43b8-96ce-0af790fe0172",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "1.473098768180865\n"
          ]
        }
      ],
      "source": [
        "import numpy as np\n",
        "\n",
        "x0 = 2 * np.pi * np.random.rand(ansatz.num_parameters)\n",
        "\n",
        "estimator = StatevectorEstimator()\n",
        "cost = cost_func_vqe(x0, ansatz, hamiltonian, estimator)\n",
        "print(cost)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "389b7e86-cd61-4184-96c3-ae91b8ad59f5",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "1.1120776913677988\n"
          ]
        }
      ],
      "source": [
        "# Estimated usage: < 1 min, benchmarked at 6 seconds on ibm_osaka, 5-23-24\n",
        "# Load some necessary packages:\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import Session, EstimatorV2 as Estimator\n",
        "\n",
        "# Select the least busy backend:\n",
        "\n",
        "backend = service.least_busy(\n",
        "    operational=True, min_num_qubits=ansatz.num_qubits, simulator=False\n",
        ")\n",
        "\n",
        "# Or get a specific backend:\n",
        "# backend = service.backend(\"ibm_brisbane\")\n",
        "\n",
        "# Use a pass manager to transpile the circuit and observable for the specific backend being used:\n",
        "\n",
        "pm = generate_preset_pass_manager(backend=backend, optimization_level=1)\n",
        "isa_ansatz = pm.run(ansatz)\n",
        "isa_hamiltonian = hamiltonian.apply_layout(layout=isa_ansatz.layout)\n",
        "\n",
        "# Set estimator options\n",
        "estimator_options = EstimatorOptions(resilience_level=1, default_shots=10_000)\n",
        "\n",
        "# Open a Quantum Compute session:\n",
        "\n",
        "with Session(backend=backend) as session:\n",
        "    estimator = Estimator(mode=session, options=estimator_options)\n",
        "    cost = cost_func_vqe(x0, isa_ansatz, isa_hamiltonian, estimator)\n",
        "\n",
        "# Close session after done\n",
        "session.close()\n",
        "print(cost)"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "16c94186-366c-4ac4-8745-4e7da283f8da",
      "metadata": {},
      "source": [
        "Wir werden dieses Beispiel in Anwendungen wieder aufgreifen, um zu untersuchen, wie ein Optimierer zur Iteration durch den Suchraum eingesetzt werden kann. Dazu gehören im Allgemeinen:\n",
        "\n",
        "* Einsatz eines Optimierers zur Ermittlung optimaler Parameter\n",
        "* Bindung optimaler Parameter an den Ansatz zur Ermittlung der Eigenwerte\n",
        "* Übertragung der Eigenwerte auf unsere Problemstellung\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "d14d5800-24c1-4e4b-b692-9bdb9ef4440f",
      "metadata": {},
      "source": [
        "<span id=\"measurement-strategy-speed-versus-accuracy\" />\n",
        "\n",
        "## Messstrategie: Geschwindigkeit versus Genauigkeit\n",
        "\n",
        "Wie bereits erwähnt, verwenden wir einen verrauschten Quantencomputer als *Blackbox-Orakel*, bei dem Rauschen die abgerufenen Werte nicht deterministisch machen kann, was zu zufälligen Fluktuationen führt, die wiederum die Konvergenz bestimmter Optimierer zu einer vorgeschlagenen Lösung beeinträchtigen oder sogar ganz verhindern. Dies ist ein allgemeines Problem, mit dem wir uns auseinandersetzen müssen, wenn wir schrittweise den Quantennutzen erforschen und Fortschritte in Richtung Quantenvorteil machen:\n",
        "\n",
        "![Ein Diagramm, das zeigt, wie die Simulationskosten mit der Komplexität der Schaltung variieren. Mit einem klassischen Computer wächst sie exponentiell. Bei der Quantenfehlerminderung sollte es einen Übergang geben, bei dem dies vorteilhaft wird. Die Quantenfehlerkorrektur ermöglicht ein lineares Wachstum der Simulationskosten und wird sicherlich zu einem Vorteil führen.](https://quantum.cloud.ibm.com/learning/images/courses/variational-algorithm-design/cost-functions/cost-function-path-to-quantum-advantage.svg)\n",
        "\n",
        "Wir können die Optionen zur Fehlerunterdrückung und Fehlerminderung der „ IBM Quantum “-Primitive nutzen, um Rauschen zu bekämpfen und den Nutzen heutiger Quantencomputer zu maximieren.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "f23f0d82-d4f9-4485-aad3-cd2f8fd19b4f",
      "metadata": {
        "gloss": {
          "overhead": {
            "text": "Extra costs introduced by new techniques, relative to a base implementation.",
            "title": "Overhead"
          }
        }
      },
      "source": [
        "<span id=\"error-suppression\" />\n",
        "\n",
        "### Fehlerunterdrückung\n",
        "\n",
        "[Unter Fehlerunterdrückung](/docs/guides/error-mitigation-and-suppression-techniques) versteht man Techniken, mit denen eine Schaltung während der Kompilierung optimiert und umgewandelt wird, um Fehler zu minimieren. Dies ist eine grundlegende Technik zur Fehlerbehandlung, die in der Regel zu einem gewissen Maß an klassischer Vorverarbeitungs <DefinitionTooltip definition=\"Zusätzliche Kosten, die durch neue Techniken im Vergleich zu einer Basisimplementierung entstehen.\">Systemaufwand</DefinitionTooltip> t der gesamten Laufzeit führt. Der Mehraufwand umfasst das Transpiling von Schaltkreisen für die Ausführung auf Quantenhardware durch:\n",
        "\n",
        "* Darstellung der Schaltung unter Verwendung der auf einem Quantensystem verfügbaren nativen Gatter\n",
        "* Abbildung der virtuellen Qubits auf physikalische Qubits\n",
        "* Hinzufügen von SWAPs auf der Grundlage von Konnektivitätsanforderungen\n",
        "* Optimierung von 1Q und 2Q gates\n",
        "* Dynamische Entkopplung inaktiver Qubits, um die Auswirkungen der Dekohärenz zu verhindern.\n",
        "\n",
        "Primitive ermöglichen die Verwendung von Fehlerunterdrückungstechniken, indem die Option `optimization_level` gesetzt und erweiterte Transpilierungsoptionen ausgewählt werden. In einem späteren Kurs werden wir uns mit verschiedenen Methoden zum Aufbau von Schaltkreisen befassen, um die Ergebnisse zu verbessern, aber für die meisten Fälle empfehlen wir die Einstellung `optimization_level=3`.\n",
        "\n",
        "Wir werden den Wert der zunehmenden Optimierung im Umsetzungsprozess anhand einer Beispielschaltung mit einem einfachen Idealverhalten veranschaulichen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "9e0bf552-32b0-477e-9ac3-4ab643c30623",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/variational-algorithm-design/cost-functions/extracted-outputs/9e0bf552-32b0-477e-9ac3-4ab643c30623-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 24,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit.circuit import Parameter, QuantumCircuit\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "\n",
        "theta = Parameter(\"theta\")\n",
        "\n",
        "qc = QuantumCircuit(2)\n",
        "qc.x(1)\n",
        "qc.h(0)\n",
        "qc.cp(theta, 0, 1)\n",
        "qc.h(0)\n",
        "observables = SparsePauliOp.from_list([(\"ZZ\", 1)])\n",
        "\n",
        "qc.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1c2b0408-e004-4a04-8aa5-cf519744082b",
      "metadata": {},
      "source": [
        "Die obige Schaltung kann sinusförmige Erwartungswerte der gegebenen Beobachtungsgröße liefern, vorausgesetzt, wir fügen Phasen ein, die ein geeignetes Intervall abdecken, wie z. B. $[0,2\\pi]$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "id": "99b23f88-ddcb-45dc-a86d-3d48ff990319",
      "metadata": {},
      "outputs": [],
      "source": [
        "## Setup phases\n",
        "import numpy as np\n",
        "\n",
        "phases = np.linspace(0, 2 * np.pi, 50)\n",
        "\n",
        "# phases need to be expressed as a list of lists in order to work\n",
        "individual_phases = [[phase] for phase in phases]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ca040383-9c8c-4a38-8e40-bad1b26c6085",
      "metadata": {},
      "source": [
        "Wir können einen Simulator verwenden, um die Nützlichkeit einer optimierten Transpilation zu zeigen. Wir werden weiter unten auf die Verwendung echter Hardware zurückkommen, um die Nützlichkeit der Fehlerbegrenzung zu demonstrieren. Wir verwenden QiskitRuntimeService, um ein reales Backend zu erhalten (in diesem Fall ibm\\_brisbane), und verwenden AerSimulator, um dieses Backend zu simulieren, einschließlich seines Rauschverhaltens.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "6f0e6f32-02f7-471f-a9f3-f30d22f8cca4",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_aer import AerSimulator\n",
        "\n",
        "# get a real backend from the runtime service\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.backend(\"ibm_brisbane\")\n",
        "\n",
        "# generate a simulator that mimics the real quantum system with the latest calibration results\n",
        "backend_sim = AerSimulator.from_backend(backend)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "38df43e8-b11e-4671-b8fb-7a2eb74f648a",
      "metadata": {},
      "source": [
        "Wir können nun einen Pass-Manager verwenden, um die Schaltung in die „Befehlssatzarchitektur“ (ISA) des Backends zu transpilieren. Dies ist eine neue Anforderung im „ IBM Quantum Compute Service“: Alle an ein Backend übermittelten Schaltkreise müssen den Einschränkungen der Zielarchitektur des Backends entsprechen, d. h., sie müssen unter Berücksichtigung der ISA des Backends geschrieben sein – also des Befehlssatzes, den das Gerät verstehen und ausführen kann. Diese Zielbeschränkungen werden durch Faktoren wie die nativen Basisgatter des Geräts, seine Qubit-Konnektivität und – sofern relevant – seine Impuls- und sonstigen Befehlszeitvorgaben bestimmt.\n",
        "\n",
        "Beachten Sie, dass wir dies im vorliegenden Fall zweimal tun werden: einmal mit Optimierungsgrad = 0 und einmal mit einem Wert von 3. Wir werden jedes Mal das Primitiv \"Schätzer\" verwenden, um die Erwartungswerte der Beobachtungsgröße bei verschiedenen Phasenwerten zu schätzen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "4dc699cd-0bb4-4b48-adcc-0bbe99218a26",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Import estimator and specify that we are using the simulated backend:\n",
        "\n",
        "from qiskit_ibm_runtime import EstimatorV2 as Estimator\n",
        "\n",
        "estimator = Estimator(mode=backend_sim)\n",
        "\n",
        "circuit = qc"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "7990eb62-879d-4beb-b452-f3d86b164d11",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Use a pass manager to transpile the circuit and observable for the backend being simulated.\n",
        "# Start with no optimization:\n",
        "\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "pm = generate_preset_pass_manager(backend=backend_sim, optimization_level=0)\n",
        "isa_circuit = pm.run(circuit)\n",
        "isa_observables = observables.apply_layout(layout=isa_circuit.layout)\n",
        "\n",
        "noisy_exp_values = []\n",
        "pub = (isa_circuit, isa_observables, [individual_phases])\n",
        "cost = estimator.run([pub]).result()[0].data.evs\n",
        "noisy_exp_values = cost[0]\n",
        "\n",
        "# Repeat above steps, but now with optimization = 3:\n",
        "\n",
        "exp_values_with_opt_es = []\n",
        "pm = generate_preset_pass_manager(backend=backend_sim, optimization_level=3)\n",
        "isa_circuit = pm.run(circuit)\n",
        "isa_observables = observables.apply_layout(layout=isa_circuit.layout)\n",
        "\n",
        "pub = (isa_circuit, isa_observables, [individual_phases])\n",
        "cost = estimator.run([pub]).result()[0].data.evs\n",
        "exp_values_with_opt_es = cost[0]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0cc8d4ea-81ef-4146-b8ed-9c7edae72be3",
      "metadata": {},
      "source": [
        "Schließlich können wir die Ergebnisse grafisch darstellen und sehen, dass die Genauigkeit der Berechnung auch ohne Optimierung recht gut war, sich aber durch die Erhöhung der Optimierung auf Stufe 3 deutlich verbesserte. Beachten Sie, dass in tieferen, komplizierteren Schaltungen der Unterschied zwischen den Optimierungsstufen 0 und 3 wahrscheinlich größer ist. Dies ist eine sehr einfache Schaltung, die als Spielzeugmodell verwendet wird.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "id": "f3d3f805-f8d0-474d-9a88-2d1f164639b0",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/variational-algorithm-design/cost-functions/extracted-outputs/f3d3f805-f8d0-474d-9a88-2d1f164639b0-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "\n",
        "plt.plot(phases, noisy_exp_values, \"o\", label=\"opt=0\")\n",
        "plt.plot(phases, exp_values_with_opt_es, \"o\", label=\"opt=3\")\n",
        "plt.plot(phases, 2 * np.sin(phases / 2) ** 2 - 1, label=\"ideal\")\n",
        "plt.ylabel(\"Expectation\")\n",
        "plt.legend()\n",
        "plt.show()"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "2394d082-2278-48cf-95b0-3363ed723ec1",
      "metadata": {
        "gloss": {
          "bias": {
            "text": "A systematic drift in the measured quantities, usually caused by errors.",
            "title": "Bias"
          }
        }
      },
      "source": [
        "<span id=\"error-mitigation\" />\n",
        "\n",
        "### Fehlerbehebung\n",
        "\n",
        "Unter [Fehlerminderung](/docs/guides/error-mitigation-and-suppression-techniques) versteht man Techniken, mit denen Benutzer Schaltungsfehler reduzieren können, indem sie das Rauschen des Bausteins zum Zeitpunkt der Ausführung modellieren. In der Regel führt dies zu einem Aufwandsaufschlag bei der Quantenvorverarbeitung im Zusammenhang mit dem Modelltraining sowie zu einem Aufwandsaufschlag bei der klassischen Nachbearbeitung, um Fehler in den Rohdaten mithilfe des generierten Modells zu beheben.\n",
        "\n",
        "Die Option `resilience_level` des Primitivs „ IBM Quantum “ legt fest, inwieweit das System gegen Fehler gewappnet sein soll. Höhere Stufen liefern genauere Ergebnisse, was jedoch aufgrund des durch die Quantenabtastung verursachten Mehraufwands mit längeren Verarbeitungszeiten einhergeht. Anhand der Resilienzstufen lässt sich das Verhältnis zwischen Kosten und Genauigkeit konfigurieren, wenn Sie bei Ihrer primitiven Abfrage eine Fehlerminderung anwenden.\n",
        "\n",
        "Wenn wir eine Technik zur Fehlerbegrenzung anwenden, erwarten wir, dass die <DefinitionTooltip definition=\"Eine systematische Abweichung in den gemessenen Größen, die in der Regel durch Fehler verursacht wird.\">systematischer Fehler</DefinitionTooltip> in unseren Ergebnissen im Vergleich zur vorherigen, ungemilderten Verzerrung reduziert wird. In einigen Fällen kann die Verzerrung sogar verschwinden. Dies hat jedoch seinen Preis. In dem Maße, in dem wir die Verzerrung in unseren geschätzten Mengen reduzieren, wird die statistische Variabilität (d. h. die Varianz) zunehmen, was wir durch eine weitere Erhöhung der Anzahl der Schüsse pro Kreislauf in unserem Stichprobenverfahren berücksichtigen können. Dies führt zu einem Mehraufwand, der über den zur Reduzierung der Verzerrung erforderlichen Aufwand hinausgeht, und wird daher nicht standardmäßig durchgeführt. Wir können dieses Verhalten ganz einfach einstellen, indem wir die Anzahl der Schüsse pro Schaltkreis in options.executions.shots anpassen, wie im folgenden Beispiel gezeigt.\n",
        "\n",
        "![Ein Diagramm, das breitere oder schmalere Verteilungen zeigt, wie beim Kompromiss zwischen Verzerrung und Varianz /var.](https://quantum.cloud.ibm.com/learning/images/courses/variational-algorithm-design/cost-functions/cost-function-bias-variance-trade-off.svg)\n",
        "\n",
        "In diesem Kurs werden wir diese Modelle zur Fehlerminderung auf allgemeiner Ebene untersuchen, um zu veranschaulichen, wie die Primitive von „ IBM Quantum “ zur Fehlerminderung beitragen können, ohne dabei auf alle Implementierungsdetails einzugehen.\n",
        "\n",
        "<span id=\"twirled-readout-error-extinction-t-rex\" />\n",
        "\n",
        "### Twirled Readout Error Extinction (T-REx)\n",
        "\n",
        "Twirled Readout Error Extinction (T-REx) verwendet eine Technik, die als Pauli-Twirling bekannt ist, um das Rauschen zu reduzieren, das während des Prozesses der Quantenmessung entsteht. Diese Technik setzt keine spezifische Form von Lärm voraus, was sie sehr allgemein und effektiv macht.\n",
        "\n",
        "Gesamter Arbeitsablauf:\n",
        "\n",
        "1. Erfassen von Daten für den Nullzustand mit zufälligen Bitflips (Pauli X vor der Messung)\n",
        "2. Erfassen von Daten für den gewünschten (verrauschten) Zustand mit zufälligen Bitflips (Pauli X vor der Messung)\n",
        "3. Berechnen Sie die spezielle Funktion für jeden Datensatz und teilen Sie sie.\n",
        "\n",
        " \n",
        "\n",
        "![Ein Diagramm mit den Mess- und Kalibrierungsschaltungen für T-REX.](https://quantum.cloud.ibm.com/learning/images/courses/variational-algorithm-design/cost-functions/cost-function-trex-data-collection.svg)\n",
        "\n",
        "Wir können dies mit `options.resilience_level = 1` einstellen, wie im folgenden Beispiel gezeigt wird.\n",
        "\n",
        "<span id=\"zero-noise-extrapolation\" />\n",
        "\n",
        "### Null-Rausch-Extrapolation\n",
        "\n",
        "Bei der rauschfreien Extrapolation (ZNE) wird zunächst das Rauschen in der Schaltung, die den gewünschten Quantenzustand vorbereitet, verstärkt, es werden Messungen für verschiedene Rauschpegel durchgeführt, und aus diesen Messungen wird das rauschfreie Ergebnis abgeleitet.\n",
        "\n",
        "Gesamter Arbeitsablauf:\n",
        "\n",
        "1. Verstärkung des Schaltungsrauschens für verschiedene Rauschfaktoren\n",
        "2. Jede geräuschverstärkte Schaltung ausführen\n",
        "3. Extrapolation zurück zur Null-Lärm-Grenze\n",
        "\n",
        " \n",
        "\n",
        "![Ein Diagramm, das die Schritte von ZNE zeigt. Der Lärm wird durch verschiedene Faktoren künstlich verstärkt. Dann werden die Werte auf den Wert extrapoliert, den sie bei Nullrauschen haben sollten.](https://quantum.cloud.ibm.com/learning/images/courses/variational-algorithm-design/cost-functions/cost-function-zne-stages.svg)\n",
        "\n",
        "Das können wir mit `options.resilience_level = 2`… einstellen. Wir könnten dies noch weiter optimieren, indem wir verschiedene `noise_factors`, `noise_amplifiers`, und untersuchen `extrapolators`, doch dies würde den Rahmen dieses Kurses sprengen. Wir empfehlen Ihnen, mit diesen [Optionen wie hier beschrieben](/docs/guides/error-mitigation-and-suppression-techniques) zu experimentieren.\n",
        "\n",
        "Jede Methode ist mit einem gewissen Overhead verbunden: ein Kompromiss zwischen der Anzahl der erforderlichen Quantenberechnungen (Zeit) und der Genauigkeit unserer Ergebnisse:\n",
        "\n",
        "$$\n",
        "\\begin{array}{c|c|c|c}\n",
        "  \\text{Methods} & R=1 \\text{, T-REx} & R=2 \\text{, ZNE} \\\\[1mm]\n",
        "  \\hline\n",
        "  \\text{Assumptions} & \\text{None} & \\text{Ability to scale noise} \\\\[1mm]\n",
        "  \\text{Qubit overhead} & 1 & 1 \\\\[1mm]\n",
        "  \\text{Sampling overhead} & 2 & N_{\\text{noise-factors}} \\\\[1mm]\n",
        "  \\text{Bias} & 0 & \\mathcal{O}(\\lambda^{N_{\\text{noise-factors}}}) \\\\[1mm]\n",
        "\\end{array}\n",
        "$$\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "8b46120f-5312-4450-bf51-13be198428fa",
      "metadata": {},
      "source": [
        "<span id=\"using-the-options-in-quantum-compute-for-mitigation-and-suppression\" />\n",
        "\n",
        "### Nutzung der Optionen in „Quantum Compute“ zur Risikominderung und Unterdrückung\n",
        "\n",
        "So berechnen Sie einen Erwartungswert unter Verwendung der Fehlerminderung und -unterdrückung im „ IBM Quantum “ Compute Service. Wir können genau dieselbe Schaltung und dieselbe Observable wie zuvor verwenden, diesmal jedoch die Optimierungsstufe auf Stufe 2 festlegen und nun die *Robustheit* bzw. die eingesetzten Fehlerminderungstechniken anpassen. Dieser Fehlerminderungsprozess wird im Verlauf einer Optimierungsschleife mehrfach durchgeführt.\n",
        "\n",
        "Wir führen diesen Teil auf echter Hardware durch, da die Fehlerminderung auf Simulatoren nicht möglich ist.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "57a6b723-6e73-4693-95bf-f0782a8eb854",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Estimated usage: 8 minutes, benchmarked on an Eagle processor, 5-23-24\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import (\n",
        "    Session,\n",
        "    EstimatorOptions,\n",
        "    EstimatorV2 as Estimator,\n",
        ")\n",
        "\n",
        "# We select the least busy backend\n",
        "\n",
        "# Select the least busy backend\n",
        "# backend = service.least_busy(\n",
        "#    operational=True, min_num_qubits=ansatz.num_qubits, simulator=False\n",
        "# )\n",
        "\n",
        "# Or use a specific backend\n",
        "backend = service.backend(\"ibm_brisbane\")\n",
        "\n",
        "# Initialize some variables to save the results from different runs:\n",
        "\n",
        "exp_values_with_em0_es = []\n",
        "exp_values_with_em1_es = []\n",
        "exp_values_with_em2_es = []\n",
        "\n",
        "# Use a pass manager to optimize the circuit and observables for the backend chosen:\n",
        "\n",
        "pm = generate_preset_pass_manager(backend=backend, optimization_level=2)\n",
        "isa_circuit = pm.run(circuit)\n",
        "isa_observables = observables.apply_layout(layout=isa_circuit.layout)\n",
        "\n",
        "# Open a session and run with no error mitigation:\n",
        "\n",
        "estimator_options = EstimatorOptions(resilience_level=0, default_shots=10_000)\n",
        "\n",
        "with Session(backend=backend) as session:\n",
        "    estimator = Estimator(mode=session, options=estimator_options)\n",
        "\n",
        "    pub = (isa_circuit, isa_observables, [individual_phases])\n",
        "    cost = estimator.run([pub]).result()[0].data.evs\n",
        "\n",
        "session.close()\n",
        "\n",
        "exp_values_with_em0_es = cost[0]\n",
        "\n",
        "# Open a session and run with resilience = 1:\n",
        "\n",
        "estimator_options = EstimatorOptions(resilience_level=1, default_shots=10_000)\n",
        "\n",
        "with Session(backend=backend) as session:\n",
        "    estimator = Estimator(mode=session, options=estimator_options)\n",
        "\n",
        "    pub = (isa_circuit, isa_observables, [individual_phases])\n",
        "    cost = estimator.run([pub]).result()[0].data.evs\n",
        "\n",
        "session.close()\n",
        "\n",
        "exp_values_with_em1_es = cost[0]\n",
        "\n",
        "# Open a session and run with resilience = 2:\n",
        "\n",
        "estimator_options = EstimatorOptions(resilience_level=2, default_shots=10_000)\n",
        "\n",
        "with Session(backend=backend) as session:\n",
        "    estimator = Estimator(mode=session, options=estimator_options)\n",
        "\n",
        "    pub = (isa_circuit, isa_observables, [individual_phases])\n",
        "    cost = estimator.run([pub]).result()[0].data.evs\n",
        "\n",
        "session.close()\n",
        "\n",
        "exp_values_with_em2_es = cost[0]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "48215ea6-3045-4880-8671-265e2fb33e5e",
      "metadata": {},
      "source": [
        "Wie zuvor können wir die resultierenden Erwartungswerte als Funktion des Phasenwinkels für die drei verwendeten Stufen der Fehlerminderung aufzeichnen. Mit großer Mühe kann man erkennen, dass die Fehlerbegrenzung die Ergebnisse leicht verbessert. Auch dieser Effekt ist in tieferen, komplizierteren Schaltkreisen viel stärker ausgeprägt.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 93,
      "id": "474f1a1a-ee90-468d-9390-fdb455aeb142",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/variational-algorithm-design/cost-functions/extracted-outputs/474f1a1a-ee90-468d-9390-fdb455aeb142-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "\n",
        "plt.plot(phases, exp_values_with_em0_es, \"o\", label=\"unmitigated\")\n",
        "plt.plot(phases, exp_values_with_em1_es, \"o\", label=\"resil = 1\")\n",
        "plt.plot(phases, exp_values_with_em2_es, \"o\", label=\"resil = 2\")\n",
        "plt.plot(phases, 2 * np.sin(phases / 2) ** 2 - 1, label=\"ideal\")\n",
        "plt.ylabel(\"Expectation\")\n",
        "plt.legend()\n",
        "plt.show()"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "1e683c07-96f3-47d5-8981-8d3aeb8be81f",
      "metadata": {},
      "source": [
        "<span id=\"summary\" />\n",
        "\n",
        "## Zusammenfassung\n",
        "\n",
        "In dieser Lektion haben Sie gelernt, wie man eine Kostenfunktion erstellt:\n",
        "\n",
        "* Erstellen einer Kostenfunktion\n",
        "* Wie man „ IBM Quantum “-Primitive nutzt, um Rauschen zu mindern und zu unterdrücken\n",
        "* Wie man eine Messstrategie definiert, um Geschwindigkeit und Genauigkeit zu optimieren\n",
        "\n",
        "Das ist unsere Arbeitsbelastung durch Variationen auf hohem Niveau:\n",
        "\n",
        "![Ein Diagramm, das den Quantenkreislauf mit den Einheiten zeigt, die den Referenzzustand und den Variationszustand vorbereiten, gefolgt von den Messungen. Diese werden verwendet, um die Kostenfunktion zu bewerten.](https://quantum.cloud.ibm.com/learning/images/courses/variational-algorithm-design/cost-functions/cost-function-circuit.svg)\n",
        "\n",
        "Unsere Kostenfunktion läuft während jeder Iteration der Optimierungsschleife. In der nächsten Lektion wird untersucht, wie der klassische Optimierer unsere Kostenfunktionsbewertung zur Auswahl neuer Parameter verwendet.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "2751c16d-a9c1-407b-ab88-a9dcf768bc2e",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "1.1.0\n",
            "0.23.0\n"
          ]
        }
      ],
      "source": [
        "import qiskit\n",
        "import qiskit_ibm_runtime\n",
        "\n",
        "print(qiskit.version.get_version_info())\n",
        "print(qiskit_ibm_runtime.version.get_version_info())"
      ]
    },
    {
      "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": 2
}