주 콘텐츠로 건너뛰기

시간 진화 회로를 위한 근사 양자 컴파일

사용 추정 시간: Eagle 프로세서에서 약 5분 (참고: 이것은 추정값일 뿐이며 실제 실행 시간은 다를 수 있습니다.)

배경

이 튜토리얼은 텐서 네트워크를 활용한 근사 양자 컴파일(AQC-Tensor)을 Qiskit과 함께 구현하여 양자 회로 성능을 향상시키는 방법을 설명합니다. Qiskit 프레임워크의 상태 준비 및 최적화 방식을 따라, 트로터화된 시간 진화 맥락 내에서 AQC-Tensor를 적용하여 시뮬레이션 정확도를 유지하면서 회로 깊이를 줄입니다. 초기 Trotter 회로에서 저깊이 안사츠(ansatz) 회로를 생성하고, 이를 텐서 네트워크로 최적화한 뒤 양자 하드웨어 실행을 위해 준비하는 방법을 배웁니다.

주요 목표는 감소된 회로 깊이로 모델 해밀토니안의 시간 진화를 시뮬레이션하는 것입니다. 이는 행렬 곱 상태(MPS)와 같은 텐서 네트워크를 활용하여 초기 회로를 압축하고 최적화하는 AQC-Tensor Qiskit 애드온 qiskit-addon-aqc-tensor를 통해 달성됩니다. 반복적인 조정을 통해 압축된 안사츠 회로는 원래 회로에 대한 충실도를 유지하면서 근거리 양자 하드웨어에서도 실행 가능한 수준을 유지합니다. 자세한 내용은 해당 문서에서 확인할 수 있으며, 시작하기 위한 간단한 예제도 제공됩니다.

근사 양자 컴파일은 하드웨어 결맞음 시간을 초과하는 양자 시뮬레이션에서 특히 유리하며, 복잡한 시뮬레이션을 더 효율적으로 수행할 수 있도록 합니다. 이 튜토리얼은 Qiskit에서 AQC-Tensor 워크플로우를 설정하는 과정을 안내하며, 해밀토니안 초기화, Trotter 회로 생성, 대상 장치를 위한 최종 최적화 회로의 트랜스파일(transpilation)을 다룹니다.

요구 사항

이 튜토리얼을 시작하기 전에 다음 항목이 설치되어 있는지 확인하세요:

  • Qiskit SDK v1.0 이상 (시각화 지원 포함)
  • Qiskit Runtime v0.22 이상 (pip install qiskit-ibm-runtime)
  • AQC-Tensor Qiskit 애드온 (pip install 'qiskit-addon-aqc-tensor[aer,quimb-jax]')

설정

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-addon-aqc-tensor qiskit-addon-utils qiskit-ibm-runtime quimb rustworkx scipy
import numpy as np
import quimb.tensor
import datetime
import matplotlib.pyplot as plt

from scipy.optimize import OptimizeResult, minimize

from qiskit.quantum_info import SparsePauliOp, Pauli
from qiskit.transpiler import CouplingMap
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit import QuantumCircuit
from qiskit.synthesis import SuzukiTrotter

from qiskit_addon_utils.problem_generators import (
generate_time_evolution_circuit,
)
from qiskit_addon_aqc_tensor.ansatz_generation import (
generate_ansatz_from_circuit,
)
from qiskit_addon_aqc_tensor.objective import MaximizeStateFidelity
from qiskit_addon_aqc_tensor.simulation.quimb import QuimbSimulator
from qiskit_addon_aqc_tensor.simulation import tensornetwork_from_circuit
from qiskit_addon_aqc_tensor.simulation import compute_overlap

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator

from rustworkx.visualization import graphviz_draw

파트 I. 소규모 예제

이 튜토리얼의 첫 번째 파트에서는 10개 사이트로 구성된 소규모 예제를 사용하여 양자 시뮬레이션 문제를 실행 가능한 양자 회로로 매핑하는 과정을 설명합니다. 여기서는 10-사이트 XXZ 모델의 동역학을 탐색하며, 더 큰 시스템으로 확장하기 전에 관리 가능한 양자 회로를 구성하고 최적화합니다.

XXZ 모델은 스핀 상호작용과 자기적 특성을 연구하기 위해 물리학에서 널리 연구되는 모델입니다. 체인을 따라 인접한 사이트 간의 사이트 의존적 상호작용을 갖는 개방 경계 조건으로 해밀토니안을 설정합니다.

모델 해밀토니안과 관측량

10-사이트 XXZ 모델의 해밀토니안은 다음과 같이 정의됩니다:

H^XXZ=i=1L1Ji,(i+1)(XiX(i+1)+YiY(i+1)+2ZiZ(i+1)),\hat{\mathcal{H}}_{XXZ} = \sum_{i=1}^{L-1} J_{i,(i+1)}\left(X_i X_{(i+1)}+Y_i Y_{(i+1)}+ 2\cdot Z_i Z_{(i+1)} \right) \, ,

여기서 Ji,(i+1)J_{i,(i+1)}는 간선 (i,i+1)(i, i+1)에 대응하는 무작위 계수이며, L=10L=10은 사이트 수입니다.

감소된 회로 깊이로 이 시스템의 진화를 시뮬레이션함으로써, AQC-Tensor를 사용하여 회로를 압축하고 최적화하는 방법에 대한 통찰을 얻을 수 있습니다.

해밀토니안과 관측량 설정

문제를 매핑하기 전에, 10-사이트 XXZ 모델을 위한 결합 맵(coupling map), 해밀토니안, 관측량을 설정해야 합니다.

# L is the number of sites, also the length of the 1D spin chain
L = 10

# Generate the coupling map
edge_list = [(i - 1, i) for i in range(1, L)]
# Generate an edge-coloring so we can make hw-efficient circuits
even_edges = edge_list[::2]
odd_edges = edge_list[1::2]
coupling_map = CouplingMap(edge_list)

# Generate random coefficients for our XXZ Hamiltonian
np.random.seed(0)
Js = np.random.rand(L - 1) + 0.5 * np.ones(L - 1)
hamiltonian = SparsePauliOp(Pauli("I" * L))
for i, edge in enumerate(even_edges + odd_edges):
hamiltonian += SparsePauliOp.from_sparse_list(
[
("XX", (edge), Js[i] / 2),
("YY", (edge), Js[i] / 2),
("ZZ", (edge), Js[i]),
],
num_qubits=L,
)

