VQE를 이용한 하이젠베르크 체인의 바닥 상태 에너지 추정
사용량 추정치: Eagle r3 프로세서에서 약 2분 (참고: 이는 추정치일 뿐이며 실제 실행 시간은 다를 수 있습니다.)
배경
이 튜토리얼에서는 하이젠베르크 체인을 시뮬레이션하고 바닥 상태 에너지를 추정하기 위한 Qiskit 패턴을 빌드, 배포 및 실행하는 방법을 설명합니다. Qiskit 패턴과 Qiskit Serverless를 활용해 클라우드에서 관리형 실행을 위해 배포하는 방법에 대한 자세한 내용은 IBM Quantum® Platform 문서 페이지를 참조하세요.
요구 사항
이 튜토리얼을 시작하기 전에 다음 항목이 설치되어 있는지 확인하세요:
- 시각화 지원이 포함된 Qiskit SDK v1.2 이상
- Qiskit Runtime v0.28 이상 (
pip install qiskit-ibm-runtime) - Qiskit Serverless (pip install qiskit_serverless)
- IBM Catalog (pip install qiskit-ibm-catalog)
설정
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-ibm-catalog qiskit-ibm-runtime scipy
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import minimize
from typing import Sequence
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.primitives.base import BaseEstimatorV2
from qiskit.circuit.library import XGate
from qiskit.circuit.library import efficient_su2
from qiskit.transpiler import PassManager
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.transpiler.passes.scheduling import (
ALAPScheduleAnalysis,
PadDynamicalDecoupling,
)
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import Session, Estimator
from qiskit_ibm_catalog import QiskitServerless, QiskitFunction
def visualize_results(results):
plt.plot(results["cost_history"], lw=2)
plt.xlabel("Iteration")
plt.ylabel("Energy")
plt.show()
def build_callback(
ansatz: QuantumCircuit,
hamiltonian: SparsePauliOp,
estimator: BaseEstimatorV2,
callback_dict: dict,
):
def callback(current_vector):
# Keep track of the number of iterations
callback_dict["iters"] += 1
# Set the prev_vector to the latest one
callback_dict["prev_vector"] = current_vector
# Compute the value of the cost function at the current vector
current_cost = (
estimator.run([(ansatz, hamiltonian, [current_vector])])
.result()[0]
.data.evs[0]
)
callback_dict["cost_history"].append(current_cost)
# Print to screen on single line
print(
"Iters. done: {} [Current cost: {}]".format(
callback_dict["iters"], current_cost
),
end="\r",
flush=True,
)
return callback
1단계: 고전적 입력을 양자 문제로 매핑
- 입력: 스핀 수
- 출력: 하이젠베르크 체인을 모델링하는 Ansatz와 해밀토니안
10개의 스핀으로 구성된 하이젠베르크 체인을 모델링하는 Ansatz와 해밀토니안을 구성합니다. 먼저 몇 가지 범용 패키지를 가져오고 헬퍼 함수를 만듭니다.
num_spins = 10
ansatz = efficient_su2(num_qubits=num_spins, reps=3)
# Remember to insert your token in the QiskitRuntimeService constructor
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, min_num_qubits=num_spins, simulator=False
)
coupling = backend.target.build_coupling_map()
reduced_coupling = coupling.reduce(list(range(num_spins)))
edge_list = reduced_coupling.graph.edge_list()
ham_list = []
for edge in edge_list:
ham_list.append(("ZZ", edge, 0.5))
ham_list.append(("YY", edge, 0.5))
ham_list.append(("XX", edge, 0.5))
for qubit in reduced_coupling.physical_qubits:
ham_list.append(("Z", [qubit], np.random.random() * 2 - 1))
hamiltonian = SparsePauliOp.from_sparse_list(ham_list, num_qubits=num_spins)
ansatz.draw("mpl", style="iqp")

