注釈
このページは `tutorials/algorithms/02_vqe_advanced_options.ipynb`__から生成されました。
高度なVQEオプション¶
最初のアルゴリズムチュートリアルでは、基本的な VQE アルゴリズムをどのように設定するかを学びました。VQE` <https://qiskit.org/documentation/stubs/qiskit.algorithms.minimum_eigensolvers.VQE.html>`__, QAOA, VQD など、Qiskitの変分アルゴリズムの能力をフルに発揮するために、より高度な設定パラメーターの与え方をみていただきます。特に、収束を監視するための callback
のセットアップ方法と、カスタム initial point
と gradient
の使用方法について説明します。
コールバック¶
コールバック・メソッドを使用すると、アルゴリズムの実行と最小値への収束に伴い、最適化の進捗状況を監視することができます。コールバックは、オプティマイザーによる機能評価ごとに呼び出され、現在のオプティマイザー値、評価回数、現在のオプティマイザー・パラメータなどを提供します。特定のオプティマイザーによっては、オプティマイザーの各反復(ステップ)とは限らないので、例えば、オプティマイザーが有限差分ベースの勾配を計算するためにコスト関数を呼び出している場合、コールバックを通じてこれが見えるようになることに注意してください。
このセクションでは、VQE
のコールバックを利用して、選択したオプティマイザーのセットで基底状態のエネルギーへの収束パスをプロットする方法を示します。
まず、VQE用の量子ビット演算子が必要です。この例では、アルゴリズム紹介で使用したものと同じ演算子を使用することができます。これは、もともとQiskit NatureによってH2分子に対して計算されたものです。
[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),
]
)
次のステップでは、VQE
内で期待値を評価するための Estimator
をインスタンス化します。簡単のために、Qiskit Terraのデフォルトインストールに同梱されている qiskit.primitives.Estimator
を選択することができます。
[2]:
from qiskit.primitives import Estimator
estimator = Estimator()
これで VQE
コールバックを使用してオプティマイザーを比較する準備が整いました。H2ハミルトニアンの最小エネルギーは非常に簡単に求めることができるので、最大反復回数(maxiter
)はあまり大きくする必要はありません。選択した試行波動関数(つまりansatz)として、再び TwoLocal
を使用することができます。
[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
保存されたコールバック・データから、各オプティマイザーが行う目的関数の呼び出しごとのエネルギー値をプロットすることができます。勾配の計算に有限差分法を用いたオプティマイザーでは、勾配を得るために多くの評価では近くにある点の値を計算していて、プロットのように特徴的なステップを示します。 (近くにある点は非常に似た値をもち、その差を今回のグラフのスケールで見ることはできません。)
[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");

最後に、上記の問題はまだ古典的に簡単に扱えるので、 NumPyMinimumEigensolver
を使って解の参照値を計算することができます。
[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
アルゴリズムが最小エネルギーに収束していく過程で、VQE
の解とこの正確な参照値との差をプロットすることができるようになりました。
[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");

勾配¶
Qiskit の変分アルゴリズムにおいて、提供されるオプティマイザーが勾配ベースの手法を使用する場合、デフォルトの勾配メソッドは有限差分法となります。しかし、これらのクラスには gradient
パラメーターを介してカスタム勾配を渡すオプションがあり、primitiveの使用を完全にサポートする Qiskit の gradient フレームワーク内で提供されるメソッドのいずれかを使用することが可能です。このセクションでは、VQEワークフローでカスタムgradients を使用する方法について説明します。
まず、対応するprimitive とprimitive gradient の両方を初期化します。
[7]:
from qiskit.algorithms.gradients import FiniteDiffEstimatorGradient
estimator = Estimator()
gradient = FiniteDiffEstimatorGradient(estimator, epsilon=0.01)
さて、上記の FiniteDiffEstimatorGradient
を用いて SLSQP の実行を検査することができます。
[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");

初期点(Initial point)¶
デフォルトでは、最適化はansatzで定義された境界内のランダムな点から開始されます。 initial_point
オプションを使用すると、この点を、ansatz のパラメーター数に応じたカスタムリストで上書きすることができます。
なぜカスタム初期点を設定するの? と思うかもしれません。このオプションは、問題の妥当な開始点についての推測がある場合、あるいは以前の実験からの情報を知っている場合に便利です。
この機能を実証するために、前回のVQEの実行結果を見てみましょう。
[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}
上記の結果から optimal_point
を取り出し、それを initial_point
として後続の計算に使用することができます。
注記: initial_point
は VQE
クラスのキーワードのみの引数になりました (つまり、 keyword=value
構文に従って設定する必要があります)。
[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.
cost_function_evals
を見ると、初期点がいかにアルゴリズムの収束を早めたかがわかります(すでに最適解を提供しているため、わずか1回の反復です)。
これは、特に、密接に関連した2つの問題があり、一方の問題の解から他方の問題を推測できるような場合に有効です。例えば、化学の解離プロファイルのプロットでは、分子の原子間距離を変化させ、それぞれの距離に対する最小固有値を計算することがよくあります。距離の変化が小さい場合、解はやはり以前の解に近いと考えられます。そのため、ある解の最適点を次のステップの出発点として利用するという手法が一般的です。また、より複雑な手法として、先行解を直接利用するのではなく、外挿を適用して先行解に基づく初期位置を計算する方法もあります。
[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.