# Generate a ZZ observable between the two middle qubits
observable = SparsePauliOp.from_sparse_list(
[("ZZ", (L // 2 - 1, L // 2), 1.0)], num_qubits=L
)

print("Hamiltonian:", hamiltonian)
print("Observable:", observable)
graphviz_draw(coupling_map.graph, method="circo")
Hamiltonian: SparsePauliOp(['IIIIIIIIII', 'IIIIIIIIXX', 'IIIIIIIIYY', 'IIIIIIIIZZ', 'IIIIIIXXII', 'IIIIIIYYII', 'IIIIIIZZII', 'IIIIXXIIII', 'IIIIYYIIII', 'IIIIZZIIII', 'IIXXIIIIII', 'IIYYIIIIII', 'IIZZIIIIII', 'XXIIIIIIII', 'YYIIIIIIII', 'ZZIIIIIIII', 'IIIIIIIXXI', 'IIIIIIIYYI', 'IIIIIIIZZI', 'IIIIIXXIII', 'IIIIIYYIII', 'IIIIIZZIII', 'IIIXXIIIII', 'IIIYYIIIII', 'IIIZZIIIII', 'IXXIIIIIII', 'IYYIIIIIII', 'IZZIIIIIII'],
coeffs=[1. +0.j, 0.52440675+0.j, 0.52440675+0.j, 1.0488135 +0.j,
0.60759468+0.j, 0.60759468+0.j, 1.21518937+0.j, 0.55138169+0.j,
0.55138169+0.j, 1.10276338+0.j, 0.52244159+0.j, 0.52244159+0.j,
1.04488318+0.j, 0.4618274 +0.j, 0.4618274 +0.j, 0.9236548 +0.j,
0.57294706+0.j, 0.57294706+0.j, 1.14589411+0.j, 0.46879361+0.j,
0.46879361+0.j, 0.93758721+0.j, 0.6958865 +0.j, 0.6958865 +0.j,
1.391773 +0.j, 0.73183138+0.j, 0.73183138+0.j, 1.46366276+0.j])
Observable: SparsePauliOp(['IIIIZZIIII'],
coeffs=[1.+0.j])

이전 코드 셀의 출력

해밀토니안이 정의되었으므로, 초기 상태를 구성하는 단계로 넘어갈 수 있습니다.

# Generate an initial state
initial_state = QuantumCircuit(L)
for i in range(L):
if i % 2:
initial_state.x(i)

1단계: 고전적 입력을 양자 문제로 매핑하기

이제 스핀-스핀 상호작용과 외부 자기장을 특성화하는 해밀토니안을 구성했으므로, AQC-Tensor 워크플로우의 세 가지 주요 단계를 따릅니다:

  1. 최적화된 AQC Circuit 생성: Trotterization을 사용하여 초기 시간 진행을 근사한 후, Circuit 깊이를 줄이기 위해 압축합니다.
  2. 나머지 시간 진행 Circuit 생성: 초기 구간 이후의 나머지 시간에 대한 시간 진행을 포착합니다.
  3. Circuit 결합: 최적화된 AQC Circuit과 나머지 시간 진행 Circuit을 하나의 완전한 시간 진행 Circuit으로 합칩니다.

이 접근 방식은 목표 시간 진행에 대한 낮은 깊이의 ansatz를 만들어, 근미래 양자 하드웨어 제약 조건 내에서 효율적인 시뮬레이션을 지원합니다.

고전적으로 시뮬레이션할 시간 진행 구간 결정

우리의 목표는 앞서 정의한 모델 해밀토니안의 시간 진행을 Trotter 시간 진행을 사용하여 시뮬레이션하는 것입니다. 이 과정을 양자 하드웨어에 적합하게 만들기 위해, 시간 진행을 두 구간으로 나눕니다:

  • 초기 구간: ti=0.0t_i = 0.0에서 tf=0.2t_f = 0.2까지의 이 초기 구간은 MPS로 시뮬레이션 가능하며 AQC-Tensor를 사용하여 효율적으로 "컴파일"할 수 있습니다. AQC-Tensor Qiskit 애드온을 사용하여 이 구간에 대한 압축 Circuit을 생성하며, 이를 aqc_target_circuit이라고 합니다. 이 구간은 텐서 네트워크 시뮬레이터에서 시뮬레이션되므로, 하드웨어 리소스에 큰 영향 없이 더 많은 수의 Trotter 레이어를 사용할 수 있습니다. 이 구간에 대해 aqc_target_num_trotter_steps = 32로 설정합니다.

  • 후속 구간: t=0.2t = 0.2에서 t=0.4t = 0.4까지의 이 나머지 구간은 양자 하드웨어에서 실행되며, 이를 subsequent_circuit이라고 합니다. 하드웨어 제한을 고려하여, 관리 가능한 Circuit 깊이를 유지하기 위해 가능한 한 적은 수의 Trotter 레이어를 사용하는 것이 목표입니다. 이 구간에서는 subsequent_num_trotter_steps = 3을 사용합니다.

분할 시간 선택

고전적 시뮬레이션 가능성과 하드웨어 실현 가능성의 균형을 맞추기 위해 t=0.2t = 0.2를 분할 시간으로 선택합니다. 시간 진행 초반에는 XXZ 모델의 얽힘이 MPS와 같은 고전적 방법으로 정확하게 근사할 수 있을 만큼 낮게 유지됩니다.

분할 시간을 선택할 때, 얽힘이 여전히 고전적으로 관리 가능하면서도 하드웨어에서 실행될 부분을 단순화할 만큼 충분한 시간 진행을 포착하는 지점을 선택하는 것이 좋은 지침입니다. 다양한 해밀토니안에 대한 최적의 균형을 찾기 위해 시행착오가 필요할 수 있습니다.

# Generate the AQC target circuit (initial segment)
aqc_evolution_time = 0.2
aqc_target_num_trotter_steps = 32

aqc_target_circuit = initial_state.copy()
aqc_target_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_target_num_trotter_steps),
time=aqc_evolution_time,
),
inplace=True,
)
# Generate the subsequent circuit
subsequent_num_trotter_steps = 3
subsequent_evolution_time = 0.2

subsequent_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=subsequent_num_trotter_steps),
time=subsequent_evolution_time,
)
subsequent_circuit.draw("mpl", fold=-1)

Output of the previous code cell

의미 있는 비교를 가능하게 하기 위해 두 개의 추가 Circuit을 생성합니다:

  • AQC 비교 Circuit: 이 Circuit은 aqc_evolution_time까지 시간 진행을 수행하지만 subsequent_circuit과 동일한 Trotter 스텝 간격을 사용합니다. 증가된 Trotter 스텝 수를 사용하지 않았을 때 관찰할 수 있는 시간 진행과 비교하기 위한 aqc_target_circuit의 비교 대상으로 사용됩니다. 이 Circuit을 aqc_comparison_circuit이라고 합니다.

  • 기준 Circuit: 이 Circuit은 정확한 결과를 얻기 위한 기준으로 사용됩니다. 텐서 네트워크를 사용하여 전체 시간 진행을 시뮬레이션하여 정확한 결과를 계산하며, AQC-Tensor의 효율성을 평가하기 위한 참고값을 제공합니다. 이 Circuit을 reference_circuit이라고 합니다.

# Generate the AQC comparison circuit
aqc_comparison_num_trotter_steps = int(
subsequent_num_trotter_steps
/ subsequent_evolution_time
* aqc_evolution_time
)
print(
"Number of Trotter steps for comparison:",
aqc_comparison_num_trotter_steps,
)

aqc_comparison_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_comparison_num_trotter_steps),
time=aqc_evolution_time,
)
Number of Trotter steps for comparison: 3
# Generate the reference circuit
evolution_time = 0.4
reps = 200

reference_circuit = initial_state.copy()
reference_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=reps),
time=evolution_time,
),
inplace=True,
)

더 적은 스텝의 Trotter Circuit에서 ansatz와 초기 파라미터 생성하기