2단계: 양자 하드웨어 실행을 위한 문제 최적화
- 입력: 추상 Circuit, 관측 가능량
- 출력: 선택된 QPU에 최적화된 대상 Circuit 및 관측 가능량
Qiskit의 generate_preset_pass_manager 함수를 사용하여 선택된 QPU에 맞게 Circuit의 최적화 루틴을 자동으로 생성합니다. 사전 설정 Pass Manager 중 가장 높은 최적화 수준을 제공하는 optimization_level=3을 선택합니다. 또한 디코히어런스 오류를 억제하기 위해 ALAPScheduleAnalysis 및 PadDynamicalDecoupling 스케줄링 패스를 포함합니다.
target = backend.target
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
pm.scheduling = PassManager(
[
ALAPScheduleAnalysis(durations=target.durations()),
PadDynamicalDecoupling(
durations=target.durations(),
dd_sequence=[XGate(), XGate()],
pulse_alignment=target.pulse_alignment,
),
]
)
ansatz_ibm = pm.run(ansatz)
observable_ibm = hamiltonian.apply_layout(ansatz_ibm.layout)
ansatz_ibm.draw("mpl", scale=0.6, style="iqp", fold=-1, idle_wires=False)

3단계: Qiskit 프리미티브를 사용하여 실행
- 입력: 대상 Circuit 및 관측 가능량
- 출력: 최적화 결과
Circuit 파라미터를 최적화하여 시스템의 추정 바닥 상태 에너지를 최소화합니다. 최적화 중 비용 함 수를 평가하기 위해 Qiskit Runtime의 Estimator 프리미티브를 사용합니다.
이 데모에서는 qiskit-ibm-runtime 프리미티브를 사용하여 QPU에서 실행합니다. qiskit statevector 기반 프리미티브로 실행하려면 Qiskit IBM Runtime 프리미티브를 사용하는 코드 블록을 주석 처리된 블록으로 교체하세요.
# SciPy minimizer routine
def cost_func(
params: Sequence,
ansatz: QuantumCircuit,
hamiltonian: SparsePauliOp,
estimator: BaseEstimatorV2,
) -> float:
"""Ground state energy evaluation."""
return (
estimator.run([(ansatz, hamiltonian, [params])])
.result()[0]
.data.evs[0]
)
num_params = ansatz_ibm.num_parameters
params = 2 * np.pi * np.random.random(num_params)
callback_dict = {
"prev_vector": None,
"iters": 0,
"cost_history": [],
}
# Evaluate the problem on a QPU by using Qiskit IBM Runtime
with Session(backend=backend) as session:
estimator = Estimator()
callback = build_callback(
ansatz_ibm, observable_ibm, estimator, callback_dict
)
res = minimize(
cost_func,
x0=params,
args=(ansatz_ibm, observable_ibm, estimator),
callback=callback,
method="cobyla",
options={"maxiter": 100},
)
visualize_results(callback_dict)
4단계: 후처리 및 원하는 고전적 형식으로 결과 반환
- 입력: 최적화 중 바닥 상태 에너지 추정값
- 출력: 추정된 바닥 상태 에너지
print(f'Estimated ground state energy: {res["fun"]}')
클라우드에 Qiskit 패턴 배포
이를 위해 위의 소스 코드를 ./source/heisenberg.py 파일로 옮기고, 입력을 받아 최종 솔루션을 반환하는 스크립트로 코드를 래핑한 후, qiskit-ibm-catalog의 QiskitFunction 클래스를 사용하여 원격 클러스터에 업로드합니다. 외부 의존성 지정, 입력 인수 전달 등에 대한 안내는 Qiskit Serverless 가이드를 참조하세요.
패턴의 입력은 체인의 스핀 수이며, 출력은 시스템의 바닥 상태 에너지 추정값입니다.
# Authenticate to the remote cluster and submit the pattern for remote execution
serverless = QiskitServerless()
heisenberg_function = QiskitFunction(
title="ibm_heisenberg",
entrypoint="heisenberg.py",
working_dir="./source/",
)
serverless.upload(heisenberg_function)
관리형 서비스로 Qiskit 패턴 실행
패턴을 클라우드에 업로드한 후, QiskitServerless 클라이언트를 사용하여 간편하게 실행할 수 있습니다.
# Run the pattern on the remote cluster
ibm_heisenberg = serverless.load("ibm_heisenberg")
job = serverless.run(ibm_heisenberg)
solution = job.result()
print(solution)
print(job.logs())