Japanese
言語
English
Bengali
French
German
Japanese
Korean
Portuguese
Spanish
Tamil

注釈

このページは `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 pointgradient の使用方法について説明します。

コールバック

コールバック・メソッドを使用すると、アルゴリズムの実行と最小値への収束に伴い、最適化の進捗状況を監視することができます。コールバックは、オプティマイザーによる機能評価ごとに呼び出され、現在のオプティマイザー値、評価回数、現在のオプティマイザー・パラメータなどを提供します。特定のオプティマイザーによっては、オプティマイザーの各反復(ステップ)とは限らないので、例えば、オプティマイザーが有限差分ベースの勾配を計算するためにコスト関数を呼び出している場合、コールバックを通じてこれが見えるようになることに注意してください。

このセクションでは、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");
../../_images/tutorials_algorithms_02_vqe_advanced_options_10_0.png

最後に、上記の問題はまだ古典的に簡単に扱えるので、 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");
../../_images/tutorials_algorithms_02_vqe_advanced_options_14_0.png

勾配

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");
../../_images/tutorials_algorithms_02_vqe_advanced_options_19_0.png

初期点(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_pointVQE クラスのキーワードのみの引数になりました (つまり、 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 SoftwareVersion
qiskit-terra0.22.2
qiskit-aer0.11.1
qiskit-ignis0.7.1
qiskit-ibmq-provider0.19.2
qiskit0.39.2
System information
Python version3.10.2
Python compilerClang 13.0.0 (clang-1300.0.29.30)
Python buildv3.10.2:a58ebcc701, Jan 13 2022 14:50:16
OSDarwin
CPUs8
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.