네 개의 Circuit을 구성했으므로, AQC-Tensor 워크플로우를 진행합니다. 먼저 목표 Circuit과 동일한 시간 진행을 갖지만 더 적은 Trotter 스텝(따라서 더 적은 레이어)을 갖는 "좋은" Circuit을 구성합니다.

그런 다음 이 "좋은" Circuit을 AQC-Tensor의 generate_ansatz_from_circuit 함수에 전달합니다. 이 함수는 Circuit의 2-Qubit 연결성을 분석하고 두 가지를 반환합니다:

  1. 입력 Circuit과 동일한 2-Qubit 연결성을 갖는 일반화된 파라미터화 ansatz Circuit.
  2. ansatz에 대입하면 입력(좋은) Circuit을 만들어내는 파라미터.

곧 이 파라미터를 사용하여 ansatz Circuit을 목표 MPS에 최대한 가깝게 만들기 위해 반복적으로 조정할 것입니다.

aqc_ansatz_num_trotter_steps = 1

aqc_good_circuit = initial_state.copy()
aqc_good_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_ansatz_num_trotter_steps),
time=aqc_evolution_time,
),
inplace=True,
)

aqc_ansatz, aqc_initial_parameters = generate_ansatz_from_circuit(
aqc_good_circuit
)
aqc_ansatz.draw("mpl", fold=-1)

Output of the previous code cell

print(f"AQC Comparison circuit: depth {aqc_comparison_circuit.depth()}")
print(f"Target circuit: depth {aqc_target_circuit.depth()}")
print(
f"Ansatz circuit: depth {aqc_ansatz.depth()}, with {len(aqc_initial_parameters)} parameters"
)
AQC Comparison circuit: depth 36
Target circuit: depth 385
Ansatz circuit: depth 7, with 156 parameters

텐서 네트워크 시뮬레이션을 위한 설정 선택

여기서는 Quimb의 행렬 곱 상태(MPS) Circuit 시뮬레이터를 사용하며, 그래디언트를 제공하기 위해 jax를 함께 사용합니다.

simulator_settings = QuimbSimulator(
quimb.tensor.CircuitMPS, autodiff_backend="jax"
)

다음으로, AQC-Tensor를 사용하여 근사할 목표 상태의 MPS 표현을 구축합니다. 이 표현은 얽힘을 효율적으로 처리할 수 있게 하며, 추가 최적화를 위해 양자 상태를 컴팩트하게 기술합니다.

aqc_target_mps = tensornetwork_from_circuit(
aqc_target_circuit, simulator_settings
)
print("Target MPS maximum bond dimension:", aqc_target_mps.psi.max_bond())

# Obtains the reference MPS, where we can obtain the exact expectation value by examining the `local_expectation``
reference_mps = tensornetwork_from_circuit(
reference_circuit, simulator_settings
)
reference_expval = reference_mps.local_expectation(
quimb.pauli("Z") & quimb.pauli("Z"), (L // 2 - 1, L // 2)
).real.item()
print("Reference MPS maximum bond dimension:", reference_mps.psi.max_bond())
Target MPS maximum bond dimension: 5
Reference MPS maximum bond dimension: 7

더 많은 수의 Trotter 스텝을 목표 상태에 사용함으로써, 초기 Circuit에 비해 Trotter 오류를 효과적으로 줄였습니다. 초기 Circuit이 준비하는 상태와 목표 상태 간의 충실도(ψ1ψ22|\langle \psi_1 | \psi_2 \rangle|^2)를 평가하여 이 차이를 정량화할 수 있습니다.

good_mps = tensornetwork_from_circuit(aqc_good_circuit, simulator_settings)
starting_fidelity = abs(compute_overlap(good_mps, aqc_target_mps)) ** 2
print("Starting fidelity:", starting_fidelity)
Starting fidelity: 0.9982464959067222

MPS 계산을 사용한 ansatz 파라미터 최적화

이 단계에서는 SciPy의 L-BFGS 최적화기를 사용하여 간단한 비용 함수 MaximizeStateFidelity를 최소화함으로써 ansatz 파라미터를 최적화합니다. AQC-Tensor를 사용하지 않은 초기 Circuit의 충실도를 초과하도록 보장하는 충실도 정지 기준을 선택합니다. 이 임계값에 도달하면, 압축된 Circuit은 원래 Circuit에 비해 Trotter 오류가 낮고 깊이도 줄어들게 됩니다. 추가 CPU 시간을 사용하면 충실도를 더욱 높일 수 있습니다.

# Setting values for the optimization
aqc_stopping_fidelity = 1
aqc_max_iterations = 500

stopping_point = 1.0 - aqc_stopping_fidelity
objective = MaximizeStateFidelity(
aqc_target_mps, aqc_ansatz, simulator_settings
)

def callback(intermediate_result: OptimizeResult):
fidelity = 1 - intermediate_result.fun
print(
f"{datetime.datetime.now()} Intermediate result: Fidelity {fidelity:.8f}"
)
if intermediate_result.fun < stopping_point:
# Good enough for now
raise StopIteration

result = minimize(
objective,
aqc_initial_parameters,
method="L-BFGS-B",
jac=True,
options={"maxiter": aqc_max_iterations},
callback=callback,
)
if (
result.status
not in (
0,
1,
99,
)
): # 0 => success; 1 => max iterations reached; 99 => early termination via StopIteration
raise RuntimeError(
f"Optimization failed: {result.message} (status={result.status})"
)

print(f"Done after {result.nit} iterations.")
aqc_final_parameters = result.x
2025-04-14 11:46:52.174235 Intermediate result: Fidelity 0.99795851
2025-04-14 11:46:52.218249 Intermediate result: Fidelity 0.99822826
2025-04-14 11:46:52.280924 Intermediate result: Fidelity 0.99829675
2025-04-14 11:46:52.356214 Intermediate result: Fidelity 0.99832474
2025-04-14 11:46:52.411609 Intermediate result: Fidelity 0.99836131
2025-04-14 11:46:52.453747 Intermediate result: Fidelity 0.99839954
2025-04-14 11:46:52.496184 Intermediate result: Fidelity 0.99846517
2025-04-14 11:46:52.542046 Intermediate result: Fidelity 0.99865029
2025-04-14 11:46:52.583679 Intermediate result: Fidelity 0.99872332
2025-04-14 11:46:52.628732 Intermediate result: Fidelity 0.99892359
2025-04-14 11:46:52.690386 Intermediate result: Fidelity 0.99900640
2025-04-14 11:46:52.759398 Intermediate result: Fidelity 0.99907169
2025-04-14 11:46:52.819496 Intermediate result: Fidelity 0.99911423
2025-04-14 11:46:52.884505 Intermediate result: Fidelity 0.99918716
2025-04-14 11:46:52.947919 Intermediate result: Fidelity 0.99921278
2025-04-14 11:46:53.012808 Intermediate result: Fidelity 0.99924853
2025-04-14 11:46:53.083626 Intermediate result: Fidelity 0.99928797
2025-04-14 11:46:53.153235 Intermediate result: Fidelity 0.99933028
2025-04-14 11:46:53.221371 Intermediate result: Fidelity 0.99935757
2025-04-14 11:46:53.286211 Intermediate result: Fidelity 0.99938140
2025-04-14 11:46:53.352391 Intermediate result: Fidelity 0.99940964
2025-04-14 11:46:53.420472 Intermediate result: Fidelity 0.99944051
2025-04-14 11:46:53.486279 Intermediate result: Fidelity 0.99946828
2025-04-14 11:46:53.552338 Intermediate result: Fidelity 0.99948723
2025-04-14 11:46:53.618688 Intermediate result: Fidelity 0.99951011
2025-04-14 11:46:53.690878 Intermediate result: Fidelity 0.99954718
2025-04-14 11:46:53.762725 Intermediate result: Fidelity 0.99956267
2025-04-14 11:46:53.829784 Intermediate result: Fidelity 0.99958949
2025-04-14 11:46:53.897477 Intermediate result: Fidelity 0.99960498
2025-04-14 11:46:53.954633 Intermediate result: Fidelity 0.99961308
2025-04-14 11:46:54.010125 Intermediate result: Fidelity 0.99962894
2025-04-14 11:46:54.064717 Intermediate result: Fidelity 0.99964121
2025-04-14 11:46:54.118892 Intermediate result: Fidelity 0.99964348
2025-04-14 11:46:54.183236 Intermediate result: Fidelity 0.99964860
2025-04-14 11:46:54.245521 Intermediate result: Fidelity 0.99965695
2025-04-14 11:46:54.305792 Intermediate result: Fidelity 0.99966398
2025-04-14 11:46:54.355819 Intermediate result: Fidelity 0.99967816
2025-04-14 11:46:54.409580 Intermediate result: Fidelity 0.99968293
2025-04-14 11:46:54.457979 Intermediate result: Fidelity 0.99968936
2025-04-14 11:46:54.505891 Intermediate result: Fidelity 0.99969223
2025-04-14 11:46:54.551084 Intermediate result: Fidelity 0.99970009
2025-04-14 11:46:54.601817 Intermediate result: Fidelity 0.99970724
2025-04-14 11:46:54.650097 Intermediate result: Fidelity 0.99970987
2025-04-14 11:46:54.714727 Intermediate result: Fidelity 0.99971237
2025-04-14 11:46:54.780052 Intermediate result: Fidelity 0.99971916
2025-04-14 11:46:54.871994 Intermediate result: Fidelity 0.99971940
2025-04-14 11:46:54.958244 Intermediate result: Fidelity 0.99972465
2025-04-14 11:46:55.011057 Intermediate result: Fidelity 0.99972763
2025-04-14 11:46:55.175339 Intermediate result: Fidelity 0.99972894
2025-04-14 11:46:56.688912 Intermediate result: Fidelity 0.99972894
Done after 50 iterations.
parameters = [float(param) for param in aqc_final_parameters]
print("Final parameters:", parameters)
Final parameters: [-7.853983035039254, 1.5707966468427772, 1.5707962768868613, -1.570798010835122, 1.570794480409574, 1.5707972214146968, -1.570796593027083, 1.5707968206822998, -1.5707959018046258, -1.5707991700969144, 1.5707965852600927, 4.712386891737442, -7.853980840717957, 1.5707967508132654, 1.5707943162503217, -1.5707955382023582, 1.5707958007156742, 1.570796096113293, -1.5707928509846847, 1.5707971042943747, -1.570797909276557, -1.5707941020637393, 1.5707980179540793, 4.712389823219363, -1.5707928752386107, 1.5707996426312891, -1.5707975640471001, -1.570794132802984, 1.5707944361599957, 4.712390747060803, 0.1048818190315936, 0.06686710468840577, -0.0668645844756557, -3.1415923537135466, 1.2374931269696063, 6.323169390432535e-07, 3.53229204771738e-08, 2.1091105688681484, 6.283186439944202, 0.12152258846156239, 0.07961752617254866, -0.07961775088604585, -1.6564278051174865e-06, 2.0771163596472384, 3.141592651630471, -6.283185775192653, 1.7691609006726954, 3.1415922910116216, 0.19837572065074083, 0.11114901449078964, -0.11115124544944892, -3.141591983034976, 0.8570788408766729, 4.201601390404146e-07, -3.141593736550978, 0.34652010942396333, 6.283186232785291, 0.13606356527241956, 0.03891676349289617, -0.03891524189533726, -1.5707965732853424, 1.5707968967088564, -0.3086133992238162, 1.5707957152428194, 1.5707968398959653, -0.32062737993080026, 0.11027416939993417, 0.0726167290795046, -0.07262020423334464, -2.3729431959735024e-06, 1.8204437429254703, 9.299060301196612e-07, -3.141592899563451, 2.103269568939461, 3.1415937539734626, 0.11536891854817125, 0.09099022308254198, -0.09098864958606581, -3.1415913307373127, 2.078429034357281, -1.509777998069368e-06, -3.1415922600663255, 1.5189162645358172, -3.1415878461323583, 0.09999070991480716, 0.04352011445148391, -0.04351849541849812, -1.570797642506462, 1.570795238023824, 0.8903442644396505, 1.5707962698006606, 1.5707946765132268, 0.9098791754570567, 0.10448284343424026, 0.07317037684936827, -0.07316718173961152, -3.141592682240966, 2.1665363080039612, -7.450882112394189e-07, -5.771181304929921e-07, 2.615334999517103, -3.1415914971653898, 0.1890887078648001, 0.13578163074571992, -0.13578078143610256, 7.156734195912883e-07, 1.7915385305413096, -5.188866034727312e-07, 1.2827742939197711e-06, 1.2348316581417487, 6.28318357406372, 0.08061187643781703, 0.03820789039271876, -0.03820731868804904, 1.5707964027727628, 1.570798734462218, 4.387336153720882, -1.570795722044763, 1.570798457375325, 4.450361734163248, 0.092360147257953, 0.06047700345049011, -0.06048592856713045, -3.141591214829027, 2.6593289993286047, -2.366937342261038e-07, 8.112162974032695e-08, 1.8907014631413432, 8.355881261853104e-07, 0.23303641819370874, 0.14331998953606456, -0.1433194488304741, -3.141591621822901, 0.7455776479558791, 3.1415914520163586, -3.1415933560496105, 0.7603938554148255, -1.6230983177616282e-06, 0.07186349688535713, 0.03197144517771341, -0.031971177878588546, -4.712389048748508, 1.5707948403165752, 1.2773619319829186, -1.5707990802172127, 1.5707957676951863, 1.289083769394045, 0.13644999397718796, 0.032761460443590046, -0.032762060585195645, -1.5707977610073176, 1.5707964181578042, -3.4826435600366983, -4.712389691708343, 1.570794277502252, 2.799088046133275]

이 시점에서는 ansatz Circuit에 대한 최종 파라미터를 찾기만 하면 됩니다. 그런 다음 최적화된 AQC Circuit과 나머지 시간 진행 Circuit을 합쳐 양자 하드웨어에서 실행할 완전한 시간 진행 Circuit을 만들 수 있습니다.

aqc_final_circuit = aqc_ansatz.assign_parameters(aqc_final_parameters)
aqc_final_circuit.compose(subsequent_circuit, inplace=True)
aqc_final_circuit.draw("mpl", fold=-1)

Output of the previous code cell

또한 aqc_comparison_circuit을 나머지 시간 진행 Circuit과 합쳐야 합니다. 이 Circuit은 AQC-Tensor로 최적화된 Circuit과 원래 Circuit의 성능을 비교하는 데 사용됩니다.

aqc_comparison_circuit.compose(subsequent_circuit, inplace=True)
aqc_comparison_circuit.draw("mpl", fold=-1)

Output of the previous code cell

Step 2: 양자 하드웨어 실행을 위한 문제 최적화

하드웨어를 선택합니다. 여기서는 최소 127 큐비트를 갖춘 IBM Quantum® 디바이스 중 사용 가능한 것을 사용합니다.

service = QiskitRuntimeService()
backend = service.least_busy(min_num_qubits=127)
print(backend)

PUB(Circuit 및 관측값)을 Backend ISA(Instruction Set Architecture)에 맞게 변환(transpile)합니다. optimization_level=3으로 설정하면 Transpiler가 Circuit을 1차원 큐비트 체인에 맞게 최적화하여 Circuit 충실도에 영향을 미치는 노이즈를 줄입니다. Circuit이 Backend와 호환되는 형식으로 변환되면, 수정된 큐비트 레이아웃과 일치하도록 관측값에도 동일한 변환을 적용합니다.

pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=3
)
isa_circuit = pass_manager.run(aqc_final_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
print("Observable info:", isa_observable)
print("Circuit depth:", isa_circuit.depth())
isa_circuit.draw("mpl", fold=-1, idle_wires=False)
Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ'],
coeffs=[1.+0.j])
Circuit depth: 111

이전 코드 셀의 출력

비교 Circuit에 대한 변환을 수행합니다.

isa_comparison_circuit = pass_manager.run(aqc_comparison_circuit)
isa_comparison_observable = observable.apply_layout(
isa_comparison_circuit.layout
)
print("Observable info:", isa_comparison_observable)
print("Circuit depth:", isa_comparison_circuit.depth())
isa_comparison_circuit.draw("mpl", fold=-1, idle_wires=False)
Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ'],
coeffs=[1.+0.j])
Circuit depth: 158

이전 코드 셀의 출력

Step 3: Qiskit 프리미티브를 사용하여 실행

이 단계에서는 변환된 Circuit을 양자 하드웨어(또는 시뮬레이션된 Backend)에서 실행합니다. qiskit_ibm_runtimeEstimatorV2 클래스를 사용하여 Estimator를 설정하고 Circuit을 실행하여 지정된 관측값을 측정합니다. 작업 결과는 관측값에 대한 기댓값을 제공하여 대상 하드웨어에서의 Circuit 성능에 대한 통찰을 줍니다.

estimator = Estimator(backend)
job = estimator.run([(isa_circuit, isa_observable)])
print("Job ID:", job.job_id())
job.result()
Job ID: czyhqdxd8drg008hx0yg
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(), dtype=float64>)), metadata={'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': False, 'enable_measure': True, 'num_randomizations': 'auto', 'shots_per_randomization': 'auto', 'interleave_randomizations': True, 'strategy': 'active-accum'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': False, 'pec_mitigation': False}, 'version': 2})

비교 Circuit에 대한 실행을 수행합니다.

job_comparison = estimator.run([(isa_comparison_circuit, isa_observable)])
print("Job Comparison ID:", job.job_id())
job_comparison.result()
Job Comparison ID: czyhqdxd8drg008hx0yg
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(), dtype=float64>)), metadata={'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': False, 'enable_measure': True, 'num_randomizations': 'auto', 'shots_per_randomization': 'auto', 'interleave_randomizations': True, 'strategy': 'active-accum'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': False, 'pec_mitigation': False}, 'version': 2})

Step 4: 후처리 및 원하는 고전적 형식으로 결과 반환

이 경우에는 재구성이 필요하지 않습니다. 실행 출력에서 기댓값에 직접 접근하여 결과를 바로 확인할 수 있습니다.

# AQC results
hw_results = job.result()
hw_results_dicts = [pub_result.data.__dict__ for pub_result in hw_results]
hw_expvals = [
pub_result_data["evs"].tolist() for pub_result_data in hw_results_dicts
]
aqc_expval = hw_expvals[0]

# AQC comparison results
hw_comparison_results = job_comparison.result()
hw_comparison_results_dicts = [
pub_result.data.__dict__ for pub_result in hw_comparison_results
]
hw_comparison_expvals = [
pub_result_data["evs"].tolist()
for pub_result_data in hw_comparison_results_dicts
]
aqc_compare_expval = hw_comparison_expvals[0]

print(f"Exact: \t{reference_expval:.4f}")
print(
f"AQC: \t{aqc_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_expval):.4f}"
)
print(
f"AQC Comparison:\t{aqc_compare_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_compare_expval):.4f}"
)
Exact:         	-0.5252
AQC: -0.4903, |∆| = 0.0349
AQC Comparison: 0.5424, |∆| = 1.0676

AQC, 비교, 정확한 Circuit의 결과를 비교하는 막대 그래프입니다.

plt.style.use("seaborn-v0_8")

labels = ["AQC Result", "AQC Comparison Result"]
values = [abs(aqc_expval), abs(aqc_compare_expval)]

plt.figure(figsize=(10, 6))
bars = plt.bar(labels, values, color=["tab:blue", "tab:purple"])
plt.axhline(
y=abs(reference_expval), color="red", linestyle="--", label="Exact Result"
)
plt.xlabel("Results")
plt.ylabel("Absolute Expected Value")
plt.title("AQC Result vs AQC Comparison Result (Absolute Values)")
plt.legend()
for bar in bars:
y_val = bar.get_height()
plt.text(
bar.get_x() + bar.get_width() / 2.0,
y_val,
round(y_val, 2),
va="bottom",
)
plt.show()

이전 코드 셀의 출력

파트 II: 규모 확장

이 튜토리얼의 두 번째 부분은 이전 예제를 바탕으로 50개 사이트의 더 큰 시스템으로 확장하여, 더 복잡한 양자 시뮬레이션 문제를 실행 가능한 양자 Circuit에 매핑하는 방법을 보여줍니다. 여기서는 50-사이트 XXZ 모델의 동역학을 탐구하여, 보다 현실적인 시스템 규모를 반영하는 대규모 양자 Circuit을 구축하고 최적화합니다.

50-사이트 XXZ 모델의 해밀토니안은 다음과 같이 정의됩니다:

H^XXZ=i=1L1Ji,(i+1)(XiX(i+1)+YiY(i+1)+2ZiZ(i+1)),\hat{\mathcal{H}}_{XXZ} = \sum_{i=1}^{L-1} J_{i,(i+1)}\left(X_i X_{(i+1)}+Y_i Y_{(i+1)}+ 2\cdot Z_i Z_{(i+1)} \right) \, ,

여기서 Ji,(i+1)J_{i,(i+1)}은 에지 (i,i+1)(i, i+1)에 해당하는 무작위 계수이고, L=50L=50은 사이트 수입니다. 해밀토니안의 결합 맵과 에지를 정의합니다.

L = 50  # L = length of our 1D spin chain

# Generate the edge list for this spin-chain
edge_list = [(i - 1, i) for i in range(1, L)]
# Generate an edge-coloring so we can make hw-efficient circuits
even_edges = edge_list[::2]
odd_edges = edge_list[1::2]

# Instantiate a CouplingMap object
coupling_map = CouplingMap(edge_list)

# Generate random coefficients for our XXZ Hamiltonian
np.random.seed(0)
Js = np.random.rand(L - 1) + 0.5 * np.ones(L - 1)

hamiltonian = SparsePauliOp(Pauli("I" * L))
for i, edge in enumerate(even_edges + odd_edges):
hamiltonian += SparsePauliOp.from_sparse_list(
[
("XX", (edge), Js[i] / 2),
("YY", (edge), Js[i] / 2),
("ZZ", (edge), Js[i]),
],
num_qubits=L,
)

observable = SparsePauliOp.from_sparse_list(
[("ZZ", (L // 2 - 1, L // 2), 1.0)], num_qubits=L
)

# Generate an initial state
L = hamiltonian.num_qubits
initial_state = QuantumCircuit(L)
for i in range(L):
if i % 2:
initial_state.x(i)

Step 1: 고전적 입력을 양자 문제로 변환하기

이 더 큰 문제에서는 먼저 50-사이트 XXZ 모델의 해밀토니안을 구성하고, 모든 사이트에 걸쳐 스핀-스핀 상호작용과 외부 자기장을 정의합니다. 이후 세 가지 주요 단계를 따릅니다:

  1. 최적화된 AQC Circuit 생성: Trotterization을 사용하여 초기 시간 발전을 근사한 후, 이 세그먼트를 압축하여 Circuit 깊이를 줄입니다.
  2. 나머지 시간 발전 Circuit 생성: 초기 세그먼트 이후의 나머지 시간 발전을 포착합니다.
  3. Circuit 결합: 최적화된 AQC Circuit과 나머지 발전 Circuit을 병합하여 실행 가능한 완전한 시간 발전 Circuit을 만듭니다.

AQC 대상 Circuit(초기 세그먼트)을 생성합니다.

aqc_evolution_time = 0.2
aqc_target_num_trotter_steps = 32

aqc_target_circuit = initial_state.copy()
aqc_target_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_target_num_trotter_steps),
time=aqc_evolution_time,
),
inplace=True,
)

후속 Circuit(나머지 세그먼트)을 생성합니다.

subsequent_num_trotter_steps = 3
subsequent_evolution_time = 0.2

subsequent_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=subsequent_num_trotter_steps),
time=subsequent_evolution_time,
)

AQC 비교 Circuit(초기 세그먼트이지만 후속 Circuit과 동일한 수의 Trotter 스텝 사용)을 생성합니다.

# Generate the AQC comparison circuit
aqc_comparison_num_trotter_steps = int(
subsequent_num_trotter_steps
/ subsequent_evolution_time
* aqc_evolution_time
)
print(
"Number of Trotter steps for comparison:",
aqc_comparison_num_trotter_steps,
)

aqc_comparison_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_comparison_num_trotter_steps),
time=aqc_evolution_time,
)
Number of Trotter steps for comparison: 3

참조 Circuit을 생성합니다.

evolution_time = 0.4
reps = 200

reference_circuit = initial_state.copy()
reference_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=reps),
time=evolution_time,
),
inplace=True,
)

더 적은 스텝의 Trotter Circuit으로부터 ansatz와 초기 파라미터를 생성합니다.

aqc_ansatz_num_trotter_steps = 1

aqc_good_circuit = initial_state.copy()
aqc_good_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_ansatz_num_trotter_steps),
time=aqc_evolution_time,
),
inplace=True,
)

aqc_ansatz, aqc_initial_parameters = generate_ansatz_from_circuit(
aqc_good_circuit
)
print(f"AQC Comparison circuit: depth {aqc_comparison_circuit.depth()}")
print(f"Target circuit: depth {aqc_target_circuit.depth()}")
print(
f"Ansatz circuit: depth {aqc_ansatz.depth()}, with {len(aqc_initial_parameters)} parameters"
)
AQC Comparison circuit: depth 36
Target circuit: depth 385
Ansatz circuit: depth 7, with 816 parameters

텐서 네트워크 시뮬레이션을 위한 설정을 구성한 후, 최적화를 위한 대상 상태의 행렬 곱 상태(MPS) 표현을 생성합니다. 그런 다음 초기 Circuit과 대상 상태 사이의 피델리티를 평가하여 Trotter 오차의 차이를 정량화합니다.

simulator_settings = QuimbSimulator(
quimb.tensor.CircuitMPS, autodiff_backend="jax"
)

# Build the matrix-product representation of the state to be approximated by AQC
aqc_target_mps = tensornetwork_from_circuit(
aqc_target_circuit, simulator_settings
)
print("Target MPS maximum bond dimension:", aqc_target_mps.psi.max_bond())

