Bemerkung
Diese Seite wurde aus tutorials/circuits_advanced/1_advanced_circuits.ipynb generiert.
Erweiterte WQE Einstellungen¶
In the first algorithms tutorial, you learned how to set up a basic VQE algorithm. Now, you will see how to provide more advanced configuration parameters to explore the full range of capabilities of Qiskit’s variational algorithms: VQE,
QAOA and VQD among others. In particular, this tutorial will cover how to set up a callback
to monitor convergence and the use of custom initial point
s and gradient
s.
Callback¶
Rückrufmethoden können zur Überwachung der Optimierungsfortschritte verwendet werden, da der Algorithmus ausgeführt wird und auf das Minimum konvergiert. Der Callback wird für jede funktionale Auswertung durch das Optimierungsprogramm aufgerufen und stellt den aktuellen Optimierungsprogrammwert, die Auswertungsanzahl, die aktuellen Optimierungsparameter des Optimierungsprogramms usw. bereit. Beachten Sie, dass dies, abhängig vom jeweiligen Optimierungsprogramm, nicht jede Iteration (Schritt) des Optimierungsprogramms sein kann, so dass zum Beispiel, wenn das Optimierungsprogramm die Kostenfunktion aufruft, um einen endlichen Unterschied basierten Gradienten zu berechnen, dies über den Callback sichtbar ist.
Dieser Abschnitt zeigt, wie man Callbacks in VQE
nutzen kann, um den Konvergenzpfad zur Grundzustandsenergie mit einem ausgewählten Satz von Optimierern zu plotten.
Zunächst benötigen Sie einen Qubit-Operator für VQE. Für dieses Beispiel können Sie den gleichen Operator wie in der Einführung der Algorithmen verwenden, der ursprünglich von Qiskit Nature für ein H2-Molekül berechnet wurde.
[1]:
from qiskit.quantum_info import SparsePauliOp
H2_op = SparsePauliOp.from_list(
[
("II", -1.052373245772859),
("IZ", 0.39793742484318045),
("ZI", -0.39793742484318045),
("ZZ", -0.01128010425623538),
("XX", 0.18093119978423156),
]
)
Der nächste Schritt besteht darin, den Estimator
der Wahl für die Auswertung der Erwartungswerte innerhalb von VQE
zu instanziieren. Der Einfachheit halber können Sie den qiskit.primitives.Estimator
wählen, der mit der Standardinstallation von Qiskit Terra geliefert wird.
[2]:
from qiskit.primitives import Estimator
estimator = Estimator()
You are now ready to compare a set of optimizers through the VQE
callback. The minimum energy of the H2 Hamiltonian can be found quite easily, so the maximum number of iterations (maxiter
) does not have to be very large. You can once again use TwoLocal
as the selected trial wavefunction (i.e. ansatz).
[3]:
import numpy as np
from qiskit.algorithms.minimum_eigensolvers import VQE
from qiskit.algorithms.optimizers import COBYLA, L_BFGS_B, SLSQP
from qiskit.circuit.library import TwoLocal
from qiskit.utils import algorithm_globals
# we will iterate over these different optimizers
optimizers = [COBYLA(maxiter=80), L_BFGS_B(maxiter=60), SLSQP(maxiter=60)]
converge_counts = np.empty([len(optimizers)], dtype=object)
converge_vals = np.empty([len(optimizers)], dtype=object)
for i, optimizer in enumerate(optimizers):
print("\rOptimizer: {} ".format(type(optimizer).__name__), end="")
algorithm_globals.random_seed = 50
ansatz = TwoLocal(rotation_blocks="ry", entanglement_blocks="cz")
counts = []
values = []
def store_intermediate_result(eval_count, parameters, mean, std):
counts.append(eval_count)
values.append(mean)
vqe = VQE(estimator, ansatz, optimizer, callback=store_intermediate_result)
result = vqe.compute_minimum_eigenvalue(operator=H2_op)
converge_counts[i] = np.asarray(counts)
converge_vals[i] = np.asarray(values)
print("\rOptimization complete ");
Optimization complete
Anhand der gespeicherten Rückrufdaten können Sie nun den Energiewert bei jedem Aufruf der Zielfunktion durch den Optimierer aufzeichnen. Ein Optimierer, der eine Finite-Differenzen-Methode zur Berechnung des Gradienten verwendet, hat diese charakteristische schrittweise Darstellung, bei der er für eine Reihe von Auswertungen den Wert für nahe beieinander liegende Punkte berechnet, um einen Gradienten zu ermitteln (die nahe beieinander liegenden Punkte haben sehr ähnliche Werte, deren Unterschied auf der Skala des Diagramms hier nicht zu sehen ist).
[4]:
import pylab
pylab.rcParams["figure.figsize"] = (12, 8)
for i, optimizer in enumerate(optimizers):
pylab.plot(converge_counts[i], converge_vals[i], label=type(optimizer).__name__)
pylab.xlabel("Eval count")
pylab.ylabel("Energy")
pylab.title("Energy convergence for various optimizers")
pylab.legend(loc="upper right");

