注釈
このページは tutorials/algorithms/04_vqe_advanced.ipynb から生成されました。
VQE の高度な使い方¶
VQE のより高度な機能を設定して使用するために、いくつかのパラメーターが用意されています。このチュートリアルでは、 initial_point
, expectation
, gradient
などのパラメーターについて説明します。
また、 Matrix Product State 法による Aer の利用法など、シミュレーターの高度な使用法についても解説します。
[1]:
from qiskit import Aer
from qiskit.opflow import X, Z, I
from qiskit.utils import QuantumInstance, algorithm_globals
from qiskit.algorithms import VQE
from qiskit.algorithms.optimizers import SLSQP
from qiskit.circuit.library import TwoLocal
ここでは、他の VQE アルゴリズムのチュートリアルで用いたものと同じ演算子を使用します。
[2]:
H2_op = (-1.052373245772859 * I ^ I) + \
(0.39793742484318045 * I ^ Z) + \
(-0.39793742484318045 * Z ^ I) + \
(-0.01128010425623538 * Z ^ Z) + \
(0.18093119978423156 * X ^ X)
初期点(Initial point)¶
initial_point
パラメーターによって、最適化の開始点を指定することができます。この点はansatzを構成するパラメーターのリストです。デフォルトで初期値は None
であり、この場合 VQE が初期点を選びます。このときの選択は、初期状態に基づいてansatzに好ましい点がある場合にはその点が選ばれ、そうでない場合にはansatzの全ての境界条件に適合するランダムな初期点が選ばれます。初期点が与えられたときは、それが優先的に用いられます。ただしその長さは、ansatz回路のパラメーター数と一致していなければならないことに注意してください。
なぜ初期点を使うのでしょうか。理由としては、問題にとって合理的な出発点が推測される場合や、過去の実験から得られた情報がある場合などが考えられます。
使い方を説明するために、まず algorithms introduction チュートリアルの最初の作業例を繰り返して、解を与える最適点を求めてみましょう。
[3]:
seed = 50
algorithm_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('statevector_simulator'), seed_transpiler=seed, seed_simulator=seed)
ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SLSQP(maxiter=1000)
vqe = VQE(ansatz, optimizer=slsqp, quantum_instance=qi)
result = vqe.compute_minimum_eigenvalue(operator=H2_op)
print(result)
optimizer_evals = result.optimizer_evals
{ 'aux_operator_eigenvalues': None,
'cost_function_evals': 65,
'eigenstate': array([ 9.54859564e-05+0.j, -9.93766269e-01+0.j, 1.11483597e-01+0.j,
1.76972625e-05+0.j]),
'eigenvalue': -1.8572750175780233,
'optimal_parameters': { ParameterVectorElement(θ[3]): 6.092947703759512,
ParameterVectorElement(θ[2]): 0.5470754201428533,
ParameterVectorElement(θ[1]): 4.426962028746158,
ParameterVectorElement(θ[4]): -2.5983257006415026,
ParameterVectorElement(θ[5]): 1.5683259438037993,
ParameterVectorElement(θ[6]): -4.717618238771348,
ParameterVectorElement(θ[7]): 0.3602072544539939,
ParameterVectorElement(θ[0]): 4.2965202693703635},
'optimal_point': array([ 4.29652027, 4.42696203, 0.54707542, 6.0929477 , -2.5983257 ,
1.56832594, -4.71761824, 0.36020725]),
'optimal_value': -1.8572750175780233,
'optimizer_evals': 65,
'optimizer_time': 0.08263611793518066}
上の結果から optimal_point
を取り出して、これを initial_point
として用います。
[4]:
initial_pt = result.optimal_point
algorithm_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('statevector_simulator'), seed_transpiler=seed, seed_simulator=seed)
ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SLSQP(maxiter=1000)
vqe = VQE(ansatz, optimizer=slsqp, initial_point=initial_pt, quantum_instance=qi)
result1 = vqe.compute_minimum_eigenvalue(operator=H2_op)
print(result1)
optimizer_evals1 = result1.optimizer_evals
print()
print(f'optimizer_evals is {optimizer_evals1} with initial point versus {optimizer_evals} without it.')
{ 'aux_operator_eigenvalues': None,
'cost_function_evals': 9,
'eigenstate': array([ 9.54859564e-05+0.j, -9.93766269e-01+0.j, 1.11483597e-01+0.j,
1.76972625e-05+0.j]),
'eigenvalue': -1.8572750175780233,
'optimal_parameters': { ParameterVectorElement(θ[3]): 6.092947703759512,
ParameterVectorElement(θ[6]): -4.717618238771348,
ParameterVectorElement(θ[5]): 1.5683259438037993,
ParameterVectorElement(θ[1]): 4.426962028746158,
ParameterVectorElement(θ[2]): 0.5470754201428533,
ParameterVectorElement(θ[7]): 0.3602072544539939,
ParameterVectorElement(θ[4]): -2.5983257006415026,
ParameterVectorElement(θ[0]): 4.2965202693703635},
'optimal_point': array([ 4.29652027, 4.42696203, 0.54707542, 6.0929477 , -2.5983257 ,
1.56832594, -4.71761824, 0.36020725]),
'optimal_value': -1.8572750175780233,
'optimizer_evals': 9,
'optimizer_time': 0.027410030364990234}
optimizer_evals is 9 with initial point versus 65 without it.
optimizer_evals
を見ると、初期値が与えられず (デフォルトでNone) ランダムな値から開始した場合より、結果がより早く得られているのがわかります。
これが役に立つのは、例えばある問題に対する解を、それに非常に似た問題に対する解の推測のために使える場合です。化学は、分子の原子間距離を変えて、解離プロファイルをプロットするという、非常に良い例です。距離の変化が小さい場合には、解がその前の解の近くにあることが予測されます。一つの手法として単純に、ある解の最適点を次のステップの出発点として用いることがあります。現在では、前の解を直接的に用いるのではなく、前の解に基づいて外挿を行って初期値を計算するという、より複雑なテクニックが可能となっています。
期待値(Expectation)¶
VQE が求めるハミルトニアン演算子のエネルギーは、パラメータ化された ansatz で評価された場合の期待値です。期待値を計算するために VQE は expectation オブジェクトのインスタンスを使います。これは expectation
パラメーターで与えられるか、デフォルトの None
の値を持っている場合には、VQE は ExpectationFactory を使って、与えられたバックエンドに基づき適切なインスタンスを作ります。
ほとんどの場合は VQE に作成させるだけで十分です。しかし Qiskit Aer の qasm_simulator はスナップショット命令をサポートしており、演算子の期待値計算と組み合わせて用いることができます。これを使えば、状態ベクトル・シミュレーターのように、出力はショット・ノイズの乗らない理想的なものとなります。通常はショット・ノイズ (サンプリング・ノイズ) を乗せて、より実デバイスに近い結果が得られるよう、 qasm_simulator を選択するので、 VQE には ExpectationFactory に渡される include_custom
フラグが用意されています。これを True に設定すると、スナップショット命令を使用した AerPauliExpectation
が返され、デフォルトの False に設定すると、通常の PauliExpectation
が返されます。
以下の例では、結果が statevector_simulator と一致する、 include_custom=True
の場合を示しています。実際は、 statevector_simulator を直接使うよりもこちらの方法の方が、実行がより速くなることがあります。これは、 Pauli 行列の和として表されたハミルトニアンは行列形式に変換される必要がありますが、include_custom をTrue としてスナップショット命令を使うことで、これを回避できるからです。
[5]:
from qiskit import Aer
algorithm_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('aer_simulator'), seed_transpiler=seed, seed_simulator=seed)
ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SLSQP(maxiter=1000)
vqe = VQE(ansatz, optimizer=slsqp, quantum_instance=qi, include_custom=True)
result = vqe.compute_minimum_eigenvalue(operator=H2_op)
optimal_value1 = result.optimal_value
print(result)
{ 'aux_operator_eigenvalues': None,
'cost_function_evals': 65,
'eigenstate': {'01': 0.9921567416492215, '10': 0.125},
'eigenvalue': -1.8572750175807682,
'optimal_parameters': { ParameterVectorElement(θ[0]): 4.296520300933687,
ParameterVectorElement(θ[4]): -2.598325866938012,
ParameterVectorElement(θ[3]): 6.092947713510392,
ParameterVectorElement(θ[1]): 4.426962159645716,
ParameterVectorElement(θ[2]): 0.5470752986013949,
ParameterVectorElement(θ[6]): -4.717618259450455,
ParameterVectorElement(θ[5]): 1.5683258132970883,
ParameterVectorElement(θ[7]): 0.3602071559531031},
'optimal_point': array([ 4.2965203 , 4.42696216, 0.5470753 , 6.09294771, -2.59832587,
1.56832581, -4.71761826, 0.36020716]),
'optimal_value': -1.8572750175807682,
'optimizer_evals': 65,
'optimizer_time': 0.17621898651123047}
念のため、ここで再度 aer_simulator を実行しますが、include_custom をデフォルトの False のままにしておきます。おそらくSLSQPオプティマイザーのショットノイズが原因で、最適化が突然終了してしまいます。
[6]:
algorithm_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('aer_simulator'), seed_transpiler=seed, seed_simulator=seed)
ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SLSQP(maxiter=1000)
vqe = VQE(ansatz, optimizer=slsqp, quantum_instance=qi)
result = vqe.compute_minimum_eigenvalue(operator=H2_op)
optimal_value = result.optimal_value
print('The optimal value can be seen to be wrong too, i.e. '
f'{optimal_value:.3f} versus the correct {optimal_value1:.3f}.')
print()
print(result)
The optimal value can be seen to be wrong too, i.e. -1.074 versus the correct -1.857.
{ 'aux_operator_eigenvalues': None,
'cost_function_evals': 9,
'eigenstate': { '00': 0.7781187248742958,
'01': 0.4881406047441659,
'10': 0.39404750665370286,
'11': 0.03125},
'eigenvalue': -1.0741921795698932,
'optimal_parameters': { ParameterVectorElement(θ[0]): 3.611860069224077,
ParameterVectorElement(θ[7]): 0.6984088030463615,
ParameterVectorElement(θ[3]): 5.949536809130025,
ParameterVectorElement(θ[2]): 0.6019852007557844,
ParameterVectorElement(θ[6]): -5.466043598406607,
ParameterVectorElement(θ[1]): 4.19301252102391,
ParameterVectorElement(θ[4]): -3.3070470445355764,
ParameterVectorElement(θ[5]): 1.8462931831829383},
'optimal_point': array([ 3.61186007, 4.19301252, 0.6019852 , 5.94953681, -3.30704704,
1.84629318, -5.4660436 , 0.6984088 ]),
'optimal_value': -1.0741921795698932,
'optimizer_evals': 9,
'optimizer_time': 0.07421207427978516}
オプティマイザーをノイズの多い環境で動作するよう設計された SPSA に変更すると、より良い結果が得られます。ただし、ノイズが結果に影響を与えているため、正確性は劣ります。
[7]:
from qiskit.algorithms.optimizers import SPSA
algorithm_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('aer_simulator'), seed_transpiler=seed, seed_simulator=seed)
ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SPSA(maxiter=100)
vqe = VQE(ansatz, optimizer=slsqp, quantum_instance=qi)
result = vqe.compute_minimum_eigenvalue(operator=H2_op)
print(result)
{ 'aux_operator_eigenvalues': None,
'cost_function_evals': 200,
'eigenstate': {'01': 0.993140536379419, '10': 0.11692679333668567},
'eigenvalue': -1.8618521132262453,
'optimal_parameters': { ParameterVectorElement(θ[4]): -2.3482177711024588,
ParameterVectorElement(θ[2]): -0.23941337071033308,
ParameterVectorElement(θ[7]): 0.22755580746001913,
ParameterVectorElement(θ[3]): 6.6402821272229335,
ParameterVectorElement(θ[6]): -4.707529314643045,
ParameterVectorElement(θ[1]): 3.325806567734803,
ParameterVectorElement(θ[5]): 3.0118347096051314,
ParameterVectorElement(θ[0]): 3.676715527111659},
'optimal_point': array([ 3.67671553, 3.32580657, -0.23941337, 6.64028213, -2.34821777,
3.01183471, -4.70752931, 0.22755581]),
'optimal_value': -1.8618521132262453,
'optimizer_evals': 200,
'optimizer_time': 1.2898402214050293}
上述のように、期待値オブジェクトは明示的に与えることができます。 (そのため、内部の ExpectationFactory
や include_custom は決して使用されることも、必要とされることはありません) 以下では AerPauliExpectation
を作成し、 VQE に渡しています。結果は、 include_custom を True として VQE に独自の期待値オブジェクトを作成させた、上の結果と一致していることが確認できます。
[8]:
from qiskit.opflow import AerPauliExpectation
algorithm_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('aer_simulator'), seed_transpiler=seed, seed_simulator=seed)
ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SLSQP(maxiter=1000)
vqe = VQE(ansatz, optimizer=slsqp, quantum_instance=qi,
expectation=AerPauliExpectation())
result = vqe.compute_minimum_eigenvalue(operator=H2_op)
print(result)
{ 'aux_operator_eigenvalues': None,
'cost_function_evals': 65,
'eigenstate': {'01': 0.9921567416492215, '10': 0.125},
'eigenvalue': -1.8572750175807682,
'optimal_parameters': { ParameterVectorElement(θ[3]): 6.092947713510392,
ParameterVectorElement(θ[7]): 0.3602071559531031,
ParameterVectorElement(θ[6]): -4.717618259450455,
ParameterVectorElement(θ[4]): -2.598325866938012,
ParameterVectorElement(θ[5]): 1.5683258132970883,
ParameterVectorElement(θ[0]): 4.296520300933687,
ParameterVectorElement(θ[1]): 4.426962159645716,
ParameterVectorElement(θ[2]): 0.5470752986013949},
'optimal_point': array([ 4.2965203 , 4.42696216, 0.5470753 , 6.09294771, -2.59832587,
1.56832581, -4.71761826, 0.36020716]),
'optimal_value': -1.8572750175807682,
'optimizer_evals': 65,
'optimizer_time': 0.20196986198425293}
デフォルトでは、 include_custom が False の場合 (あるいは Aer のaer_simulator や実デバイスを使用している場合) に選ばれる PauliExpectation
オブジェクトは、 Pauli 行列を交換演算子の組としてグループ化します。これは、期待値を計算するための回路数を減らすことができるので、効率的です。しかし何らかの理由で、Pauli 演算子ごとに回路を実行したい場合には、 PauliExpectation を作成する際に、グループ化をオフにすることができます。以下に示すように、この方法で動作させるためには、明示的に VQE へこのような期待値インスタンスを渡す必要があります。
[9]:
from qiskit.opflow import PauliExpectation
algorithm_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('aer_simulator'), seed_transpiler=seed, seed_simulator=seed)
ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SPSA(maxiter=100)
vqe = VQE(ansatz, optimizer=slsqp, quantum_instance=qi,
expectation=PauliExpectation(group_paulis=False))
result = vqe.compute_minimum_eigenvalue(operator=H2_op)
print(result)
{ 'aux_operator_eigenvalues': None,
'cost_function_evals': 200,
'eigenstate': {'01': 0.9916644782889019, '10': 0.1288470508005519},
'eigenvalue': -1.8649830308114583,
'optimal_parameters': { ParameterVectorElement(θ[5]): 2.9887235542230144,
ParameterVectorElement(θ[0]): 3.6833958480452034,
ParameterVectorElement(θ[1]): 3.3827104248942383,
ParameterVectorElement(θ[7]): 0.18495131387681946,
ParameterVectorElement(θ[6]): -4.76651492079903,
ParameterVectorElement(θ[2]): -0.2651696479429452,
ParameterVectorElement(θ[3]): 6.699789960055848,
ParameterVectorElement(θ[4]): -2.282559635034206},
'optimal_point': array([ 3.68339585, 3.38271042, -0.26516965, 6.69978996, -2.28255964,
2.98872355, -4.76651492, 0.18495131]),
'optimal_value': -1.8649830308114583,
'optimizer_evals': 200,
'optimizer_time': 2.700853109359741}
勾配(Gradient)¶
勾配ベースの手法を用いるオプティマイザーには、通常は単純な有限差分で求められるデフォルトの勾配の代わりとして、ユーザー定義の勾配を与えることができます。勾配はオプティマイザーの gradient
パラメーターを介して、間接的にオプティマイザーに渡されます。
Monitoring VQE Convergence チュートリアルでは、ユーザー定義の gradient
の使用法が示されているので、そちらを参照してください。また Gradients framework チュートリアルには、勾配そのものについての詳細が示されています。
Quantum Instance と高度なシミュレーション¶
aer_simulator_statevector
や aer_simulator
、実デバイスのバックエンドから作成された QuantumInstance を渡す方法が一般的ですが、 Aer の高度なシミュレーション・モードを使用することも可能です。例えば、 Aer の Matrix Product State メソッドは簡単に用いることができて、より多くの量子ビットに拡張できる可能性をもっています。
[10]:
algorithm_globals.random_seed = seed
from qiskit.providers.aer import QasmSimulator
quantum_instance = QuantumInstance(QasmSimulator(method='matrix_product_state'), shots=1)
ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SLSQP(maxiter=1000)
vqe = VQE(ansatz, optimizer=slsqp, quantum_instance=qi, include_custom=True)
result = vqe.compute_minimum_eigenvalue(operator=H2_op)
print(result)
{ 'aux_operator_eigenvalues': None,
'cost_function_evals': 65,
'eigenstate': {'01': 0.9921567416492215, '10': 0.125},
'eigenvalue': -1.8572750175807682,
'optimal_parameters': { ParameterVectorElement(θ[2]): 0.5470752986013949,
ParameterVectorElement(θ[3]): 6.092947713510392,
ParameterVectorElement(θ[5]): 1.5683258132970883,
ParameterVectorElement(θ[7]): 0.3602071559531031,
ParameterVectorElement(θ[4]): -2.598325866938012,
ParameterVectorElement(θ[1]): 4.426962159645716,
ParameterVectorElement(θ[6]): -4.717618259450455,
ParameterVectorElement(θ[0]): 4.296520300933687},
'optimal_point': array([ 4.2965203 , 4.42696216, 0.5470753 , 6.09294771, -2.59832587,
1.56832581, -4.71761826, 0.36020716]),
'optimal_value': -1.8572750175807682,
'optimizer_evals': 65,
'optimizer_time': 0.2019951343536377}
[11]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
Qiskit | None |
Terra | 0.18.0.dev0+5920b66 |
Aer | 0.9.0 |
Ignis | 0.7.0.dev0+8195559 |
Aqua | None |
IBM Q Provider | None |
System information | |
Python | 3.8.8 (default, Apr 13 2021, 12:59:45) [Clang 10.0.0 ] |
OS | Darwin |
CPUs | 2 |
Memory (Gb) | 12.0 |
Thu May 27 11:03:27 2021 EDT |
This code is a part of Qiskit
© Copyright IBM 2017, 2021.
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.
[ ]: