주 콘텐츠로 건너뛰기

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

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

학습 성과​

  • AQC-Tensor Qiskit 애드온을 사용하여 깊은 Trotter 회로를 얕은 ansatz 회로로 압축하는 방법

  • Trotter 회로에서 파라미터화된 ansatz를 생성하고 텐서 네트워크(MPS) 방법으로 파라미터를 최적화하는 방법

  • 압축된 회로의 충실도를 목표 시간 진화와 비교하고 양자 하드웨어에서 실행하는 방법

전제 조건​

배경​

이 튜토리얼은 텐서 네트워크를 활용한 근사 양자 컴파일(AQC-Tensor)을 Qiskit과 함께 구현하여 양자 회로 성능을 향상시키는 방법을 설명합니다. AQC-Tensor는 시뮬레이션 정확도를 유지하면서 깊은 Trotter 회로를 더 얕고 하드웨어에 친화적인 회로로 압축합니다.

AQC-Tensor의 작동 원리​

kk번의 Trotter 스텝을 사용하여 해밀토니안 HH를 총 시간 tt 동안 시뮬레이션한다고 생각해 보세요. 전체 Trotter 회로는 다음과 같습니다:

Ufull=[UTrotter(t/k)]kU_{\text{full}} = \left[U_{\text{Trotter}}(t/k)\right]^k

단순한 접근 방식으로는 회로 깊이를 관리 가능한 수준으로 유지하기 위해 Trotter 스텝 수를 줄이지만, 이는 상당한 Trotter 오차를 초래합니다. AQC-Tensor는 정확도와 깊이를 분리함으로써 이 문제를 해결합니다:

  1. 목표 회로(높은 정확도, 깊은 회로): 동일한 시간 진화를 위해 스텝 수가 많은—예를 들어 10k10k개—Trotter 회로를 구성합니다. 이 회로는 Trotter 오차가 훨씬 적지만 하드웨어에서 실행하기에는 너무 깊습니다. 그러나 이 회로는 행렬 곱 상태(MPS)로 고전적으로 시뮬레이션만 하므로 깊이는 문제가 되지 않습니다.

  2. Ansatz 회로(낮은 깊이, 파라미터화): 단일 스텝 Trotter 회로와 동일한 구조를 가진 파라미터화된 회로 V(θ)V(\theta)를 정의합니다. V(θinit)=UTrotter(t/k)V(\theta_{\text{init}}) = U_{\text{Trotter}}(t/k)가 되도록 초기화한 뒤, V(θ)V(\theta)가 높은 정확도의 목표 상태를 최대한 가깝게 재현하도록 θ\theta를 반복적으로 최적화합니다.

그 결과 단일 Trotter 스텝의 깊이를 유지하면서도 많은 스텝의 정확도를 달성하는 회로가 생성되어, 가까운 미래의(단기) 양자 하드웨어에서 실행 가능해집니다.

AQC-Tensor를 사용하기 적합한 경우​

AQC-Tensor는 다음과 같은 경우에 가장 효과적입니다:

  • 회로 깊이가 하드웨어 결맞음 시간을 초과할 때. Trotter 시뮬레이션에 장치가 지원할 수 있는 것보다 더 많은 Trotter 스텝이 필요한 경우, AQC-Tensor로 시간 진화를 더 얕은 회로로 압축할 수 있습니다.

  • 얽힘이 고전적으로 처리 가능한 경우. 시간 진화된 상태의 총 얽힘은 주로 Trotter 스텝 수 kk가 아닌 시간 tt에 따라 달라집니다. 즉 tt가 결합 차원을 관리 가능한 수준으로 유지할 만큼 충분히 짧다면, 10k10k개의 스텝을 갖는 목표 회로는 일반적으로 kk개의 스텝을 갖는 회로보다 MPS로 표현하기 어렵지 않습니다.

  • 자연스러운 ansatz가 존재할 때. ansatz가 Trotter 회로의 구조를 반영하므로, 잘 정의된 초기 파라미터를 갖는 물리적으로 동기부여된 출발점을 제공하여 임의의 변분 ansatz에서 발생할 수 있는 수렴 문제를 방지합니다.