Da das obige Problem klassisch immer noch leicht lösbar ist, können Sie NumPyMinimumEigensolver
verwenden, um einen Referenzwert für die Lösung zu berechnen.
[5]:
from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver
from qiskit.opflow import PauliSumOp
numpy_solver = NumPyMinimumEigensolver()
result = numpy_solver.compute_minimum_eigenvalue(operator=PauliSumOp(H2_op))
ref_value = result.eigenvalue.real
print(f"Reference value: {ref_value:.5f}")
Reference value: -1.85728
Sie können nun die Differenz zwischen der VQE
- Lösung und diesem exakten Referenzwert darstellen, während der Algorithmus gegen das Energieminimum konvergiert.
[6]:
pylab.rcParams["figure.figsize"] = (12, 8)
for i, optimizer in enumerate(optimizers):
pylab.plot(
converge_counts[i],
abs(ref_value - converge_vals[i]),
label=type(optimizer).__name__,
)
pylab.xlabel("Eval count")
pylab.ylabel("Energy difference from solution reference value")
pylab.title("Energy convergence for various optimizers")
pylab.yscale("log")
pylab.legend(loc="upper right");

Gradienten¶
In Qiskit’s variational algorithms, if the provided optimizer uses a gradient-based technique, the default gradient method will be finite differences. However, these classes include an option to pass custom gradients via the gradient
parameter, which can be any of the provided methods within Qiskit’s gradient framework, which fully supports the use of primitives. This section shows how to use custom gradients in
the VQE workflow.
Der erste Schritt besteht darin, sowohl das entsprechende Primitiv als auch den primitiven Gradienten zu initialisieren:
[7]:
from qiskit.algorithms.gradients import FiniteDiffEstimatorGradient
estimator = Estimator()
gradient = FiniteDiffEstimatorGradient(estimator, epsilon=0.01)
Nun können Sie einen SLSQP-Lauf mit Hilfe des FiniteDiffEstimatorGradient
von oben untersuchen:
[8]:
algorithm_globals.random_seed = 50
ansatz = TwoLocal(rotation_blocks="ry", entanglement_blocks="cz")
optimizer = SLSQP(maxiter=100)
counts = []
values = []
def store_intermediate_result(eval_count, parameters, mean, std):
counts.append(eval_count)
values.append(mean)
vqe = VQE(
estimator, ansatz, optimizer, callback=store_intermediate_result, gradient=gradient
)
result = vqe.compute_minimum_eigenvalue(operator=H2_op)
print(f"Value using Gradient: {result.eigenvalue.real:.5f}")
Value using Gradient: -1.85728
[9]:
pylab.rcParams["figure.figsize"] = (12, 8)
pylab.plot(counts, values, label=type(optimizer).__name__)
pylab.xlabel("Eval count")
pylab.ylabel("Energy")
pylab.title("Energy convergence using Gradient")
pylab.legend(loc="upper right");

