트로터 오류를 줄이기 위한 다중 제품 공식
예상 소요 시간: Heron r2 프로세서에서 4분 (참고: 이는 단지 예상치일 뿐입니다.) (실행 시간은 다를 수 있습니다.)
학습 성과
- 다중 제품 공식(MPF)이 여러 개의 얕은 회로에서 얻은 기대값을 결합함으로써 해밀토니안 시뮬레이션에서 트로터 오차를 어떻게 줄이는가
- MPF가 표준 상품 구성보다 유리한 경우와 그렇지 않은 경우
- 이
qiskit_addon_mpf패키지를 사용하여 정적 및 동적 MPF 계수를 계산하는 방법 - IBM Quantum® 하드웨어에서 트랜스파일링, 오류 완화 및 후처리를 포함한 MPF 워크플로를 처음부터 끝까지 실행하는 방법
전제조건
- 해밀턴 연산 시뮬레이션 회로의 조합 방법 — Qiskit에서 트로터(곱 공식) 회로를 소개합니다.
SuzukiTrotterQiskit의 제품 공식, 특히 및LieTrotter합성 클래스.- Qiskit primitives 그리고 Estimator 인터페이스.
배경
다중 제품 포뮬러란 무엇인가요?
양자 컴퓨터에서 양자 시스템을 시뮬레이션할 때, 핵심 과제는 해밀토니안 에 대한 시간 진화 연산자 를 근사화하는 것이다. 표준적인 접근 방식은 트로터-스즈키 분해라고도 알려진 곱 공식 (PF)을 사용하는 것이다. 이 방법은 를 개별 유니터리 연산 을 효율적으로 구현할 수 있는 항들로 분해한 다음, 전체 진화 과정을 이러한 더 단순한 유니터리 연산들의 순서된 곱으로 근사화합니다.
1차 곱 공식(리-트로터)은 다음과 같습니다:
이는 2차 오차를 초래합니다: . 고차 대칭 공식 (여기서 는 대칭 곱 공식의 차수를 나타냅니다 [1] 참조)은 로 더 빠르게 수렴하지만, 단계당 회로 깊이가 더 깊어지는 대가를 치릅니다.
고정 차수 에서의 오차를 줄이기 위해, 일반적으로 전체 진화 시간 을 개의 작은 트로터 단계로 나눕니다. 각 단계에서는 를 곱셈 공식으로 근사화하며, 이러한 단계들이 연결됩니다:
차 대칭 공식의 경우, 잔여 트로터 오차는 의 비율로 증가합니다. 따라서 를 증가시키면 트로터 오차를 빠르게 억제할 수 있지만, 동시에 회로 깊이가 선형적으로 증가하게 되며, 노이즈가 많은 하드웨어에서는 이는 게이트 노이즈의 누적량이 더 많아진다는 것을 의미합니다. 트로터 오차( 가 클수록 유리함) 와 하드웨어 노이즈( 가 작을수록 유리함) 사이의 이러한 상충 관계야말로, 다중 제품 공식이 해결하기 위해 고안된 바로 그 문제입니다. MPF는 고정된 순서 로 의 다양한 선택 결과를 결합하는 것에 관한 것이며, 기본 곱셈 공식의 순서를 변경하는 것은 아니라는 점에 유의하십시오.
다중 제품 공식(MPF) [1]은 각각 서로 다른 수의 트로터 단계 ( 단계 수 집합)를 사용하는 여러 개의 더 얕은 트로터 회로에서 얻은 기대값의 가중 선형 조합 을 구성합니다:
여기서 는 시간 에서 관측량 의 기대값으로, 단계의 트로터 회로를 통해 추정된 것이며, 계수 는 조합에서 주요 트로터 오차 항들이 상쇄되도록 선택된다. 4단계 에서 이 식을 다시 살펴보면서, 트로터(Trotter)의 결과를 종합하기 위해 이를 명시적으로 계산할 것입니다. 실무상 가장 중요한 점은 MPF의 가장 깊은 회로에 필요한 단계 수가 에 불과하다는 것인데, 이는 동일한 유효 트로터 오차를 직접 구하는 데 필요한 단일 보다 훨씬 적은 수치입니다. 회로 깊이가 얕기 때문에 MPF 방식은 노이즈가 많은 하드웨어에 더 적합합니다.
계수는 어떻게 결정되나요?
MPF 계수에는 두 가지 계열이 있습니다:
정적 계수는 해밀토니안, 초기 상태 및 진화 시간과 무관하다. 이는 선행 트로터 오차 항들이 상쇄되도록 하는 선형 연립방정식 을 풀어서 구할 수 있다. 차 대칭 곱 공식과 함께 사용되는 일련의 트로터 단계 에 대해, 의 역수 제곱으로 트로터 오차를 전개하면 다음과 같은 형태의 제약 방정식이 도출된다:
여기서 정수 지수 는 선택된 곱셈 공식에 대한 연속적인 트로터 오차 항들의 차수를 나타낸다. 대칭적인 차 PF의 경우, 의 주 오차는 의 비율을 따르며, 후속 보정은 의 비율을 따릅니다. 따라서 지수는 입니다. 비대칭 PF의 경우, 홀수 지수와 짝수 지수가 모두 기여하며 입니다. 전체 도출 과정은 참고문헌 [1] 을 참조하십시오. 위 방정식계의 첫 번째 방정식은 편향 없음( 한 극한에서 MPF가 정확한 기대값을 재현함)을 보장하며, 나머지 방정식들은 첫 번째 트로터 오차 항들을 차례로 상쇄합니다. 결과로 얻어진 -노름 이 너무 클 경우(이는 표본 잡음을 증폭시킴), 대신 을 상한으로 설정하고 을 최소화하는 근사 최적화 문제를 풀 수 있습니다.
동적 계수 [2], [3]은 또한 해밀토니안, 초기 상태 및 진화 시간 에 따라 달라진다. 이 계수들은 실제 시간 진화 상태와 MPF 근사치 사이의 프로베니우스 노름 거리를 최소화한다:
여기서 는 서로 다른 단계 수 에 대한 트로터 진화 상태들 간의 중첩을 나타내는 그램 행렬이며, 는 (근사적인) 정확한 상태와의 중첩을 측정한다. qiskit_addon_mpf이 튜토리얼에서는 텐서 네트워크 기법, 특히 의 TeNPy-based 백엔드를 사용하여 이러한 수치를 효율적으로 계산합니다.
MPF는 언제 사용해야 할까요?
MPF는 다음과 같은 경우에 가장 큰 이점을 제공합니다:
- 회로 깊이가 병목 지점입니다. 하드웨어 노이즈로 인해 실행 가능한 깊이가 제한되는 경우, MPF를 사용하여 더 얕은 회로에서도 더 높은 유효 트로터 정확도를 달성하십시오.
- 완전한 상태 준비가 아니라 정확한 기대값이 필요합니다. MPF는 기대값 수준에서 작동합니다. 즉, 양자 상태가 아닌 고전적인 수치를 결합합니다. 따라서 Estimator 프리미티브를 사용할 때 관측 가능한 추정값을 구하는 데 이상적입니다.
- 트로터 스텝 횟수를 적당히 조합합니다. 일반적으로 – 의 서로 다른 단계 수 를 조합하면, 를 관리 가능한 수준으로 유지하면서 몇 가지 주요 트로터 오차 항을 상쇄하는 데 충분합니다.
MPF가 도움이 되지 않을 수 있는 경우
- 진화 시간이 매우 짧습니다. 가 충분히 작아서 단일 저차 트로터 공식만으로도 이미 정확도가 보장되는 경우, 여러 회로를 실행하는 데 드는 부하가 불필요해집니다.
- 상태 준비 작업. MPF는 보정된 양자 상태가 아니라 보정된 기대값 을 산출합니다. 실제 시간 경과에 따른 상태(예를 들어, 다른 양자 서브루틴의 입력으로 사용하기 위한 경우)가 필요한 경우에는 MPF를 적용할 수 없습니다.
- 수렴 조건을 위반하는 트로터 걸음 수. 정적 계수 도출 과정에서는 각 개별 를 의 급수로 전개하는데, 이 전개는 일 때만 잘 수렴한다. 주어진 에 비해 를 너무 작게 선택하면, 가장 얕은 회로가 섭동 영역을 훨씬 벗어나게 되며, MPF가 상쇄하지 못한 고차 오차 항이 커지게 되고, 상쇄를 위해 큰 계수가 필요할 수 있다. -노름 은 실용적인 진단 기준이 됩니다. 즉, 일 때, 샘플링 오버헤드 가 Trotter 오차 감소 효과를 상쇄할 수 있습니다. 자세한 내용은 트로터 계단 선택 가이드를 참조하세요.
이 튜토리얼에서 다루는 내용
이 튜토리얼에서는 MPF 워크플로우의 전 과정을 두 단계에 걸쳐 단계별로 안내합니다. 먼저, 소규모 시뮬레이터 예제 (10-큐비트 하이젠베르크 사슬)를 통해 문제를 설정하고, 정적 및 동적 MPF 계수를 계산하며, 그 결과로 얻은 기대값을 정확한 대각화 결과와 비교하는 방법을 보여줍니다. 이어서, 대규모 하드웨어 예제 (50-큐비트 XXZ 체인)를 통해 트랜스파일링 방법, 오류 완화 기능을 갖춘 IBM Quantum 하드웨어에서 실행하는 방법, 그리고 MPF 계수를 사용하여 결과를 후처리하는 방법을 보여줍니다. 이 문서 전반에 걸쳐 표준 Qiskit 도구와 함께 이 qiskit_addon_mpf 패키지를 사용합니다.
요구사항
이 튜토리얼을 시작하기 전에 다음이 설치되어 있는지 확인하세요:
- Qiskit SDK v2.0 또는 그 이후 버전, 시각화 기능 지원
- Qiskit Runtime v0.22 이상 (
pip install qiskit-ibm-runtime) - Qiskit Aer 시뮬레이터 (
pip install qiskit-aer) - TeNPy 백엔드를 사용하는 MPF Qiskit 애드온 (
pip install "qiskit-addon-mpf[tenpy]") - Qiskit 애드온 유틸리티 (
pip install qiskit-addon-utils) - SciPy (
pip install scipy)
설정
아래에서는 이 튜토리얼 전반에 걸쳐 사용된 모든 패키지 임포트 문들을 하나의 셀에 모아 놓았습니다. XXPlusYYGate또한 인접한 rxx 및 ryy 회전을 하나의 로 병합하는 트랜스파일러 패스를 CollectAndCollapse 정의합니다. 이 처리는 1단계에서 회로를 구성할 때(게이트 수를 적게 유지하기 위해)뿐만 아니라, 4단계에서 동적 MPF를 위한 계층적 구조를 추출할 때도 간접적으로 적용됩니다( TeNPy 는 융합되지 않은 회전 쌍이 아닌 2-큐비트 게이트를 기대하기 때문입니다).
import warnings
import numpy as np
import matplotlib.pyplot as plt
from functools import partial
from copy import deepcopy
from qiskit import QuantumCircuit
from qiskit.quantum_info import Pauli, SparsePauliOp, Statevector
from qiskit.synthesis import SuzukiTrotter
from qiskit.transpiler import CouplingMap, PassManager
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.circuit.library import XXPlusYYGate
from qiskit.transpiler.passes.optimization.collect_and_collapse import (
CollectAndCollapse,
collect_using_filter_function,
collapse_to_operation,
)
from qiskit_aer import AerSimulator
from qiskit_ibm_runtime import EstimatorV2 as Estimator, QiskitRuntimeService
from qiskit_addon_utils.problem_generators import (
generate_xyz_hamiltonian,
generate_time_evolution_circuit,
)
from qiskit_addon_utils.slicing import slice_by_depth
from qiskit_addon_mpf.static import setup_static_lse
from qiskit_addon_mpf.dynamic import setup_dynamic_lse
from qiskit_addon_mpf.costs import (
setup_exact_problem,
setup_sum_of_squares_problem,
setup_frobenius_problem,
)
from qiskit_addon_mpf.backends.tenpy_layers import (
LayerModel,
LayerwiseEvolver,
)
from qiskit_addon_mpf.backends.tenpy_tebd import MPOState, MPS_neel_state
from scipy.linalg import expm
# Suppress TeNPy's `unit_cell_width` future-API warning. The default
# (`unit_cell_width=len(sites)`) is correct for Chain lattices, which is what
# `CouplingMap.from_line(...)` produces here, so the warning is informational.
warnings.filterwarnings(
"ignore",
message=r".*unit_cell_width.*",
category=UserWarning,
)
# --- Helper: collect XX + YY rotations into a single gate ---
def filter_function(node):
return node.op.name in {"rxx", "ryy"}
collect_function = partial(
collect_using_filter_function,
filter_function=filter_function,
split_blocks=True,
min_block_size=1,
)
def collapse_to_xx_plus_yy(block):
param = 0.0
for node in block.data:
param += node.operation.params[0]
return XXPlusYYGate(param)
collapse_function = partial(
collapse_to_operation,
collapse_function=collapse_to_xx_plus_yy,
)
pm = PassManager()
pm.append(CollectAndCollapse(collect_function, collapse_function))소규모 시뮬레이터 예시
1단계: 고전적 입력을 양자 문제에 매핑하기
먼저, 네엘 상태 를 초기 상태로 삼아, 직선 상의 10-큐비트 하이젠베르크 모델을 고려합니다. 해밀토니안은 다음과 같습니다:
여기서 는 최인접 이웃 결합 강도이다. 우리는 체인의 중간에 위치한 한 쌍의 큐비트에 대해 ZZ 상관함수 를 측정하고, 2차 곱 공식 을 적용한 트로터 단계를 사용합니다.
L = 10
# Generate coupling map and Hamiltonian
coupling_map = CouplingMap.from_line(L, bidirectional=False)
hamiltonian = generate_xyz_hamiltonian(
coupling_map,
coupling_constants=(1.0, 1.0, 1.0),
ext_magnetic_field=(0.0, 0.0, 0.0),
)
print(hamiltonian)Output:
SparsePauliOp(['IIIIIIIXXI', 'IIIIIIIYYI', 'IIIIIIIZZI', 'IIIIIXXIII', 'IIIIIYYIII', 'IIIIIZZIII', 'IIIXXIIIII', 'IIIYYIIIII', 'IIIZZIIIII', 'IXXIIIIIII', 'IYYIIIIIII', 'IZZIIIIIII', 'IIIIIIIIXX', 'IIIIIIIIYY', 'IIIIIIIIZZ', 'IIIIIIXXII', 'IIIIIIYYII', 'IIIIIIZZII', 'IIIIXXIIII', 'IIIIYYIIII', 'IIIIZZIIII', 'IIXXIIIIII', 'IIYYIIIIII', 'IIZZIIIIII', 'XXIIIIIIII', 'YYIIIIIIII', 'ZZIIIIIIII'],
coeffs=[1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,
1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,
1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j])
# Observable: ZZ on the middle pair of qubits
observable = SparsePauliOp.from_sparse_list(
[("ZZ", (L // 2 - 1, L // 2), 1.0)], num_qubits=L
)
print(observable)Output:
SparsePauliOp(['IIIIZZIIII'],
coeffs=[1.+0.j])
# MPF parameters
mpf_trotter_steps = [1, 2, 4]
order = 2
symmetric = False
trotter_times = np.arange(0.5, 1.55, 0.1)
exact_evolution_times = np.arange(trotter_times[0], 1.55, 0.05)트로터 회로 만들기
우리는 각 시간점과 각 트로터 단계 수에 대해 근사 트로터 시간 진화를 구현하는 회로를 생성합니다. ‘설정’ 섹션에서 정의된 이 CollectAndCollapse 패스는 XX 및 YY 회전을 단일 XX+YY 게이트로 통합하여, 이후 더 효율적인 텐서 네트워크 시뮬레이션을 준비합니다.
# Initial Neel state preparation
initial_state_circ = QuantumCircuit(L)
initial_state_circ.x([i for i in range(L) if i % 2 != 0])
all_circs = []
for total_time in trotter_times:
mpf_trotter_circs = [
generate_time_evolution_circuit(
hamiltonian,
time=total_time,
synthesis=SuzukiTrotter(reps=num_steps, order=order),
)
for num_steps in mpf_trotter_steps
]
mpf_trotter_circs = pm.run(
mpf_trotter_circs
) # Collect XX and YY into XX + YY
mpf_circuits = [
initial_state_circ.compose(circuit) for circuit in mpf_trotter_circs
]
all_circs.append(mpf_circuits)mpf_circuits[-1].draw("mpl", fold=-1)Output:
2단계: 양자 하드웨어 실행을 위한 문제 최적화
소규모 예제로는 Aer 시뮬레이터를 다룰 것입니다. 회로가 실행 준비가 되기 전에 두 가지 변환이 이루어집니다:
-
해밀토니안 시뮬레이션 단계에서의 게이트 수집.
XXPlusYYGate‘Setup’ 셀에서는 인접한rxx및ryy회전을 하나의 로 통합하는 패스를CollectAndCollapse구축했습니다. 우리는 1단계(호출pm.run(...))에서 트로터 회로를 구축할 때 이미 이 단계를 적용했습니다. 이를 통해 2-큐비트 게이트의 수를 줄일 수 있을 뿐만 아니라, 이후 동적 계수 계산을 위한 텐서 네트워크 시뮬레이션에 더 적합한 구조를 얻을 수 있다. -
시뮬레이터의 ISA로 전환합니다. 아래에서는 Qiskit 프리셋 패스 매니저를 실행하여
optimization_level=3각 Trotter 회로를 시뮬레이터의 명령어 집합 아키텍처(ISA) 수준으로 변환합니다.
aer_sim = AerSimulator()
pm_sim = generate_preset_pass_manager(backend=aer_sim, optimization_level=3)
isa_circs_all_times = [
pm_sim.run([deepcopy(c) for c in mpf_circuits])
for mpf_circuits in all_circs
]3단계: Qiskit primitives 명령어로 실행합니다
소규모 예제의 경우, ISA로 변환된 트로터 회로를 Aer가 지원하는 프리미티브를 EstimatorV2 통해 실행합니다. 이렇게 하면 각 쌍에 대해 잡음이 없는 기준값을 얻을 수 있습니다. 이 값들이 바로 MPF가 4단계에서 결합할 값들입니다. 나중에 각 개별 제품 공식과 MPF의 전체 시계열 곡선을 그래프로 나타낼 수 있도록 진화 시점을 훑어봅니다.
estimator = Estimator(mode=aer_sim)
mpf_expvals_all_times, mpf_stds_all_times = [], []
for isa_circuits in isa_circs_all_times:
result = estimator.run(
[(circuit, observable) for circuit in isa_circuits], precision=0.005
).result()
mpf_expvals_all_times.append([res.data.evs for res in result])
mpf_stds_all_times.append([res.data.stds for res in result])4단계: 후처리 수행 및 원하는 클래식 형식으로 결과 반환
4단계에서는 MPF가 실제로 구축됩니다. 비록 여기서는 계수 * 가* 계산되지만(동적 변형의 경우 이 계산이 상당한 연산 부하를 유발할 수 있음), 개념적으로 이는 3단계의 양자 측정 결과를 하나의 보정된 기대값으로 결합하는 고전적인 방법이기 때문에, 우리는 전체 계수 및 결합 작업 흐름을 후처리 단계로 간주합니다.
MPF가 실제 동역학을 얼마나 잘 재현하는지 평가하기 위해, 먼저 해밀토니안을 직접 지수 함수화하여 시간 경과에 따른 정확한 기대값을 계산한다. 이 문제가 해결 가능한 이유는 이기 때문이며, 아래의 대규모 하드웨어 예시에서는 대신 텐서 네트워크 추정값에 의존해야 할 것입니다.
exact_expvals = []
for t in exact_evolution_times:
exp_H = expm(-1j * t * hamiltonian.to_matrix())
initial_state = Statevector(initial_state_circ).data
time_evolved_state = exp_H @ initial_state
exact_obs = (
time_evolved_state.conj()
@ observable.to_matrix()
@ time_evolved_state
).real
exact_expvals.append(exact_obs)정적 MPF 계수
정적 MPF는 진화 시간, 해밀토니안 및 초기 상태와 무관한 계수 를 사용합니다. 배경에서 설명한 선형 방정식 시스템 을 세우고, 계수를 구합니다. 행렬 은 트로터 단계 수 , 곱 공식의 차수 , 그리고 공식이 대칭인지 여부(이는 지수 를 결정함)에 의해 결정된다.
이 소규모 예제에서는 비대칭 차수( )의 스즈키-트로터 공식(따라서 및 , 결과적으로 )을 적용한 를 사용합니다. 이에 따라 시스템은 다음과 같이 표현됩니다:
첫 번째 행은 편향 없음( )을 보장하며, 두 번째와 세 번째 행은 각각 선행 트로터 오차 항 과 차순위 트로터 오차 항 을 상쇄합니다.
LSE 설정하기
우리는 에서 qiskit_addon_mpf.static 를 사용하여 setup_static_lse 앞서 설명한 행렬 과 우변 벡터 를 구성합니다. 행렬 은 뿐만 아니라, 곱의 공식 선택 — 특히 그 차수 와 대칭 여부 — 에 따라서도 달라집니다. 이 symmetric 플래그는 지수 패턴 을 제어합니다(대칭 식은 짝수 차수의 트로터 오차 항만을 생성합니다; 참고문헌 [1] 참조). 참고 문헌 [2] 에 제시된 바와 같이, 기본 PF가 대칭적일 때조차 를 설정하는 symmetric=True 것이 엄밀히 말해 반드시 필요한 것은 아니라는 점에 유의해야 한다. 비대칭 LSE는 여전히 유효하며(단, 불필요한 추가 제약 조건을 부과할 뿐이다).
이 예제에서는 1단계에서 이미 와 symmetric = False 를 설정해 order = 2 두었습니다.
lse = setup_static_lse(mpf_trotter_steps, order=order, symmetric=symmetric)구성된 행렬 과 벡터 을 검토하여, 이들이 위에 기술된 시스템과 일치하는지 확인하십시오.
lse.AOutput:
array([[1. , 1. , 1. ],
[1. , 0.25 , 0.0625 ],
[1. , 0.125 , 0.015625]])
lse.bOutput:
array([1., 0., 0.])
LSE를 바탕으로, ( 의 직접 해법)을 통해 lse.solve() 정적 계수 를 구합니다.
mpf_coeffs = lse.solve()
print(
f"The static coefficients associated with the ansatze are: {mpf_coeffs}"
)Output:
The static coefficients associated with the ansatze are: [ 0.04761905 -0.57142857 1.52380952]
정확한 모델을 사용하여 에 최적화하십시오
을 계산하는 대신, setup_exact_model을 사용하여 LSE를 제약 조건으로 삼고, 그 최적 해가 을 산출하는 cvxpy.Problem 인스턴스를 생성할 수 있습니다.
model_exact, coeffs_exact = setup_exact_problem(lse)
model_exact.solve()
print(coeffs_exact.value)Output:
[ 0.04761905 -0.57142857 1.52380952]
print(
"L1 norm of the exact coefficients:",
np.linalg.norm(coeffs_exact.value, ord=1),
)Output:
L1 norm of the exact coefficients: 2.1428571428556378
에 대한 근사 모델을 사용한 최적화
선택된 값 집합에 대한 규범이 너무 높다고 판단될 수도 있습니다. 만약 그런 상황이고 다른 값을 선택할 수 없다면, 을 최소화하면서 -노름을 선택한 임계값으로 제한하는 근사 해법을 사용할 수 있습니다. ‘근사 모델 사용 방법’ 가이드를 확인해 보세요.
model_approx, coeffs_approx = setup_sum_of_squares_problem(
lse, max_l1_norm=1.5
)
model_approx.solve()
print(coeffs_approx.value)
print(
"L1 norm of the approximate coefficients:",
np.linalg.norm(coeffs_approx.value, ord=1),
)Output:
[-1.10294118e-03 -2.48897059e-01 1.25000000e+00]
L1 norm of the approximate coefficients: 1.5
동적 MPF 계수
정적 MPF는 해밀토니안 및 상태에 구애받지 않는 방식으로 트로터 오차 항을 상쇄하므로, 주어진 해밀토니안과 초기 상태에 대해 반드시 가능한 한 가장 작은 근사 오차를 산출하는 것은 아니다. qiskit_addon_mpf반면, 동적 MPF(참고문헌 [2], [3] )는 각 시간 에서 프로베니우스 노름 거리 를 최소화하는 시간 의존적 계수 를 구합니다. ‘배경’ 절에서 설명한 바와 같이, 이를 위해서는 트로터 진화 상태들 간의 중첩 행렬 과 정확한 상태와의 중첩 이 필요하며, 이 두 가지 모두 본 논문에서 텐서 네트워크( TeNPy ) 백엔드를 사용하여 추정합니다.
동적 LSE를 설정하려면 다음 세 가지 요소가 필요합니다:
- 이 애드온이 각 에 대해 실행하여 를 MPS/MPO 형식으로 생성하는 근사적 에볼버 팩토리입니다.
slice_by_depth우리는 순서- 트로터 회로의 층별 구조(각 층당 하나씩)를 바탕으로 이를 구축하며, 이를 TeNPy 의 절단 매개변수를 갖는 형태로LayerwiseEvolver감싸서 구성합니다. - 고정밀도 기준 를 생성하는 정확한 진화기 팩토리입니다. 우리는 정확한 진화를 대리하기 위해 작은 시간 단계의 4차 스즈키-트로터 회로(
dt=0.1,order=4)를 사용합니다. - TeNPy 시뮬레이션에 초기값을 제공하는 아이덴티티 팩토리와 초기 상태 MPS입니다.
아래 코드는 근사적 진화 알고리즘 생성기를 구현합니다.
# Create approximate time-evolution circuits
single_2nd_order_circ = generate_time_evolution_circuit(
hamiltonian, time=1.0, synthesis=SuzukiTrotter(reps=1, order=order)
)
single_2nd_order_circ = pm.run(single_2nd_order_circ) # collect XX and YY
# Find layers in the circuit
layers = slice_by_depth(single_2nd_order_circ, max_slice_depth=1)
# Create tensor network models
models = [
LayerModel.from_quantum_circuit(layer, conserve="Sz") for layer in layers
]
# Create the time-evolution object
approx_factory = partial(
LayerwiseEvolver,
layers=models,
options={
"preserve_norm": False,
"trunc_params": {
"chi_max": 64,
"svd_min": 1e-8,
"trunc_cut": None,
},
"max_delta_t": 2,
},
)텐서 네트워크 시뮬레이션의 세부 사항을 결정하는 LayerwiseEvolver 의 옵션은 잘못 정의된 최적화 문제를 설정하지 않도록 신중하게 선택해야 합니다.
dt=0.1우리는 작은 시간 간격을 사용하여 4차 스즈키-트로터 공식을 통해 정확한 시간 진화 상태를 근사화한다. TeNPy 의 트런케이션 매개변수는 정확도에 영향을 미칠 수 있으므로, 다양한 값을 시도해 보는 것이 중요합니다.
single_4th_order_circ = generate_time_evolution_circuit(
hamiltonian, time=1.0, synthesis=SuzukiTrotter(reps=1, order=4)
)
single_4th_order_circ = pm.run(single_4th_order_circ)
exact_model_layers = [
LayerModel.from_quantum_circuit(layer, conserve="Sz")
for layer in slice_by_depth(single_4th_order_circ, max_slice_depth=1)
]
exact_factory = partial(
LayerwiseEvolver,
layers=exact_model_layers,
dt=0.1,
options={
"preserve_norm": False,
"trunc_params": {
"chi_max": 64,
"svd_min": 1e-8,
"trunc_cut": None,
},
"max_delta_t": 2,
},
)마지막으로, 초기 MPO 상태를 산출하는 를 identity_factory 정의하고, 층상 트로터 모델에서 사용하는 격자와 일치하는 MPS로서 네엘 초기 상태를 준비한다.
def identity_factory():
return MPOState.initialize_from_lattice(models[0].lat, conserve=True)
mps_initial_state = MPS_neel_state(models[0].lat)공장이 설정되었으므로, 이제 각 진화 시점에서 동적 계수를 계산합니다. 각 에 대해, setup_dynamic_lse TeNPy, 를 통해 관련 중첩 행렬을 구축하고, setup_frobenius_problem 프로베니우스 노름 비용을 최소화하는 를 cvxpy.Problem 반환합니다. mpf_dynamic_coeffs_list해법기는 해당 시간에 맞춰 조정된 계수 를 반환하며, 우리는 이를 에 수집합니다. 주어진 에 대해 솔버가 실패할 경우, 계수를 0으로 설정하여 루프가 계속 진행되도록 합니다.
mpf_dynamic_coeffs_list = []
for t in trotter_times:
print(f"Computing dynamic coefficients for time={t}")
lse = setup_dynamic_lse(
mpf_trotter_steps,
t,
identity_factory,
exact_factory,
approx_factory,
mps_initial_state,
)
problem, coeffs = setup_frobenius_problem(lse)
try:
problem.solve()
mpf_dynamic_coeffs_list.append(coeffs.value)
except Exception as error:
mpf_dynamic_coeffs_list.append(np.zeros(len(mpf_trotter_steps)))
print(error, "Calculation Failed for time", t)
print("")Output:
Computing dynamic coefficients for time=0.5
Computing dynamic coefficients for time=0.6
Computing dynamic coefficients for time=0.7
Computing dynamic coefficients for time=0.7999999999999999
Computing dynamic coefficients for time=0.8999999999999999
Computing dynamic coefficients for time=0.9999999999999999
Computing dynamic coefficients for time=1.0999999999999999
Computing dynamic coefficients for time=1.1999999999999997
Computing dynamic coefficients for time=1.2999999999999998
Computing dynamic coefficients for time=1.4
Computing dynamic coefficients for time=1.4999999999999998
트로터의 기대값과 MPF 계수를 결합한다
이제 각 계수 집합(정적-정확, 정적-근사, 동적)에 대해 를 계산하고, 회로별 표준 오차를 전파한 뒤, 그 결과로 얻은 시계열을 정확한 대각화 곡선과 비교하여 그래프로 표시합니다.
sym = {1: "^", 2: "s", 4: "p"}
# Get expectation values at all times for each Trotter step
for k, step in enumerate(mpf_trotter_steps):
trotter_curve, trotter_curve_error = [], []
for trotter_expvals, trotter_stds in zip(
mpf_expvals_all_times, mpf_stds_all_times
):
trotter_curve.append(trotter_expvals[k])
trotter_curve_error.append(trotter_stds[k])
plt.errorbar(
trotter_times,
trotter_curve,
yerr=trotter_curve_error,
alpha=0.5,
markersize=4,
marker=sym[step],
color="grey",
label=f"{mpf_trotter_steps[k]} Trotter steps",
)
# Get expectation values at all times for the static MPF with exact coeffs
exact_mpf_curve, exact_mpf_curve_error = [], []
for trotter_expvals, trotter_stds in zip(
mpf_expvals_all_times, mpf_stds_all_times
):
mpf_std = np.sqrt(
sum(
[
(coeff**2) * (std**2)
for coeff, std in zip(coeffs_exact.value, trotter_stds)
]
)
)
exact_mpf_curve_error.append(mpf_std)
exact_mpf_curve.append(trotter_expvals @ coeffs_exact.value)
plt.errorbar(
trotter_times,
exact_mpf_curve,
yerr=exact_mpf_curve_error,
markersize=4,
marker="o",
label="Static MPF - Exact",
color="purple",
)
# Get expectation values at all times for the static MPF with approximate coeffs
approx_mpf_curve, approx_mpf_curve_error = [], []
for trotter_expvals, trotter_stds in zip(
mpf_expvals_all_times, mpf_stds_all_times
):
mpf_std = np.sqrt(
sum(
[
(coeff**2) * (std**2)
for coeff, std in zip(coeffs_approx.value, trotter_stds)
]
)
)
approx_mpf_curve_error.append(mpf_std)
approx_mpf_curve.append(trotter_expvals @ coeffs_approx.value)
plt.errorbar(
trotter_times,
approx_mpf_curve,
yerr=approx_mpf_curve_error,
markersize=4,
marker="o",
label="Static MPF - Approx",
color="orange",
)
# Get expectation values at all times for the dynamic MPF
dynamic_mpf_curve, dynamic_mpf_curve_error = [], []
for trotter_expvals, trotter_stds, dynamic_coeffs in zip(
mpf_expvals_all_times, mpf_stds_all_times, mpf_dynamic_coeffs_list
):
mpf_std = np.sqrt(
sum(
[
(coeff**2) * (std**2)
for coeff, std in zip(dynamic_coeffs, trotter_stds)
]
)
)
dynamic_mpf_curve_error.append(mpf_std)
dynamic_mpf_curve.append(trotter_expvals @ dynamic_coeffs)
plt.errorbar(
trotter_times,
dynamic_mpf_curve,
yerr=dynamic_mpf_curve_error,
markersize=4,
marker="o",
label="Dynamic MPF",
color="pink",
)
# Exact expectation values
plt.plot(
exact_evolution_times,
exact_expvals,
color="red",
linestyle="--",
label="Exact time-evolution",
)
plt.title(f"$\\langle Z_{{{L//2-1}}} Z_{{{L//2}}} \\rangle$ vs time")
plt.xlabel("Time")
plt.ylabel("Expectation Value")
plt.legend(loc="upper center", bbox_to_anchor=(0.5, -0.2), ncol=2)
plt.grid(alpha=0.1)
plt.tight_layout()
plt.show()Output:
위의 그래프는 트로터 오차와 표본 오차 간의 상호작용을 보여줍니다.
-
트로터 오류. 시간이 지남에 따라 개별 제품의 공식(회색 마커)은 정확한 곡선에서 점점 더 멀어집니다. 회로는 편차가 가장 크고 가장 얕지만, 이미 인 영역에 속하므로, 주요 오차 항인 의 값이 큽니다. MPF 조합(색상 표시기)은 이러한 선행 트로터 오차 항 중 몇 가지를 상쇄하므로, 어떤 단일 회로보다 훨씬 더 정확하게 곡선을 따라갑니다. 남아 있는 오차는 MPF가 상쇄 하지 못하는 고차 트로터 항을 반영합니다. 즉, , 정적 MPF는 오차의 첫 두 차수만 제거할 뿐이며, 값이 커지면 상쇄되지 않은 잔여 오차가 결국 지배적이게 됩니다. 따라서 MPF는 매우 얕은 회로가 임의의 시점에서 정확성을 유지한다고 보장하지 않습니다.
-
표본 오차. MPF 곡선에서 오차 막대가 더 넓게 나타나는 것은 선형 조합의 직접적인 결과입니다. 회로별 독립 표준 오차 를 합산하면 총 분산 이 됩니다. 따라서 가 클수록(실제로는 우리가 제어하는 가 클수록), 주어진 목표 불확실도에 도달하기 위해 더 많은 측정 횟수가 필요합니다. 이것이 ‘Background’의 근사 해법기 옵션에 숨겨진 절충점입니다. 즉, 이 오버헤드를 관리 가능한 수준으로 유지하기 위해 에 상한을 설정합니다. 중요한 점은, 트로터 오차와 달리 표본 오차는 에 비례하여 줄어들기 때문에, 더 많은 샷을 사용함으로써 항상 이를 줄일 수 있다는 것이다.
아래의 대규모 하드웨어 예시에서, 하드웨어 노이즈는 각 에 추가적인 오차 원인으로 유입되며, 이 역시 MPF 계수에 의해 증폭됩니다. 해당 섹션에서는 오류 완화 기법이 MPF와 어떻게 상호작용하는지 살펴보겠습니다.
대규모 하드웨어 예시
이 절에서는 문제를 시뮬레이션으로 정확히 재현할 수 있는 범위를 넘어 확장해 보겠습니다. 우리는 시간 에서 50-큐비트 XXZ 사슬을 사용하여 문헌 [3] 에 제시된 결과 중 일부를 재현합니다. 소규모 예제와 동일한 4단계 워크플로를 따르되, 이번에는 오류 완화 기능이 적용된 실제 양자 하드웨어를 대상으로 합니다. 템플릿에서와 마찬가지로, 각 단계는 코드 내에 직접 표시되어 있으며, 중간 결과를 살펴볼 필요가 있는 경우 하나의 단계가 여러 셀에 걸쳐 표시될 수 있습니다.
이 매핑 과정은 소규모 예시와 유사합니다. 즉, 해밀토니안을 정의하고, 트로터 매개변수를 선택하며, MPF 계수(정적 및 동적)를 계산한 뒤, 회로를 구성합니다. 주요 차이점은 다음과 같습니다:
- (참고문헌 [3] )에서 추출한 무작위 결합을 갖는 50개 사이트의 XXZ 해밀토니안.
- 인 대칭 2차 트로터 공식 (따라서 ,
symmetric=True). - 단일 고정 진화 시간 . 를 대입하면 가 되며, 이를 통해 얕은 구성 요소들이 MPF가 의존하는 선행 오차 모델이 유효한 트로터 수렴 영역 내에 유지된다.
- 기준으로 삼기 위해, ‘ ’ 트로터 단계를 사용한 단일 회로 비교 실험을 추가로 수행하였다. 우리가 를 선택한 이유는, 이 회로의 하드웨어 상 2-큐비트 처리 깊이가 가장 깊은 MPF 구성 요소( )에 다중 MPF 회로 실행에 따른 오버헤드를 더한 값보다 더 깊기 때문입니다. 이 깊이는 노이즈 제한 영역에 해당할 만큼 충분히 깊으며, 이 영역에서 MPF 조합이 단일 회로 기준선보다 우수한 성능을 보일 것으로 예상됩니다. 이는 MPF 조합에 대한 “단일 심층 회로” 비교일 뿐, MPF의 유효 트로터 오차를 대상으로 하는 회로는 아닙니다(후자의 경우 훨씬 더 많은 단계가 필요하기 때문입니다).
여기서는 아직 1단계(매핑 및 회로 구성)에 있지만, 이 셀에서는 정적 계수뿐만 아니라 동적 계수도 미리 계산한다는 점에 유의하십시오. 동적 계수는 및 에 따라 달라지지만 양자 측정 결과에는 영향을 받지 않으므로, 4단계 이전의 어느 시점에서나 계산할 수 있습니다. MPF와 관련된 모든 설정을 한곳에 모아두기 위해 지금 이렇게 하고 있습니다.
# -------------------------Step 1-------------------------
L = 50
coupling_map = CouplingMap.from_line(L, bidirectional=False)
# XXZ Hamiltonian with random couplings (Ref. [3])
np.random.seed(0)
even_edges = list(coupling_map.get_edges())[::2]
odd_edges = list(coupling_map.get_edges())[1::2]
Js = np.random.uniform(0.5, 1.5, size=L)
hamiltonian = SparsePauliOp(Pauli("I" * L))
for i, edge in enumerate(even_edges + odd_edges):
hamiltonian += SparsePauliOp.from_sparse_list(
[
("XX", (edge), 2 * Js[i]),
("YY", (edge), 2 * Js[i]),
("ZZ", (edge), 4 * Js[i]),
],
num_qubits=L,
)
observable = SparsePauliOp.from_sparse_list(
[("ZZ", (L // 2 - 1, L // 2), 1.0)], num_qubits=L
)
total_time = 3
mpf_trotter_steps = [3, 4, 6]
order = 2
symmetric = True
# Static coefficients
lse = setup_static_lse(mpf_trotter_steps, order=order, symmetric=symmetric)
mpf_coeffs = lse.solve()
print(f"Static coefficients: {mpf_coeffs}")
print(f"L1 norm: {np.linalg.norm(mpf_coeffs, ord=1)}")
model_approx, coeffs_approx = setup_sum_of_squares_problem(
lse, max_l1_norm=2.0
)
model_approx.solve()
print(f"Approximate coefficients: {coeffs_approx.value}")
print(f"L1 norm (approx): {np.linalg.norm(coeffs_approx.value, ord=1)}")
# -------------------------Dynamic coefficients-------------------------
single_2nd_order_circ = generate_time_evolution_circuit(
hamiltonian, time=1.0, synthesis=SuzukiTrotter(reps=1, order=order)
)
single_2nd_order_circ = pm.run(single_2nd_order_circ)
layers = slice_by_depth(single_2nd_order_circ, max_slice_depth=1)
models = [
LayerModel.from_quantum_circuit(layer, conserve="Sz") for layer in layers
]
approx_factory = partial(
LayerwiseEvolver,
layers=models,
options={
"preserve_norm": False,
"trunc_params": {"chi_max": 64, "svd_min": 1e-8, "trunc_cut": None},
"max_delta_t": 4,
},
)
single_4th_order_circ = generate_time_evolution_circuit(
hamiltonian, time=1.0, synthesis=SuzukiTrotter(reps=1, order=4)
)
single_4th_order_circ = pm.run(single_4th_order_circ)
exact_model_layers = [
LayerModel.from_quantum_circuit(layer, conserve="Sz")
for layer in slice_by_depth(single_4th_order_circ, max_slice_depth=1)
]
exact_factory = partial(
LayerwiseEvolver,
layers=exact_model_layers,
dt=0.1,
options={
"preserve_norm": False,
"trunc_params": {"chi_max": 64, "svd_min": 1e-8, "trunc_cut": None},
"max_delta_t": 3,
},
)
def identity_factory():
return MPOState.initialize_from_lattice(models[0].lat, conserve=True)
mps_initial_state = MPS_neel_state(models[0].lat)
print(f"Computing dynamic coefficients for time={total_time}")
lse_dyn = setup_dynamic_lse(
mpf_trotter_steps,
total_time,
identity_factory,
exact_factory,
approx_factory,
mps_initial_state,
)
problem, coeffs_dyn = setup_frobenius_problem(lse_dyn)
try:
problem.solve()
mpf_dynamic_coeffs = coeffs_dyn.value
except Exception as error:
mpf_dynamic_coeffs = np.zeros(len(mpf_trotter_steps))
print(error, "Calculation Failed")
# -------------------------Step 1 (cont): Build circuits-------------------------
mpf_circuits = []
for k in mpf_trotter_steps:
circuit = QuantumCircuit(L)
circuit.x([i for i in range(L) if i % 2])
trotter_circ = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=k, order=order),
time=total_time,
)
circuit.compose(trotter_circ, qubits=range(L), inplace=True)
mpf_circuits.append(circuit)
# Baseline "single deep circuit" comparison run with k=10 Trotter steps.
# Its two-qubit depth is deeper than the deepest MPF constituent (k_max=6) plus
# the overhead of running multiple circuits, pushing it into the noise-limited
# regime where MPF is expected to outperform. It does NOT target the MPF's effective
# Trotter error (which would require many more steps).
comp_circuit = QuantumCircuit(L)
comp_circuit.x([i for i in range(L) if i % 2])
trotter_circ = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=10, order=order),
time=total_time,
)
comp_circuit.compose(trotter_circ, qubits=range(L), inplace=True)
mpf_circuits.append(comp_circuit)Output:
Static coefficients: [ 0.42857143 -1.82857143 2.4 ]
L1 norm: 4.65714285714286
Approximate coefficients: [-0.4942491 0.40206845 1.09218065]
L1 norm (approx): 1.9884981979026675
Computing dynamic coefficients for time=3
이제 선택한 백엔드에 맞춰 회로를 최적화합니다. optimization_level=3우리는 Qiskit의 사전 설정 패스 매니저를 사용하며, 이 매니저는 적절한 물리적 큐비트 세트를 자동으로 선택하고 각 회로를 장치 토폴로지에 매핑합니다.
# -------------------------Step 2-------------------------
service = QiskitRuntimeService()
# backend = service.least_busy(operational=True, simulator=False, min_num_qubits=L)
backend = service.backend("ibm_fez")
print(backend)
transpiler = generate_preset_pass_manager(
optimization_level=3, backend=backend
)
transpiled_circuits = [transpiler.run(circ) for circ in mpf_circuits]
isa_observables = [
observable.apply_layout(circ.layout) for circ in transpiled_circuits
]Output:
<IBMBackend('ibm_fez')>
실제 하드웨어에서 더 복잡한 회로를 구동하려면 강력한 오류 완화 조치가 필요합니다. 본 연구에서는 동적 분리, 게이트 및 측정 트위링, 측정 오차 완화, 제로 노이즈 외삽법(ZNE)을 구현합니다. 여기서 사용하는 ZNE 잡음 계수(1, 1.2, 1.4)는 얕은 회로 시나리오에서보다 작다는 점에 유의해야 합니다. 이는 더 깊은 MPF 구성 요소들이 이미 잡음 임계치에 근접해 있으며, 잡음이 크게 증폭될 경우 ZNE 외삽이 신뢰할 수 있는 범위를 벗어나게 되기 때문입니다.
우리는 4개의 회로 전체( 의 MPF 구성 요소 3개와 의 기준선)를 하나의 Estimator 작업으로 제출합니다.
# -------------------------Step 3-------------------------
estimator = Estimator(mode=backend)
estimator.options.default_shots = 30000
# Error suppression/mitigation
estimator.options.dynamical_decoupling.enable = True
estimator.options.twirling.enable_gates = True
estimator.options.twirling.enable_measure = True
estimator.options.twirling.num_randomizations = "auto"
estimator.options.twirling.strategy = "active-accum"
estimator.options.resilience.measure_mitigation = True
estimator.options.experimental.execution_path = "gen3-turbo"
estimator.options.resilience.zne_mitigation = True
estimator.options.resilience.zne.noise_factors = (1, 1.2, 1.4)
estimator.options.resilience.zne.extrapolator = "linear"
estimator.options.environment.job_tags = ["TUT_MPF"]
job_50 = estimator.run(
[
(circ, observable)
for circ, observable in zip(transpiled_circuits, isa_observables)
]
)작업 결과에서 회로별 기대값과 표준편차를 추출한 다음, 소규모 예제와 정확히 동일한 방식으로 각 MPF 계수 집합과 결합합니다: , 전파된 분산은 입니다.
# -------------------------Step 4-------------------------
result = job_50.result()
evs = [res.data.evs for res in result]
std = [res.data.stds for res in result]
print(evs)
print(std)Output:
[array(-0.07916195), array(-0.04479681), array(-0.2560756), array(-0.06045848)]
[array(0.04605538), array(0.10056336), array(0.14426151), array(0.04059092)]
exact_mpf_std = np.sqrt(
sum([(coeff**2) * (std**2) for coeff, std in zip(mpf_coeffs, std[:3])])
)
print(
"Exact static MPF expectation value: ",
evs[:3] @ mpf_coeffs,
"+-",
exact_mpf_std,
)
approx_mpf_std = np.sqrt(
sum(
[
(coeff**2) * (std**2)
for coeff, std in zip(coeffs_approx.value, std[:3])
]
)
)
print(
"Approximate static MPF expectation value: ",
evs[:3] @ coeffs_approx.value,
"+-",
approx_mpf_std,
)
dynamic_mpf_std = np.sqrt(
sum(
[
(coeff**2) * (std**2)
for coeff, std in zip(mpf_dynamic_coeffs, std[:3])
]
)
)
print(
"Dynamic MPF expectation value: ",
evs[:3] @ mpf_dynamic_coeffs,
"+-",
dynamic_mpf_std,
)Output:
Exact static MPF expectation value: -0.5665938395816946 +- 0.3925273058119915
Approximate static MPF expectation value: -0.25856647611537903 +- 0.164249927266166
Dynamic MPF expectation value: -0.12667812062949296 +- 0.06059471006973169
sym = {3: "^", 4: "s", 6: "p"}
for k, step in enumerate(mpf_trotter_steps):
plt.errorbar(
k,
evs[k],
yerr=std[k],
alpha=0.5,
markersize=4,
marker=sym[step],
color="grey",
label=f"{mpf_trotter_steps[k]} Trotter steps",
)
plt.errorbar(
3,
evs[-1],
yerr=std[-1],
alpha=0.5,
markersize=8,
marker="x",
color="blue",
label="10 Trotter steps",
)
plt.errorbar(
4,
evs[:3] @ mpf_coeffs,
yerr=exact_mpf_std,
markersize=4,
marker="o",
color="purple",
label="Static MPF",
)
plt.errorbar(
5,
evs[:3] @ coeffs_approx.value,
yerr=approx_mpf_std,
markersize=4,
marker="o",
color="orange",
label="Approximate static MPF",
)
plt.errorbar(
6,
evs[:3] @ mpf_dynamic_coeffs,
yerr=dynamic_mpf_std,
markersize=4,
marker="o",
color="pink",
label="Dynamic MPF",
)
exact_obs = -0.24384471447172074 # Calculated via Tensor Network calculation
plt.axhline(
y=exact_obs, linestyle="--", color="red", label="Exact time-evolution"
)
plt.title(
f"$\\langle Z_{{{L//2-1}}} Z_{{{L//2}}} \\rangle$ at time {total_time} for the different methods"
)
plt.xlabel("Method")
plt.ylabel("Expectation Value")
plt.legend(loc="upper center", bbox_to_anchor=(0.5, -0.2), ncol=2)
plt.grid(alpha=0.1)
plt.tight_layout()
plt.show()Output:
위의 하드웨어 결과에 대해 몇 가지 관찰 사항을 말씀드리자면:
-
하드웨어 측면에서 더 깊이 파고드는 데는 비용이 듭니다. 단일 회로 기준선은 상황을 명확히 보여줍니다. 회로는 사실상 정확합니다( 대 기준값 ). 반면, 더 깊은 기준선은 더 나쁘며( , 의 오차), 더 나은 것은 아닙니다. 트로터 오차가 이미 작을 경우, 단계를 추가하면 주로 회로가 더 깊어지면서 게이트 노이즈와 비결합 현상이 더 많이 누적될 뿐이다. 이것이 바로 MPF가 설계된 목적입니다. 즉, 얕은 구성 요소만을 사용하여 깊은 회로 수준의 정확도를 달성하는 것입니다.
-
소노름 MPF가 딥 싱글 서킷보다 성능이 더 우수하다. 대략적인 정적 MPF( 로 상한 설정)는 에 도달하며, 이는 기준치보다 정도 낮은 수치로, 의 기준치보다는 훨씬 더 가까운 수준입니다. 동적 MPF( ) 역시 그 기준치를 여유 있게 상회합니다. 두 방법 모두 얕은 회로만을 결합하지만, 깊은 단일 회로로는 도출할 수 없었던 답을 찾아냅니다.
-
계수 노름은 수학적 최적성보다 더 중요합니다. 정확한 정적 MPF는 를 가지며, 모든 추정기 중 가장 성능이 떨어집니다( , 오차 범위가 보다 큽니다). 계수의 노름이 크기 때문에 각 에 대한 잔류 게이트 노이즈, 비고전성, ZNE 오차가 대략 동일한 배수로 증폭되어, 이를 통해 얻는 트로터 오차 상쇄 효과를 압도해 버립니다. 노름(대략적 정적 해법, )에 상한을 설정하면 이러한 과부하 현상이 해소되고 최상의 추정값을 얻을 수 있다. 비록 그 계수들이 더 이상 주요 트로터 오차를 정확히 상쇄하지는 못하더라도 말이다.
-
개별 얕은 코스도 여전히 경쟁력을 갖출 수 있다. 이곳에서 유일한 구성 요소( )는 그 자체로 본질적으로 정확한 값을 나타냅니다. 이번 실행에서는 근사 정적 MPF보다 심지어 아주 약간 더 가까운 값을 보여줍니다. 문제는 “수렴했으나 아직 잡음에 의해 제한되지는 않은” 최적의 지점에 어떤 가 위치하는지 미리 알 수 없다는 점이며, 트로터 수렴을 보장하기 위해 단순히 더 깊은 곳( )으로 가는 것처럼 안전해 보이는 선택이 바로 실패하는 선택이라는 것입니다. MPF는 적절한 깊이를 추측할 필요가 없는, 얕은 회로들의 원리에 입각한 조합을 제공합니다.
실무적으로 얻을 수 있는 교훈은, 하드웨어 환경에서 MPF를 사용할 때는 각 개별 에 대해 강력한 오차 완화 기법을 병행해야 하며, 계수 -노름은 적정 수준으로 유지해야 하고(근사 해법기 또는 동적 MPF 사용), 트로터 단계 는 가 성립하도록 선택해야 한다는 점입니다. 여기서 의 를 적용하면 가 되며, 이를 통해 정적 MPF가 의존하는 선행 오차 모델이 유효한 수렴 영역 내에 구성 요소들을 유지할 수 있습니다. 이러한 선택에 따라, 여기에서 소노름 MPF는 수렴된 단일 회로와 동등한 성능을 보이는 반면, 단순히 “깊이만 늘리는” 방식의 기준 모델은 그렇지 못하여, 문헌 [3] 에서 제시된 ‘깊이 대 정확도’의 이점을 재현해 냅니다. 또한 개별 실행 결과에는 잡음이 존재한다는 점에 유의해야 합니다. 동일한 작업을 다른 방식으로 제출하거나(또는 다른 백엔드에서 실행할 경우) 정확한 순위가 달라질 수 있습니다. 그러나 확고한 경향은 다음과 같습니다. small- 의 MPF는 좋은 성능을 보이며, large- 의 exact-static MPF는 하드웨어 잡음의 영향을 더 크게 받으며, over-deep 단일 회로는 잡음에 의해 성능이 제한됩니다.
다음 단계
이 글이 흥미로웠다면, 다음 자료도 참고해 보시기 바랍니다:
- MPF용 트로터 단계 선택 방법 — 불안정성을 방지하기 위한 ‘ ’ 값 선정에 관한 실용적인 지침
- 근사 모델 사용 방법 — 근사 정적 MPF에 대한 -노름 제약 조건 및 솔버 옵션 조정
qiskit-addon-mpfAPI 참조 — 정적, 동적 및 백엔드 모듈에 대한 전체 문서
참조
[1] 바스케스, A. C., Egger, D. J., Ochsner, D., & Woerner, S. 하드웨어 친화적인 해밀토니안 시뮬레이션을 위한 잘 조건화된 다중 산출식. Quantum, 제7권, 1067쪽 (2023)
[2] Zhuk, S., Robertson, N. F., & Bravyi, S. 해밀토니안 시뮬레이션을 위한 트로터 오차 상한 및 동적 다중 곱 공식. Physical Review Research, 6(3), 033309 (2024)
[3] 로버트슨, N. F., 등 텐서 네트워크를 활용한 동적 다중곱 공식. arXiv:2407.17405 (2024)