이 접근 방식은 일반적인 회로 압축과는 다릅니다: 더 적은 게이트로 임의의 유니터리를 근사하는 대신, AQC-Tensor는 동일한 게이트 구조를 유지하고 파라미터를 최적화하여 Trotter 오차를 줄입니다. 자세한 내용은 AQC-Tensor 문서를 참조하세요.

이 튜토리얼은 해밀토니안 정의, Trotter 회로 생성, 텐서 네트워크 최적화를 통한 회로 압축, IBM Quantum® 하드웨어에서의 실행에 이르는 전체 상태 준비 AQC-Tensor 워크플로우를 안내합니다.

요구 사항​

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

  • Qiskit SDK v2.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.linalg import expm
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 qiskit_ibm_runtime.fake_provider import FakeKyiv

from rustworkx.visualization import graphviz_draw

소규모 시뮬레이터 예제​

이 섹션에서는 10사이트 시스템을 사용하여 AQC-Tensor 워크플로우를 단계별로 설명합니다. 우리는 스핀 상호작용과 자기적 특성을 조사하기 위해 널리 연구되는 모델인 10사이트 XXZ 스핀 체인의 동역학을 시뮬레이션합니다.

해밀토니안은 다음과 같습니다:

H^XXZ=∑i=1L−1Ji,(i+1)(XiX(i+1)+YiY(i+1)+2⋅ZiZ(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입니다.

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

이 단계에서 수행할 작업:

  1. 해밀토니안, 관측량, 초기 상태를 정의합니다.
  2. 나중에 비교를 위해 고전적으로 정확한 기댓값을 계산합니다.
  3. 높은 정확도의 Trotter 회로(AQC 목표)를 생성하고 AQC-Tensor를 사용하여 낮은 깊이의 ansatz로 압축합니다.

해밀토니안, 관측량, 초기 상태 설정​

# L is the number of sites in the 1D spin chain
L = 10

# Generate the coupling map
edge_list = [(i - 1, i) for i in range(1, L)]
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
)

# Generate an initial Néel state |1010101010⟩
initial_state_circuit = QuantumCircuit(L)
for i in range(L):
if i % 2:
initial_state_circuit.x(i)

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])

Output of the previous code cell

정확한 기댓값 계산​

이 크기의 시스템에서는 행렬 지수화를 사용하여 정확한 시간 진화 기댓값을 직접 계산할 수 있습니다. 이는 AQC 회로의 정확도를 평가하기 위한 기준값으로 사용됩니다.

aqc_evolution_time = 0.2

# Each baseline Trotter step covers dt = aqc_evolution_time / 3
# The subsequent (uncompressed) step covers 1 additional dt
subsequent_evolution_time = aqc_evolution_time / 3
total_evolution_time = aqc_evolution_time + subsequent_evolution_time

# Compute exact expectation value via matrix exponentiation
H_matrix = hamiltonian.to_matrix()
U_exact = expm(-1j * H_matrix * total_evolution_time)

# Build the initial state vector (Néel state)
initial_state_vec = np.zeros(2**L)
state_idx = sum(2**i for i in range(L) if i % 2)
initial_state_vec[state_idx] = 1.0

# Evolve and compute expectation value
evolved_state = U_exact @ initial_state_vec
obs_matrix = observable.to_matrix()
exact_expval = (evolved_state.conj() @ obs_matrix @ evolved_state).real

print(f"AQC evolution time: {aqc_evolution_time}")
print(f"Subsequent evolution time: {subsequent_evolution_time:.6f}")
print(f"Total evolution time: {total_evolution_time:.6f}")
print(f"Exact expectation value: {exact_expval:.6f}")
AQC evolution time: 0.2
Subsequent evolution time: 0.066667
Total evolution time: 0.266667
Exact expectation value: -0.700899

AQC 목표 회로 생성​