Anfangspunkt¶
Standardmäßig beginnt die Optimierung an einem zufälligen Punkt innerhalb der durch den Ansatz definierten Grenzen. Die Option initial_point
erlaubt es, diesen Punkt mit einer benutzerdefinierten Liste von Werten zu überschreiben, die der Anzahl der Ansatzparameter entspricht.
You might wonder… Why set a custom initial point? Well, this option can come in handy if you have a guess for a reasonable starting point for the problem, or perhaps know information from a prior experiment.
To demonstrate this feature, let’s look at the results from our previous VQE run:
[10]:
print(result)
cost_function_evals = result.cost_function_evals
{ 'aux_operators_evaluated': None,
'cost_function_evals': 9,
'eigenvalue': -1.8572750175655812,
'optimal_circuit': <qiskit.circuit.library.n_local.two_local.TwoLocal object at 0x13ef7dd20>,
'optimal_parameters': { ParameterVectorElement(θ[0]): 4.296519450348719,
ParameterVectorElement(θ[3]): 6.092947832767056,
ParameterVectorElement(θ[1]): 4.426962358395531,
ParameterVectorElement(θ[7]): 0.36021017470898664,
ParameterVectorElement(θ[4]): -2.598326651673288,
ParameterVectorElement(θ[5]): 1.5683250498282322,
ParameterVectorElement(θ[2]): 0.5470777607659094,
ParameterVectorElement(θ[6]): -4.717616147449751},
'optimal_point': array([ 4.29651945, 4.42696236, 0.54707776, 6.09294783, -2.59832665,
1.56832505, -4.71761615, 0.36021017]),
'optimal_value': -1.8572750175655812,
'optimizer_evals': None,
'optimizer_result': <qiskit.algorithms.optimizers.optimizer.OptimizerResult object at 0x13010b6a0>,
'optimizer_time': 0.3502693176269531}
Nun können Sie den optimalen_Punkt
aus dem obigen Ergebnis nehmen und ihn als Initial_Punkt
für eine weitere Berechnung verwenden.
Anmerkung: initial_point
ist jetzt ein reines Schlüsselwort-Argument der Klasse VQE
(d.h. es muss nach der Syntax keyword=value
gesetzt werden).
[11]:
initial_pt = result.optimal_point
estimator1 = Estimator()
gradient1 = FiniteDiffEstimatorGradient(estimator, epsilon=0.01)
ansatz1 = TwoLocal(rotation_blocks="ry", entanglement_blocks="cz")
optimizer1 = SLSQP(maxiter=1000)
vqe1 = VQE(
estimator1, ansatz1, optimizer1, gradient=gradient1, initial_point=initial_pt
)
result1 = vqe1.compute_minimum_eigenvalue(operator=H2_op)
print(result1)
cost_function_evals1 = result1.cost_function_evals
print()
{ 'aux_operators_evaluated': None,
'cost_function_evals': 1,
'eigenvalue': -1.8572750175655812,
'optimal_circuit': <qiskit.circuit.library.n_local.two_local.TwoLocal object at 0x1411b9780>,
'optimal_parameters': { ParameterVectorElement(θ[0]): 4.296519450348719,
ParameterVectorElement(θ[1]): 4.426962358395531,
ParameterVectorElement(θ[4]): -2.598326651673288,
ParameterVectorElement(θ[5]): 1.5683250498282322,
ParameterVectorElement(θ[3]): 6.092947832767056,
ParameterVectorElement(θ[2]): 0.5470777607659094,
ParameterVectorElement(θ[6]): -4.717616147449751,
ParameterVectorElement(θ[7]): 0.36021017470898664},
'optimal_point': array([ 4.29651945, 4.42696236, 0.54707776, 6.09294783, -2.59832665,
1.56832505, -4.71761615, 0.36021017]),
'optimal_value': -1.8572750175655812,
'optimizer_evals': None,
'optimizer_result': <qiskit.algorithms.optimizers.optimizer.OptimizerResult object at 0x1411e3f10>,
'optimizer_time': 0.05097508430480957}
[12]:
print(
f"cost_function_evals is {cost_function_evals1} with initial point versus {cost_function_evals} without it."
)
cost_function_evals is 1 with initial point versus 9 without it.
Wenn man sich die cost_function_evals
ansieht, kann man feststellen, wie der Ausgangspunkt dem Algorithmus geholfen hat, schneller zu konvergieren (in nur 1 Iteration, da wir bereits die optimale Lösung geliefert haben).
This can be particularly useful in cases where we have two closely related problems, and the solution to one problem can be used to guess the other’s. A good example might be plotting dissociation profiles in chemistry, where we change the inter-atomic distances of a molecule and compute its minimum eigenvalue for each distance. When the distance changes are small, we expect the solution to still be close to the prior one. Thus, a popular technique is to simply use the optimal point from one solution as the starting point for the next step. There also exist more complex techniques, where we can apply extrapolation to compute an initial position based on prior solution(s) rather than directly use the prior solution.
[13]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
qiskit-terra | 0.22.2 |
qiskit-aer | 0.11.1 |
qiskit-ignis | 0.7.1 |
qiskit-ibmq-provider | 0.19.2 |
qiskit | 0.39.2 |
System information | |
Python version | 3.10.2 |
Python compiler | Clang 13.0.0 (clang-1300.0.29.30) |
Python build | v3.10.2:a58ebcc701, Jan 13 2022 14:50:16 |
OS | Darwin |
CPUs | 8 |
Memory (Gb) | 64.0 |
Fri Nov 18 01:08:34 2022 CET |
This code is a part of Qiskit
© Copyright IBM 2017, 2022.
This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.