# Obtains the reference MPS, where we can obtain the exact expectation value by examining the `local_expectation``
reference_mps = tensornetwork_from_circuit(
reference_circuit, simulator_settings
)
reference_expval = reference_mps.local_expectation(
quimb.pauli("Z") & quimb.pauli("Z"), (L // 2 - 1, L // 2)
).real.item()

# Compute the starting fidelity
good_mps = tensornetwork_from_circuit(aqc_good_circuit, simulator_settings)
starting_fidelity = abs(compute_overlap(good_mps, aqc_target_mps)) ** 2
print("Starting fidelity:", starting_fidelity)
Target MPS maximum bond dimension: 5
Starting fidelity: 0.9926466919924161

ansatz 파라미터를 최적화하기 위해, SciPy의 L-BFGS 최적화기를 사용하여 MaximizeStateFidelity 비용 함수를 최소화합니다. 종료 기준은 AQC-Tensor 없이 초기 Circuit의 피델리티를 초과하도록 설정합니다. 이를 통해 압축된 Circuit이 더 낮은 Trotter 오차와 감소된 깊이를 모두 갖도록 보장합니다.

# Setting values for the optimization
aqc_stopping_fidelity = 1
aqc_max_iterations = 500

stopping_point = 1.0 - aqc_stopping_fidelity
objective = MaximizeStateFidelity(
aqc_target_mps, aqc_ansatz, simulator_settings
)

def callback(intermediate_result: OptimizeResult):
fidelity = 1 - intermediate_result.fun
print(
f"{datetime.datetime.now()} Intermediate result: Fidelity {fidelity:.8f}"
)
if intermediate_result.fun < stopping_point:
# Good enough for now
raise StopIteration

result = minimize(
objective,
aqc_initial_parameters,
method="L-BFGS-B",
jac=True,
options={"maxiter": aqc_max_iterations},
callback=callback,
)
if (
result.status
not in (
0,
1,
99,
)
): # 0 => success; 1 => max iterations reached; 99 => early termination via StopIteration
raise RuntimeError(
f"Optimization failed: {result.message} (status={result.status})"
)

print(f"Done after {result.nit} iterations.")
aqc_final_parameters = result.x
2025-04-14 11:48:28.705807 Intermediate result: Fidelity 0.99795851
2025-04-14 11:48:28.743265 Intermediate result: Fidelity 0.99822826
2025-04-14 11:48:28.776629 Intermediate result: Fidelity 0.99829675
2025-04-14 11:48:28.816153 Intermediate result: Fidelity 0.99832474
2025-04-14 11:48:28.856437 Intermediate result: Fidelity 0.99836131
2025-04-14 11:48:28.896432 Intermediate result: Fidelity 0.99839954
2025-04-14 11:48:28.936670 Intermediate result: Fidelity 0.99846517
2025-04-14 11:48:28.982069 Intermediate result: Fidelity 0.99865029
2025-04-14 11:48:29.026130 Intermediate result: Fidelity 0.99872332
2025-04-14 11:48:29.067426 Intermediate result: Fidelity 0.99892359
2025-04-14 11:48:29.110742 Intermediate result: Fidelity 0.99900640
2025-04-14 11:48:29.161362 Intermediate result: Fidelity 0.99907169
2025-04-14 11:48:29.207933 Intermediate result: Fidelity 0.99911423
2025-04-14 11:48:29.266772 Intermediate result: Fidelity 0.99918716
2025-04-14 11:48:29.331727 Intermediate result: Fidelity 0.99921278
2025-04-14 11:48:29.401694 Intermediate result: Fidelity 0.99924853
2025-04-14 11:48:29.467980 Intermediate result: Fidelity 0.99928797
2025-04-14 11:48:29.533281 Intermediate result: Fidelity 0.99933028
2025-04-14 11:48:29.600833 Intermediate result: Fidelity 0.99935757
2025-04-14 11:48:29.670816 Intermediate result: Fidelity 0.99938140
2025-04-14 11:48:29.736928 Intermediate result: Fidelity 0.99940964
2025-04-14 11:48:29.802931 Intermediate result: Fidelity 0.99944051
2025-04-14 11:48:29.869177 Intermediate result: Fidelity 0.99946828
2025-04-14 11:48:29.940156 Intermediate result: Fidelity 0.99948723
2025-04-14 11:48:30.005751 Intermediate result: Fidelity 0.99951011
2025-04-14 11:48:30.070853 Intermediate result: Fidelity 0.99954718
2025-04-14 11:48:30.139171 Intermediate result: Fidelity 0.99956267
2025-04-14 11:48:30.210506 Intermediate result: Fidelity 0.99958949
2025-04-14 11:48:30.279647 Intermediate result: Fidelity 0.99960498
2025-04-14 11:48:30.348016 Intermediate result: Fidelity 0.99961308
2025-04-14 11:48:30.414311 Intermediate result: Fidelity 0.99962894
2025-04-14 11:48:30.488910 Intermediate result: Fidelity 0.99964121
2025-04-14 11:48:30.561298 Intermediate result: Fidelity 0.99964348
2025-04-14 11:48:30.632214 Intermediate result: Fidelity 0.99964860
2025-04-14 11:48:30.705703 Intermediate result: Fidelity 0.99965695
2025-04-14 11:48:30.775679 Intermediate result: Fidelity 0.99966398
2025-04-14 11:48:30.842629 Intermediate result: Fidelity 0.99967816
2025-04-14 11:48:30.912357 Intermediate result: Fidelity 0.99968293
2025-04-14 11:48:30.979420 Intermediate result: Fidelity 0.99968936
2025-04-14 11:48:31.049196 Intermediate result: Fidelity 0.99969223
2025-04-14 11:48:31.125391 Intermediate result: Fidelity 0.99970009
2025-04-14 11:48:31.201256 Intermediate result: Fidelity 0.99970724
2025-04-14 11:48:31.272424 Intermediate result: Fidelity 0.99970987
2025-04-14 11:48:31.338907 Intermediate result: Fidelity 0.99971237
2025-04-14 11:48:31.404800 Intermediate result: Fidelity 0.99971916
2025-04-14 11:48:31.475226 Intermediate result: Fidelity 0.99971940
2025-04-14 11:48:31.547746 Intermediate result: Fidelity 0.99972465
2025-04-14 11:48:31.622827 Intermediate result: Fidelity 0.99972763
2025-04-14 11:48:31.819516 Intermediate result: Fidelity 0.99972894
2025-04-14 11:48:33.444538 Intermediate result: Fidelity 0.99972894
Done after 50 iterations.
parameters = [float(param) for param in aqc_final_parameters]

최적화된 ansatz와 나머지 시간 발전 Circuit을 조합하여 트랜스파일을 위한 최종 Circuit을 구성합니다.

aqc_final_circuit = aqc_ansatz.assign_parameters(aqc_final_parameters)
aqc_final_circuit.compose(subsequent_circuit, inplace=True)
aqc_comparison_circuit.compose(subsequent_circuit, inplace=True)

2단계: 양자 하드웨어 실행을 위한 문제 최적화

백엔드를 선택합니다.

service = QiskitRuntimeService()
backend = service.least_busy(min_num_qubits=127)
print(backend)

완성된 Circuit을 대상 하드웨어에 맞게 트랜스파일하여 실행을 준비합니다. 결과로 생성된 ISA Circuit은 백엔드에서 실행하도록 전송할 수 있습니다.

pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=3
)
isa_circuit = pass_manager.run(aqc_final_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
print("Observable info:", isa_observable)
print("Circuit depth:", isa_circuit.depth())
isa_circuit.draw("mpl", fold=-1, idle_wires=False)
Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j])
Circuit depth: 122

이전 코드 셀의 출력

isa_comparison_circuit = pass_manager.run(aqc_comparison_circuit)
isa_comparison_observable = observable.apply_layout(
isa_comparison_circuit.layout
)
print("Observable info:", isa_comparison_observable)
print("Circuit depth:", isa_comparison_circuit.depth())
isa_comparison_circuit.draw("mpl", fold=-1, idle_wires=False)
Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j])
Circuit depth: 158

이전 코드 셀의 출력

3단계: Qiskit 프리미티브를 사용하여 실행

이 단계에서는 qiskit_ibm_runtimeEstimatorV2를 사용하여 트랜스파일된 Circuit을 양자 하드웨어(또는 시뮬레이션된 백엔드)에서 실행하고 지정된 관측 가능량을 측정합니다. 작업 결과는 대상 하드웨어에서의 Circuit 성능에 대한 유용한 인사이트를 제공합니다.

이 대규모 예제에서는 EstimatorOptions를 활용하여 하드웨어 실험의 파라미터를 효과적으로 관리하고 제어하는 방법을 살펴봅니다. 이러한 설정은 선택 사항이지만, 실험 파라미터를 추적하고 최적의 결과를 위해 실행 옵션을 세밀하게 조정하는 데 유용합니다.

사용 가능한 실행 옵션의 전체 목록은 qiskit-ibm-runtime 문서를 참조하세요.

twirling_options = {
"enable_gates": True,
"enable_measure": True,
"num_randomizations": 300,
"shots_per_randomization": 100,
"strategy": "active",
}

zne_options = {
"amplifier": "gate_folding",
"noise_factors": [1, 2, 3],
"extrapolated_noise_factors": list(np.linspace(0, 3, 31)),
"extrapolator": ["exponential", "linear", "fallback"],
}

