{
  "cells": [
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "2a246d14-ed2b-4573-bf97-939c3628b3bb",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Optimierungsschleifen\"\n",
        "description: \"In dieser Lektion wird die Verwendung klassischer Optimierungsschleifen zur Eingabe von Parametern in Quantenberechnungen erläutert.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore nabla */}\n",
        "\n",
        "<span id=\"optimization-loops\" />\n",
        "\n",
        "# Optimierungsschleifen\n",
        "\n",
        "In dieser Lektion werden wir lernen, wie man einen *Optimierer* verwendet, um die parametrisierten Quantenzustände unseres Ansatzes iterativ zu untersuchen:\n",
        "\n",
        "* Bootstrap einer Optimierungsschleife\n",
        "* Verstehen von Kompromissen bei der Verwendung lokaler und globaler Optimierer\n",
        "* Erforschen Sie unfruchtbare Plateaus und wie Sie sie vermeiden können\n",
        "\n",
        "Auf einer hohen Ebene sind Optimierer von zentraler Bedeutung für die Erkundung unseres Suchraums. Der Optimierer verwendet Kostenfunktionsbewertungen, um den nächsten Satz von Parametern in einer Variationsschleife auszuwählen, und wiederholt den Prozess, bis er einen stabilen Zustand erreicht. In diesem Stadium wird ein optimaler Satz von Parameterwerten $\\vec\\theta^*$ zurückgegeben.\n",
        "\n",
        "![Ein Diagramm einiger wichtiger Faktoren bei der Optimierung, einschließlich unfruchtbarer Plateaus, gradientenbasierter vs. gradientenfreier Optimierer und Bootstrapping.](https://quantum.cloud.ibm.com/learning/images/courses/variational-algorithm-design/optimization-loops/optimization-workflow.svg)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "110421e7-0d2a-4653-a195-1925c809ca61",
      "metadata": {},
      "source": [
        "<span id=\"local-and-global-optimizers\" />\n",
        "\n",
        "## Lokale und globale Optimierer\n",
        "\n",
        "Wir werden zunächst unser Problem aufstellen, bevor wir die einzelnen Optimierungsklassen untersuchen. Wir beginnen mit einer Schaltung, die acht Variationsparameter enthält:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "15cf7810-1d25-4c54-aff0-91d3a0c51cec",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/variational-algorithm-design/optimization-loops/extracted-outputs/15cf7810-1d25-4c54-aff0-91d3a0c51cec-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",
        "from qiskit.circuit.library import TwoLocal\n",
        "import numpy as np\n",
        "\n",
        "theta_list = (2 * np.pi * np.random.rand(1, 8)).tolist()\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",
        "ansatz.decompose().draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "950569c0-4e8f-49b5-b118-7cd227bd5ce1",
      "metadata": {},
      "outputs": [],
      "source": [
        "def cost_func_vqe(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",
        "    return cost"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "f536ee50-373f-4873-b258-66c5c07c65c5",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.primitives import StatevectorEstimator\n",
        "\n",
        "estimator = StatevectorEstimator()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c9dcec4d-5b84-4731-8b5a-cb8a0f7572d5",
      "metadata": {},
      "source": [
        "<span id=\"local-optimizers\" />\n",
        "\n",
        "### Lokale Optimierer\n",
        "\n",
        "Lokale Optimierer suchen nach einem Punkt, der die Kostenfunktion minimiert, beginnend mit einem oder mehreren Anfangspunkten $C(\\vec{\\theta_0})$ und bewegen sich in aufeinanderfolgenden Iterationen zu verschiedenen Punkten, je nachdem, was sie in der Region, die sie gerade bewerten, beobachten. Dies bedeutet, dass die Konvergenz dieser Algorithmen in der Regel schnell ist, aber stark vom Ausgangspunkt abhängen kann. Lokale Optimierer sind nicht in der Lage, über die Region, in der sie bewerten, hinauszuschauen, und können besonders anfällig für lokale Minima sein, indem sie Konvergenz melden, wenn sie eines finden, und andere Zustände mit günstigeren Bewertungen ignorieren.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "447df742-13bd-4d4a-a364-41d34380cbc9",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              " message: Optimization terminated successfully\n",
              " success: True\n",
              "  status: 0\n",
              "     fun: -3.9999999964520634\n",
              "       x: [ 1.000e+00  1.000e+00 -1.571e+00 -4.556e-05 -1.207e+00\n",
              "           -1.935e+00  4.079e-01 -4.079e-01]\n",
              "     nit: 12\n",
              "     jac: [ 0.000e+00  0.000e+00 -7.957e-04  2.543e-04  1.381e-03\n",
              "            1.381e-03  5.430e-04  5.431e-04]\n",
              "    nfev: 112\n",
              "    njev: 12"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# SciPy minimizer routine\n",
        "from scipy.optimize import minimize\n",
        "\n",
        "x0 = np.ones(8)\n",
        "\n",
        "result = minimize(\n",
        "    cost_func_vqe, x0, args=(ansatz, observable, estimator), method=\"SLSQP\"\n",
        ")\n",
        "\n",
        "result"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "25fee3f4-9d3e-45ce-a104-dfde5e132b2b",
      "metadata": {},
      "source": [
        "<span id=\"global-optimizers\" />\n",
        "\n",
        "### Globale Optimierer\n",
        "\n",
        "Globale Optimierer suchen nach dem Punkt, der die Kostenfunktion über mehrere Regionen ihres Bereichs minimiert (d.h. nicht lokal), indem sie diese iterativ (d.h. bei der Iteration $i$ ) über einen vom Optimierer festgelegten Satz von Parametervektoren $\\Theta_i := \\\\{ {\\vec\\theta_{i,j} | j \\in \\mathcal{J}_\\text{opt}^i} \\\\}$ auswerten. Dadurch sind sie weniger anfällig für lokale Minima und einigermaßen unabhängig von der Initialisierung, konvergieren aber auch deutlich langsamer zu einer vorgeschlagenen Lösung.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8d794038-8e75-4649-a978-a9e832ebed60",
      "metadata": {},
      "source": [
        "<span id=\"bootstrapping-optimization\" />\n",
        "\n",
        "### Bootstrapping-Optimierung\n",
        "\n",
        "*Bootstrapping*, d. h. die Festlegung der Anfangswerte für die Parameter $\\vec\\theta$ auf der Grundlage einer vorherigen Optimierung, kann unserem Optimierer helfen, schneller zu einer Lösung zu konvergieren. Wir bezeichnen dies als den Anfangspunkt $\\vec\\theta_0$ und $|\\psi(\\vec\\theta_0)\\rangle = U_V(\\vec\\theta_0)|\\rho\\rangle$ als den Anfangszustand. Dieser Ausgangszustand unterscheidet sich von unserem Referenzzustand $|\\rho\\rangle$, da ersterer sich auf die Anfangsparameter konzentriert, die während unserer Optimierungsschleife festgelegt werden, während letzterer sich auf die Verwendung bekannter \"Referenz\"-Lösungen konzentriert. Sie können übereinstimmen, wenn $U_V(\\vec\\theta_0) \\equiv I$ (d.h. die Identitätsoperation).\n",
        "\n",
        "Wenn lokale Optimierer zu nicht-optimalen lokalen Minima konvergieren, können wir versuchen, die Optimierung global zu booten und die Konvergenz lokal zu verfeinern. Dies erfordert zwar die Einrichtung von zwei Variationsrechnungen, ermöglicht es dem Optimierer jedoch, eine optimalere Lösung zu finden als der lokale Optimierer allein.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "07ba982e-9280-4e15-9d8c-4b6460b1deea",
      "metadata": {
        "gloss": {
          "hyperparameter": {
            "text": "A hyperparameter is a parameter that we use to control our algorithm. The ‘hyper’ distinguishes it from the parameters (θ) that our algorithm is trying to find.",
            "title": "Hyperparameter"
          },
          "local-minimum": {
            "text": "A local minimum is the lowest point of the function, for a small range of values of theta. In contrast, a global minimum is <i>the</i> lowest point, anywhere in our function (that is, for any value of theta). <a href='https://en.wikipedia.org/wiki/Maxima_and_minima'>Read more</a>.",
            "title": "Local minimum"
          }
        }
      },
      "source": [
        "<span id=\"gradient-based-and-gradient-free-optimizers\" />\n",
        "\n",
        "## Gradientenbasierte und gradientenfreie Optimierer\n",
        "\n",
        "<span id=\"gradient-based\" />\n",
        "\n",
        "### Gradientenbasiert\n",
        "\n",
        "Wenn wir für unsere Kostenfunktion $C(\\vec\\theta)$ den Gradienten der Funktion $\\vec{\\nabla} C(\\vec\\theta)$ ausgehend von einem Anfangspunkt kennen, ist die einfachste Art, die Funktion zu minimieren, die Aktualisierung der Parameter in Richtung des steilsten Abstiegs der Funktion. Das heißt, wir aktualisieren die Parameter mit $\\vec\\theta_{n+1} = \\vec\\theta_n - \\eta \\vec{\\nabla} C(\\vec\\theta)$, wobei $\\eta$ die Lernrate ist - eine kleine, positive <DefinitionTooltip definition=\"Ein Hyperparameter ist ein Parameter, den wir zur Steuerung unseres Algorithmus verwenden. Der Begriff Hyper unterscheidet ihn von den Parametern (θ), die unser Algorithmus zu finden versucht.\">Hyperparameter</DefinitionTooltip>, die die Größe der Aktualisierung steuert. Wir fahren damit fort, bis wir zu einer <DefinitionTooltip definition=\"Ein lokales Minimum ist der niedrigste Punkt der Funktion für einen kleinen Wertebereich von Theta. Im Gegensatz dazu ist ein globales Minimum der niedrigste Punkt irgendwo in unserer Funktion (d. h. für jeden Wert von θ).\">lokales Minimum</DefinitionTooltip> der Kostenfunktion konvergieren, $C({\\vec\\theta^*})$.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "0897260d-f3f8-4caa-8a79-f258e41f8e21",
      "metadata": {},
      "source": [
        "Mit dieser Kostenfunktion und einem Optimierer können wir die optimalen Parameter berechnen\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "aad81101-3c19-4946-8453-3db9df3369c0",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "  message: Optimization terminated successfully.\n",
              "  success: True\n",
              "   status: 0\n",
              "      fun: -3.9999999999997025\n",
              "        x: [ 1.000e+00  1.000e+00  1.571e+00  3.220e-07  2.009e-01\n",
              "            -2.009e-01  6.342e-01 -6.342e-01]\n",
              "      nit: 14\n",
              "      jac: [-1.192e-07 -2.980e-08  8.345e-07  1.103e-06  5.960e-08\n",
              "             0.000e+00 -5.960e-08  2.980e-08]\n",
              " hess_inv: [[ 1.000e+00  1.872e-10 ...  5.077e-05  3.847e-05]\n",
              "            [ 1.872e-10  1.000e+00 ... -5.208e-05 -4.060e-05]\n",
              "            ...\n",
              "            [ 5.077e-05 -5.208e-05 ...  7.243e-01 -2.604e-01]\n",
              "            [ 3.847e-05 -4.060e-05 ... -2.604e-01  8.179e-01]]\n",
              "     nfev: 144\n",
              "     njev: 16"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# SciPy minimizer routine\n",
        "from scipy.optimize import minimize\n",
        "\n",
        "x0 = np.ones(8)\n",
        "\n",
        "result = minimize(\n",
        "    cost_func_vqe, x0, args=(ansatz, observable, estimator), method=\"BFGS\"\n",
        ")\n",
        "\n",
        "result"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "0f7d55f8-e3a2-4223-b456-17b39a81f524",
      "metadata": {},
      "source": [
        "Die Hauptnachteile dieser Art von Optimierung sind die Konvergenzgeschwindigkeit, die sehr langsam sein kann, und die Tatsache, dass es keine Garantie für das Erreichen der optimalen Lösung gibt.\n",
        "\n",
        "![diagramm von f(theta) gegen theta, mehrere Punkte zeigen verschiedene Zustände eines Gradientenabstiegsalgorithmus, der das Minimum einer Kurve findet.](https://quantum.cloud.ibm.com/learning/images/courses/variational-algorithm-design/optimization-loops/optimization-gradient-descent.svg)\n",
        "\n",
        "<span id=\"gradient-free\" />\n",
        "\n",
        "### Gradientenfrei\n",
        "\n",
        "Gradientenfreie Optimierungsalgorithmen benötigen keine Gradienteninformationen und können in Situationen nützlich sein, in denen die Berechnung des Gradienten schwierig, teuer oder zu verrauscht ist. Sie sind auch robuster bei der Suche nach globalen Optima, während gradientenbasierte Methoden dazu neigen, zu lokalen Optima zu konvergieren. Wir werden einige Fälle untersuchen, in denen ein gradientenfreier Optimierer helfen kann, unfruchtbare Plateaus zu vermeiden. Allerdings erfordern gradientenfreie Verfahren einen höheren Rechenaufwand, insbesondere bei Problemen mit hochdimensionalen Suchräumen.\n",
        "\n",
        "Hier ist ein Beispiel, das den [`COBYLA`](https://docs.scipy.org/doc/scipy/reference/optimize.minimize-cobyla.html#optimize-minimize-cobyla) optimierer verwendet:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "c749695b-3124-47d8-8a23-10c9d5965a7f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              " message: Optimization terminated successfully.\n",
              " success: True\n",
              "  status: 1\n",
              "     fun: -3.999999973369678\n",
              "       x: [ 1.631e+00  1.492e+00  1.571e+00  3.142e+00  1.375e+00\n",
              "           -1.767e+00  1.484e+00  1.658e+00]\n",
              "    nfev: 137\n",
              "   maxcv: 0.0"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# SciPy minimizer routine\n",
        "from scipy.optimize import minimize\n",
        "\n",
        "x0 = np.ones(8)\n",
        "\n",
        "result = minimize(\n",
        "    cost_func_vqe, x0, args=(ansatz, observable, estimator), method=\"COBYLA\"\n",
        ")\n",
        "\n",
        "result"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "396d0617-9f20-48cb-b57c-30dfa54293ff",
      "metadata": {
        "gloss": {
          "barren-plateaus": {
            "text": "When gradients of parametrized quantum circuits become exponentially small with respect to the number of qubits, making optimization difficult and potentially impossible.",
            "title": "Barren Plateaus"
          }
        }
      },
      "source": [
        "<span id=\"barren-plateaus\" />\n",
        "\n",
        "## Karge Hochebenen\n",
        "\n",
        "In der Tat kann die Kostenlandschaft recht kompliziert sein, wie die Hügel und Täler im folgenden Beispiel zeigen. Die Optimierungsmethode navigiert uns durch die Kostenlandschaft und sucht nach dem Minimum, wie durch die schwarzen Punkte und Linien dargestellt. Wir können sehen, dass zwei der drei Suchen in einem lokalen Minimum der Landschaft enden, anstatt in einem globalen.\n",
        "\n",
        "![Ein komplizierter gekrümmter Verteiler mit vielen Spitzen und Tälern.](https://quantum.cloud.ibm.com/learning/images/courses/variational-algorithm-design/optimization-loops/optimization-loss-landscape.svg)\n",
        "\n",
        "Unabhängig von der Art der verwendeten Optimierungsmethode kann es für die Methode schwierig sein, die geeignete Suchrichtung zu bestimmen, wenn die Kostenlandschaft relativ flach ist. Dieses Szenario wird als <DefinitionTooltip definition=\"Wenn die Gradienten parametrisierter Quantenschaltungen im Verhältnis zur Anzahl der Qubits exponentiell klein werden, wird die Optimierung schwierig und möglicherweise unmöglich.\">karge Hochebene,</DefinitionTooltip> bezeichnet, bei dem die Kostenlandschaft immer flacher wird (und es somit schwieriger ist, die Richtung zum Minimum zu bestimmen). Für ein breites Spektrum parametrisierter Quantenschaltungen nimmt die Wahrscheinlichkeit, dass der Gradient entlang einer vernünftigen Richtung mit einer festen Genauigkeit ungleich Null ist, exponentiell ab, wenn die Anzahl der Qubits steigt.\n",
        "\n",
        "![Ein Diagramm eines geografischen Plateaus im Vergleich zu einem Berghang, um zu erklären, warum ein Gefälle uns hilft, ein Minimum zu finden, und ein Plateau unsere Bemühungen behindert.](https://quantum.cloud.ibm.com/learning/images/courses/variational-algorithm-design/optimization-loops/optimization-barren-plateaus.svg)\n",
        "\n",
        "Obwohl dieser Bereich noch Gegenstand aktiver Forschung ist, haben wir einige Empfehlungen zur Verbesserung der Optimierungsleistung:\n",
        "\n",
        "* **Bootstrapping** kann der Optimierungsschleife helfen, nicht in einem Parameterraum stecken zu bleiben, in dem der Gradient klein ist.\n",
        "* **Experimentieren mit hardware-effizienten Ansätzen** : Da wir ein verrauschtes Quantensystem als Blackbox-Orakel verwenden, kann die Qualität dieser Auswertungen die Leistung des Optimierers beeinflussen. Die Verwendung von hardwareeffizienten Ansätzen, wie z. B [`EfficientSU2`](/docs/api/qiskit/qiskit.circuit.library.EfficientSU2)kann die Erzeugung exponentiell kleiner Gradienten vermeiden.\n",
        "* **Experimente zur Fehlerunterdrückung und Fehlerminderung** : Die Primitive „ IBM Quantum “ bieten eine einfache Schnittstelle, um mit verschiedenen Werten für bzw `optimization_level``resilience_setting` . zu experimentieren. Dadurch lassen sich die Auswirkungen von Rauschen verringern und der Optimierungsprozess effizienter gestalten.\n",
        "* **Experimentieren mit gradientenfreien Optimierern** : Im Gegensatz zu gradientenbasierten Optimierungsalgorithmen stützen sich Optimierer wie `COBYLA` nicht auf Gradienteninformationen, um die Parameter zu optimieren, und sind daher weniger wahrscheinlich von dem unfruchtbaren Plateau betroffen.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "182110bc-f952-4460-8048-d1df961ba860",
      "metadata": {},
      "source": [
        "<span id=\"summary\" />\n",
        "\n",
        "## Zusammenfassung\n",
        "\n",
        "In dieser Lektion haben Sie gelernt, wie Sie Ihre Optimierungsschleife definieren:\n",
        "\n",
        "* Bootstrap einer Optimierungsschleife\n",
        "* Verstehen von Kompromissen bei der Verwendung lokaler und globaler Optimierer\n",
        "* Erforschen Sie unfruchtbare Plateaus und wie Sie sie vermeiden können\n",
        "\n",
        "Unsere Variationsarbeit auf hohem Niveau ist abgeschlossen:\n",
        "\n",
        "![Ein Quantenschaltkreis mit einer Unitary, die den Referenzzustand herstellt, und einer zweiten Unitary, die den Zustand durch Variationsparameter verändert.](https://quantum.cloud.ibm.com/learning/images/courses/variational-algorithm-design/optimization-loops/optimization-circuit.svg)\n",
        "\n",
        "Als Nächstes werden wir spezifische Variationsalgorithmen unter Berücksichtigung dieses Rahmens untersuchen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}