이제 AQC 목표로 사용할 Trotter 회로를 구성합니다. 이 회로는 높은 정확도를 위해 많은 수의 Trotter 스텝(32)을 사용합니다. MPS로만 고전적으로 시뮬레이션될 뿐—하드웨어에서 실행되지 않으므로—깊은 깊이는 문제가 되지 않습니다.

aqc_target_num_trotter_steps = 32

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

ansatz, 초기 파라미터, 후속 회로 및 기준 회로 생성​

다음으로, AQC 목표와 동일한 시간 진화를 갖지만 Trotter 스텝 수가 훨씬 적은(단 하나) "좋은" 회로를 구성합니다. 이 회로를 generate_ansatz_from_circuit에 전달하면 다음을 반환합니다:

  1. 동일한 2-큐비트 연결성을 갖는 일반화된 파라미터화 ansatz 회로.

  2. ansatz에 대입하면 입력 회로를 재현하는 초기 파라미터.

추가로 다음을 구성합니다:

  • 후속 회로: AQC 최적화된 구간 이후에 추가될(압축되지 않은) 하나의 Trotter 스텝을 갖는 회로. AQC-Tensor 초기 상태 튜토리얼의 접근 방식을 따릅니다.
  • 기준 Trotter 회로: 전체 시간 진화(aqc_evolution_time + subsequent_evolution_time)에 걸쳐 4개의 Trotter 스텝을 사용하는 회로. 이는 비교 기준 역할을 합니다: AQC 없이 하드웨어에서 실행할 경우의 결과를 나타냅니다. AQC ansatz(3개의 압축된 스텝 + 1개의 비압축 스텝)는 더 낮은 깊이에서 더 높은 정확도를 달성합니다.
aqc_ansatz_num_trotter_steps = 1

aqc_good_circuit = initial_state_circuit.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
)

# Subsequent circuit: 1 non-compressed Trotter step appended after AQC
subsequent_num_trotter_steps = 1
subsequent_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=subsequent_num_trotter_steps),
time=subsequent_evolution_time,
)

# Baseline Trotter circuit: 4 Trotter steps over total evolution time, no AQC
baseline_num_trotter_steps = 4
baseline_circuit = initial_state_circuit.copy()
baseline_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=baseline_num_trotter_steps),
time=total_evolution_time,
),
inplace=True,
)

print(
f"Target circuit: depth {aqc_target_circuit.depth(lambda x: x.operation.num_qubits == 2)}"
)
print(
f"Baseline circuit: depth {baseline_circuit.depth(lambda x: x.operation.num_qubits == 2)} ({baseline_num_trotter_steps} Trotter steps, time={total_evolution_time:.4f})"
)
print(
f"Subsequent circuit: depth {subsequent_circuit.depth(lambda x: x.operation.num_qubits == 2)} ({subsequent_num_trotter_steps} Trotter step, time={subsequent_evolution_time:.4f})"
)
print(
f"Ansatz circuit: depth {aqc_ansatz.depth(lambda x: x.operation.num_qubits == 2)}, with {len(aqc_initial_parameters)} parameters"
)
aqc_ansatz.draw("mpl", fold=-1)
Target circuit: depth 384
Baseline circuit: depth 48 (4 Trotter steps, time=0.2667)
Subsequent circuit: depth 12 (1 Trotter step, time=0.0667)
Ansatz circuit: depth 3, with 156 parameters

Output of the previous code cell

텐서 네트워크 시뮬레이션 설정 및 목표 MPS 구축​

그래디언트 기반 최적화를 위한 자동 미분을 제공하는 JAX와 함께 quimb 행렬 곱 상태(MPS) Circuit 시뮬레이터를 사용합니다. 그런 다음 목표 상태의 MPS 표현을 구축하고, 초기 ansatz와 목표 상태 사이의 시작 충실도를 평가합니다. 이 문제 인스턴스는 비교적 작은 예제이므로 시작 충실도는 꽤 높습니다.

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

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