meas_learning_options = {
"num_randomizations": 512,
"shots_per_randomization": 512,
}

resilience_options = {
"measure_mitigation": True,
"zne_mitigation": True,
"zne": zne_options,
"measure_noise_learning": meas_learning_options,
}

estimator_options = {
"resilience": resilience_options,
"twirling": twirling_options,
}

estimator = Estimator(backend, options=estimator_options)
job = estimator.run([(isa_circuit, isa_observable)])
print("Job ID:", job.job_id())
job.result()
Job ID: czyjx6crxz8g008f63r0
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), evs_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), ensemble_stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), evs_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>), stds_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>)), metadata={'shots': 30000, 'target_precision': 0.005773502691896258, 'circuit_metadata': {}, 'resilience': {'zne': {'extrapolator': 'exponential'}}, 'num_randomizations': 300})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': True, 'enable_measure': True, 'num_randomizations': 300, 'shots_per_randomization': 100, 'interleave_randomizations': True, 'strategy': 'active'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': True, 'pec_mitigation': False, 'zne': {'noise_factors': [1, 2, 3], 'extrapolator': ['exponential', 'linear', 'fallback'], 'extrapolated_noise_factors': [0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9, 1, 1.1, 1.2000000000000002, 1.3, 1.4000000000000001, 1.5, 1.6, 1.7000000000000002, 1.8, 1.9000000000000001, 2, 2.1, 2.2, 2.3000000000000003, 2.4000000000000004, 2.5, 2.6, 2.7, 2.8000000000000003, 2.9000000000000004, 3]}}, 'version': 2})
job_comparison = estimator.run([(isa_comparison_circuit, isa_observable)])
print("Job Comparison ID:", job.job_id())
job_comparison.result()
Job Comparison ID: czyjx6crxz8g008f63r0
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), evs_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), ensemble_stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), evs_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>), stds_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>)), metadata={'shots': 30000, 'target_precision': 0.005773502691896258, 'circuit_metadata': {}, 'resilience': {'zne': {'extrapolator': 'exponential'}}, 'num_randomizations': 300})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': True, 'enable_measure': True, 'num_randomizations': 300, 'shots_per_randomization': 100, 'interleave_randomizations': True, 'strategy': 'active'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': True, 'pec_mitigation': False, 'zne': {'noise_factors': [1, 2, 3], 'extrapolator': ['exponential', 'linear', 'fallback'], 'extrapolated_noise_factors': [0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9, 1, 1.1, 1.2000000000000002, 1.3, 1.4000000000000001, 1.5, 1.6, 1.7000000000000002, 1.8, 1.9000000000000001, 2, 2.1, 2.2, 2.3000000000000003, 2.4000000000000004, 2.5, 2.6, 2.7, 2.8000000000000003, 2.9000000000000004, 3]}}, 'version': 2})

4단계: 결과 후처리 및 원하는 고전적 형식으로 반환

이전과 마찬가지로 재구성이 필요하지 않으며, 실행 출력에서 기댓값에 직접 접근하여 결과를 확인할 수 있습니다.

# AQC results
hw_results = job.result()
hw_results_dicts = [pub_result.data.__dict__ for pub_result in hw_results]
hw_expvals = [
pub_result_data["evs"].tolist() for pub_result_data in hw_results_dicts
]
aqc_expval = hw_expvals[0]

# AQC comparison results
hw_comparison_results = job_comparison.result()
hw_comparison_results_dicts = [
pub_result.data.__dict__ for pub_result in hw_comparison_results
]
hw_comparison_expvals = [
pub_result_data["evs"].tolist()
for pub_result_data in hw_comparison_results_dicts
]
aqc_compare_expval = hw_comparison_expvals[0]

print(f"Exact: \t{reference_expval:.4f}")
print(
f"AQC: \t{aqc_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_expval):.4f}"
)
print(
f"AQC Comparison:\t{aqc_compare_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_compare_expval):.4f}"
)
Exact:         	-0.5888
AQC: -0.4809, |∆| = 0.1078
AQC Comparison: 1.1764, |∆| = 1.7652

50사이트 XXZ 모델에 대한 AQC, 비교, 정확한 Circuit의 결과를 플롯합니다.

labels = ["AQC Result", "AQC Comparison Result"]
values = [abs(aqc_expval), abs(aqc_compare_expval)]

plt.figure(figsize=(10, 6))
bars = plt.bar(labels, values, color=["tab:blue", "tab:purple"])
plt.axhline(
y=abs(reference_expval), color="red", linestyle="--", label="Exact Result"
)
plt.xlabel("Results")
plt.ylabel("Absolute Expected Value")
plt.title("AQC Result vs AQC Comparison Result (Absolute Values)")
plt.legend()
for bar in bars:
y_val = bar.get_height()
plt.text(
bar.get_x() + bar.get_width() / 2.0,
y_val,
round(y_val, 2),
va="bottom",
)

plt.show()

이전 코드 셀의 출력

결론

이 튜토리얼에서는 텐서 네트워크를 활용한 근사 양자 컴파일(AQC-Tensor)을 사용하여 대규모 양자 동역학 시뮬레이션을 위한 Circuit을 압축하고 최적화하는 방법을 보여주었습니다. 소규모 및 대규모 하이젠베르크 모델 모두에 AQC-Tensor를 적용하여 트로터화된 시간 발전에 필요한 Circuit 깊이를 줄였습니다. 단순화된 Trotter Circuit에서 매개변수화된 안사츠를 생성하고 행렬 곱 상태(MPS) 기법으로 최적화함으로써, 정확하고 효율적인 목표 발전의 낮은 깊이 근사를 달성했습니다.

이 워크플로우는 양자 시뮬레이션 확장을 위한 AQC-Tensor의 핵심 장점을 보여줍니다.

  • 상당한 Circuit 압축: AQC-Tensor는 복잡한 시간 발전에 필요한 Circuit 깊이를 줄여 현재 장치에서의 실행 가능성을 높였습니다.
  • 효율적인 최적화: MPS 접근법은 파라미터 최적화를 위한 강력한 프레임워크를 제공하여 충실도와 계산 효율성 사이의 균형을 맞췄습니다.
  • 하드웨어 실행 준비 완료: 최종 최적화된 Circuit을 트랜스파일하여 대상 양자 하드웨어의 제약 조건을 충족시켰습니다.

더 큰 양자 장치와 더 발전된 알고리즘이 등장함에 따라, AQC-Tensor와 같은 기법은 근 미래 하드웨어에서 복잡한 양자 시뮬레이션을 실행하는 데 필수적이 될 것이며, 확장 가능한 양자 응용 프로그램의 깊이와 충실도 관리에서 유망한 진전을 보여줍니다.

튜토리얼 설문조사

이 짧은 설문조사를 통해 본 튜토리얼에 대한 피드백을 제공해 주세요. 여러분의 의견은 콘텐츠 품질과 사용자 경험을 개선하는 데 도움이 됩니다.

설문조사 링크

Note: This survey is provided by IBM Quantum and relates to the original English content. To give feedback on doQumentation's website, translations, or code execution, please open a GitHub issue.