Optimierungsschleifen
In dieser Lektion lernen wir, wie man einen Optimierer einsetzt, um die parametrisierten Quantenzustände unseres Ansatzes iterativ zu erkunden:
- Eine Optimierungsschleife bootstrappen
- Kompromisse beim Einsatz lokaler und globaler Optimierer verstehen
- Barren Plateaus erkunden und wie man sie vermeidet
Auf hoher Ebene sind Optimierer zentral für die Erkundung unseres Suchraums. Der Optimierer nutzt Auswertungen der Kostenfunktion, um in einer variationalen Schleife den nächsten Parametersatz auszuwählen, und wiederholt diesen Prozess, bis ein stabiler Zustand erreicht ist. In diesem Stadium wird ein optimaler Satz von Parameterwerten zurückgegeben.
Lokale und globale Optimierer
Zunächst richten wir unser Problem ein, bevor wir jede Optimiererklasse erkunden. Wir beginnen mit einem Circuit, der acht variationale Parameter enthält:
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit scipy
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.circuit.library import TwoLocal
import numpy as np
theta_list = (2 * np.pi * np.random.rand(1, 8)).tolist()
observable = SparsePauliOp.from_list([("XX", 1), ("YY", -3)])
reference_circuit = QuantumCircuit(2)
reference_circuit.x(0)
variational_form = TwoLocal(
2,
rotation_blocks=["rz", "ry"],
entanglement_blocks="cx",
entanglement="linear",
reps=1,
)
ansatz = reference_circuit.compose(variational_form)
ansatz.decompose().draw("mpl")
def cost_func_vqe(params, ansatz, hamiltonian, estimator):
"""Return estimate of energy from estimator
Parameters:
params (ndarray): Array of ansatz parameters
ansatz (QuantumCircuit): Parameterized ansatz circuit
hamiltonian (SparsePauliOp): Operator representation of Hamiltonian
estimator (Estimator): Estimator primitive instance
Returns:
float: Energy estimate
"""
pub = (ansatz, hamiltonian, params)
cost = estimator.run([pub]).result()[0].data.evs
return cost
from qiskit.primitives import StatevectorEstimator
estimator = StatevectorEstimator()
Lokale Optimierer
Lokale Optimierer suchen nach einem Punkt, der die Kostenfunktion minimiert, ausgehend von einem oder mehreren Anfangspunkten , und wechseln zu anderen Punkten basierend auf dem, was sie in der Region beobachten, die sie bei aufeinanderfolgenden Iterationen auswerten. Das bedeutet, dass die Konvergenz dieser Algorithmen in der Regel schnell ist, aber stark vom Anfangspunkt abhängen kann. Lokale Optimierer können nicht über die Region hinaussehen, in der sie auswerten, und sind besonders anfällig für lokale Minima: Sie melden Konvergenz, sobald sie eines finden, und ignorieren andere Zustände mit günstigeren Auswertungen.
# SciPy minimizer routine
from scipy.optimize import minimize
x0 = np.ones(8)
result = minimize(
cost_func_vqe, x0, args=(ansatz, observable, estimator), method="SLSQP"
)
result
message: Optimization terminated successfully
success: True
status: 0
fun: -3.9999999964520634
x: [ 1.000e+00 1.000e+00 -1.571e+00 -4.556e-05 -1.207e+00
-1.935e+00 4.079e-01 -4.079e-01]
nit: 12
jac: [ 0.000e+00 0.000e+00 -7.957e-04 2.543e-04 1.381e-03
1.381e-03 5.430e-04 5.431e-04]
nfev: 112
njev: 12
Globale Optimierer
Globale Optimierer suchen nach dem Punkt, der die Kostenfunktion über mehrere Regionen ihres Definitionsbereichs hinweg minimiert (d.h. nicht-lokal), und werten sie iterativ (d.h. bei Iteration ) über eine Menge von Parametervektoren aus, die vom Optimierer bestimmt wird. Dadurch sind sie weniger anfällig für lokale Minima und etwas unabhängig von der Initialisierung, konvergieren jedoch deutlich langsamer zu einer vorgeschlagenen Lösung.
Bootstrapping der Optimierung
Bootstrapping, also das Setzen des Anfangswerts für die Parameter basierend auf einer vorherigen Optimierung, kann unserem Optimierer helfen, schneller zu einer Lösung zu konvergieren. Wir bezeichnen dies als Anfangspunkt , und als Anfangszustand. Dieser Anfangszustand unterscheidet sich von unserem Referenzzustand : Ersterer konzentriert sich auf anfängliche Parameter, die während unserer Optimierungsschleife gesetzt werden, während Letzterer auf die Verwendung bekannter „Referenz"-Lösungen ausgerichtet ist. Sie können übereinstimmen, wenn gilt (d.h. die Identitätsoperation).
Wenn lokale Optimierer zu nicht-optimalen lokalen Minima konvergieren, können wir versuchen, die Optimierung global zu bootstrappen und die Konvergenz lokal zu verfeinern. Obwohl dies die Einrichtung zweier variationaler Arbeitsabläufe erfordert, ermöglicht es dem Optimierer, eine optimalere Lösung zu finden als der lokale Optimierer allein.
Gradientenbasierte und gradientenfreie Optimierer
Gradientenbasiert
Für unsere Kostenfunktion gilt: Wenn wir Zugriff auf den Gradienten der Funktion ab einem Anfangspunkt haben, ist der einfachste Weg zur Minimierung der Funktion, die Parameter in Richtung des steilsten Abstiegs zu aktualisieren. Das heißt, wir aktualisieren die Parameter als , wobei die Lernrate ist – ein kleiner, positiver Hyperparameter, der die Größe der Aktualisierung steuert. Wir setzen dies fort, bis wir zu einem lokalen Minimum der Kostenfunktion konvergieren. Wir können diese Kostenfunktion und einen Optimierer verwenden, um optimale Parameter zu berechnen.
# SciPy minimizer routine
from scipy.optimize import minimize
x0 = np.ones(8)
result = minimize(
cost_func_vqe, x0, args=(ansatz, observable, estimator), method="BFGS"
)
result
message: Optimization terminated successfully.
success: True
status: 0
fun: -3.9999999999997025
x: [ 1.000e+00 1.000e+00 1.571e+00 3.220e-07 2.009e-01
-2.009e-01 6.342e-01 -6.342e-01]
nit: 14
jac: [-1.192e-07 -2.980e-08 8.345e-07 1.103e-06 5.960e-08
0.000e+00 -5.960e-08 2.980e-08]
hess_inv: [[ 1.000e+00 1.872e-10 ... 5.077e-05 3.847e-05]
[ 1.872e-10 1.000e+00 ... -5.208e-05 -4.060e-05]
...
[ 5.077e-05 -5.208e-05 ... 7.243e-01 -2.604e-01]
[ 3.847e-05 -4.060e-05 ... -2.604e-01 8.179e-01]]
nfev: 144
njev: 16
Die wesentlichen Nachteile dieser Art der Optimierung sind die Konvergenzgeschwindigkeit, die sehr langsam sein kann, und die fehlende Garantie, die optimale Lösung zu erreichen.
Gradientenfrei
Gradientenfreie Optimierungsalgorithmen benötigen keine Gradienteninformation und können in Situationen nützlich sein, in denen die Berechnung des Gradienten schwierig, teuer oder zu verrauscht ist. Sie tendieren auch dazu, robuster bei der Suche nach globalen Optima zu sein, während gradientenbasierte Methoden dazu neigen, zu lokalen Optima zu konvergieren. Wir werden einige Szenarien erkunden, in denen ein gradientenfreier Optimierer Barren Plateaus helfen kann zu vermeiden. Gradientenfreie Methoden erfordern jedoch höhere Rechenressourcen, insbesondere bei Problemen mit hochdimensionalen Suchräumen.
Hier ist ein Beispiel, das stattdessen den COBYLA-Optimierer verwendet:
# SciPy minimizer routine
from scipy.optimize import minimize
x0 = np.ones(8)
result = minimize(
cost_func_vqe, x0, args=(ansatz, observable, estimator), method="COBYLA"
)
result
message: Optimization terminated successfully.
success: True
status: 1
fun: -3.999999973369678
x: [ 1.631e+00 1.492e+00 1.571e+00 3.142e+00 1.375e+00
-1.767e+00 1.484e+00 1.658e+00]
nfev: 137
maxcv: 0.0
Barren Plateaus
Tatsächlich kann die Kostenlandschaft ziemlich komplex sein, wie die Hügel und Täler im folgenden Beispiel zeigen. Die Optimierungsmethode navigiert uns durch die Kostenlandschaft auf der Suche nach dem Minimum, dargestellt durch die schwarzen Punkte und Linien. Wir können sehen, dass zwei der drei Suchen in einem lokalen Minimum der Landschaft enden, statt im globalen.
Unabhängig von der verwendeten Optimierungsmethode kann es schwierig sein, die geeignete Suchrichtung zu bestimmen, wenn die Kostenlandschaft relativ flach ist. Dieses Szenario wird als Barren Plateau bezeichnet, bei dem die Kostenlandschaft zunehmend flacher wird (und es damit schwieriger wird, die Richtung zum Minimum zu bestimmen). Für eine breite Palette parametrisierter Quantencircuits nimmt die Wahrscheinlichkeit, dass der Gradient in einer vernünftigen Richtung ungleich null ist (bei einer bestimmten Genauigkeit), exponentiell mit der Anzahl der Qubits ab.
Während dieses Gebiet noch aktiv erforscht wird, haben wir einige Empfehlungen zur Verbesserung der Optimierungsleistung:
- Bootstrapping kann helfen, zu verhindern, dass die Optimierungsschleife in einem Parameterraum stecken bleibt, in dem der Gradient klein ist.
- 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 hardware-effizienter Ansätze, wie
EfficientSU2, kann die Erzeugung exponentiell kleiner Gradienten vermeiden. - Experimentieren mit Fehlersuppression und Fehlerminderung: Die Qiskit Runtime Primitives bieten eine einfache Schnittstelle, um mit verschiedenen Werten für
optimization_levelbzw.resilience_settingzu experimentieren. Dies kann den Einfluss von Rauschen verringern und den Optimierungsprozess effizienter gestalten. - Experimentieren mit gradientenfreien Optimierern: Im Gegensatz zu gradientenbasierten Optimierungsalgorithmen stützen sich Optimierer wie
COBYLAnicht auf Gradienteninformationen zur Parameteroptimierung und sind daher weniger anfällig für das Barren-Plateau-Problem.
Zusammenfassung
In dieser Lektion hast du gelernt, wie du deine Optimierungsschleife definierst:
- Eine Optimierungsschleife bootstrappen
- Kompromisse beim Einsatz lokaler und globaler Optimierer verstehen
- Barren Plateaus erkunden und wie man sie vermeidet
Unser variationaler Arbeitsablauf auf hoher Ebene ist nun vollständig:
Als Nächstes werden wir spezifische variationale Algorithmen mit diesem Framework erkunden.