good_mps = tensornetwork_from_circuit(aqc_good_circuit, simulator_settings)
starting_fidelity = abs(compute_overlap(good_mps, aqc_target_mps)) ** 2
print(f"Starting fidelity: {starting_fidelity:.6f}")
Target MPS maximum bond dimension: 5
Starting fidelity: 0.998246

Ansatz 파라미터 최적화​

L-BFGS-B 최적화기를 사용하여 MaximizeStateFidelity 비용 함수를 최소화합니다. 최적화기는 ansatz 회로와 목표 MPS 사이의 충실도를 최대화하도록 ansatz 파라미터를 반복적으로 조정합니다.

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:
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):
raise RuntimeError(
f"Optimization failed: {result.message} (status={result.status})"
)

print(f"Done after {result.nit} iterations.")
aqc_final_parameters = result.x
2026-05-18 13:14:49.731596 Intermediate result: Fidelity 0.99952882
2026-05-18 13:14:49.734425 Intermediate result: Fidelity 0.99958531
2026-05-18 13:14:49.737101 Intermediate result: Fidelity 0.99960093
2026-05-18 13:14:49.739813 Intermediate result: Fidelity 0.99961046
2026-05-18 13:14:49.742969 Intermediate result: Fidelity 0.99962560
2026-05-18 13:14:49.745916 Intermediate result: Fidelity 0.99964395
2026-05-18 13:14:49.748615 Intermediate result: Fidelity 0.99968150
2026-05-18 13:14:49.753684 Intermediate result: Fidelity 0.99970569
2026-05-18 13:14:49.756208 Intermediate result: Fidelity 0.99973788
2026-05-18 13:14:49.759067 Intermediate result: Fidelity 0.99975385
2026-05-18 13:14:49.762321 Intermediate result: Fidelity 0.99976458
2026-05-18 13:14:49.765526 Intermediate result: Fidelity 0.99977661
2026-05-18 13:14:49.768496 Intermediate result: Fidelity 0.99978663
2026-05-18 13:14:49.771278 Intermediate result: Fidelity 0.99980236
2026-05-18 13:14:49.773735 Intermediate result: Fidelity 0.99981607
2026-05-18 13:14:49.776339 Intermediate result: Fidelity 0.99982811
2026-05-18 13:14:49.779177 Intermediate result: Fidelity 0.99985827
2026-05-18 13:14:49.782243 Intermediate result: Fidelity 0.99988354
2026-05-18 13:14:49.784904 Intermediate result: Fidelity 0.99991608
2026-05-18 13:14:49.787737 Intermediate result: Fidelity 0.99993336
2026-05-18 13:14:49.790414 Intermediate result: Fidelity 0.99993956
2026-05-18 13:14:49.793029 Intermediate result: Fidelity 0.99994421
2026-05-18 13:14:49.795585 Intermediate result: Fidelity 0.99994743
2026-05-18 13:14:49.835045 Intermediate result: Fidelity 0.99994791
2026-05-18 13:14:49.839786 Intermediate result: Fidelity 0.99994803
2026-05-18 13:14:49.842403 Intermediate result: Fidelity 0.99994898
2026-05-18 13:14:49.873779 Intermediate result: Fidelity 0.99994898
Done after 27 iterations.

최종 AQC 회로 조립​

최적화된 파라미터를 확보한 후, 이를 ansatz에 바인딩하고 후속(비압축) Trotter 스텝을 추가합니다. 결과 회로는 단일 압축 Trotter 스텝과 하나의 비압축 스텝의 깊이를 가지지만, 압축된 부분은 32개의 Trotter 스텝의 정확도를 근사합니다.

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

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

이 소규모 예제에서는 하드웨어 실행을 로컬에서 시뮬레이션하기 위해 가짜 백엔드(FakeKyiv)를 사용합니다. AQC 최적화 회로(aqc_final_circuit)와 기준 Trotter 회로(baseline_circuit, AQC 없이 전체 시간 진화에 걸쳐 4개의 Trotter 스텝)를 모두 백엔드의 ISA(명령 세트 아키텍처)로 트랜스파일하며, 회로 깊이를 더욱 줄이기 위해 optimization_level=3을 사용합니다.

