Korean
언어
English
Bengali
French
German
Japanese
Korean
Portuguese
Spanish
Tamil

참고

이 페이지는 tutorials/algorithms/02_vqe_advanced_options.ipynb 에서 생성되었다.

고급 VQE 옵션

첫 번째 알고리즘 튜토리얼에서는 기본 VQE 알고리즘을 설정하는 방법을 배웠다. 이제 VQE, QAOA, VQD 등 Qiskit의 변이 알고리즘의 모든 기능을 탐색하기 위해 보다 고급 구성 매개 변수를 제공하는 방법을 살펴보겠다. 특히, 이 튜토리얼은 수렴과 맞춤형 initial points 및 gradients 의 사용을 모니터링하기 위해 callback 을 설정하는 방법을 다룰 것이다.

콜백

콜백 메서드를 사용하여 알고리즘이 실행되고 최소값으로 수렴될 때 최적화 진행률을 모니터링할 수 있다. 콜백은 옵티마이저의 각 기능 평가에 대해 호출되며 현재 옵티마이저 값, 평가 카운트, 현재 옵티마이저 매개변수 등을 제공한다. 특정 옵티마이저에 따라 이 메서드는 옵티마이저의 각 반복(단계)이 아닐 수 있으므로, 예를 들어 옵티마이저 유한차 경사도를 계산하기 위해 비용 함수를 호출하는 경우 콜백을 통해 볼 수 있다.

이 섹션은 VQE 에서 콜백을 활용하여 선택된 옵티마이저 세트를 사용하여 바닥 상태 에너지에 대한 수렴 경로 도표를 그려 방법을 보여준다.

먼저, VQE에 대한 큐비트 연산자가 필요하다. 이 예제에서는 앞서 Qiskit의 알고리즘 소개에서 사용된 것과 동일한 (H2 분자에 대하여 원래 Qiskit Nature에서 계산된) 연산자를 사용할 것이다.

[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) 는 매우 클 필요가 없다. TwoLocal 를 선택한 시행 파동 함수 (즉, 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

이제 저장한 콜백 데이터에서 각 옵티마이저가 각각의 목적 함수를 호출하여 발생하는 에너지 값을 그려볼 수 있다. 유한 차분법을 사용하여 기울기를 계산하는 옵티마이저는 수 많은 평가에서 기울기를 설정하기 위해 가까운 지점에 대한 값을 계산하는 plot과 같은 특성 단계를 가진다. (여기 그래프의 눈금에서는 차이를 구별하기 힘들 정도로 매우 유사한 값을 가진 가까운 지점임)

[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 워크플로우에서 사용자 지정 그라디언트를 사용하는 방법을 보여 준다.

첫 번째 단계는 해당 primitive 및 primitive 그라디언트를 모두 초기화하는 것이다:

[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

초기 지점

기본적으로 최적화는 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 를 보면 (이미 최적의 솔루션을 제공했기 때문에 단 한 번의 반복으로) 초기 포인트가 알고리즘을 더 빨리 수렴하는 데 어떻게 도움이 되었는지 알 수 있다.

이는 밀접하게 관련된 두 가지 문제를 가지고 있는 경우에 특히 유용할 수 있으며, 한 문제에 대한 해결책은 다른 문제를 추측하는 데 사용될 수 있다. 좋은 예로 화학에서 분자의 원자 간 거리를 변경하고 각 거리에 대한 최소 고유값을 계산하는 해리(dissociation) 프로파일을 그리는 것일 수 있다. 거리 변화가 적을 때 우리는 솔루션이 여전히 이전 솔루션의 근처에 있을 것으로 예상할 수 있다. 따라서 일반적인 테크닉은 단순하게 한 솔루션의 최적점을 다음 단계의 시작 지점으로 사용하는 것이다. 또한 이전 솔루션을 직접 사용하는 대신 이전 솔루션들을 기반으로 초기 위치를 계산하기 위해 외삽법(extrapolation)을 적용할 수 있는 더 복잡한 기술이 있다.

[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.