backend = FakeKyiv()

pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=3
)

# Transpile the AQC-optimized circuit (compressed + subsequent step)
isa_circuit = pass_manager.run(aqc_final_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
print(
"AQC circuit depth:",
isa_circuit.depth(lambda x: x.operation.num_qubits == 2),
)

# Transpile the baseline Trotter circuit (no AQC optimization)
isa_baseline_circuit = pass_manager.run(baseline_circuit)
isa_baseline_observable = observable.apply_layout(isa_baseline_circuit.layout)
print(
"Baseline Trotter circuit depth:",
isa_baseline_circuit.depth(lambda x: x.operation.num_qubits == 2),
)
AQC circuit depth: 15
Baseline Trotter circuit depth: 27

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

가짜 백엔드로 AQC 최적화 회로와 기준 Trotter 회로를 모두 실행하여 ZZ 관측량을 측정하기 위해 EstimatorV2 프리미티브를 사용합니다.

estimator = Estimator(backend)

# Run both circuits
aqc_result = estimator.run([(isa_circuit, isa_observable)]).result()
baseline_result = estimator.run(
[(isa_baseline_circuit, isa_baseline_observable)]
).result()

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

두 실행에서 기댓값을 추출하고 정확한 결과와 비교합니다. 기준 Trotter 회로는 동일한 회로 깊이에서 AQC 없이 얻을 수 있는 결과를 보여주며, AQC 회로는 텐서 네트워크 최적화를 통한 개선을 보여줍니다.

aqc_expval = aqc_result[0].data.evs.tolist()
baseline_expval = baseline_result[0].data.evs.tolist()

print(f"Exact: {exact_expval:.4f}")
print(
f"Baseline Trotter: {baseline_expval:.4f}, |\u0394| = {np.abs(exact_expval - baseline_expval):.4f} (depth {isa_baseline_circuit.depth(lambda x: x.operation.num_qubits == 2)}, {baseline_num_trotter_steps} steps)"
)
print(
f"AQC (3+1): {aqc_expval:.4f}, |\u0394| = {np.abs(exact_expval - aqc_expval):.4f} (depth {isa_circuit.depth(lambda x: x.operation.num_qubits == 2)}, compressed+subsequent)"
)
Exact: -0.7009
Baseline Trotter: -0.5400, |Δ| = 0.1609 (depth 27, 4 steps)
AQC (3+1): -0.5728, |Δ| = 0.1281 (depth 15, compressed+subsequent)
plt.style.use("seaborn-v0_8")

labels = [
f"Baseline Trotter\n({baseline_num_trotter_steps} steps, depth {isa_baseline_circuit.depth(lambda x: x.operation.num_qubits == 2)})",
f"AQC (3+1)\n(depth {isa_circuit.depth(lambda x: x.operation.num_qubits == 2)})",
]
values = [baseline_expval, aqc_expval]
colors = ["tab:orange", "tab:blue"]

plt.figure(figsize=(8, 5))
bars = plt.bar(labels, values, color=colors, width=0.5)
plt.axhline(
y=exact_expval,
color="tab:green",
linestyle="--",
linewidth=2,
label=f"Exact ({exact_expval:.4f})",
)
plt.ylabel("Expected Value")
plt.title(
"AQC-Tensor (3 compressed + 1 uncompressed) vs Baseline Trotter (10-site XXZ)"
)
plt.legend()
for bar in bars:
y_val = bar.get_height()
plt.text(
bar.get_x() + bar.get_width() / 2.0,
y_val,
f"{y_val:.4f}",
ha="center",
va="bottom" if y_val >= 0 else "top",
)
plt.axhline(y=0, color="black", linewidth=0.3)
plt.tight_layout()
plt.show()

Output of the previous code cell

대규모 하드웨어 예제​

이제 더 현실적인 문제 크기에서 AQC-Tensor를 시연하기 위해 50-사이트 XXZ 모델로 확장합니다. 워크플로우는 소규모 예제와 동일합니다: AQC를 통해 3개의 Trotter 스텝을 압축하고 하나의 비압축 스텝을 추가합니다.

이 크기의 시스템에서는 행렬 지수화가 불가능하므로(2502^{50} 차원), 전체 시간에 걸쳐 진화된 높은 정확도의 MPS에서 기준 기댓값을 직접 계산합니다.

1~4단계 통합​

# -------------------------Step 1-------------------------

# Define the 50-site spin chain
L = 50
edge_list = [(i - 1, i) for i in range(1, L)]
even_edges = edge_list[::2]
odd_edges = edge_list[1::2]
coupling_map = CouplingMap(edge_list)

# Random 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
)

# Initial Néel state
initial_state_circuit = QuantumCircuit(L)
for i in range(L):
if i % 2:
initial_state_circuit.x(i)

# Time parameters
aqc_evolution_time = 0.2
subsequent_evolution_time = aqc_evolution_time / 3
total_evolution_time = aqc_evolution_time + subsequent_evolution_time

# AQC target circuit (high-accuracy, 32 Trotter steps for AQC portion)
aqc_target_num_trotter_steps = 32

aqc_target_circuit = initial_state_circuit.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 ansatz from 1-step Trotter circuit
aqc_good_circuit = initial_state_circuit.copy()
aqc_good_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=1),
time=aqc_evolution_time,
),
inplace=True,
)

aqc_ansatz, aqc_initial_parameters = generate_ansatz_from_circuit(
aqc_good_circuit
)

# Subsequent circuit: 1 non-compressed Trotter step
subsequent_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=1),
time=subsequent_evolution_time,
)

# Baseline Trotter circuit: 4 Trotter steps over total evolution time, no AQC
baseline_num_trotter_steps = 4
baseline_circuit = initial_state_circuit.copy()
baseline_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=baseline_num_trotter_steps),
time=total_evolution_time,
),
inplace=True,
)
print(
f"Target circuit: depth {aqc_target_circuit.depth(lambda x: x.operation.num_qubits == 2)}"
)
print(
f"Ansatz circuit: depth {aqc_ansatz.depth(lambda x: x.operation.num_qubits == 2)}, with {len(aqc_initial_parameters)} parameters"
)
print(
f"Subsequent circuit: depth {subsequent_circuit.depth(lambda x: x.operation.num_qubits == 2)}"
)
print(
f"Baseline circuit: depth {baseline_circuit.depth(lambda x: x.operation.num_qubits == 2)} ({baseline_num_trotter_steps} steps, time={total_evolution_time:.4f})"
)

# Build target MPS and compute reference expectation value
simulator_settings = QuimbSimulator(
quimb.tensor.CircuitMPS, autodiff_backend="jax"
)
aqc_target_mps = tensornetwork_from_circuit(
aqc_target_circuit, simulator_settings
)
print("Target MPS maximum bond dimension:", aqc_target_mps.psi.max_bond())

# For the reference expectation value, we need the full evolution (AQC + subsequent)
# Build a high-accuracy full circuit for MPS reference
full_target_circuit = initial_state_circuit.copy()
full_target_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_target_num_trotter_steps),
time=total_evolution_time,
),
inplace=True,
)
full_target_mps = tensornetwork_from_circuit(
full_target_circuit, simulator_settings
)
exact_expval = full_target_mps.local_expectation(
quimb.pauli("Z") & quimb.pauli("Z"), (L // 2 - 1, L // 2)
).real.item()
print(f"Reference expectation value (from MPS): {exact_expval:.6f}")

# Optimize ansatz parameters
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}"
)

result = minimize(
objective,
aqc_initial_parameters,
method="L-BFGS-B",
jac=True,
options={"maxiter": 500},
callback=callback,
)
if result.status not in (0, 1, 99):
raise RuntimeError(
f"Optimization failed: {result.message} (status={result.status})"
)
print(f"Done after {result.nit} iterations.")

# Assemble the final AQC circuit: optimized ansatz + subsequent Trotter step
aqc_final_circuit = aqc_ansatz.assign_parameters(result.x)
aqc_final_circuit.compose(subsequent_circuit, inplace=True)

# -------------------------Step 2-------------------------

service = QiskitRuntimeService()
backend = service.least_busy(min_num_qubits=127)
print(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(
"AQC circuit depth:",
isa_circuit.depth(lambda x: x.operation.num_qubits == 2),
)

# Also transpile the baseline Trotter circuit (4 Trotter steps, no AQC)
isa_baseline_circuit = pass_manager.run(baseline_circuit)
isa_baseline_observable = observable.apply_layout(isa_baseline_circuit.layout)
print(
"Baseline Trotter circuit depth:",
isa_baseline_circuit.depth(lambda x: x.operation.num_qubits == 2),
)

# -------------------------Step 3-------------------------

# Submit both circuits in a single job
estimator = Estimator(backend)
estimator.options.environment.job_tags = ["TUT_AQCTE"]

job = estimator.run(
[
(isa_circuit, isa_observable),
(isa_baseline_circuit, isa_baseline_observable),
]
)
print("Job ID:", job.job_id())
Target circuit: depth 385
Ansatz circuit: depth 7, with 816 parameters
Subsequent circuit: depth 12
Baseline circuit: depth 49 (4 steps, time=0.2667)
Target MPS maximum bond dimension: 5
Reference expectation value (from MPS): -0.738669
2026-05-18 13:02:11.219150 Intermediate result: Fidelity 0.99795732
2026-05-18 13:02:11.232256 Intermediate result: Fidelity 0.99822481
2026-05-18 13:02:11.245160 Intermediate result: Fidelity 0.99829520
2026-05-18 13:02:11.257765 Intermediate result: Fidelity 0.99832379
2026-05-18 13:02:11.270280 Intermediate result: Fidelity 0.99836416
2026-05-18 13:02:11.284116 Intermediate result: Fidelity 0.99840073
2026-05-18 13:02:11.296856 Intermediate result: Fidelity 0.99846863
2026-05-18 13:02:11.309602 Intermediate result: Fidelity 0.99865244
2026-05-18 13:02:11.322012 Intermediate result: Fidelity 0.99872665
2026-05-18 13:02:11.334195 Intermediate result: Fidelity 0.99892335
2026-05-18 13:02:11.346570 Intermediate result: Fidelity 0.99901045
2026-05-18 13:02:11.359202 Intermediate result: Fidelity 0.99907181
2026-05-18 13:02:11.371511 Intermediate result: Fidelity 0.99911125
2026-05-18 13:02:11.383870 Intermediate result: Fidelity 0.99918585
2026-05-18 13:02:11.396184 Intermediate result: Fidelity 0.99921504
2026-05-18 13:02:11.408543 Intermediate result: Fidelity 0.99924936
2026-05-18 13:02:11.422557 Intermediate result: Fidelity 0.99929226
2026-05-18 13:02:11.436275 Intermediate result: Fidelity 0.99933099
2026-05-18 13:02:11.449511 Intermediate result: Fidelity 0.99935792
2026-05-18 13:02:11.462093 Intermediate result: Fidelity 0.99937925
2026-05-18 13:02:11.475783 Intermediate result: Fidelity 0.99940690
2026-05-18 13:02:11.490254 Intermediate result: Fidelity 0.99944409
2026-05-18 13:02:11.503292 Intermediate result: Fidelity 0.99946840
2026-05-18 13:02:11.516064 Intermediate result: Fidelity 0.99949378
2026-05-18 13:02:11.532861 Intermediate result: Fidelity 0.99951380
2026-05-18 13:02:11.546182 Intermediate result: Fidelity 0.99955313
2026-05-18 13:02:11.559168 Intermediate result: Fidelity 0.99955707
2026-05-18 13:02:11.571753 Intermediate result: Fidelity 0.99959306
2026-05-18 13:02:11.584257 Intermediate result: Fidelity 0.99960486
2026-05-18 13:02:11.597610 Intermediate result: Fidelity 0.99961714
2026-05-18 13:02:11.610106 Intermediate result: Fidelity 0.99962953
2026-05-18 13:02:11.622515 Intermediate result: Fidelity 0.99963525
2026-05-18 13:02:11.635543 Intermediate result: Fidelity 0.99964658
2026-05-18 13:02:11.649044 Intermediate result: Fidelity 0.99965027
2026-05-18 13:02:11.664148 Intermediate result: Fidelity 0.99965802
2026-05-18 13:02:11.678033 Intermediate result: Fidelity 0.99966731
2026-05-18 13:02:11.692714 Intermediate result: Fidelity 0.99967780
2026-05-18 13:02:11.706753 Intermediate result: Fidelity 0.99968567
2026-05-18 13:02:11.720780 Intermediate result: Fidelity 0.99969139
2026-05-18 13:02:11.733471 Intermediate result: Fidelity 0.99969628
2026-05-18 13:02:11.745998 Intermediate result: Fidelity 0.99970331
2026-05-18 13:02:11.758424 Intermediate result: Fidelity 0.99970796
2026-05-18 13:02:11.771986 Intermediate result: Fidelity 0.99971165
2026-05-18 13:02:11.785841 Intermediate result: Fidelity 0.99971892
2026-05-18 13:02:11.799105 Intermediate result: Fidelity 0.99972226
2026-05-18 13:02:11.811623 Intermediate result: Fidelity 0.99972441
2026-05-18 13:02:11.824114 Intermediate result: Fidelity 0.99972679
2026-05-18 13:02:11.837179 Intermediate result: Fidelity 0.99972965
2026-05-18 13:02:12.345479 Intermediate result: Fidelity 0.99972965
Done after 49 iterations.
<IBMBackend('ibm_pittsburgh')>
AQC circuit depth: 71
Baseline Trotter circuit depth: 111
Job ID: d85kc6o0bvlc73d5nhn0
# -------------------------Step 4-------------------------

hw_results = job.result()
aqc_expval = hw_results[0].data.evs.tolist()
baseline_expval = hw_results[1].data.evs.tolist()

print(f"Exact (MPS): {exact_expval:.4f}")
print(
f"Baseline Trotter: {baseline_expval:.4f}, |\u0394| = {np.abs(exact_expval - baseline_expval):.4f}"
)
print(
f"AQC (3+1): {aqc_expval:.4f}, |\u0394| = {np.abs(exact_expval - aqc_expval):.4f}"
)

labels = [
f"Baseline Trotter\n({baseline_num_trotter_steps} steps, depth {isa_baseline_circuit.depth(lambda x: x.operation.num_qubits == 2)})",
f"AQC (3+1)\n(depth {isa_circuit.depth(lambda x: x.operation.num_qubits == 2)})",
]
values = [baseline_expval, aqc_expval]
colors = ["tab:orange", "tab:blue"]

plt.figure(figsize=(8, 5))
bars = plt.bar(labels, values, color=colors, width=0.5)
plt.axhline(
y=exact_expval,
color="tab:green",
linestyle="--",
linewidth=2,
label=f"Exact ({exact_expval:.4f})",
)
plt.ylabel("Expected Value")
plt.title(
"AQC-Tensor (3 compressed + 1 uncompressed) vs Baseline Trotter (50-site XXZ)"
)
plt.legend()
for bar in bars:
y_val = bar.get_height()
plt.text(
bar.get_x() + bar.get_width() / 2.0,
y_val,
f"{y_val:.4f}",
ha="center",
va="bottom" if y_val >= 0 else "top",
)
plt.axhline(y=0, color="black", linewidth=0.3)
plt.tight_layout()
plt.show()
Exact (MPS): -0.7387
Baseline Trotter: -0.5955, |Δ| = 0.1432
AQC (3+1): -0.6734, |Δ| = 0.0653

이전 코드 셀의 출력

다음 단계​

추천

이 내용이 흥미로우셨다면 다음 자료를 살펴보세요: